summaryrefslogtreecommitdiff
path: root/libavformat
diff options
context:
space:
mode:
Diffstat (limited to 'libavformat')
-rw-r--r--libavformat/4xm.c31
-rw-r--r--libavformat/Makefile167
-rw-r--r--libavformat/a64.c11
-rw-r--r--libavformat/aacdec.c26
-rw-r--r--libavformat/ac3dec.c77
-rw-r--r--libavformat/act.c207
-rw-r--r--libavformat/adp.c98
-rw-r--r--libavformat/adtsenc.c45
-rw-r--r--libavformat/adxdec.c16
-rw-r--r--libavformat/aea.c29
-rw-r--r--libavformat/afc.c79
-rw-r--r--libavformat/aiff.h14
-rw-r--r--libavformat/aiffdec.c109
-rw-r--r--libavformat/aiffenc.c198
-rw-r--r--libavformat/allformats.c111
-rw-r--r--libavformat/amr.c28
-rw-r--r--libavformat/anm.c10
-rw-r--r--libavformat/apc.c19
-rw-r--r--libavformat/ape.c52
-rw-r--r--libavformat/apetag.c100
-rw-r--r--libavformat/apetag.h12
-rw-r--r--libavformat/apngdec.c446
-rw-r--r--libavformat/aqtitledec.c148
-rw-r--r--libavformat/asf.c9
-rw-r--r--libavformat/asf.h19
-rw-r--r--libavformat/asfcrypt.c8
-rw-r--r--libavformat/asfcrypt.h8
-rw-r--r--libavformat/asfdec.c325
-rw-r--r--libavformat/asfenc.c246
-rw-r--r--libavformat/assdec.c261
-rw-r--r--libavformat/assenc.c195
-rw-r--r--libavformat/ast.c29
-rw-r--r--libavformat/ast.h30
-rw-r--r--libavformat/astdec.c122
-rw-r--r--libavformat/astenc.c214
-rw-r--r--libavformat/au.c103
-rw-r--r--libavformat/audiointerleave.c28
-rw-r--r--libavformat/audiointerleave.h8
-rw-r--r--libavformat/avc.c10
-rw-r--r--libavformat/avc.h8
-rw-r--r--libavformat/avformat.h771
-rw-r--r--libavformat/avformatres.rc55
-rw-r--r--libavformat/avi.h8
-rw-r--r--libavformat/avidec.c516
-rw-r--r--libavformat/avienc.c177
-rw-r--r--libavformat/avio.c120
-rw-r--r--libavformat/avio.h119
-rw-r--r--libavformat/avio_internal.h25
-rw-r--r--libavformat/aviobuf.c234
-rw-r--r--libavformat/avisynth.c48
-rw-r--r--libavformat/avlanguage.c8
-rw-r--r--libavformat/avlanguage.h8
-rw-r--r--libavformat/avr.c99
-rw-r--r--libavformat/avs.c13
-rw-r--r--libavformat/bethsoftvid.c17
-rw-r--r--libavformat/bfi.c18
-rw-r--r--libavformat/bink.c40
-rw-r--r--libavformat/bintext.c388
-rw-r--r--libavformat/bit.c156
-rw-r--r--libavformat/bluray.c235
-rw-r--r--libavformat/bmv.c8
-rw-r--r--libavformat/boadec.c79
-rw-r--r--libavformat/brstm.c297
-rw-r--r--libavformat/c93.c10
-rw-r--r--libavformat/cache.c322
-rw-r--r--libavformat/caf.c56
-rw-r--r--libavformat/caf.h8
-rw-r--r--libavformat/cafdec.c55
-rw-r--r--libavformat/cafenc.c286
-rw-r--r--libavformat/cavsvideodec.c10
-rw-r--r--libavformat/cdg.c22
-rw-r--r--libavformat/cdxl.c69
-rw-r--r--libavformat/cinedec.c325
-rw-r--r--libavformat/concat.c13
-rw-r--r--libavformat/concatdec.c652
-rw-r--r--libavformat/crcenc.c9
-rw-r--r--libavformat/crypto.c179
-rw-r--r--libavformat/cutils.c29
-rw-r--r--libavformat/dashenc.c18
-rw-r--r--libavformat/data_uri.c118
-rw-r--r--libavformat/dauddec.c12
-rw-r--r--libavformat/daudenc.c8
-rw-r--r--libavformat/dfa.c25
-rw-r--r--libavformat/diracdec.c23
-rw-r--r--libavformat/dnxhddec.c10
-rw-r--r--libavformat/dsfdec.c159
-rw-r--r--libavformat/dsicin.c13
-rw-r--r--libavformat/dtsdec.c92
-rw-r--r--libavformat/dtshddec.c139
-rw-r--r--libavformat/dump.c62
-rw-r--r--libavformat/dv.c185
-rw-r--r--libavformat/dv.h10
-rw-r--r--libavformat/dvbsub.c68
-rw-r--r--libavformat/dvenc.c121
-rw-r--r--libavformat/dxa.c38
-rw-r--r--libavformat/eacdata.c17
-rw-r--r--libavformat/electronicarts.c115
-rw-r--r--libavformat/epafdec.c104
-rw-r--r--libavformat/ffm.h60
-rw-r--r--libavformat/ffmdec.c723
-rw-r--r--libavformat/ffmenc.c372
-rw-r--r--libavformat/ffmeta.h8
-rw-r--r--libavformat/ffmetadec.c26
-rw-r--r--libavformat/ffmetaenc.c8
-rw-r--r--libavformat/file.c70
-rw-r--r--libavformat/filmstripdec.c20
-rw-r--r--libavformat/filmstripenc.c10
-rw-r--r--libavformat/flac_picture.c33
-rw-r--r--libavformat/flac_picture.h11
-rw-r--r--libavformat/flacdec.c86
-rw-r--r--libavformat/flacenc.c39
-rw-r--r--libavformat/flacenc.h8
-rw-r--r--libavformat/flacenc_header.c8
-rw-r--r--libavformat/flic.c27
-rw-r--r--libavformat/flv.h32
-rw-r--r--libavformat/flvdec.c356
-rw-r--r--libavformat/flvenc.c182
-rw-r--r--libavformat/format.c202
-rw-r--r--libavformat/framecrcenc.c53
-rw-r--r--libavformat/framehash.c11
-rw-r--r--libavformat/frmdec.c110
-rw-r--r--libavformat/ftp.c801
-rw-r--r--libavformat/g722.c11
-rw-r--r--libavformat/g723_1.c10
-rw-r--r--libavformat/g729dec.c103
-rw-r--r--libavformat/gif.c418
-rw-r--r--libavformat/gifdec.c334
-rw-r--r--libavformat/golomb_tab.c1
-rw-r--r--libavformat/gopher.c8
-rw-r--r--libavformat/gsmdec.c13
-rw-r--r--libavformat/gxf.c85
-rw-r--r--libavformat/gxf.h8
-rw-r--r--libavformat/gxfenc.c184
-rw-r--r--libavformat/h261dec.c45
-rw-r--r--libavformat/h263dec.c31
-rw-r--r--libavformat/h264dec.c8
-rw-r--r--libavformat/hdsenc.c42
-rw-r--r--libavformat/hevc.c10
-rw-r--r--libavformat/hevc.h10
-rw-r--r--libavformat/hevcdec.c8
-rw-r--r--libavformat/hls.c1518
-rw-r--r--libavformat/hlsenc.c343
-rw-r--r--libavformat/hlsproto.c14
-rw-r--r--libavformat/hnm.c12
-rw-r--r--libavformat/http.c256
-rw-r--r--libavformat/http.h14
-rw-r--r--libavformat/httpauth.c13
-rw-r--r--libavformat/httpauth.h8
-rw-r--r--libavformat/icecast.c53
-rw-r--r--libavformat/icodec.c182
-rw-r--r--libavformat/icoenc.c203
-rw-r--r--libavformat/id3v1.c26
-rw-r--r--libavformat/id3v1.h8
-rw-r--r--libavformat/id3v2.c463
-rw-r--r--libavformat/id3v2.h36
-rw-r--r--libavformat/id3v2enc.c157
-rw-r--r--libavformat/idcin.c56
-rw-r--r--libavformat/idroqdec.c15
-rw-r--r--libavformat/idroqenc.c38
-rw-r--r--libavformat/iff.c597
-rw-r--r--libavformat/ilbc.c8
-rw-r--r--libavformat/img2.c15
-rw-r--r--libavformat/img2.h63
-rw-r--r--libavformat/img2_alias_pix.c73
-rw-r--r--libavformat/img2_brender_pix.c57
-rw-r--r--libavformat/img2dec.c598
-rw-r--r--libavformat/img2enc.c139
-rw-r--r--libavformat/ingenientdec.c21
-rw-r--r--libavformat/internal.h97
-rw-r--r--libavformat/ipmovie.c52
-rw-r--r--libavformat/ircam.c47
-rw-r--r--libavformat/ircam.h30
-rw-r--r--libavformat/ircamdec.c115
-rw-r--r--libavformat/ircamenc.c62
-rw-r--r--libavformat/isom.c143
-rw-r--r--libavformat/isom.h57
-rw-r--r--libavformat/iss.c28
-rw-r--r--libavformat/iv8.c8
-rw-r--r--libavformat/ivfdec.c8
-rw-r--r--libavformat/ivfenc.c13
-rw-r--r--libavformat/jacosubdec.c274
-rw-r--r--libavformat/jacosubenc.c42
-rw-r--r--libavformat/jvdec.c22
-rw-r--r--libavformat/latmenc.c91
-rw-r--r--libavformat/libavformat.v16
-rw-r--r--libavformat/libgme.c201
-rw-r--r--libavformat/libmodplug.c382
-rw-r--r--libavformat/libnut.c324
-rw-r--r--libavformat/libquvi.c150
-rw-r--r--libavformat/librtmp.c25
-rw-r--r--libavformat/libsmbclient.c212
-rw-r--r--libavformat/libssh.c318
-rw-r--r--libavformat/lmlm4.c14
-rw-r--r--libavformat/loasdec.c94
-rw-r--r--libavformat/lrc.c34
-rw-r--r--libavformat/lrc.h29
-rw-r--r--libavformat/lrcdec.c248
-rw-r--r--libavformat/lrcenc.c152
-rw-r--r--libavformat/lvfdec.c152
-rw-r--r--libavformat/lxfdec.c20
-rw-r--r--libavformat/m4vdec.c22
-rw-r--r--libavformat/matroska.c57
-rw-r--r--libavformat/matroska.h56
-rw-r--r--libavformat/matroskadec.c1335
-rw-r--r--libavformat/matroskaenc.c836
-rw-r--r--libavformat/md5enc.c87
-rw-r--r--libavformat/md5proto.c8
-rw-r--r--libavformat/metadata.c10
-rw-r--r--libavformat/metadata.h8
-rw-r--r--libavformat/mgsts.c106
-rw-r--r--libavformat/microdvddec.c203
-rw-r--r--libavformat/microdvdenc.c67
-rw-r--r--libavformat/mkvtimestamp_v2.c50
-rw-r--r--libavformat/mlvdec.c458
-rw-r--r--libavformat/mm.c9
-rw-r--r--libavformat/mmf.c92
-rw-r--r--libavformat/mms.c8
-rw-r--r--libavformat/mms.h8
-rw-r--r--libavformat/mmsh.c77
-rw-r--r--libavformat/mmst.c33
-rw-r--r--libavformat/mov.c1281
-rw-r--r--libavformat/mov_chan.c13
-rw-r--r--libavformat/mov_chan.h8
-rw-r--r--libavformat/movenc.c1570
-rw-r--r--libavformat/movenc.h29
-rw-r--r--libavformat/movenchint.c23
-rw-r--r--libavformat/mp3dec.c259
-rw-r--r--libavformat/mp3enc.c148
-rw-r--r--libavformat/mpc.c19
-rw-r--r--libavformat/mpc8.c48
-rw-r--r--libavformat/mpeg.c614
-rw-r--r--libavformat/mpeg.h11
-rw-r--r--libavformat/mpegenc.c96
-rw-r--r--libavformat/mpegts.c863
-rw-r--r--libavformat/mpegts.h25
-rw-r--r--libavformat/mpegtsenc.c469
-rw-r--r--libavformat/mpegvideodec.c55
-rw-r--r--libavformat/mpjpeg.c41
-rw-r--r--libavformat/mpl2dec.c146
-rw-r--r--libavformat/mpsubdec.c144
-rw-r--r--libavformat/msnwc_tcp.c14
-rw-r--r--libavformat/mtv.c58
-rw-r--r--libavformat/mux.c558
-rw-r--r--libavformat/mvdec.c34
-rw-r--r--libavformat/mvi.c12
-rw-r--r--libavformat/mxf.c14
-rw-r--r--libavformat/mxf.h22
-rw-r--r--libavformat/mxfdec.c1132
-rw-r--r--libavformat/mxfenc.c749
-rw-r--r--libavformat/mxg.c24
-rw-r--r--libavformat/ncdec.c10
-rw-r--r--libavformat/network.c62
-rw-r--r--libavformat/network.h33
-rw-r--r--libavformat/nistspheredec.c131
-rw-r--r--libavformat/noproxy-test.c8
-rw-r--r--libavformat/nsvdec.c67
-rw-r--r--libavformat/nullenc.c10
-rw-r--r--libavformat/nut.c196
-rw-r--r--libavformat/nut.h16
-rw-r--r--libavformat/nutdec.c270
-rw-r--r--libavformat/nutenc.c493
-rw-r--r--libavformat/nuv.c101
-rw-r--r--libavformat/oggdec.c396
-rw-r--r--libavformat/oggdec.h8
-rw-r--r--libavformat/oggenc.c79
-rw-r--r--libavformat/oggparsecelt.c27
-rw-r--r--libavformat/oggparsedirac.c10
-rw-r--r--libavformat/oggparseflac.c38
-rw-r--r--libavformat/oggparseogm.c28
-rw-r--r--libavformat/oggparseopus.c100
-rw-r--r--libavformat/oggparseskeleton.c25
-rw-r--r--libavformat/oggparsespeex.c10
-rw-r--r--libavformat/oggparsetheora.c40
-rw-r--r--libavformat/oggparsevorbis.c113
-rw-r--r--libavformat/oggparsevp8.c8
-rw-r--r--libavformat/oma.c8
-rw-r--r--libavformat/oma.h8
-rw-r--r--libavformat/omadec.c32
-rw-r--r--libavformat/omaenc.c13
-rw-r--r--libavformat/options.c22
-rw-r--r--libavformat/options_table.h43
-rw-r--r--libavformat/os_support.c15
-rw-r--r--libavformat/os_support.h31
-rw-r--r--libavformat/paf.c21
-rw-r--r--libavformat/pcm.c26
-rw-r--r--libavformat/pcm.h9
-rw-r--r--libavformat/pcmdec.c68
-rw-r--r--libavformat/pcmenc.c8
-rw-r--r--libavformat/pjsdec.c138
-rw-r--r--libavformat/pmpdec.c92
-rw-r--r--libavformat/psxstr.c71
-rw-r--r--libavformat/pva.c44
-rw-r--r--libavformat/pvfdec.c75
-rw-r--r--libavformat/qcp.c20
-rw-r--r--libavformat/qtpalette.h8
-rw-r--r--libavformat/r3d.c19
-rw-r--r--libavformat/rawdec.c183
-rw-r--r--libavformat/rawdec.h42
-rw-r--r--libavformat/rawenc.c85
-rw-r--r--libavformat/rawenc.h8
-rw-r--r--libavformat/rawvideodec.c40
-rw-r--r--libavformat/rdt.c36
-rw-r--r--libavformat/rdt.h8
-rw-r--r--libavformat/realtextdec.c156
-rw-r--r--libavformat/redspark.c170
-rw-r--r--libavformat/replaygain.c8
-rw-r--r--libavformat/replaygain.h8
-rw-r--r--libavformat/riff.c49
-rw-r--r--libavformat/riff.h44
-rw-r--r--libavformat/riffdec.c104
-rw-r--r--libavformat/riffenc.c97
-rw-r--r--libavformat/rl2.c27
-rw-r--r--libavformat/rm.c8
-rw-r--r--libavformat/rm.h10
-rw-r--r--libavformat/rmdec.c282
-rw-r--r--libavformat/rmenc.c17
-rw-r--r--libavformat/rmsipr.c8
-rw-r--r--libavformat/rmsipr.h8
-rw-r--r--libavformat/rpl.c39
-rw-r--r--libavformat/rsd.c168
-rw-r--r--libavformat/rso.c8
-rw-r--r--libavformat/rso.h8
-rw-r--r--libavformat/rsodec.c29
-rw-r--r--libavformat/rsoenc.c8
-rw-r--r--libavformat/rtmp.h8
-rw-r--r--libavformat/rtmpcrypt.c8
-rw-r--r--libavformat/rtmpcrypt.h8
-rw-r--r--libavformat/rtmpdh.c8
-rw-r--r--libavformat/rtmpdh.h8
-rw-r--r--libavformat/rtmphttp.c8
-rw-r--r--libavformat/rtmppkt.c9
-rw-r--r--libavformat/rtmppkt.h8
-rw-r--r--libavformat/rtmpproto.c93
-rw-r--r--libavformat/rtp.c11
-rw-r--r--libavformat/rtp.h8
-rw-r--r--libavformat/rtpdec.c34
-rw-r--r--libavformat/rtpdec.h12
-rw-r--r--libavformat/rtpdec_ac3.c157
-rw-r--r--libavformat/rtpdec_amr.c8
-rw-r--r--libavformat/rtpdec_asf.c23
-rw-r--r--libavformat/rtpdec_formats.h10
-rw-r--r--libavformat/rtpdec_g726.c8
-rw-r--r--libavformat/rtpdec_h261.c8
-rw-r--r--libavformat/rtpdec_h263.c8
-rw-r--r--libavformat/rtpdec_h263_rfc2190.c8
-rw-r--r--libavformat/rtpdec_h264.c10
-rw-r--r--libavformat/rtpdec_hevc.c117
-rw-r--r--libavformat/rtpdec_ilbc.c8
-rw-r--r--libavformat/rtpdec_jpeg.c31
-rw-r--r--libavformat/rtpdec_latm.c17
-rw-r--r--libavformat/rtpdec_mpeg12.c212
-rw-r--r--libavformat/rtpdec_mpeg4.c22
-rw-r--r--libavformat/rtpdec_mpegts.c20
-rw-r--r--libavformat/rtpdec_qcelp.c8
-rw-r--r--libavformat/rtpdec_qdm2.c18
-rw-r--r--libavformat/rtpdec_qt.c10
-rw-r--r--libavformat/rtpdec_svq3.c13
-rw-r--r--libavformat/rtpdec_vp8.c8
-rw-r--r--libavformat/rtpdec_xiph.c26
-rw-r--r--libavformat/rtpenc.c25
-rw-r--r--libavformat/rtpenc.h12
-rw-r--r--libavformat/rtpenc_aac.c8
-rw-r--r--libavformat/rtpenc_amr.c8
-rw-r--r--libavformat/rtpenc_chain.c14
-rw-r--r--libavformat/rtpenc_chain.h8
-rw-r--r--libavformat/rtpenc_h261.c12
-rw-r--r--libavformat/rtpenc_h263.c12
-rw-r--r--libavformat/rtpenc_h263_rfc2190.c23
-rw-r--r--libavformat/rtpenc_h264.c8
-rw-r--r--libavformat/rtpenc_hevc.c8
-rw-r--r--libavformat/rtpenc_jpeg.c8
-rw-r--r--libavformat/rtpenc_latm.c8
-rw-r--r--libavformat/rtpenc_mpegts.c8
-rw-r--r--libavformat/rtpenc_mpv.c8
-rw-r--r--libavformat/rtpenc_vp8.c8
-rw-r--r--libavformat/rtpenc_xiph.c11
-rw-r--r--libavformat/rtpproto.c66
-rw-r--r--libavformat/rtpproto.h8
-rw-r--r--libavformat/rtsp.c132
-rw-r--r--libavformat/rtsp.h23
-rw-r--r--libavformat/rtspcodes.h125
-rw-r--r--libavformat/rtspdec.c43
-rw-r--r--libavformat/rtspenc.c17
-rw-r--r--libavformat/samidec.c140
-rw-r--r--libavformat/sapdec.c13
-rw-r--r--libavformat/sapenc.c17
-rw-r--r--libavformat/sauce.c10
-rw-r--r--libavformat/sauce.h8
-rw-r--r--libavformat/sbgdec.c1512
-rw-r--r--libavformat/sctp.c16
-rw-r--r--libavformat/sdp.c28
-rw-r--r--libavformat/sdr2.c121
-rw-r--r--libavformat/seek-test.c43
-rw-r--r--libavformat/segafilm.c87
-rw-r--r--libavformat/segment.c871
-rw-r--r--libavformat/sierravmd.c91
-rw-r--r--libavformat/siff.c29
-rw-r--r--libavformat/smacker.c67
-rw-r--r--libavformat/smjpeg.c8
-rw-r--r--libavformat/smjpeg.h8
-rw-r--r--libavformat/smjpegdec.c14
-rw-r--r--libavformat/smjpegenc.c10
-rw-r--r--libavformat/smoothstreamingenc.c34
-rw-r--r--libavformat/smush.c18
-rw-r--r--libavformat/sol.c15
-rw-r--r--libavformat/sox.h8
-rw-r--r--libavformat/soxdec.c38
-rw-r--r--libavformat/soxenc.c25
-rw-r--r--libavformat/spdif.c8
-rw-r--r--libavformat/spdif.h11
-rw-r--r--libavformat/spdifdec.c35
-rw-r--r--libavformat/spdifenc.c30
-rw-r--r--libavformat/srtdec.c168
-rw-r--r--libavformat/srtenc.c112
-rw-r--r--libavformat/srtp.c8
-rw-r--r--libavformat/srtp.h8
-rw-r--r--libavformat/srtpproto.c8
-rw-r--r--libavformat/stldec.c141
-rw-r--r--libavformat/subfile.c147
-rw-r--r--libavformat/subtitles.c398
-rw-r--r--libavformat/subtitles.h210
-rw-r--r--libavformat/subviewer1dec.c124
-rw-r--r--libavformat/subviewerdec.c194
-rw-r--r--libavformat/supdec.c109
-rw-r--r--libavformat/swf.c8
-rw-r--r--libavformat/swf.h105
-rw-r--r--libavformat/swfdec.c361
-rw-r--r--libavformat/swfenc.c26
-rw-r--r--libavformat/takdec.c50
-rw-r--r--libavformat/tcp.c42
-rw-r--r--libavformat/tedcaptionsdec.c366
-rw-r--r--libavformat/tee.c495
-rw-r--r--libavformat/thp.c55
-rw-r--r--libavformat/tiertexseq.c10
-rw-r--r--libavformat/tls.c63
-rw-r--r--libavformat/tmv.c10
-rw-r--r--libavformat/tta.c102
-rw-r--r--libavformat/tty.c43
-rw-r--r--libavformat/txd.c33
-rw-r--r--libavformat/udp.c384
-rw-r--r--libavformat/uncodedframecrcenc.c172
-rw-r--r--libavformat/unix.c11
-rw-r--r--libavformat/url-test.c8
-rw-r--r--libavformat/url.c8
-rw-r--r--libavformat/url.h18
-rw-r--r--libavformat/urldecode.c8
-rw-r--r--libavformat/urldecode.h8
-rw-r--r--libavformat/utils.c2296
-rw-r--r--libavformat/vc1test.c15
-rw-r--r--libavformat/vc1testenc.c11
-rw-r--r--libavformat/version.h18
-rw-r--r--libavformat/vivo.c313
-rw-r--r--libavformat/voc.c8
-rw-r--r--libavformat/voc.h9
-rw-r--r--libavformat/vocdec.c56
-rw-r--r--libavformat/vocenc.c22
-rw-r--r--libavformat/vorbiscomment.c9
-rw-r--r--libavformat/vorbiscomment.h8
-rw-r--r--libavformat/vplayerdec.c128
-rw-r--r--libavformat/vqf.c29
-rw-r--r--libavformat/w64.c50
-rw-r--r--libavformat/w64.h31
-rw-r--r--libavformat/wavdec.c415
-rw-r--r--libavformat/wavenc.c476
-rw-r--r--libavformat/wc3movie.c21
-rw-r--r--libavformat/webmdashenc.c386
-rw-r--r--libavformat/webpenc.c169
-rw-r--r--libavformat/webvttdec.c223
-rw-r--r--libavformat/webvttenc.c100
-rw-r--r--libavformat/westwood_aud.c14
-rw-r--r--libavformat/westwood_vqa.c53
-rw-r--r--libavformat/wtv.h60
-rw-r--r--libavformat/wtv_common.c84
-rw-r--r--libavformat/wtvdec.c (renamed from libavformat/wtv.c)348
-rw-r--r--libavformat/wtvenc.c845
-rw-r--r--libavformat/wv.c8
-rw-r--r--libavformat/wv.h8
-rw-r--r--libavformat/wvdec.c22
-rw-r--r--libavformat/wvenc.c12
-rw-r--r--libavformat/xa.c11
-rw-r--r--libavformat/xmv.c198
-rw-r--r--libavformat/xwma.c16
-rw-r--r--libavformat/yop.c25
-rw-r--r--libavformat/yuv4mpeg.h8
-rw-r--r--libavformat/yuv4mpegdec.c83
-rw-r--r--libavformat/yuv4mpegenc.c156
486 files changed, 46655 insertions, 9218 deletions
diff --git a/libavformat/4xm.c b/libavformat/4xm.c
index 6253ffb58e..240e5a40ea 100644
--- a/libavformat/4xm.c
+++ b/libavformat/4xm.c
@@ -1,21 +1,21 @@
/*
* 4X Technologies .4xm File Demuxer (no muxer)
- * Copyright (c) 2003 The ffmpeg Project
+ * Copyright (c) 2003 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -134,8 +134,11 @@ static int parse_strk(AVFormatContext *s,
return AVERROR_INVALIDDATA;
track = AV_RL32(buf + 8);
- if (track < 0)
+ if ((unsigned)track >= UINT_MAX / sizeof(AudioTrack) - 1) {
+ av_log(s, AV_LOG_ERROR, "current_track too large\n");
return AVERROR_INVALIDDATA;
+ }
+
if (track + 1 > fourxm->track_count) {
if (av_reallocp_array(&fourxm->tracks, track + 1, sizeof(AudioTrack)))
return AVERROR(ENOMEM);
@@ -155,6 +158,11 @@ static int parse_strk(AVFormatContext *s,
av_log(s, AV_LOG_ERROR, "audio header invalid\n");
return AVERROR_INVALIDDATA;
}
+ if (!fourxm->tracks[track].adpcm && fourxm->tracks[track].bits<8) {
+ av_log(s, AV_LOG_ERROR, "bits unspecified for non ADPCM\n");
+ return AVERROR_INVALIDDATA;
+ }
+
/* allocate a new AVStream */
st = avformat_new_stream(s, NULL);
if (!st)
@@ -222,9 +230,14 @@ static int fourxm_read_header(AVFormatContext *s)
for (i = 0; i < header_size - 8; i++) {
fourcc_tag = AV_RL32(&header[i]);
size = AV_RL32(&header[i + 4]);
+ if (size > header_size - i - 8 && (fourcc_tag == vtrk_TAG || fourcc_tag == strk_TAG)) {
+ av_log(s, AV_LOG_ERROR, "chunk larger than array %d>%d\n", size, header_size - i - 8);
+ return AVERROR_INVALIDDATA;
+ }
if (fourcc_tag == std__TAG) {
if (header_size - i < 16) {
+ av_log(s, AV_LOG_ERROR, "std TAG truncated\n");
ret = AVERROR_INVALIDDATA;
goto fail;
}
@@ -280,7 +293,7 @@ static int fourxm_read_packet(AVFormatContext *s,
return ret;
fourcc_tag = AV_RL32(&header[0]);
size = AV_RL32(&header[4]);
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR(EIO);
switch (fourcc_tag) {
case LIST_TAG:
@@ -309,8 +322,10 @@ static int fourxm_read_packet(AVFormatContext *s,
if (ret < 0) {
av_free_packet(pkt);
- } else
+ } else {
packet_read = 1;
+ av_shrink_packet(pkt, ret + 8);
+ }
break;
case snd__TAG:
diff --git a/libavformat/Makefile b/libavformat/Makefile
index ea91a6cfac..073a42dd45 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -1,3 +1,5 @@
+include $(SUBDIR)../config.mak
+
NAME = avformat
HEADERS = avformat.h \
@@ -12,7 +14,6 @@ OBJS = allformats.o \
format.o \
id3v1.o \
id3v2.o \
- log2_tab.o \
metadata.o \
mux.o \
options.o \
@@ -28,6 +29,7 @@ OBJS-$(CONFIG_RIFFENC) += riffenc.o
OBJS-$(CONFIG_RTPDEC) += rdt.o \
rtp.o \
rtpdec.o \
+ rtpdec_ac3.o \
rtpdec_amr.o \
rtpdec_asf.o \
rtpdec_g726.o \
@@ -50,47 +52,69 @@ OBJS-$(CONFIG_RTPDEC) += rdt.o \
rtpdec_xiph.o \
srtp.o
OBJS-$(CONFIG_RTPENC_CHAIN) += rtpenc_chain.o rtp.o
+OBJS-$(CONFIG_SHARED) += log2_tab.o golomb_tab.o
# muxers/demuxers
OBJS-$(CONFIG_A64_MUXER) += a64.o rawenc.o
-OBJS-$(CONFIG_AAC_DEMUXER) += aacdec.o rawdec.o
+OBJS-$(CONFIG_AAC_DEMUXER) += aacdec.o apetag.o img2.o rawdec.o
OBJS-$(CONFIG_AC3_DEMUXER) += ac3dec.o rawdec.o
OBJS-$(CONFIG_AC3_MUXER) += rawenc.o
+OBJS-$(CONFIG_ACT_DEMUXER) += act.o
+OBJS-$(CONFIG_ADF_DEMUXER) += bintext.o sauce.o
+OBJS-$(CONFIG_ADP_DEMUXER) += adp.o
OBJS-$(CONFIG_ADX_DEMUXER) += adxdec.o
OBJS-$(CONFIG_ADX_MUXER) += rawenc.o
-OBJS-$(CONFIG_ADTS_MUXER) += adtsenc.o
+OBJS-$(CONFIG_ADTS_MUXER) += adtsenc.o apetag.o img2.o \
+ id3v2enc.o
OBJS-$(CONFIG_AEA_DEMUXER) += aea.o pcm.o
-OBJS-$(CONFIG_AIFF_DEMUXER) += aiffdec.o pcm.o
-OBJS-$(CONFIG_AIFF_MUXER) += aiffenc.o
+OBJS-$(CONFIG_AFC_DEMUXER) += afc.o
+OBJS-$(CONFIG_AIFF_DEMUXER) += aiffdec.o pcm.o isom.o \
+ mov_chan.o
+OBJS-$(CONFIG_AIFF_MUXER) += aiffenc.o isom.o id3v2enc.o
OBJS-$(CONFIG_AMR_DEMUXER) += amr.o
OBJS-$(CONFIG_AMR_MUXER) += amr.o
OBJS-$(CONFIG_ANM_DEMUXER) += anm.o
OBJS-$(CONFIG_APC_DEMUXER) += apc.o
OBJS-$(CONFIG_APE_DEMUXER) += ape.o apetag.o img2.o
+OBJS-$(CONFIG_APNG_DEMUXER) += apngdec.o
+OBJS-$(CONFIG_AQTITLE_DEMUXER) += aqtitledec.o subtitles.o
OBJS-$(CONFIG_ASF_DEMUXER) += asfdec.o asf.o asfcrypt.o \
avlanguage.o
OBJS-$(CONFIG_ASF_MUXER) += asfenc.o asf.o
-OBJS-$(CONFIG_ASS_DEMUXER) += assdec.o
+OBJS-$(CONFIG_ASS_DEMUXER) += assdec.o subtitles.o
OBJS-$(CONFIG_ASS_MUXER) += assenc.o
+OBJS-$(CONFIG_AST_DEMUXER) += ast.o astdec.o
+OBJS-$(CONFIG_AST_MUXER) += ast.o astenc.o
OBJS-$(CONFIG_AU_DEMUXER) += au.o pcm.o
OBJS-$(CONFIG_AU_MUXER) += au.o rawenc.o
OBJS-$(CONFIG_AVI_DEMUXER) += avidec.o
-OBJS-$(CONFIG_AVI_MUXER) += avienc.o
+OBJS-$(CONFIG_AVI_MUXER) += avienc.o mpegtsenc.o avlanguage.o
OBJS-$(CONFIG_AVISYNTH) += avisynth.o
OBJS-$(CONFIG_AVM2_MUXER) += swfenc.o swf.o
+OBJS-$(CONFIG_AVR_DEMUXER) += avr.o pcm.o
OBJS-$(CONFIG_AVS_DEMUXER) += avs.o vocdec.o voc.o
OBJS-$(CONFIG_BETHSOFTVID_DEMUXER) += bethsoftvid.o
OBJS-$(CONFIG_BFI_DEMUXER) += bfi.o
OBJS-$(CONFIG_BINK_DEMUXER) += bink.o
+OBJS-$(CONFIG_BINTEXT_DEMUXER) += bintext.o sauce.o
+OBJS-$(CONFIG_BIT_DEMUXER) += bit.o
+OBJS-$(CONFIG_BIT_MUXER) += bit.o
OBJS-$(CONFIG_BMV_DEMUXER) += bmv.o
+OBJS-$(CONFIG_BOA_DEMUXER) += boadec.o
+OBJS-$(CONFIG_BRSTM_DEMUXER) += brstm.o
OBJS-$(CONFIG_C93_DEMUXER) += c93.o vocdec.o voc.o
OBJS-$(CONFIG_CAF_DEMUXER) += cafdec.o caf.o mov.o mov_chan.o \
isom.o replaygain.o
+OBJS-$(CONFIG_CAF_MUXER) += cafenc.o caf.o riff.o isom.o
OBJS-$(CONFIG_CAVSVIDEO_DEMUXER) += cavsvideodec.o rawdec.o
OBJS-$(CONFIG_CAVSVIDEO_MUXER) += rawenc.o
OBJS-$(CONFIG_CDG_DEMUXER) += cdg.o
OBJS-$(CONFIG_CDXL_DEMUXER) += cdxl.o
+OBJS-$(CONFIG_CINE_DEMUXER) += cinedec.o
+OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o
OBJS-$(CONFIG_CRC_MUXER) += crcenc.o
+OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o
+OBJS-$(CONFIG_DATA_MUXER) += rawdec.o
OBJS-$(CONFIG_DASH_MUXER) += dashenc.o isom.o
OBJS-$(CONFIG_DAUD_DEMUXER) += dauddec.o
OBJS-$(CONFIG_DAUD_MUXER) += daudenc.o
@@ -99,16 +123,22 @@ OBJS-$(CONFIG_DIRAC_DEMUXER) += diracdec.o rawdec.o
OBJS-$(CONFIG_DIRAC_MUXER) += rawenc.o
OBJS-$(CONFIG_DNXHD_DEMUXER) += dnxhddec.o rawdec.o
OBJS-$(CONFIG_DNXHD_MUXER) += rawenc.o
+OBJS-$(CONFIG_DSF_DEMUXER) += dsfdec.o
OBJS-$(CONFIG_DSICIN_DEMUXER) += dsicin.o
+OBJS-$(CONFIG_DTSHD_DEMUXER) += dtshddec.o
OBJS-$(CONFIG_DTS_DEMUXER) += dtsdec.o rawdec.o
OBJS-$(CONFIG_DTS_MUXER) += rawenc.o
OBJS-$(CONFIG_DV_DEMUXER) += dv.o
OBJS-$(CONFIG_DV_MUXER) += dvenc.o
+OBJS-$(CONFIG_DVBSUB_DEMUXER) += dvbsub.o
OBJS-$(CONFIG_DXA_DEMUXER) += dxa.o
OBJS-$(CONFIG_EA_CDATA_DEMUXER) += eacdata.o
OBJS-$(CONFIG_EA_DEMUXER) += electronicarts.o
OBJS-$(CONFIG_EAC3_DEMUXER) += ac3dec.o rawdec.o
OBJS-$(CONFIG_EAC3_MUXER) += rawenc.o
+OBJS-$(CONFIG_EPAF_DEMUXER) += epafdec.o pcm.o
+OBJS-$(CONFIG_FFM_DEMUXER) += ffmdec.o
+OBJS-$(CONFIG_FFM_MUXER) += ffmenc.o
OBJS-$(CONFIG_FFMETADATA_DEMUXER) += ffmetadec.o
OBJS-$(CONFIG_FFMETADATA_MUXER) += ffmetaenc.o
OBJS-$(CONFIG_FILMSTRIP_DEMUXER) += filmstripdec.o
@@ -122,17 +152,22 @@ OBJS-$(CONFIG_FLAC_MUXER) += flacenc.o flacenc_header.o \
vorbiscomment.o
OBJS-$(CONFIG_FLIC_DEMUXER) += flic.o
OBJS-$(CONFIG_FLV_DEMUXER) += flvdec.o
+OBJS-$(CONFIG_LIVE_FLV_DEMUXER) += flvdec.o
OBJS-$(CONFIG_FLV_MUXER) += flvenc.o avc.o
OBJS-$(CONFIG_FOURXM_DEMUXER) += 4xm.o
OBJS-$(CONFIG_FRAMECRC_MUXER) += framecrcenc.o framehash.o
OBJS-$(CONFIG_FRAMEMD5_MUXER) += md5enc.o framehash.o
+OBJS-$(CONFIG_FRM_DEMUXER) += frmdec.o
OBJS-$(CONFIG_GIF_MUXER) += gif.o
+OBJS-$(CONFIG_GIF_DEMUXER) += gifdec.o
OBJS-$(CONFIG_GSM_DEMUXER) += gsmdec.o
OBJS-$(CONFIG_GXF_DEMUXER) += gxf.o
OBJS-$(CONFIG_GXF_MUXER) += gxfenc.o audiointerleave.o
OBJS-$(CONFIG_G722_DEMUXER) += g722.o rawdec.o
OBJS-$(CONFIG_G722_MUXER) += rawenc.o
OBJS-$(CONFIG_G723_1_DEMUXER) += g723_1.o
+OBJS-$(CONFIG_G723_1_MUXER) += rawenc.o
+OBJS-$(CONFIG_G729_DEMUXER) += g729dec.o
OBJS-$(CONFIG_H261_DEMUXER) += h261dec.o rawdec.o
OBJS-$(CONFIG_H261_MUXER) += rawenc.o
OBJS-$(CONFIG_H263_DEMUXER) += h263dec.o rawdec.o
@@ -145,7 +180,10 @@ OBJS-$(CONFIG_HEVC_MUXER) += rawenc.o
OBJS-$(CONFIG_HLS_DEMUXER) += hls.o
OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o
OBJS-$(CONFIG_HNM_DEMUXER) += hnm.o
+OBJS-$(CONFIG_ICO_DEMUXER) += icodec.o
+OBJS-$(CONFIG_ICO_MUXER) += icoenc.o
OBJS-$(CONFIG_IDCIN_DEMUXER) += idcin.o
+OBJS-$(CONFIG_IDF_DEMUXER) += bintext.o sauce.o
OBJS-$(CONFIG_IFF_DEMUXER) += iff.o
OBJS-$(CONFIG_ILBC_DEMUXER) += ilbc.o
OBJS-$(CONFIG_ILBC_MUXER) += ilbc.o
@@ -153,37 +191,64 @@ OBJS-$(CONFIG_IMAGE2_DEMUXER) += img2dec.o img2.o
OBJS-$(CONFIG_IMAGE2_MUXER) += img2enc.o img2.o
OBJS-$(CONFIG_IMAGE2PIPE_DEMUXER) += img2dec.o img2.o
OBJS-$(CONFIG_IMAGE2PIPE_MUXER) += img2enc.o img2.o
+OBJS-$(CONFIG_IMAGE2_ALIAS_PIX_DEMUXER) += img2_alias_pix.o
+OBJS-$(CONFIG_IMAGE2_BRENDER_PIX_DEMUXER) += img2_brender_pix.o
+OBJS-$(CONFIG_IMAGE_BMP_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_DPX_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_EXR_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_J2K_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_JPEG_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_JPEGLS_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_PICTOR_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_PNG_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_SGI_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_SUNRAST_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_TIFF_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_WEBP_PIPE_DEMUXER) += img2dec.o img2.o
OBJS-$(CONFIG_INGENIENT_DEMUXER) += ingenientdec.o rawdec.o
OBJS-$(CONFIG_IPMOVIE_DEMUXER) += ipmovie.o
+OBJS-$(CONFIG_IRCAM_DEMUXER) += ircamdec.o ircam.o pcm.o
+OBJS-$(CONFIG_IRCAM_MUXER) += ircamenc.o ircam.o rawenc.o
OBJS-$(CONFIG_ISS_DEMUXER) += iss.o
OBJS-$(CONFIG_IV8_DEMUXER) += iv8.o
OBJS-$(CONFIG_IVF_DEMUXER) += ivfdec.o
OBJS-$(CONFIG_IVF_MUXER) += ivfenc.o
+OBJS-$(CONFIG_JACOSUB_DEMUXER) += jacosubdec.o subtitles.o
+OBJS-$(CONFIG_JACOSUB_MUXER) += jacosubenc.o rawenc.o
OBJS-$(CONFIG_JV_DEMUXER) += jvdec.o
OBJS-$(CONFIG_LATM_DEMUXER) += rawdec.o
-OBJS-$(CONFIG_LATM_MUXER) += latmenc.o
+OBJS-$(CONFIG_LATM_MUXER) += latmenc.o rawenc.o
OBJS-$(CONFIG_LMLM4_DEMUXER) += lmlm4.o
+OBJS-$(CONFIG_LOAS_DEMUXER) += loasdec.o rawdec.o
+OBJS-$(CONFIG_LRC_DEMUXER) += lrcdec.o lrc.o subtitles.o
+OBJS-$(CONFIG_LRC_MUXER) += lrcenc.o lrc.o
+OBJS-$(CONFIG_LVF_DEMUXER) += lvfdec.o
OBJS-$(CONFIG_LXF_DEMUXER) += lxfdec.o
OBJS-$(CONFIG_M4V_DEMUXER) += m4vdec.o rawdec.o
OBJS-$(CONFIG_M4V_MUXER) += rawenc.o
OBJS-$(CONFIG_MATROSKA_DEMUXER) += matroskadec.o matroska.o \
- isom.o rmsipr.o \
+ isom.o rmsipr.o flac_picture.o \
oggparsevorbis.o vorbiscomment.o \
flac_picture.o replaygain.o
OBJS-$(CONFIG_MATROSKA_MUXER) += matroskaenc.o matroska.o \
isom.o avc.o hevc.o \
- flacenc_header.o avlanguage.o vorbiscomment.o wv.o
+ flacenc_header.o avlanguage.o vorbiscomment.o wv.o \
+ webmdashenc.o
OBJS-$(CONFIG_MD5_MUXER) += md5enc.o
+OBJS-$(CONFIG_MGSTS_DEMUXER) += mgsts.o
+OBJS-$(CONFIG_MICRODVD_DEMUXER) += microdvddec.o subtitles.o
+OBJS-$(CONFIG_MICRODVD_MUXER) += microdvdenc.o
OBJS-$(CONFIG_MJPEG_DEMUXER) += rawdec.o
OBJS-$(CONFIG_MJPEG_MUXER) += rawenc.o
OBJS-$(CONFIG_MLP_DEMUXER) += rawdec.o
OBJS-$(CONFIG_MLP_MUXER) += rawenc.o
+OBJS-$(CONFIG_MLV_DEMUXER) += mlvdec.o riffdec.o
OBJS-$(CONFIG_MM_DEMUXER) += mm.o
-OBJS-$(CONFIG_MMF_DEMUXER) += mmf.o pcm.o
-OBJS-$(CONFIG_MMF_MUXER) += mmf.o
+OBJS-$(CONFIG_MMF_DEMUXER) += mmf.o
+OBJS-$(CONFIG_MMF_MUXER) += mmf.o rawenc.o
OBJS-$(CONFIG_MOV_DEMUXER) += mov.o isom.o mov_chan.o replaygain.o
OBJS-$(CONFIG_MOV_MUXER) += movenc.o isom.o avc.o hevc.o \
- movenchint.o mov_chan.o
+ movenchint.o mov_chan.o rtp.o
OBJS-$(CONFIG_MP2_MUXER) += mp3enc.o rawenc.o id3v2enc.o
OBJS-$(CONFIG_MP3_DEMUXER) += mp3dec.o replaygain.o
OBJS-$(CONFIG_MP3_MUXER) += mp3enc.o rawenc.o id3v2enc.o
@@ -201,6 +266,8 @@ OBJS-$(CONFIG_MPEGTS_DEMUXER) += mpegts.o isom.o
OBJS-$(CONFIG_MPEGTS_MUXER) += mpegtsenc.o
OBJS-$(CONFIG_MPEGVIDEO_DEMUXER) += mpegvideodec.o rawdec.o
OBJS-$(CONFIG_MPJPEG_MUXER) += mpjpeg.o
+OBJS-$(CONFIG_MPL2_DEMUXER) += mpl2dec.o subtitles.o
+OBJS-$(CONFIG_MPSUB_DEMUXER) += mpsubdec.o subtitles.o
OBJS-$(CONFIG_MSNWC_TCP_DEMUXER) += msnwc_tcp.o
OBJS-$(CONFIG_MTV_DEMUXER) += mtv.o
OBJS-$(CONFIG_MV_DEMUXER) += mvdec.o
@@ -209,6 +276,7 @@ OBJS-$(CONFIG_MXF_DEMUXER) += mxfdec.o mxf.o
OBJS-$(CONFIG_MXF_MUXER) += mxfenc.o mxf.o audiointerleave.o
OBJS-$(CONFIG_MXG_DEMUXER) += mxg.o
OBJS-$(CONFIG_NC_DEMUXER) += ncdec.o
+OBJS-$(CONFIG_NISTSPHERE_DEMUXER) += nistspheredec.o pcm.o
OBJS-$(CONFIG_NSV_DEMUXER) += nsvdec.o
OBJS-$(CONFIG_NULL_MUXER) += nullenc.o
OBJS-$(CONFIG_NUT_DEMUXER) += nutdec.o nut.o
@@ -228,10 +296,14 @@ OBJS-$(CONFIG_OGG_DEMUXER) += oggdec.o \
replaygain.o \
vorbiscomment.o \
flac_picture.o
+OBJS-$(CONFIG_OGA_MUXER) += oggenc.o \
+ vorbiscomment.o
OBJS-$(CONFIG_OGG_MUXER) += oggenc.o \
vorbiscomment.o
OBJS-$(CONFIG_OMA_DEMUXER) += omadec.o pcm.o oma.o
OBJS-$(CONFIG_OMA_MUXER) += omaenc.o rawenc.o oma.o id3v2enc.o
+OBJS-$(CONFIG_OPUS_MUXER) += oggenc.o \
+ vorbiscomment.o
OBJS-$(CONFIG_PAF_DEMUXER) += paf.o
OBJS-$(CONFIG_PCM_ALAW_DEMUXER) += pcmdec.o pcm.o
OBJS-$(CONFIG_PCM_ALAW_MUXER) += pcmenc.o rawenc.o
@@ -273,17 +345,22 @@ OBJS-$(CONFIG_PCM_U32LE_DEMUXER) += pcmdec.o pcm.o
OBJS-$(CONFIG_PCM_U32LE_MUXER) += pcmenc.o rawenc.o
OBJS-$(CONFIG_PCM_U8_DEMUXER) += pcmdec.o pcm.o
OBJS-$(CONFIG_PCM_U8_MUXER) += pcmenc.o rawenc.o
+OBJS-$(CONFIG_PJS_DEMUXER) += pjsdec.o subtitles.o
OBJS-$(CONFIG_PMP_DEMUXER) += pmpdec.o
OBJS-$(CONFIG_PVA_DEMUXER) += pva.o
+OBJS-$(CONFIG_PVF_DEMUXER) += pvfdec.o pcm.o
OBJS-$(CONFIG_QCP_DEMUXER) += qcp.o
OBJS-$(CONFIG_R3D_DEMUXER) += r3d.o
OBJS-$(CONFIG_RAWVIDEO_DEMUXER) += rawvideodec.o
OBJS-$(CONFIG_RAWVIDEO_MUXER) += rawenc.o
+OBJS-$(CONFIG_REALTEXT_DEMUXER) += realtextdec.o subtitles.o
+OBJS-$(CONFIG_REDSPARK_DEMUXER) += redspark.o
OBJS-$(CONFIG_RL2_DEMUXER) += rl2.o
OBJS-$(CONFIG_RM_DEMUXER) += rmdec.o rm.o rmsipr.o
OBJS-$(CONFIG_RM_MUXER) += rmenc.o rm.o
OBJS-$(CONFIG_ROQ_DEMUXER) += idroqdec.o
OBJS-$(CONFIG_ROQ_MUXER) += idroqenc.o rawenc.o
+OBJS-$(CONFIG_RSD_DEMUXER) += rsd.o
OBJS-$(CONFIG_RSO_DEMUXER) += rsodec.o rso.o pcm.o
OBJS-$(CONFIG_RSO_MUXER) += rsoenc.o rso.o
OBJS-$(CONFIG_RPL_DEMUXER) += rpl.o
@@ -307,9 +384,12 @@ OBJS-$(CONFIG_RTSP_DEMUXER) += rtsp.o rtspdec.o httpauth.o \
urldecode.o
OBJS-$(CONFIG_RTSP_MUXER) += rtsp.o rtspenc.o httpauth.o \
urldecode.o
+OBJS-$(CONFIG_SAMI_DEMUXER) += samidec.o subtitles.o
OBJS-$(CONFIG_SAP_DEMUXER) += sapdec.o
OBJS-$(CONFIG_SAP_MUXER) += sapenc.o
+OBJS-$(CONFIG_SBG_DEMUXER) += sbgdec.o
OBJS-$(CONFIG_SDP_DEMUXER) += rtsp.o
+OBJS-$(CONFIG_SDR2_DEMUXER) += sdr2.o
OBJS-$(CONFIG_SEGAFILM_DEMUXER) += segafilm.o
OBJS-$(CONFIG_SEGMENT_MUXER) += segment.o
OBJS-$(CONFIG_SHORTEN_DEMUXER) += rawdec.o
@@ -321,45 +401,72 @@ OBJS-$(CONFIG_SMOOTHSTREAMING_MUXER) += smoothstreamingenc.o isom.o
OBJS-$(CONFIG_SMUSH_DEMUXER) += smush.o
OBJS-$(CONFIG_SOL_DEMUXER) += sol.o pcm.o
OBJS-$(CONFIG_SOX_DEMUXER) += soxdec.o pcm.o
-OBJS-$(CONFIG_SOX_MUXER) += soxenc.o
+OBJS-$(CONFIG_SOX_MUXER) += soxenc.o rawenc.o
OBJS-$(CONFIG_SPDIF_DEMUXER) += spdif.o spdifdec.o
OBJS-$(CONFIG_SPDIF_MUXER) += spdif.o spdifenc.o
-OBJS-$(CONFIG_SRT_DEMUXER) += srtdec.o
-OBJS-$(CONFIG_SRT_MUXER) += rawenc.o
+OBJS-$(CONFIG_SPEEX_MUXER) += oggenc.o \
+ vorbiscomment.o
+OBJS-$(CONFIG_SRT_DEMUXER) += srtdec.o subtitles.o
+OBJS-$(CONFIG_SRT_MUXER) += srtenc.o
+OBJS-$(CONFIG_STL_DEMUXER) += stldec.o subtitles.o
OBJS-$(CONFIG_STR_DEMUXER) += psxstr.o
+OBJS-$(CONFIG_SUBVIEWER1_DEMUXER) += subviewer1dec.o subtitles.o
+OBJS-$(CONFIG_SUBVIEWER_DEMUXER) += subviewerdec.o subtitles.o
+OBJS-$(CONFIG_SUP_DEMUXER) += supdec.o
OBJS-$(CONFIG_SWF_DEMUXER) += swfdec.o swf.o
OBJS-$(CONFIG_SWF_MUXER) += swfenc.o swf.o
OBJS-$(CONFIG_TAK_DEMUXER) += takdec.o apetag.o img2.o rawdec.o
+OBJS-$(CONFIG_TEDCAPTIONS_DEMUXER) += tedcaptionsdec.o subtitles.o
+OBJS-$(CONFIG_TEE_MUXER) += tee.o
OBJS-$(CONFIG_THP_DEMUXER) += thp.o
OBJS-$(CONFIG_TIERTEXSEQ_DEMUXER) += tiertexseq.o
+OBJS-$(CONFIG_MKVTIMESTAMP_V2_MUXER) += mkvtimestamp_v2.o
OBJS-$(CONFIG_TMV_DEMUXER) += tmv.o
OBJS-$(CONFIG_TRUEHD_DEMUXER) += rawdec.o
OBJS-$(CONFIG_TRUEHD_MUXER) += rawenc.o
-OBJS-$(CONFIG_TTA_DEMUXER) += tta.o
+OBJS-$(CONFIG_TTA_DEMUXER) += tta.o apetag.o img2.o
OBJS-$(CONFIG_TTY_DEMUXER) += tty.o sauce.o
OBJS-$(CONFIG_TXD_DEMUXER) += txd.o
+OBJS-$(CONFIG_UNCODEDFRAMECRC_MUXER) += uncodedframecrcenc.o framehash.o
OBJS-$(CONFIG_VC1_DEMUXER) += rawdec.o
+OBJS-$(CONFIG_VC1_MUXER) += rawenc.o
OBJS-$(CONFIG_VC1T_DEMUXER) += vc1test.o
OBJS-$(CONFIG_VC1T_MUXER) += vc1testenc.o
+OBJS-$(CONFIG_VIVO_DEMUXER) += vivo.o
OBJS-$(CONFIG_VMD_DEMUXER) += sierravmd.o
+OBJS-$(CONFIG_VOBSUB_DEMUXER) += subtitles.o # mpeg demuxer is in the dependencies
OBJS-$(CONFIG_VOC_DEMUXER) += vocdec.o voc.o
OBJS-$(CONFIG_VOC_MUXER) += vocenc.o voc.o
+OBJS-$(CONFIG_VPLAYER_DEMUXER) += vplayerdec.o subtitles.o
OBJS-$(CONFIG_VQF_DEMUXER) += vqf.o
-OBJS-$(CONFIG_W64_DEMUXER) += wavdec.o pcm.o
+OBJS-$(CONFIG_W64_DEMUXER) += wavdec.o w64.o pcm.o
+OBJS-$(CONFIG_W64_MUXER) += wavenc.o w64.o
OBJS-$(CONFIG_WAV_DEMUXER) += wavdec.o pcm.o
OBJS-$(CONFIG_WAV_MUXER) += wavenc.o
OBJS-$(CONFIG_WC3_DEMUXER) += wc3movie.o
OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \
isom.o avc.o hevc.o \
flacenc_header.o avlanguage.o \
- wv.o vorbiscomment.o
+ wv.o vorbiscomment.o \
+ webmdashenc.o
+OBJS-$(CONFIG_WEBM_DASH_MANIFEST_DEMUXER)+= matroskadec.o matroska.o \
+ isom.o rmsipr.o flac_picture.o \
+ oggparsevorbis.o vorbiscomment.o \
+ flac_picture.o replaygain.o
+OBJS-$(CONFIG_WEBM_DASH_MANIFEST_MUXER) += webmdashenc.o matroska.o
+OBJS-$(CONFIG_WEBP_MUXER) += webpenc.o
+OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.o subtitles.o
+OBJS-$(CONFIG_WEBVTT_MUXER) += webvttenc.o
OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood_aud.o
OBJS-$(CONFIG_WSVQA_DEMUXER) += westwood_vqa.o
-OBJS-$(CONFIG_WTV_DEMUXER) += wtv.o asfdec.o asf.o asfcrypt.o \
+OBJS-$(CONFIG_WTV_DEMUXER) += wtvdec.o wtv_common.o asfdec.o asf.o asfcrypt.o \
avlanguage.o mpegts.o isom.o
+OBJS-$(CONFIG_WTV_MUXER) += wtvenc.o wtv_common.o \
+ mpegtsenc.o asf.o
OBJS-$(CONFIG_WV_DEMUXER) += wvdec.o wv.o apetag.o img2.o
OBJS-$(CONFIG_WV_MUXER) += wvenc.o wv.o apetag.o img2.o
OBJS-$(CONFIG_XA_DEMUXER) += xa.o
+OBJS-$(CONFIG_XBIN_DEMUXER) += bintext.o sauce.o
OBJS-$(CONFIG_XMV_DEMUXER) += xmv.o
OBJS-$(CONFIG_XWMA_DEMUXER) += xwma.o
OBJS-$(CONFIG_YOP_DEMUXER) += yop.o
@@ -367,15 +474,26 @@ OBJS-$(CONFIG_YUV4MPEGPIPE_MUXER) += yuv4mpegenc.o
OBJS-$(CONFIG_YUV4MPEGPIPE_DEMUXER) += yuv4mpegdec.o
# external libraries
+OBJS-$(CONFIG_LIBGME_DEMUXER) += libgme.o
+OBJS-$(CONFIG_LIBMODPLUG_DEMUXER) += libmodplug.o
+OBJS-$(CONFIG_LIBNUT_DEMUXER) += libnut.o
+OBJS-$(CONFIG_LIBNUT_MUXER) += libnut.o
+OBJS-$(CONFIG_LIBQUVI_DEMUXER) += libquvi.o
OBJS-$(CONFIG_LIBRTMP) += librtmp.o
+OBJS-$(CONFIG_LIBSSH_PROTOCOL) += libssh.o
+OBJS-$(CONFIG_LIBSMBCLIENT_PROTOCOL) += libsmbclient.o
# protocols I/O
OBJS-$(CONFIG_APPLEHTTP_PROTOCOL) += hlsproto.o
+OBJS-$(CONFIG_BLURAY_PROTOCOL) += bluray.o
+OBJS-$(CONFIG_CACHE_PROTOCOL) += cache.o
OBJS-$(CONFIG_CONCAT_PROTOCOL) += concat.o
OBJS-$(CONFIG_CRYPTO_PROTOCOL) += crypto.o
+OBJS-$(CONFIG_DATA_PROTOCOL) += data_uri.o
OBJS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpcrypt.o rtmpdh.o
OBJS-$(CONFIG_FFRTMPHTTP_PROTOCOL) += rtmphttp.o
OBJS-$(CONFIG_FILE_PROTOCOL) += file.o
+OBJS-$(CONFIG_FTP_PROTOCOL) += ftp.o
OBJS-$(CONFIG_GOPHER_PROTOCOL) += gopher.o
OBJS-$(CONFIG_HLS_PROTOCOL) += hlsproto.o
OBJS-$(CONFIG_HTTP_PROTOCOL) += http.o httpauth.o urldecode.o
@@ -395,13 +513,21 @@ OBJS-$(CONFIG_RTMPTS_PROTOCOL) += rtmpproto.o rtmppkt.o
OBJS-$(CONFIG_RTP_PROTOCOL) += rtpproto.o
OBJS-$(CONFIG_SCTP_PROTOCOL) += sctp.o
OBJS-$(CONFIG_SRTP_PROTOCOL) += srtpproto.o srtp.o
+OBJS-$(CONFIG_SUBFILE_PROTOCOL) += subfile.o
OBJS-$(CONFIG_TCP_PROTOCOL) += tcp.o
OBJS-$(CONFIG_TLS_PROTOCOL) += tls.o
OBJS-$(CONFIG_UDP_PROTOCOL) += udp.o
+OBJS-$(CONFIG_UDPLITE_PROTOCOL) += udp.o
OBJS-$(CONFIG_UNIX_PROTOCOL) += unix.o
OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o
+# libavdevice dependencies
+OBJS-$(CONFIG_IEC61883_INDEV) += dv.o
+
+# Windows resource file
+SLIBOBJS-$(HAVE_GNU_WINDRES) += avformatres.o
+
SKIPHEADERS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh.h
SKIPHEADERS-$(CONFIG_NETWORK) += network.h rtsp.h
@@ -415,4 +541,5 @@ TOOLS = aviocat \
ismindex \
pktdumper \
probetest \
+ seek_print \
sidxindex \
diff --git a/libavformat/a64.c b/libavformat/a64.c
index c8e8e646b5..a7f93a27b1 100644
--- a/libavformat/a64.c
+++ b/libavformat/a64.c
@@ -2,25 +2,24 @@
* a64 muxer
* Copyright (c) 2009 Tobias Bindhammer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libavcodec/avcodec.h"
-#include "libavcodec/a64enc.h"
#include "libavcodec/bytestream.h"
#include "avformat.h"
#include "rawenc.h"
@@ -41,7 +40,7 @@ static int a64_write_header(AVFormatContext *s)
return AVERROR_INVALIDDATA;
}
- switch (avctx->codec->id) {
+ switch (avctx->codec_id) {
case AV_CODEC_ID_A64_MULTI:
header[2] = 0x00;
header[3] = AV_RB32(avctx->extradata+0);
diff --git a/libavformat/aacdec.c b/libavformat/aacdec.c
index 566c3e78b9..1b98e1ddb5 100644
--- a/libavformat/aacdec.c
+++ b/libavformat/aacdec.c
@@ -3,20 +3,20 @@
* Copyright (c) 2008 Michael Niedermayer <michaelni@gmx.at>
* Copyright (c) 2009 Robert Swain ( rob opendot cl )
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -25,15 +25,16 @@
#include "internal.h"
#include "rawdec.h"
#include "id3v1.h"
+#include "apetag.h"
static int adts_aac_probe(AVProbeData *p)
{
int max_frames = 0, first_frames = 0;
int fsize, frames;
- uint8_t *buf0 = p->buf;
- uint8_t *buf2;
- uint8_t *buf;
- uint8_t *end = buf0 + p->buf_size - 7;
+ const uint8_t *buf0 = p->buf;
+ const uint8_t *buf2;
+ const uint8_t *buf;
+ const uint8_t *end = buf0 + p->buf_size - 7;
buf = buf0;
@@ -55,6 +56,7 @@ static int adts_aac_probe(AVProbeData *p)
fsize = (AV_RB32(buf2 + 3) >> 13) & 0x1FFF;
if (fsize < 7)
break;
+ fsize = FFMIN(fsize, end - buf2);
buf2 += fsize;
}
max_frames = FFMAX(max_frames, frames);
@@ -84,9 +86,15 @@ static int adts_aac_read_header(AVFormatContext *s)
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = s->iformat->raw_codec_id;
- st->need_parsing = AVSTREAM_PARSE_FULL;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
ff_id3v1_read(s);
+ if (s->pb->seekable &&
+ !av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) {
+ int64_t cur = avio_tell(s->pb);
+ ff_ape_parse_tag(s);
+ avio_seek(s->pb, cur, SEEK_SET);
+ }
// LCM of all possible ADTS sample rates
avpriv_set_pts_info(st, 64, 1, 28224000);
diff --git a/libavformat/ac3dec.c b/libavformat/ac3dec.c
index 4ceffa54dd..58ef44d45a 100644
--- a/libavformat/ac3dec.c
+++ b/libavformat/ac3dec.c
@@ -2,20 +2,20 @@
* RAW AC-3 and E-AC-3 demuxer
* Copyright (c) 2007 Justin Ruggles <justin.ruggles@gmail.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -27,8 +27,8 @@
static int ac3_eac3_probe(AVProbeData *p, enum AVCodecID expected_codec_id)
{
int max_frames, first_frames = 0, frames;
- uint8_t *buf, *buf2, *end;
- AC3HeaderInfo hdr;
+ const uint8_t *buf, *buf2, *end;
+ AC3HeaderInfo *phdr = NULL;
GetBitContext gbc;
enum AVCodecID codec_id = AV_CODEC_ID_AC3;
@@ -37,51 +37,54 @@ static int ac3_eac3_probe(AVProbeData *p, enum AVCodecID expected_codec_id)
end = buf + p->buf_size;
for(; buf < end; buf++) {
+ if(buf > p->buf && !(buf[0] == 0x0B && buf[1] == 0x77)
+ && !(buf[0] == 0x77 && buf[1] == 0x0B) )
+ continue;
buf2 = buf;
for(frames = 0; buf2 < end; frames++) {
- init_get_bits(&gbc, buf2, 54);
- if(avpriv_ac3_parse_header(&gbc, &hdr) < 0)
+ uint8_t buf3[4096];
+ int i;
+ if(!memcmp(buf2, "\x1\x10\0\0\0\0\0\0", 8))
+ buf2+=16;
+ if (buf[0] == 0x77 && buf[1] == 0x0B) {
+ for(i=0; i<8; i+=2) {
+ buf3[i ] = buf[i+1];
+ buf3[i+1] = buf[i ];
+ }
+ init_get_bits(&gbc, buf3, 54);
+ }else
+ init_get_bits(&gbc, buf2, 54);
+ if(avpriv_ac3_parse_header2(&gbc, &phdr) < 0)
break;
- if(buf2 + hdr.frame_size > end ||
- av_crc(av_crc_get_table(AV_CRC_16_ANSI), 0, buf2 + 2, hdr.frame_size - 2))
+ if(buf2 + phdr->frame_size > end)
break;
- if (hdr.bitstream_id > 10)
+ if (buf[0] == 0x77 && buf[1] == 0x0B) {
+ av_assert0(phdr->frame_size <= sizeof(buf3));
+ for(i=8; i<phdr->frame_size; i+=2) {
+ buf3[i ] = buf[i+1];
+ buf3[i+1] = buf[i ];
+ }
+ }
+ if(av_crc(av_crc_get_table(AV_CRC_16_ANSI), 0, gbc.buffer + 2, phdr->frame_size - 2))
+ break;
+ if (phdr->bitstream_id > 10)
codec_id = AV_CODEC_ID_EAC3;
- buf2 += hdr.frame_size;
+ buf2 += phdr->frame_size;
}
max_frames = FFMAX(max_frames, frames);
if(buf == p->buf)
first_frames = frames;
}
+ av_freep(&phdr);
if(codec_id != expected_codec_id) return 0;
// keep this in sync with mp3 probe, both need to avoid
// issues with MPEG-files!
- if (first_frames >= 4) return AVPROBE_SCORE_EXTENSION + 1;
-
- if (max_frames) {
- int pes = 0, i;
- unsigned int code = -1;
-
-#define VIDEO_ID 0x000001e0
-#define AUDIO_ID 0x000001c0
- /* do a search for mpegps headers to be able to properly bias
- * towards mpegps if we detect this stream as both. */
- for (i = 0; i<p->buf_size; i++) {
- code = (code << 8) + p->buf[i];
- if ((code & 0xffffff00) == 0x100) {
- if ((code & 0x1f0) == VIDEO_ID) pes++;
- else if((code & 0x1e0) == AUDIO_ID) pes++;
- }
- }
-
- if (pes)
- max_frames = (max_frames + pes - 1) / pes;
- }
- if (max_frames > 500) return AVPROBE_SCORE_EXTENSION;
- else if (max_frames >= 4) return AVPROBE_SCORE_EXTENSION / 2;
- else if (max_frames >= 1) return 1;
- else return 0;
+ if (first_frames>=4) return AVPROBE_SCORE_EXTENSION + 1;
+ else if(max_frames>200)return AVPROBE_SCORE_EXTENSION;
+ else if(max_frames>=4) return AVPROBE_SCORE_EXTENSION/2;
+ else if(max_frames>=1) return 1;
+ else return 0;
}
#if CONFIG_AC3_DEMUXER
diff --git a/libavformat/act.c b/libavformat/act.c
new file mode 100644
index 0000000000..7b6b8406fc
--- /dev/null
+++ b/libavformat/act.c
@@ -0,0 +1,207 @@
+/*
+ * ACT file format demuxer
+ * Copyright (c) 2007-2008 Vladimir Voroshilov
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "avformat.h"
+#include "riff.h"
+#include "internal.h"
+#include "libavcodec/get_bits.h"
+
+#define CHUNK_SIZE 512
+#define RIFF_TAG MKTAG('R','I','F','F')
+#define WAVE_TAG MKTAG('W','A','V','E')
+
+typedef struct{
+ int bytes_left_in_chunk;
+ uint8_t audio_buffer[22];///< temporary buffer for ACT frame
+ char second_packet; ///< 1 - if temporary buffer contains valid (second) G.729 packet
+} ACTContext;
+
+static int probe(AVProbeData *p)
+{
+ int i;
+
+ if ((AV_RL32(&p->buf[0]) != RIFF_TAG) ||
+ (AV_RL32(&p->buf[8]) != WAVE_TAG) ||
+ (AV_RL32(&p->buf[16]) != 16))
+ return 0;
+
+ //We can't be sure that this is ACT and not regular WAV
+ if (p->buf_size<512)
+ return 0;
+
+ for(i=44; i<256; i++)
+ if(p->buf[i])
+ return 0;
+
+ if(p->buf[256]!=0x84)
+ return 0;
+
+ for(i=264; i<512; i++)
+ if(p->buf[i])
+ return 0;
+
+ return AVPROBE_SCORE_MAX;
+}
+
+static int read_header(AVFormatContext *s)
+{
+ ACTContext* ctx = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int size;
+ AVStream* st;
+
+ int min,sec,msec;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avio_skip(pb, 16);
+ size=avio_rl32(pb);
+ ff_get_wav_header(pb, st->codec, size, 0);
+
+ /*
+ 8000Hz (Fine-rec) file format has 10 bytes long
+ packets with 10ms of sound data in them
+ */
+ if (st->codec->sample_rate != 8000) {
+ av_log(s, AV_LOG_ERROR, "Sample rate %d is not supported.\n", st->codec->sample_rate);
+ return AVERROR_INVALIDDATA;
+ }
+
+ st->codec->frame_size=80;
+ st->codec->channels=1;
+ avpriv_set_pts_info(st, 64, 1, 100);
+
+ st->codec->codec_id=AV_CODEC_ID_G729;
+
+ avio_seek(pb, 257, SEEK_SET);
+ msec=avio_rl16(pb);
+ sec=avio_r8(pb);
+ min=avio_rl32(pb);
+
+ st->duration = av_rescale(1000*(min*60+sec)+msec, st->codec->sample_rate, 1000 * st->codec->frame_size);
+
+ ctx->bytes_left_in_chunk=CHUNK_SIZE;
+
+ avio_seek(pb, 512, SEEK_SET);
+
+ return 0;
+}
+
+
+static int read_packet(AVFormatContext *s,
+ AVPacket *pkt)
+{
+ ACTContext *ctx = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int ret;
+ int frame_size=s->streams[0]->codec->sample_rate==8000?10:22;
+
+
+ if(s->streams[0]->codec->sample_rate==8000)
+ ret=av_new_packet(pkt, 10);
+ else
+ ret=av_new_packet(pkt, 11);
+
+ if(ret)
+ return ret;
+
+ if(s->streams[0]->codec->sample_rate==4400 && !ctx->second_packet)
+ {
+ ret = avio_read(pb, ctx->audio_buffer, frame_size);
+
+ if(ret<0)
+ return ret;
+ if(ret!=frame_size)
+ return AVERROR(EIO);
+
+ pkt->data[0]=ctx->audio_buffer[11];
+ pkt->data[1]=ctx->audio_buffer[0];
+ pkt->data[2]=ctx->audio_buffer[12];
+ pkt->data[3]=ctx->audio_buffer[1];
+ pkt->data[4]=ctx->audio_buffer[13];
+ pkt->data[5]=ctx->audio_buffer[2];
+ pkt->data[6]=ctx->audio_buffer[14];
+ pkt->data[7]=ctx->audio_buffer[3];
+ pkt->data[8]=ctx->audio_buffer[15];
+ pkt->data[9]=ctx->audio_buffer[4];
+ pkt->data[10]=ctx->audio_buffer[16];
+
+ ctx->second_packet=1;
+ }
+ else if(s->streams[0]->codec->sample_rate==4400 && ctx->second_packet)
+ {
+ pkt->data[0]=ctx->audio_buffer[5];
+ pkt->data[1]=ctx->audio_buffer[17];
+ pkt->data[2]=ctx->audio_buffer[6];
+ pkt->data[3]=ctx->audio_buffer[18];
+ pkt->data[4]=ctx->audio_buffer[7];
+ pkt->data[5]=ctx->audio_buffer[19];
+ pkt->data[6]=ctx->audio_buffer[8];
+ pkt->data[7]=ctx->audio_buffer[20];
+ pkt->data[8]=ctx->audio_buffer[9];
+ pkt->data[9]=ctx->audio_buffer[21];
+ pkt->data[10]=ctx->audio_buffer[10];
+
+ ctx->second_packet=0;
+ }
+ else // 8000 Hz
+ {
+ ret = avio_read(pb, ctx->audio_buffer, frame_size);
+
+ if(ret<0)
+ return ret;
+ if(ret!=frame_size)
+ return AVERROR(EIO);
+
+ pkt->data[0]=ctx->audio_buffer[5];
+ pkt->data[1]=ctx->audio_buffer[0];
+ pkt->data[2]=ctx->audio_buffer[6];
+ pkt->data[3]=ctx->audio_buffer[1];
+ pkt->data[4]=ctx->audio_buffer[7];
+ pkt->data[5]=ctx->audio_buffer[2];
+ pkt->data[6]=ctx->audio_buffer[8];
+ pkt->data[7]=ctx->audio_buffer[3];
+ pkt->data[8]=ctx->audio_buffer[9];
+ pkt->data[9]=ctx->audio_buffer[4];
+ }
+
+ ctx->bytes_left_in_chunk -= frame_size;
+
+ if(ctx->bytes_left_in_chunk < frame_size)
+ {
+ avio_skip(pb, ctx->bytes_left_in_chunk);
+ ctx->bytes_left_in_chunk=CHUNK_SIZE;
+ }
+
+ pkt->duration=1;
+
+ return ret;
+}
+
+AVInputFormat ff_act_demuxer = {
+ .name = "act",
+ .long_name = "ACT Voice file format",
+ .priv_data_size = sizeof(ACTContext),
+ .read_probe = probe,
+ .read_header = read_header,
+ .read_packet = read_packet,
+};
diff --git a/libavformat/adp.c b/libavformat/adp.c
new file mode 100644
index 0000000000..8a3661ae15
--- /dev/null
+++ b/libavformat/adp.c
@@ -0,0 +1,98 @@
+/*
+ * ADP demuxer
+ * Copyright (c) 2013 James Almer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/channel_layout.h"
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "internal.h"
+
+static int adp_probe(AVProbeData *p)
+{
+ int i, changes = 0;
+ char last = 0;
+
+ if (p->buf_size < 32)
+ return 0;
+
+ for (i = 0; i < p->buf_size - 3; i+=32) {
+ if (p->buf[i] != p->buf[i+2] || p->buf[i+1] != p->buf[i+3])
+ return 0;
+ if (p->buf[i] != last)
+ changes++;
+ last = p->buf[i];
+ }
+ if (changes <= 1)
+ return 0;
+
+ return p->buf_size < 260 ? 1 : AVPROBE_SCORE_MAX / 4;
+}
+
+static int adp_read_header(AVFormatContext *s)
+{
+ AVStream *st;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->codec_id = AV_CODEC_ID_ADPCM_DTK;
+ st->codec->channel_layout = AV_CH_LAYOUT_STEREO;
+ st->codec->channels = 2;
+ st->codec->sample_rate = 48000;
+ st->start_time = 0;
+ if (s->pb->seekable)
+ st->duration = av_get_audio_frame_duration(st->codec, avio_size(s->pb));
+
+ avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+
+ return 0;
+}
+
+static int adp_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ int ret, size = 1024;
+
+ if (avio_feof(s->pb))
+ return AVERROR_EOF;
+
+ ret = av_get_packet(s->pb, pkt, size);
+
+ if (ret != size) {
+ if (ret < 0) {
+ av_free_packet(pkt);
+ return ret;
+ }
+ av_shrink_packet(pkt, ret);
+ }
+ pkt->stream_index = 0;
+
+ return ret;
+}
+
+AVInputFormat ff_adp_demuxer = {
+ .name = "adp",
+ .long_name = NULL_IF_CONFIG_SMALL("ADP"),
+ .read_probe = adp_probe,
+ .read_header = adp_read_header,
+ .read_packet = adp_read_packet,
+ .extensions = "adp,dtk",
+};
diff --git a/libavformat/adtsenc.c b/libavformat/adtsenc.c
index 55d7510557..7448ec794f 100644
--- a/libavformat/adtsenc.c
+++ b/libavformat/adtsenc.c
@@ -3,20 +3,20 @@
* Copyright (c) 2006 Baptiste Coudurier <baptiste.coudurier@smartjog.com>
* Mans Rullgard <mans@mansr.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -24,22 +24,28 @@
#include "libavcodec/put_bits.h"
#include "libavcodec/avcodec.h"
#include "libavcodec/mpeg4audio.h"
+#include "libavutil/opt.h"
#include "avformat.h"
+#include "apetag.h"
+#include "id3v2.h"
#define ADTS_HEADER_SIZE 7
typedef struct ADTSContext {
+ AVClass *class;
int write_adts;
int objecttype;
int sample_rate_index;
int channel_conf;
int pce_size;
+ int apetag;
+ int id3v2tag;
uint8_t pce_data[MAX_PCE_SIZE];
} ADTSContext;
#define ADTS_MAX_FRAME_BYTES ((1 << 13) - 1)
-static int adts_decode_extradata(AVFormatContext *s, ADTSContext *adts, uint8_t *buf, int size)
+static int adts_decode_extradata(AVFormatContext *s, ADTSContext *adts, const uint8_t *buf, int size)
{
GetBitContext gb;
PutBitContext pb;
@@ -93,6 +99,8 @@ static int adts_write_header(AVFormatContext *s)
ADTSContext *adts = s->priv_data;
AVCodecContext *avc = s->streams[0]->codec;
+ if (adts->id3v2tag)
+ ff_id3v2_write_simple(s, 4, ID3v2_DEFAULT_MAGIC);
if (avc->extradata_size > 0)
return adts_decode_extradata(s, adts, avc->extradata,
avc->extradata_size);
@@ -162,6 +170,31 @@ static int adts_write_packet(AVFormatContext *s, AVPacket *pkt)
return 0;
}
+static int adts_write_trailer(AVFormatContext *s)
+{
+ ADTSContext *adts = s->priv_data;
+
+ if (adts->apetag)
+ ff_ape_write_tag(s);
+
+ return 0;
+}
+
+#define ENC AV_OPT_FLAG_ENCODING_PARAM
+#define OFFSET(obj) offsetof(ADTSContext, obj)
+static const AVOption options[] = {
+ { "write_id3v2", "Enable ID3v2 tag writing", OFFSET(id3v2tag), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, ENC},
+ { "write_apetag", "Enable APE tag writing", OFFSET(apetag), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, ENC},
+ { NULL },
+};
+
+static const AVClass adts_muxer_class = {
+ .class_name = "ADTS muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVOutputFormat ff_adts_muxer = {
.name = "adts",
.long_name = NULL_IF_CONFIG_SMALL("ADTS AAC (Advanced Audio Coding)"),
@@ -172,5 +205,7 @@ AVOutputFormat ff_adts_muxer = {
.video_codec = AV_CODEC_ID_NONE,
.write_header = adts_write_header,
.write_packet = adts_write_packet,
+ .write_trailer = adts_write_trailer,
+ .priv_class = &adts_muxer_class,
.flags = AVFMT_NOTIMESTAMPS,
};
diff --git a/libavformat/adxdec.c b/libavformat/adxdec.c
index fc83ff263b..ddaa201179 100644
--- a/libavformat/adxdec.c
+++ b/libavformat/adxdec.c
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2011 Justin Ruggles
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -76,14 +76,8 @@ static int adx_read_header(AVFormatContext *s)
c->header_size = avio_rb16(s->pb) + 4;
avio_seek(s->pb, -4, SEEK_CUR);
- avctx->extradata = av_mallocz(c->header_size + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!avctx->extradata)
+ if (ff_get_extradata(avctx, s->pb, c->header_size) < 0)
return AVERROR(ENOMEM);
- if (avio_read(s->pb, avctx->extradata, c->header_size) < c->header_size) {
- av_freep(&avctx->extradata);
- return AVERROR(EIO);
- }
- avctx->extradata_size = c->header_size;
if (avctx->extradata_size < 12) {
av_log(s, AV_LOG_ERROR, "Invalid extradata size.\n");
diff --git a/libavformat/aea.c b/libavformat/aea.c
index c107109cfa..db415c9543 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
*/
@@ -34,12 +34,8 @@ static int aea_read_probe(AVProbeData *p)
/* Magic is '00 08 00 00' in Little Endian*/
if (AV_RL32(p->buf)==0x800) {
- int bsm_s, bsm_e, inb_s, inb_e, ch;
- ch = p->buf[264];
- bsm_s = p->buf[2048];
- inb_s = p->buf[2048+1];
- inb_e = p->buf[2048+210];
- bsm_e = p->buf[2048+211];
+ int ch, i;
+ ch = p->buf[264];
if (ch != 1 && ch != 2)
return 0;
@@ -48,8 +44,17 @@ static int aea_read_probe(AVProbeData *p)
* the block size mode bytes have to be the same
* the info bytes have to be the same
*/
- if (bsm_s == bsm_e && inb_s == inb_e)
- return AVPROBE_SCORE_MAX / 4 + 1;
+ for (i = 2048; i + 211 < p->buf_size; i+= 212) {
+ int bsm_s, bsm_e, inb_s, inb_e;
+ bsm_s = p->buf[0];
+ inb_s = p->buf[1];
+ inb_e = p->buf[210];
+ bsm_e = p->buf[211];
+
+ if (bsm_s != bsm_e || inb_s != inb_e)
+ return 0;
+ }
+ return AVPROBE_SCORE_MAX / 4 + 1;
}
return 0;
}
diff --git a/libavformat/afc.c b/libavformat/afc.c
new file mode 100644
index 0000000000..8dbd232fc1
--- /dev/null
+++ b/libavformat/afc.c
@@ -0,0 +1,79 @@
+/*
+ * AFC demuxer
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/channel_layout.h"
+#include "avformat.h"
+#include "internal.h"
+
+typedef struct AFCDemuxContext {
+ int64_t data_end;
+} AFCDemuxContext;
+
+static int afc_read_header(AVFormatContext *s)
+{
+ AFCDemuxContext *c = s->priv_data;
+ AVStream *st;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->codec_id = AV_CODEC_ID_ADPCM_AFC;
+ st->codec->channels = 2;
+ st->codec->channel_layout = AV_CH_LAYOUT_STEREO;
+
+ if (ff_alloc_extradata(st->codec, 1))
+ return AVERROR(ENOMEM);
+ st->codec->extradata[0] = 8 * st->codec->channels;
+
+ c->data_end = avio_rb32(s->pb) + 32LL;
+ st->duration = avio_rb32(s->pb);
+ st->codec->sample_rate = avio_rb16(s->pb);
+ avio_skip(s->pb, 22);
+ avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+
+ return 0;
+}
+
+static int afc_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AFCDemuxContext *c = s->priv_data;
+ int64_t size;
+ int ret;
+
+ size = FFMIN(c->data_end - avio_tell(s->pb), 18 * 128);
+ if (size <= 0)
+ return AVERROR_EOF;
+
+ ret = av_get_packet(s->pb, pkt, size);
+ pkt->stream_index = 0;
+ return ret;
+}
+
+AVInputFormat ff_afc_demuxer = {
+ .name = "afc",
+ .long_name = NULL_IF_CONFIG_SMALL("AFC"),
+ .priv_data_size = sizeof(AFCDemuxContext),
+ .read_header = afc_read_header,
+ .read_packet = afc_read_packet,
+ .extensions = "afc",
+ .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK,
+};
diff --git a/libavformat/aiff.h b/libavformat/aiff.h
index f88f957e71..4470254022 100644
--- a/libavformat/aiff.h
+++ b/libavformat/aiff.h
@@ -2,20 +2,20 @@
* AIFF/AIFF-C muxer/demuxer common header
* Copyright (c) 2006 Patrick Guimond
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -33,16 +33,20 @@
static const AVCodecTag ff_codec_aiff_tags[] = {
{ AV_CODEC_ID_PCM_S16BE, MKTAG('N','O','N','E') },
{ AV_CODEC_ID_PCM_S8, MKTAG('N','O','N','E') },
+ { AV_CODEC_ID_PCM_U8, MKTAG('r','a','w',' ') },
{ AV_CODEC_ID_PCM_S24BE, MKTAG('N','O','N','E') },
{ AV_CODEC_ID_PCM_S32BE, MKTAG('N','O','N','E') },
{ AV_CODEC_ID_PCM_F32BE, MKTAG('f','l','3','2') },
{ AV_CODEC_ID_PCM_F64BE, MKTAG('f','l','6','4') },
{ AV_CODEC_ID_PCM_ALAW, MKTAG('a','l','a','w') },
{ AV_CODEC_ID_PCM_MULAW, MKTAG('u','l','a','w') },
+ { AV_CODEC_ID_PCM_S24BE, MKTAG('i','n','2','4') },
+ { AV_CODEC_ID_PCM_S32BE, MKTAG('i','n','3','2') },
{ AV_CODEC_ID_MACE3, MKTAG('M','A','C','3') },
{ AV_CODEC_ID_MACE6, MKTAG('M','A','C','6') },
{ AV_CODEC_ID_GSM, MKTAG('G','S','M',' ') },
- { AV_CODEC_ID_ADPCM_G726, MKTAG('G','7','2','6') },
+ { AV_CODEC_ID_ADPCM_G722, MKTAG('G','7','2','2') },
+ { AV_CODEC_ID_ADPCM_G726LE, MKTAG('G','7','2','6') },
{ AV_CODEC_ID_PCM_S16BE, MKTAG('t','w','o','s') },
{ AV_CODEC_ID_PCM_S16LE, MKTAG('s','o','w','t') },
{ AV_CODEC_ID_ADPCM_IMA_QT, MKTAG('i','m','a','4') },
diff --git a/libavformat/aiffdec.c b/libavformat/aiffdec.c
index add843b267..bc1a21937d 100644
--- a/libavformat/aiffdec.c
+++ b/libavformat/aiffdec.c
@@ -2,29 +2,33 @@
* AIFF/AIFF-C demuxer
* Copyright (c) 2006 Patrick Guimond
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
#include "libavutil/dict.h"
#include "avformat.h"
#include "internal.h"
#include "pcm.h"
#include "aiff.h"
+#include "isom.h"
+#include "id3v2.h"
+#include "mov_chan.h"
#define AIFF 0
#define AIFF_C_VERSION1 0xA2805140
@@ -54,7 +58,7 @@ static int get_tag(AVIOContext *pb, uint32_t * tag)
{
int size;
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR(EIO);
*tag = avio_rl32(pb);
@@ -70,19 +74,20 @@ static int get_tag(AVIOContext *pb, uint32_t * tag)
static void get_meta(AVFormatContext *s, const char *key, int size)
{
uint8_t *str = av_malloc(size+1);
- int res;
- if (!str) {
- avio_skip(s->pb, size);
- return;
- }
-
- res = avio_read(s->pb, str, size);
- if (res < 0)
- return;
+ if (str) {
+ int res = avio_read(s->pb, str, size);
+ if (res < 0){
+ av_free(str);
+ return;
+ }
+ size += (size&1)-res;
+ str[res] = 0;
+ av_dict_set(&s->metadata, key, str, AV_DICT_DONT_STRDUP_VAL);
+ }else
+ size+= size&1;
- str[res] = 0;
- av_dict_set(&s->metadata, key, str, AV_DICT_DONT_STRDUP_VAL);
+ avio_skip(s->pb, size);
}
/* Returns the number of sound data frames or negative on error */
@@ -111,7 +116,9 @@ static unsigned int get_aiff_header(AVFormatContext *s, int size,
size -= 18;
/* get codec id for AIFF-C */
- if (version == AIFF_C_VERSION1) {
+ if (size < 4) {
+ version = AIFF;
+ } else if (version == AIFF_C_VERSION1) {
codec->codec_tag = avio_rl32(pb);
codec->codec_id = ff_codec_get_id(ff_codec_aiff_tags, codec->codec_tag);
size -= 4;
@@ -136,16 +143,17 @@ static unsigned int get_aiff_header(AVFormatContext *s, int size,
case AV_CODEC_ID_MACE3:
codec->block_align = 2*codec->channels;
break;
+ case AV_CODEC_ID_ADPCM_G726LE:
+ codec->bits_per_coded_sample = 5;
+ case AV_CODEC_ID_ADPCM_G722:
case AV_CODEC_ID_MACE6:
codec->block_align = 1*codec->channels;
break;
case AV_CODEC_ID_GSM:
codec->block_align = 33;
break;
- case AV_CODEC_ID_QCELP:
- codec->block_align = 35;
- break;
default:
+ aiff->block_duration = 1;
break;
}
if (codec->block_align > 0)
@@ -156,7 +164,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 +193,14 @@ static int aiff_probe(AVProbeData *p)
/* aiff input */
static int aiff_read_header(AVFormatContext *s)
{
- int size, filesize;
- int64_t offset = 0;
+ int ret, size, filesize;
+ int64_t offset = 0, position;
uint32_t tag;
unsigned version = AIFF_C_VERSION1;
AVIOContext *pb = s->pb;
AVStream * st;
AIFFInputContext *aiff = s->priv_data;
+ ID3v2ExtraMeta *id3v2_extra_meta = NULL;
/* check FORM header */
filesize = get_tag(pb, &tag);
@@ -228,6 +237,18 @@ static int aiff_read_header(AVFormatContext *s)
if (offset > 0) // COMM is after SSND
goto got_sound;
break;
+ case MKTAG('I', 'D', '3', ' '):
+ position = avio_tell(pb);
+ ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, size);
+ if (id3v2_extra_meta)
+ if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0) {
+ ff_id3v2_free_extra_meta(&id3v2_extra_meta);
+ return ret;
+ }
+ ff_id3v2_free_extra_meta(&id3v2_extra_meta);
+ if (position + size > avio_tell(pb))
+ avio_skip(pb, position + size - avio_tell(pb));
+ break;
case MKTAG('F', 'V', 'E', 'R'): /* Version chunk */
version = avio_rb32(pb);
break;
@@ -248,7 +269,7 @@ static int aiff_read_header(AVFormatContext *s)
offset = avio_rb32(pb); /* Offset of sound data */
avio_rb32(pb); /* BlockSize... don't care */
offset += avio_tell(pb); /* Compute absolute data offset */
- if (st->codec->block_align) /* Assume COMM already parsed */
+ if (st->codec->block_align && !pb->seekable) /* Assume COMM already parsed */
goto got_sound;
if (!pb->seekable) {
av_log(s, AV_LOG_ERROR, "file is not seekable\n");
@@ -259,11 +280,31 @@ static int aiff_read_header(AVFormatContext *s)
case MKTAG('w', 'a', 'v', 'e'):
if ((uint64_t)size > (1<<30))
return -1;
- st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
+ if (ff_get_extradata(st->codec, pb, size) < 0)
return AVERROR(ENOMEM);
- st->codec->extradata_size = size;
- avio_read(pb, st->codec->extradata, size);
+ if (st->codec->codec_id == AV_CODEC_ID_QDM2 && size>=12*4 && !st->codec->block_align) {
+ st->codec->block_align = AV_RB32(st->codec->extradata+11*4);
+ aiff->block_duration = AV_RB32(st->codec->extradata+9*4);
+ } else if (st->codec->codec_id == AV_CODEC_ID_QCELP) {
+ char rate = 0;
+ if (size >= 25)
+ rate = st->codec->extradata[24];
+ switch (rate) {
+ case 'H': // RATE_HALF
+ st->codec->block_align = 17;
+ break;
+ case 'F': // RATE_FULL
+ default:
+ st->codec->block_align = 35;
+ }
+ aiff->block_duration = 160;
+ st->codec->bit_rate = st->codec->sample_rate * (st->codec->block_align << 3) /
+ aiff->block_duration;
+ }
+ break;
+ case MKTAG('C','H','A','N'):
+ if(ff_mov_read_chan(s, pb, st, size) < 0)
+ return AVERROR_INVALIDDATA;
break;
default: /* Jump */
if (size & 1) /* Always even aligned */
@@ -305,15 +346,23 @@ static int aiff_read_packet(AVFormatContext *s,
return AVERROR_EOF;
/* Now for that packet */
- if (st->codec->block_align >= 33) // GSM, QCLP, IMA4
+ switch (st->codec->codec_id) {
+ case AV_CODEC_ID_ADPCM_IMA_QT:
+ case AV_CODEC_ID_GSM:
+ case AV_CODEC_ID_QDM2:
+ case AV_CODEC_ID_QCELP:
size = st->codec->block_align;
- else
+ break;
+ default:
size = (MAX_SIZE / st->codec->block_align) * st->codec->block_align;
+ }
size = FFMIN(max_size, size);
res = av_get_packet(s->pb, pkt, size);
if (res < 0)
return res;
+ if (size >= st->codec->block_align)
+ pkt->flags &= ~AV_PKT_FLAG_CORRUPT;
/* Only one stream in an AIFF file */
pkt->stream_index = 0;
pkt->duration = (res / st->codec->block_align) * aiff->block_duration;
diff --git a/libavformat/aiffenc.c b/libavformat/aiffenc.c
index aa68108368..e2828e7508 100644
--- a/libavformat/aiffenc.c
+++ b/libavformat/aiffenc.c
@@ -2,44 +2,126 @@
* AIFF/AIFF-C muxer
* Copyright (c) 2006 Patrick Guimond
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdint.h>
#include "libavutil/intfloat.h"
+#include "libavutil/opt.h"
#include "avformat.h"
#include "internal.h"
#include "aiff.h"
#include "avio_internal.h"
+#include "isom.h"
+#include "id3v2.h"
typedef struct AIFFOutputContext {
+ const AVClass *class;
int64_t form;
int64_t frames;
int64_t ssnd;
+ int audio_stream_idx;
+ AVPacketList *pict_list;
+ int write_id3v2;
+ int id3v2_version;
} AIFFOutputContext;
+static int put_id3v2_tags(AVFormatContext *s, AIFFOutputContext *aiff)
+{
+ int ret;
+ uint64_t pos, end, size;
+ ID3v2EncContext id3v2 = { 0 };
+ AVIOContext *pb = s->pb;
+ AVPacketList *pict_list = aiff->pict_list;
+
+ if (!pb->seekable)
+ return 0;
+
+ if (!s->metadata && !aiff->pict_list)
+ return 0;
+
+ avio_wl32(pb, MKTAG('I', 'D', '3', ' '));
+ avio_wb32(pb, 0);
+ pos = avio_tell(pb);
+
+ ff_id3v2_start(&id3v2, pb, aiff->id3v2_version, ID3v2_DEFAULT_MAGIC);
+ ff_id3v2_write_metadata(s, &id3v2);
+ while (pict_list) {
+ if ((ret = ff_id3v2_write_apic(s, &id3v2, &pict_list->pkt)) < 0)
+ return ret;
+ pict_list = pict_list->next;
+ }
+ ff_id3v2_finish(&id3v2, pb, s->metadata_header_padding);
+
+ end = avio_tell(pb);
+ size = end - pos;
+
+ /* Update chunk size */
+ avio_seek(pb, pos - 4, SEEK_SET);
+ avio_wb32(pb, size);
+ avio_seek(pb, end, SEEK_SET);
+
+ if (size & 1)
+ avio_w8(pb, 0);
+
+ return 0;
+}
+
+static void put_meta(AVFormatContext *s, const char *key, uint32_t id)
+{
+ AVDictionaryEntry *tag;
+ AVIOContext *pb = s->pb;
+
+ if (tag = av_dict_get(s->metadata, key, NULL, 0)) {
+ int size = strlen(tag->value);
+
+ avio_wl32(pb, id);
+ avio_wb32(pb, FFALIGN(size, 2));
+ avio_write(pb, tag->value, size);
+ if (size & 1)
+ avio_w8(pb, 0);
+ }
+}
+
static int aiff_write_header(AVFormatContext *s)
{
AIFFOutputContext *aiff = s->priv_data;
AVIOContext *pb = s->pb;
- AVCodecContext *enc = s->streams[0]->codec;
+ AVCodecContext *enc;
uint64_t sample_rate;
- int aifc = 0;
+ int i, aifc = 0;
+
+ aiff->audio_stream_idx = -1;
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ if (aiff->audio_stream_idx < 0 && st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
+ aiff->audio_stream_idx = i;
+ } else if (st->codec->codec_type != AVMEDIA_TYPE_VIDEO) {
+ av_log(s, AV_LOG_ERROR, "Only audio streams and pictures are allowed in AIFF.\n");
+ return AVERROR(EINVAL);
+ }
+ }
+ if (aiff->audio_stream_idx < 0) {
+ av_log(s, AV_LOG_ERROR, "No audio stream present.\n");
+ return AVERROR(EINVAL);
+ }
+
+ enc = s->streams[aiff->audio_stream_idx]->codec;
/* First verify if format is ok */
if (!enc->codec_tag)
@@ -54,7 +136,6 @@ static int aiff_write_header(AVFormatContext *s)
ffio_wfourcc(pb, aifc ? "AIFC" : "AIFF");
if (aifc) { // compressed audio
- enc->bits_per_coded_sample = 16;
if (!enc->block_align) {
av_log(s, AV_LOG_ERROR, "block align not set\n");
return -1;
@@ -65,6 +146,17 @@ static int aiff_write_header(AVFormatContext *s)
avio_wb32(pb, 0xA2805140);
}
+ if (enc->channels > 2 && enc->channel_layout) {
+ ffio_wfourcc(pb, "CHAN");
+ avio_wb32(pb, 12);
+ ff_mov_write_chan(pb, enc->channel_layout);
+ }
+
+ put_meta(s, "title", MKTAG('N', 'A', 'M', 'E'));
+ put_meta(s, "author", MKTAG('A', 'U', 'T', 'H'));
+ put_meta(s, "copyright", MKTAG('(', 'c', ')', ' '));
+ put_meta(s, "comment", MKTAG('A', 'N', 'N', 'O'));
+
/* Common chunk */
ffio_wfourcc(pb, "COMM");
avio_wb32(pb, aifc ? 24 : 18); /* size */
@@ -93,6 +185,12 @@ static int aiff_write_header(AVFormatContext *s)
avio_wb16(pb, 0);
}
+ if (enc->codec_tag == MKTAG('Q','D','M','2') && enc->extradata_size) {
+ ffio_wfourcc(pb, "wave");
+ avio_wb32(pb, enc->extradata_size);
+ avio_write(pb, enc->extradata, enc->extradata_size);
+ }
+
/* Sound data chunk */
ffio_wfourcc(pb, "SSND");
aiff->ssnd = avio_tell(pb); /* Sound chunk size */
@@ -100,7 +198,8 @@ static int aiff_write_header(AVFormatContext *s)
avio_wb32(pb, 0); /* Data offset */
avio_wb32(pb, 0); /* Block-size (block align) */
- avpriv_set_pts_info(s->streams[0], 64, 1, s->streams[0]->codec->sample_rate);
+ avpriv_set_pts_info(s->streams[aiff->audio_stream_idx], 64, 1,
+ s->streams[aiff->audio_stream_idx]->codec->sample_rate);
/* Data is starting here */
avio_flush(pb);
@@ -110,16 +209,54 @@ static int aiff_write_header(AVFormatContext *s)
static int aiff_write_packet(AVFormatContext *s, AVPacket *pkt)
{
+ AIFFOutputContext *aiff = s->priv_data;
AVIOContext *pb = s->pb;
- avio_write(pb, pkt->data, pkt->size);
+ if (pkt->stream_index == aiff->audio_stream_idx)
+ avio_write(pb, pkt->data, pkt->size);
+ else {
+ int ret;
+ AVPacketList *pict_list, *last;
+
+ if (s->streams[pkt->stream_index]->codec->codec_type != AVMEDIA_TYPE_VIDEO)
+ return 0;
+
+ /* warn only once for each stream */
+ if (s->streams[pkt->stream_index]->nb_frames == 1) {
+ av_log(s, AV_LOG_WARNING, "Got more than one picture in stream %d,"
+ " ignoring.\n", pkt->stream_index);
+ }
+ if (s->streams[pkt->stream_index]->nb_frames >= 1)
+ return 0;
+
+ pict_list = av_mallocz(sizeof(AVPacketList));
+ if (!pict_list)
+ return AVERROR(ENOMEM);
+
+ if ((ret = av_copy_packet(&pict_list->pkt, pkt)) < 0) {
+ av_freep(&pict_list);
+ return ret;
+ }
+
+ if (!aiff->pict_list)
+ aiff->pict_list = pict_list;
+ else {
+ last = aiff->pict_list;
+ while (last->next)
+ last = last->next;
+ last->next = pict_list;
+ }
+ }
+
return 0;
}
static int aiff_write_trailer(AVFormatContext *s)
{
+ int ret;
AVIOContext *pb = s->pb;
AIFFOutputContext *aiff = s->priv_data;
- AVCodecContext *enc = s->streams[0]->codec;
+ AVPacketList *pict_list = aiff->pict_list;
+ AVCodecContext *enc = s->streams[aiff->audio_stream_idx]->codec;
/* Chunks sizes must be even */
int64_t file_size, end_size;
@@ -130,10 +267,6 @@ static int aiff_write_trailer(AVFormatContext *s)
}
if (s->pb->seekable) {
- /* File length */
- avio_seek(pb, aiff->form, SEEK_SET);
- avio_wb32(pb, file_size - aiff->form - 4);
-
/* Number of sample frames */
avio_seek(pb, aiff->frames, SEEK_SET);
avio_wb32(pb, (file_size-aiff->ssnd-12)/enc->block_align);
@@ -145,12 +278,46 @@ static int aiff_write_trailer(AVFormatContext *s)
/* return to the end */
avio_seek(pb, end_size, SEEK_SET);
+ /* Write ID3 tags */
+ if (aiff->write_id3v2)
+ if ((ret = put_id3v2_tags(s, aiff)) < 0)
+ return ret;
+
+ /* File length */
+ file_size = avio_tell(pb);
+ avio_seek(pb, aiff->form, SEEK_SET);
+ avio_wb32(pb, file_size - aiff->form - 4);
+
avio_flush(pb);
}
+ while (pict_list) {
+ AVPacketList *next = pict_list->next;
+ av_free_packet(&pict_list->pkt);
+ av_freep(&pict_list);
+ pict_list = next;
+ }
+
return 0;
}
+#define OFFSET(x) offsetof(AIFFOutputContext, x)
+#define ENC AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ { "write_id3v2", "Enable ID3 tags writing.",
+ OFFSET(write_id3v2), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, ENC },
+ { "id3v2_version", "Select ID3v2 version to write. Currently 3 and 4 are supported.",
+ OFFSET(id3v2_version), AV_OPT_TYPE_INT, {.i64 = 4}, 3, 4, ENC },
+ { NULL },
+};
+
+static const AVClass aiff_muxer_class = {
+ .class_name = "AIFF muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVOutputFormat ff_aiff_muxer = {
.name = "aiff",
.long_name = NULL_IF_CONFIG_SMALL("Audio IFF"),
@@ -158,9 +325,10 @@ AVOutputFormat ff_aiff_muxer = {
.extensions = "aif,aiff,afc,aifc",
.priv_data_size = sizeof(AIFFOutputContext),
.audio_codec = AV_CODEC_ID_PCM_S16BE,
- .video_codec = AV_CODEC_ID_NONE,
+ .video_codec = AV_CODEC_ID_PNG,
.write_header = aiff_write_header,
.write_packet = aiff_write_packet,
.write_trailer = aiff_write_trailer,
.codec_tag = (const AVCodecTag* const []){ ff_codec_aiff_tags, 0 },
+ .priv_class = &aiff_muxer_class,
};
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index cb22ae3836..ad2e649e54 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -2,20 +2,20 @@
* Register all the formats and protocols
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -62,56 +62,79 @@ void av_register_all(void)
REGISTER_MUXER (A64, a64);
REGISTER_DEMUXER (AAC, aac);
REGISTER_MUXDEMUX(AC3, ac3);
+ REGISTER_DEMUXER (ACT, act);
+ REGISTER_DEMUXER (ADF, adf);
+ REGISTER_DEMUXER (ADP, adp);
REGISTER_MUXER (ADTS, adts);
REGISTER_MUXDEMUX(ADX, adx);
REGISTER_DEMUXER (AEA, aea);
+ REGISTER_DEMUXER (AFC, afc);
REGISTER_MUXDEMUX(AIFF, aiff);
REGISTER_MUXDEMUX(AMR, amr);
REGISTER_DEMUXER (ANM, anm);
REGISTER_DEMUXER (APC, apc);
REGISTER_DEMUXER (APE, ape);
+ REGISTER_DEMUXER (APNG, apng);
+ REGISTER_DEMUXER (AQTITLE, aqtitle);
REGISTER_MUXDEMUX(ASF, asf);
REGISTER_MUXDEMUX(ASS, ass);
+ REGISTER_MUXDEMUX(AST, ast);
REGISTER_MUXER (ASF_STREAM, asf_stream);
REGISTER_MUXDEMUX(AU, au);
REGISTER_MUXDEMUX(AVI, avi);
REGISTER_DEMUXER (AVISYNTH, avisynth);
REGISTER_MUXER (AVM2, avm2);
+ REGISTER_DEMUXER (AVR, avr);
REGISTER_DEMUXER (AVS, avs);
REGISTER_DEMUXER (BETHSOFTVID, bethsoftvid);
REGISTER_DEMUXER (BFI, bfi);
+ REGISTER_DEMUXER (BINTEXT, bintext);
REGISTER_DEMUXER (BINK, bink);
+ REGISTER_MUXDEMUX(BIT, bit);
REGISTER_DEMUXER (BMV, bmv);
+ REGISTER_DEMUXER (BRSTM, brstm);
+ REGISTER_DEMUXER (BOA, boa);
REGISTER_DEMUXER (C93, c93);
- REGISTER_DEMUXER (CAF, caf);
+ REGISTER_MUXDEMUX(CAF, caf);
REGISTER_MUXDEMUX(CAVSVIDEO, cavsvideo);
REGISTER_DEMUXER (CDG, cdg);
REGISTER_DEMUXER (CDXL, cdxl);
+ REGISTER_DEMUXER (CINE, cine);
+ REGISTER_DEMUXER (CONCAT, concat);
REGISTER_MUXER (CRC, crc);
REGISTER_MUXER (DASH, dash);
+ REGISTER_MUXDEMUX(DATA, data);
REGISTER_MUXDEMUX(DAUD, daud);
REGISTER_DEMUXER (DFA, dfa);
REGISTER_MUXDEMUX(DIRAC, dirac);
REGISTER_MUXDEMUX(DNXHD, dnxhd);
+ REGISTER_DEMUXER (DSF, dsf);
REGISTER_DEMUXER (DSICIN, dsicin);
REGISTER_MUXDEMUX(DTS, dts);
+ REGISTER_DEMUXER (DTSHD, dtshd);
REGISTER_MUXDEMUX(DV, dv);
+ REGISTER_DEMUXER (DVBSUB, dvbsub);
REGISTER_DEMUXER (DXA, dxa);
REGISTER_DEMUXER (EA, ea);
REGISTER_DEMUXER (EA_CDATA, ea_cdata);
REGISTER_MUXDEMUX(EAC3, eac3);
+ REGISTER_DEMUXER (EPAF, epaf);
REGISTER_MUXER (F4V, f4v);
+ REGISTER_MUXDEMUX(FFM, ffm);
REGISTER_MUXDEMUX(FFMETADATA, ffmetadata);
REGISTER_MUXDEMUX(FILMSTRIP, filmstrip);
REGISTER_MUXDEMUX(FLAC, flac);
REGISTER_DEMUXER (FLIC, flic);
REGISTER_MUXDEMUX(FLV, flv);
+ REGISTER_DEMUXER (LIVE_FLV, live_flv);
REGISTER_DEMUXER (FOURXM, fourxm);
REGISTER_MUXER (FRAMECRC, framecrc);
REGISTER_MUXER (FRAMEMD5, framemd5);
+ REGISTER_DEMUXER (FRM, frm);
REGISTER_MUXDEMUX(G722, g722);
- REGISTER_DEMUXER (G723_1, g723_1);
- REGISTER_MUXER (GIF, gif);
+ REGISTER_MUXDEMUX(G723_1, g723_1);
+ REGISTER_DEMUXER (G729, g729);
+ REGISTER_MUXDEMUX(GIF, gif);
REGISTER_DEMUXER (GSM, gsm);
REGISTER_MUXDEMUX(GXF, gxf);
REGISTER_MUXDEMUX(H261, h261);
@@ -121,28 +144,40 @@ void av_register_all(void)
REGISTER_MUXDEMUX(HEVC, hevc);
REGISTER_MUXDEMUX(HLS, hls);
REGISTER_DEMUXER (HNM, hnm);
+ REGISTER_MUXDEMUX(ICO, ico);
REGISTER_DEMUXER (IDCIN, idcin);
+ REGISTER_DEMUXER (IDF, idf);
REGISTER_DEMUXER (IFF, iff);
REGISTER_MUXDEMUX(ILBC, ilbc);
REGISTER_MUXDEMUX(IMAGE2, image2);
REGISTER_MUXDEMUX(IMAGE2PIPE, image2pipe);
+ REGISTER_DEMUXER (IMAGE2_ALIAS_PIX, image2_alias_pix);
+ REGISTER_DEMUXER (IMAGE2_BRENDER_PIX, image2_brender_pix);
REGISTER_DEMUXER (INGENIENT, ingenient);
REGISTER_DEMUXER (IPMOVIE, ipmovie);
REGISTER_MUXER (IPOD, ipod);
+ REGISTER_MUXDEMUX(IRCAM, ircam);
REGISTER_MUXER (ISMV, ismv);
REGISTER_DEMUXER (ISS, iss);
REGISTER_DEMUXER (IV8, iv8);
REGISTER_MUXDEMUX(IVF, ivf);
+ REGISTER_MUXDEMUX(JACOSUB, jacosub);
REGISTER_DEMUXER (JV, jv);
REGISTER_MUXDEMUX(LATM, latm);
REGISTER_DEMUXER (LMLM4, lmlm4);
+ REGISTER_DEMUXER (LOAS, loas);
+ REGISTER_MUXDEMUX(LRC, lrc);
+ REGISTER_DEMUXER (LVF, lvf);
REGISTER_DEMUXER (LXF, lxf);
REGISTER_MUXDEMUX(M4V, m4v);
REGISTER_MUXER (MD5, md5);
REGISTER_MUXDEMUX(MATROSKA, matroska);
REGISTER_MUXER (MATROSKA_AUDIO, matroska_audio);
+ REGISTER_DEMUXER (MGSTS, mgsts);
+ REGISTER_MUXDEMUX(MICRODVD, microdvd);
REGISTER_MUXDEMUX(MJPEG, mjpeg);
REGISTER_MUXDEMUX(MLP, mlp);
+ REGISTER_DEMUXER (MLV, mlv);
REGISTER_DEMUXER (MM, mm);
REGISTER_MUXDEMUX(MMF, mmf);
REGISTER_MUXDEMUX(MOV, mov);
@@ -163,14 +198,18 @@ void av_register_all(void)
REGISTER_DEMUXER (MPEGTSRAW, mpegtsraw);
REGISTER_DEMUXER (MPEGVIDEO, mpegvideo);
REGISTER_MUXER (MPJPEG, mpjpeg);
+ REGISTER_DEMUXER (MPL2, mpl2);
+ REGISTER_DEMUXER (MPSUB, mpsub);
REGISTER_DEMUXER (MSNWC_TCP, msnwc_tcp);
REGISTER_DEMUXER (MTV, mtv);
REGISTER_DEMUXER (MV, mv);
REGISTER_DEMUXER (MVI, mvi);
REGISTER_MUXDEMUX(MXF, mxf);
REGISTER_MUXER (MXF_D10, mxf_d10);
+ REGISTER_MUXER (MXF_OPATOM, mxf_opatom);
REGISTER_DEMUXER (MXG, mxg);
REGISTER_DEMUXER (NC, nc);
+ REGISTER_DEMUXER (NISTSPHERE, nistsphere);
REGISTER_DEMUXER (NSV, nsv);
REGISTER_MUXER (NULL, null);
REGISTER_MUXDEMUX(NUT, nut);
@@ -200,30 +239,40 @@ void av_register_all(void)
REGISTER_MUXDEMUX(PCM_U16BE, pcm_u16be);
REGISTER_MUXDEMUX(PCM_U16LE, pcm_u16le);
REGISTER_MUXDEMUX(PCM_U8, pcm_u8);
+ REGISTER_DEMUXER (PJS, pjs);
REGISTER_DEMUXER (PMP, pmp);
REGISTER_MUXER (PSP, psp);
REGISTER_DEMUXER (PVA, pva);
+ REGISTER_DEMUXER (PVF, pvf);
REGISTER_DEMUXER (QCP, qcp);
REGISTER_DEMUXER (R3D, r3d);
REGISTER_MUXDEMUX(RAWVIDEO, rawvideo);
+ REGISTER_DEMUXER (REALTEXT, realtext);
+ REGISTER_DEMUXER (REDSPARK, redspark);
REGISTER_DEMUXER (RL2, rl2);
REGISTER_MUXDEMUX(RM, rm);
REGISTER_MUXDEMUX(ROQ, roq);
REGISTER_DEMUXER (RPL, rpl);
+ REGISTER_DEMUXER (RSD, rsd);
REGISTER_MUXDEMUX(RSO, rso);
REGISTER_MUXDEMUX(RTP, rtp);
REGISTER_MUXER (RTP_MPEGTS, rtp_mpegts);
REGISTER_MUXDEMUX(RTSP, rtsp);
+ REGISTER_DEMUXER (SAMI, sami);
REGISTER_MUXDEMUX(SAP, sap);
+ REGISTER_DEMUXER (SBG, sbg);
REGISTER_DEMUXER (SDP, sdp);
+ REGISTER_DEMUXER (SDR2, sdr2);
#if CONFIG_RTPDEC
ff_register_rtp_dynamic_payload_handlers();
ff_register_rdt_dynamic_payload_handlers();
#endif
REGISTER_DEMUXER (SEGAFILM, segafilm);
REGISTER_MUXER (SEGMENT, segment);
+ REGISTER_MUXER (SEGMENT, stream_segment);
REGISTER_DEMUXER (SHORTEN, shorten);
REGISTER_DEMUXER (SIFF, siff);
+ REGISTER_DEMUXER (SLN, sln);
REGISTER_DEMUXER (SMACKER, smacker);
REGISTER_MUXDEMUX(SMJPEG, smjpeg);
REGISTER_MUXER (SMOOTHSTREAMING, smoothstreaming);
@@ -234,42 +283,76 @@ void av_register_all(void)
REGISTER_MUXDEMUX(SPDIF, spdif);
REGISTER_MUXDEMUX(SRT, srt);
REGISTER_DEMUXER (STR, str);
+ REGISTER_DEMUXER (STL, stl);
+ REGISTER_DEMUXER (SUBVIEWER1, subviewer1);
+ REGISTER_DEMUXER (SUBVIEWER, subviewer);
+ REGISTER_DEMUXER (SUP, sup);
REGISTER_MUXDEMUX(SWF, swf);
REGISTER_DEMUXER (TAK, tak);
+ REGISTER_MUXER (TEE, tee);
+ REGISTER_DEMUXER (TEDCAPTIONS, tedcaptions);
REGISTER_MUXER (TG2, tg2);
REGISTER_MUXER (TGP, tgp);
REGISTER_DEMUXER (THP, thp);
REGISTER_DEMUXER (TIERTEXSEQ, tiertexseq);
+ REGISTER_MUXER (MKVTIMESTAMP_V2, mkvtimestamp_v2);
REGISTER_DEMUXER (TMV, tmv);
REGISTER_MUXDEMUX(TRUEHD, truehd);
REGISTER_DEMUXER (TTA, tta);
REGISTER_DEMUXER (TXD, txd);
REGISTER_DEMUXER (TTY, tty);
- REGISTER_DEMUXER (VC1, vc1);
+ REGISTER_MUXER (UNCODEDFRAMECRC, uncodedframecrc);
+ REGISTER_MUXDEMUX(VC1, vc1);
REGISTER_MUXDEMUX(VC1T, vc1t);
+ REGISTER_DEMUXER (VIVO, vivo);
REGISTER_DEMUXER (VMD, vmd);
+ REGISTER_DEMUXER (VOBSUB, vobsub);
REGISTER_MUXDEMUX(VOC, voc);
+ REGISTER_DEMUXER (VPLAYER, vplayer);
REGISTER_DEMUXER (VQF, vqf);
- REGISTER_DEMUXER (W64, w64);
+ REGISTER_MUXDEMUX(W64, w64);
REGISTER_MUXDEMUX(WAV, wav);
REGISTER_DEMUXER (WC3, wc3);
REGISTER_MUXER (WEBM, webm);
+ REGISTER_MUXDEMUX(WEBM_DASH_MANIFEST, webm_dash_manifest);
+ REGISTER_MUXER (WEBP, webp);
+ REGISTER_MUXDEMUX(WEBVTT, webvtt);
REGISTER_DEMUXER (WSAUD, wsaud);
REGISTER_DEMUXER (WSVQA, wsvqa);
- REGISTER_DEMUXER (WTV, wtv);
+ REGISTER_MUXDEMUX(WTV, wtv);
REGISTER_MUXDEMUX(WV, wv);
REGISTER_DEMUXER (XA, xa);
+ REGISTER_DEMUXER (XBIN, xbin);
REGISTER_DEMUXER (XMV, xmv);
REGISTER_DEMUXER (XWMA, xwma);
REGISTER_DEMUXER (YOP, yop);
REGISTER_MUXDEMUX(YUV4MPEGPIPE, yuv4mpegpipe);
+ /* image demuxers */
+ REGISTER_DEMUXER (IMAGE_BMP_PIPE, image_bmp_pipe);
+ REGISTER_DEMUXER (IMAGE_DPX_PIPE, image_dpx_pipe);
+ REGISTER_DEMUXER (IMAGE_EXR_PIPE, image_exr_pipe);
+ REGISTER_DEMUXER (IMAGE_J2K_PIPE, image_j2k_pipe);
+ REGISTER_DEMUXER (IMAGE_JPEG_PIPE, image_jpeg_pipe);
+ REGISTER_DEMUXER (IMAGE_JPEGLS_PIPE, image_jpegls_pipe);
+ REGISTER_DEMUXER (IMAGE_PICTOR_PIPE, image_pictor_pipe);
+ REGISTER_DEMUXER (IMAGE_PNG_PIPE, image_png_pipe);
+ REGISTER_DEMUXER (IMAGE_SGI_PIPE, image_sgi_pipe);
+ REGISTER_DEMUXER (IMAGE_SUNRAST_PIPE, image_sunrast_pipe);
+ REGISTER_DEMUXER (IMAGE_TIFF_PIPE, image_tiff_pipe);
+ REGISTER_DEMUXER (IMAGE_WEBP_PIPE, image_webp_pipe);
+
+
/* protocols */
+ REGISTER_PROTOCOL(BLURAY, bluray);
+ REGISTER_PROTOCOL(CACHE, cache);
REGISTER_PROTOCOL(CONCAT, concat);
REGISTER_PROTOCOL(CRYPTO, crypto);
+ REGISTER_PROTOCOL(DATA, data);
REGISTER_PROTOCOL(FFRTMPCRYPT, ffrtmpcrypt);
REGISTER_PROTOCOL(FFRTMPHTTP, ffrtmphttp);
REGISTER_PROTOCOL(FILE, file);
+ REGISTER_PROTOCOL(FTP, ftp);
REGISTER_PROTOCOL(GOPHER, gopher);
REGISTER_PROTOCOL(HLS, hls);
REGISTER_PROTOCOL(HTTP, http);
@@ -289,15 +372,23 @@ void av_register_all(void)
REGISTER_PROTOCOL(RTP, rtp);
REGISTER_PROTOCOL(SCTP, sctp);
REGISTER_PROTOCOL(SRTP, srtp);
+ REGISTER_PROTOCOL(SUBFILE, subfile);
REGISTER_PROTOCOL(TCP, tcp);
REGISTER_PROTOCOL(TLS, tls);
REGISTER_PROTOCOL(UDP, udp);
+ REGISTER_PROTOCOL(UDPLITE, udplite);
REGISTER_PROTOCOL(UNIX, unix);
/* external libraries */
+ REGISTER_DEMUXER (LIBGME, libgme);
+ REGISTER_DEMUXER (LIBMODPLUG, libmodplug);
+ REGISTER_MUXDEMUX(LIBNUT, libnut);
+ REGISTER_DEMUXER (LIBQUVI, libquvi);
REGISTER_PROTOCOL(LIBRTMP, librtmp);
REGISTER_PROTOCOL(LIBRTMPE, librtmpe);
REGISTER_PROTOCOL(LIBRTMPS, librtmps);
REGISTER_PROTOCOL(LIBRTMPT, librtmpt);
REGISTER_PROTOCOL(LIBRTMPTE, librtmpte);
+ REGISTER_PROTOCOL(LIBSSH, libssh);
+ REGISTER_PROTOCOL(LIBSMBCLIENT, libsmbclient);
}
diff --git a/libavformat/amr.c b/libavformat/amr.c
index afd062ee36..8f44de1b38 100644
--- a/libavformat/amr.c
+++ b/libavformat/amr.c
@@ -2,20 +2,20 @@
* amr file format
* Copyright (c) 2001 ffmpeg project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -30,6 +30,11 @@ Only mono files are supported.
#include "avformat.h"
#include "internal.h"
+typedef struct {
+ uint64_t cumulated_size;
+ uint64_t block_count;
+} AMRContext;
+
static const char AMR_header[] = "#!AMR\n";
static const char AMRWB_header[] = "#!AMR-WB\n";
@@ -110,12 +115,13 @@ static int amr_read_packet(AVFormatContext *s, AVPacket *pkt)
AVCodecContext *enc = s->streams[0]->codec;
int read, size = 0, toc, mode;
int64_t pos = avio_tell(s->pb);
+ AMRContext *amr = s->priv_data;
- if (s->pb->eof_reached) {
+ if (avio_feof(s->pb)) {
return AVERROR(EIO);
}
- // FIXME this is wrong, this should rather be in a AVParset
+ // FIXME this is wrong, this should rather be in a AVParser
toc = avio_r8(s->pb);
mode = (toc >> 3) & 0x0F;
@@ -131,15 +137,16 @@ static int amr_read_packet(AVFormatContext *s, AVPacket *pkt)
};
size = packed_size[mode];
- } else {
- assert(0);
}
if (!size || av_new_packet(pkt, size))
return AVERROR(EIO);
- /* Both AMR formats have 50 frames per second */
- s->streams[0]->codec->bit_rate = size*8*50;
+ if (amr->cumulated_size < UINT64_MAX - size) {
+ amr->cumulated_size += size;
+ /* Both AMR formats have 50 frames per second */
+ s->streams[0]->codec->bit_rate = amr->cumulated_size / ++amr->block_count * 8 * 50;
+ }
pkt->stream_index = 0;
pkt->pos = pos;
@@ -159,6 +166,7 @@ static int amr_read_packet(AVFormatContext *s, AVPacket *pkt)
AVInputFormat ff_amr_demuxer = {
.name = "amr",
.long_name = NULL_IF_CONFIG_SMALL("3GPP AMR"),
+ .priv_data_size = sizeof(AMRContext),
.read_probe = amr_probe,
.read_header = amr_read_header,
.read_packet = amr_read_packet,
diff --git a/libavformat/anm.c b/libavformat/anm.c
index 6b8e3066f3..f718797310 100644
--- a/libavformat/anm.c
+++ b/libavformat/anm.c
@@ -2,20 +2,20 @@
* Deluxe Paint Animation demuxer
* Copyright (c) 2009 Peter Ross
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -175,7 +175,7 @@ static int read_packet(AVFormatContext *s,
Page *p;
int tmp, record_size;
- if (s->pb->eof_reached)
+ if (avio_feof(s->pb))
return AVERROR(EIO);
if (anm->page < 0)
diff --git a/libavformat/apc.c b/libavformat/apc.c
index 0b6c5836c1..21bb514cd0 100644
--- a/libavformat/apc.c
+++ b/libavformat/apc.c
@@ -2,20 +2,20 @@
* CRYO APC audio format demuxer
* Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -23,6 +23,7 @@
#include "libavutil/channel_layout.h"
#include "avformat.h"
+#include "internal.h"
static int apc_probe(AVProbeData *p)
{
@@ -51,14 +52,9 @@ static int apc_read_header(AVFormatContext *s)
avio_rl32(pb); /* number of samples */
st->codec->sample_rate = avio_rl32(pb);
- st->codec->extradata_size = 2 * 4;
- st->codec->extradata = av_malloc(st->codec->extradata_size +
- FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
- return AVERROR(ENOMEM);
-
/* initial predictor values for adpcm decoder */
- avio_read(pb, st->codec->extradata, 2 * 4);
+ if (ff_get_extradata(st->codec, pb, 2 * 4) < 0)
+ return AVERROR(ENOMEM);
if (avio_rl32(pb)) {
st->codec->channels = 2;
@@ -82,6 +78,7 @@ static int apc_read_packet(AVFormatContext *s, AVPacket *pkt)
{
if (av_get_packet(s->pb, pkt, MAX_READ_SIZE) <= 0)
return AVERROR(EIO);
+ pkt->flags &= ~AV_PKT_FLAG_CORRUPT;
pkt->stream_index = 0;
return 0;
}
diff --git a/libavformat/ape.c b/libavformat/ape.c
index 09a756da60..28f73d3072 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 APEFrame {
@@ -88,10 +86,14 @@ typedef struct APEContext {
static int ape_probe(AVProbeData * p)
{
- if (p->buf[0] == 'M' && p->buf[1] == 'A' && p->buf[2] == 'C' && p->buf[3] == ' ')
- return AVPROBE_SCORE_MAX;
+ int version = AV_RL16(p->buf+4);
+ if (AV_RL32(p->buf) != MKTAG('M', 'A', 'C', ' '))
+ return 0;
- return 0;
+ if (version < APE_MIN_VERSION || version > APE_MAX_VERSION)
+ return AVPROBE_SCORE_MAX/4;
+
+ return AVPROBE_SCORE_MAX;
}
static void ape_dumpinfo(AVFormatContext * s, APEContext * ape_ctx)
@@ -172,14 +174,14 @@ static int ape_read_header(AVFormatContext * s)
tag = avio_rl32(pb);
if (tag != MKTAG('M', 'A', 'C', ' '))
- return -1;
+ return AVERROR_INVALIDDATA;
ape->fileversion = avio_rl16(pb);
if (ape->fileversion < APE_MIN_VERSION || ape->fileversion > APE_MAX_VERSION) {
av_log(s, AV_LOG_ERROR, "Unsupported file version - %d.%02d\n",
ape->fileversion / 1000, (ape->fileversion % 1000) / 10);
- return -1;
+ return AVERROR_PATCHWELCOME;
}
if (ape->fileversion >= 3980) {
@@ -258,11 +260,11 @@ static int ape_read_header(AVFormatContext * s)
if(ape->totalframes > UINT_MAX / sizeof(APEFrame)){
av_log(s, AV_LOG_ERROR, "Too many frames: %"PRIu32"\n",
ape->totalframes);
- return -1;
+ return AVERROR_INVALIDDATA;
}
if (ape->seektablelength / sizeof(*ape->seektable) < ape->totalframes) {
av_log(s, AV_LOG_ERROR,
- "Number of seek entries is less than number of frames: %zu vs. %"PRIu32"\n",
+ "Number of seek entries is less than number of frames: %"SIZE_SPECIFIER" vs. %"PRIu32"\n",
ape->seektablelength / sizeof(*ape->seektable), ape->totalframes);
return AVERROR_INVALIDDATA;
}
@@ -280,18 +282,20 @@ static int ape_read_header(AVFormatContext * s)
ape->totalsamples += ape->blocksperframe * (ape->totalframes - 1);
if (ape->seektablelength > 0) {
- ape->seektable = av_malloc(ape->seektablelength);
+ ape->seektable = av_mallocz(ape->seektablelength);
if (!ape->seektable)
return AVERROR(ENOMEM);
for (i = 0; i < ape->seektablelength / sizeof(uint32_t) && !pb->eof_reached; i++)
ape->seektable[i] = avio_rl32(pb);
if (ape->fileversion < 3810) {
- ape->bittable = av_malloc(ape->totalframes);
+ ape->bittable = av_mallocz(ape->totalframes);
if (!ape->bittable)
return AVERROR(ENOMEM);
for (i = 0; i < ape->totalframes && !pb->eof_reached; i++)
ape->bittable[i] = avio_r8(pb);
}
+ if (pb->eof_reached)
+ av_log(s, AV_LOG_WARNING, "File truncated\n");
}
ape->frames[0].pos = ape->firstframe;
@@ -340,7 +344,7 @@ static int ape_read_header(AVFormatContext * s)
/* now we are ready: build format streams */
st = avformat_new_stream(s, NULL);
if (!st)
- return -1;
+ return AVERROR(ENOMEM);
total_blocks = (ape->totalframes == 0) ? 0 : ((ape->totalframes - 1) * ape->blocksperframe) + ape->finalframeblocks;
@@ -353,11 +357,11 @@ static int ape_read_header(AVFormatContext * s)
st->nb_frames = ape->totalframes;
st->start_time = 0;
- st->duration = total_blocks / MAC_SUBFRAME_SIZE;
- avpriv_set_pts_info(st, 64, MAC_SUBFRAME_SIZE, ape->samplerate);
+ st->duration = total_blocks;
+ avpriv_set_pts_info(st, 64, 1, ape->samplerate);
- st->codec->extradata = av_malloc(APE_EXTRADATA_SIZE);
- st->codec->extradata_size = APE_EXTRADATA_SIZE;
+ if (ff_alloc_extradata(st->codec, APE_EXTRADATA_SIZE))
+ return AVERROR(ENOMEM);
AV_WL16(st->codec->extradata + 0, ape->fileversion);
AV_WL16(st->codec->extradata + 2, ape->compressiontype);
AV_WL16(st->codec->extradata + 4, ape->formatflags);
@@ -366,7 +370,7 @@ static int ape_read_header(AVFormatContext * s)
for (i = 0; i < ape->totalframes; i++) {
ape->frames[i].pts = pts;
av_add_index_entry(st, ape->frames[i].pos, ape->frames[i].pts, 0, 0, AVINDEX_KEYFRAME);
- pts += ape->blocksperframe / MAC_SUBFRAME_SIZE;
+ pts += ape->blocksperframe;
}
/* try to read APE tags */
@@ -385,7 +389,7 @@ static int ape_read_packet(AVFormatContext * s, AVPacket * pkt)
APEContext *ape = s->priv_data;
uint32_t extra_size = 8;
- if (s->pb->eof_reached)
+ if (avio_feof(s->pb))
return AVERROR_EOF;
if (ape->currentframe >= ape->totalframes)
return AVERROR_EOF;
@@ -413,6 +417,10 @@ static int ape_read_packet(AVFormatContext * s, AVPacket * pkt)
AV_WL32(pkt->data , nblocks);
AV_WL32(pkt->data + 4, ape->frames[ape->currentframe].skip);
ret = avio_read(s->pb, pkt->data + extra_size, ape->frames[ape->currentframe].size);
+ if (ret < 0) {
+ av_free_packet(pkt);
+ return ret;
+ }
pkt->pts = ape->frames[ape->currentframe].pts;
pkt->stream_index = 0;
diff --git a/libavformat/apetag.c b/libavformat/apetag.c
index bd8d0ed485..258bdaa1b6 100644
--- a/libavformat/apetag.c
+++ b/libavformat/apetag.c
@@ -3,20 +3,20 @@
* Copyright (c) 2007 Benjamin Zores <ben@geexbox.org>
* based upon libdemac from Dave Chapman.
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -29,8 +29,6 @@
#include "apetag.h"
#include "internal.h"
-#define APE_TAG_VERSION 2000
-#define APE_TAG_FOOTER_BYTES 32
#define APE_TAG_FLAG_CONTAINS_HEADER (1 << 31)
#define APE_TAG_FLAG_CONTAINS_FOOTER (1 << 30)
#define APE_TAG_FLAG_IS_HEADER (1 << 29)
@@ -64,15 +62,19 @@ static int ape_tag_read_field(AVFormatContext *s)
if (flags & APE_TAG_FLAG_IS_BINARY) {
uint8_t filename[1024];
enum AVCodecID id;
+ int ret;
AVStream *st = avformat_new_stream(s, NULL);
if (!st)
return AVERROR(ENOMEM);
- size -= avio_get_str(pb, size, filename, sizeof(filename));
- if (size <= 0) {
+ ret = avio_get_str(pb, size, filename, sizeof(filename));
+ if (ret < 0)
+ return ret;
+ if (size <= ret) {
av_log(s, AV_LOG_WARNING, "Skipping binary tag '%s'.\n", key);
return 0;
}
+ size -= ret;
av_dict_set(&st->metadata, key, filename, 0);
@@ -94,14 +96,8 @@ static int ape_tag_read_field(AVFormatContext *s)
st->attached_pic.stream_index = st->index;
st->attached_pic.flags |= AV_PKT_FLAG_KEY;
} else {
- st->codec->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
+ if (ff_get_extradata(st->codec, s->pb, size) < 0)
return AVERROR(ENOMEM);
- if (avio_read(pb, st->codec->extradata, size) != size) {
- av_freep(&st->codec->extradata);
- return AVERROR(EIO);
- }
- st->codec->extradata_size = size;
st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT;
}
} else {
@@ -134,7 +130,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;
}
@@ -176,43 +172,61 @@ int64_t ff_ape_parse_tag(AVFormatContext *s)
return tag_start;
}
+static int string_is_ascii(const uint8_t *str)
+{
+ while (*str && *str >= 0x20 && *str <= 0x7e ) str++;
+ return !*str;
+}
+
int ff_ape_write_tag(AVFormatContext *s)
{
AVDictionaryEntry *e = NULL;
- int64_t start, end;
- int size, count = 0;
-
- if (!s->pb->seekable)
- return 0;
+ int size, ret, count = 0;
+ AVIOContext *dyn_bc = NULL;
+ uint8_t *dyn_buf = NULL;
- start = avio_tell(s->pb);
-
- // header
- avio_write(s->pb, "APETAGEX", 8); // id
- avio_wl32 (s->pb, APE_TAG_VERSION); // version
- avio_wl32(s->pb, 0); // reserve space for size
- avio_wl32(s->pb, 0); // reserve space for tag count
+ if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0)
+ goto end;
// flags
- avio_wl32(s->pb, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_CONTAINS_FOOTER |
+ avio_wl32(dyn_bc, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_CONTAINS_FOOTER |
APE_TAG_FLAG_IS_HEADER);
- ffio_fill(s->pb, 0, 8); // reserved
+ ffio_fill(dyn_bc, 0, 8); // reserved
while ((e = av_dict_get(s->metadata, "", e, AV_DICT_IGNORE_SUFFIX))) {
- int val_len = strlen(e->value);
+ int val_len;
- avio_wl32(s->pb, val_len); // value length
- avio_wl32(s->pb, 0); // item flags
- avio_put_str(s->pb, e->key); // key
- avio_write(s->pb, e->value, val_len); // value
+ if (!string_is_ascii(e->key)) {
+ av_log(s, AV_LOG_WARNING, "Non ASCII keys are not allowed\n");
+ continue;
+ }
+
+ val_len = strlen(e->value);
+ avio_wl32(dyn_bc, val_len); // value length
+ avio_wl32(dyn_bc, 0); // item flags
+ avio_put_str(dyn_bc, e->key); // key
+ avio_write(dyn_bc, e->value, val_len); // value
count++;
}
+ if (!count)
+ goto end;
+
+ size = avio_close_dyn_buf(dyn_bc, &dyn_buf);
+ if (size <= 0)
+ goto end;
+ size += 20;
+
+ // header
+ avio_write(s->pb, "APETAGEX", 8); // id
+ avio_wl32(s->pb, APE_TAG_VERSION); // version
+ avio_wl32(s->pb, size);
+ avio_wl32(s->pb, count);
- size = avio_tell(s->pb) - start;
+ avio_write(s->pb, dyn_buf, size - 20);
// footer
avio_write(s->pb, "APETAGEX", 8); // id
- avio_wl32 (s->pb, APE_TAG_VERSION); // version
+ avio_wl32(s->pb, APE_TAG_VERSION); // version
avio_wl32(s->pb, size); // size
avio_wl32(s->pb, count); // tag count
@@ -220,12 +234,10 @@ int ff_ape_write_tag(AVFormatContext *s)
avio_wl32(s->pb, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_CONTAINS_FOOTER);
ffio_fill(s->pb, 0, 8); // reserved
- // update values in the header
- end = avio_tell(s->pb);
- avio_seek(s->pb, start + 12, SEEK_SET);
- avio_wl32(s->pb, size);
- avio_wl32(s->pb, count);
- avio_seek(s->pb, end, SEEK_SET);
+end:
+ if (dyn_bc && !dyn_buf)
+ avio_close_dyn_buf(dyn_bc, &dyn_buf);
+ av_freep(&dyn_buf);
- return 0;
+ return ret;
}
diff --git a/libavformat/apetag.h b/libavformat/apetag.h
index 36e3211fc8..cf2a5f8ab4 100644
--- a/libavformat/apetag.h
+++ b/libavformat/apetag.h
@@ -3,20 +3,20 @@
* Copyright (c) 2007 Benjamin Zores <ben@geexbox.org>
* based upon libdemac from Dave Chapman.
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -25,6 +25,10 @@
#include "avformat.h"
+#define APE_TAG_PREAMBLE "APETAGEX"
+#define APE_TAG_VERSION 2000
+#define APE_TAG_FOOTER_BYTES 32
+
/**
* Read and parse an APE tag
*
diff --git a/libavformat/apngdec.c b/libavformat/apngdec.c
new file mode 100644
index 0000000000..5e7a4a1150
--- /dev/null
+++ b/libavformat/apngdec.c
@@ -0,0 +1,446 @@
+/*
+ * APNG demuxer
+ * Copyright (c) 2014 Benoit Fouet
+ *
+ * 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
+ * APNG demuxer.
+ * @see https://wiki.mozilla.org/APNG_Specification
+ * @see http://www.w3.org/TR/PNG
+ */
+
+#include "avformat.h"
+#include "avio_internal.h"
+#include "internal.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+#include "libavcodec/apng.h"
+#include "libavcodec/png.h"
+#include "libavcodec/bytestream.h"
+
+#define DEFAULT_APNG_FPS 15
+
+typedef struct APNGDemuxContext {
+ const AVClass *class;
+
+ int max_fps;
+ int default_fps;
+
+ int64_t pkt_pts;
+ int pkt_duration;
+
+ int is_key_frame;
+
+ /*
+ * loop options
+ */
+ int ignore_loop;
+ uint32_t num_frames;
+ uint32_t num_play;
+ uint32_t cur_loop;
+} APNGDemuxContext;
+
+/*
+ * To be a valid APNG file, we mandate, in this order:
+ * PNGSIG
+ * IHDR
+ * ...
+ * acTL
+ * ...
+ * IDAT
+ */
+static int apng_probe(AVProbeData *p)
+{
+ GetByteContext gb;
+ int state = 0;
+ uint32_t len, tag;
+
+ bytestream2_init(&gb, p->buf, p->buf_size);
+
+ if (bytestream2_get_be64(&gb) != PNGSIG)
+ return 0;
+
+ for (;;) {
+ len = bytestream2_get_be32(&gb);
+ if (len > 0x7fffffff)
+ return 0;
+
+ tag = bytestream2_get_le32(&gb);
+ /* we don't check IDAT size, as this is the last tag
+ * we check, and it may be larger than the probe buffer */
+ if (tag != MKTAG('I', 'D', 'A', 'T') &&
+ len + 4 > bytestream2_get_bytes_left(&gb))
+ return 0;
+
+ switch (tag) {
+ case MKTAG('I', 'H', 'D', 'R'):
+ if (len != 13)
+ return 0;
+ if (av_image_check_size(bytestream2_get_be32(&gb), bytestream2_get_be32(&gb), 0, NULL))
+ return 0;
+ bytestream2_skip(&gb, 9);
+ state++;
+ break;
+ case MKTAG('a', 'c', 'T', 'L'):
+ if (state != 1 ||
+ len != 8 ||
+ bytestream2_get_be32(&gb) == 0) /* 0 is not a valid value for number of frames */
+ return 0;
+ bytestream2_skip(&gb, 8);
+ state++;
+ break;
+ case MKTAG('I', 'D', 'A', 'T'):
+ if (state != 2)
+ return 0;
+ goto end;
+ default:
+ /* skip other tags */
+ bytestream2_skip(&gb, len + 4);
+ break;
+ }
+ }
+
+end:
+ return AVPROBE_SCORE_MAX;
+}
+
+static int append_extradata(AVCodecContext *s, AVIOContext *pb, int len)
+{
+ int previous_size = s->extradata_size;
+ int new_size, ret;
+ uint8_t *new_extradata;
+
+ if (previous_size > INT_MAX - len)
+ return AVERROR_INVALIDDATA;
+
+ new_size = previous_size + len;
+ new_extradata = av_realloc(s->extradata, new_size + FF_INPUT_BUFFER_PADDING_SIZE);
+ if (!new_extradata)
+ return AVERROR(ENOMEM);
+ s->extradata = new_extradata;
+ s->extradata_size = new_size;
+
+ if ((ret = avio_read(pb, s->extradata + previous_size, len)) < 0)
+ return ret;
+
+ return previous_size;
+}
+
+static int apng_read_header(AVFormatContext *s)
+{
+ APNGDemuxContext *ctx = s->priv_data;
+ AVIOContext *pb = s->pb;
+ uint32_t len, tag;
+ AVStream *st;
+ int ret = AVERROR_INVALIDDATA, acTL_found = 0;
+
+ /* verify PNGSIG */
+ if (avio_rb64(pb) != PNGSIG)
+ return ret;
+
+ /* parse IHDR (must be first chunk) */
+ len = avio_rb32(pb);
+ tag = avio_rl32(pb);
+ if (len != 13 || tag != MKTAG('I', 'H', 'D', 'R'))
+ return ret;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ /* set the timebase to something large enough (1/100,000 of second)
+ * to hopefully cope with all sane frame durations */
+ avpriv_set_pts_info(st, 64, 1, 100000);
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codec->codec_id = AV_CODEC_ID_APNG;
+ st->codec->width = avio_rb32(pb);
+ st->codec->height = avio_rb32(pb);
+ if ((ret = av_image_check_size(st->codec->width, st->codec->height, 0, s)) < 0)
+ return ret;
+
+ /* extradata will contain every chunk up to the first fcTL (excluded) */
+ st->codec->extradata = av_malloc(len + 12 + FF_INPUT_BUFFER_PADDING_SIZE);
+ if (!st->codec->extradata)
+ return AVERROR(ENOMEM);
+ st->codec->extradata_size = len + 12;
+ AV_WB32(st->codec->extradata, len);
+ AV_WL32(st->codec->extradata+4, tag);
+ AV_WB32(st->codec->extradata+8, st->codec->width);
+ AV_WB32(st->codec->extradata+12, st->codec->height);
+ if ((ret = avio_read(pb, st->codec->extradata+16, 9)) < 0)
+ goto fail;
+
+ while (!avio_feof(pb)) {
+ if (acTL_found && ctx->num_play != 1) {
+ int64_t size = avio_size(pb);
+ int64_t offset = avio_tell(pb);
+ if (size < 0) {
+ ret = size;
+ goto fail;
+ } else if (offset < 0) {
+ ret = offset;
+ goto fail;
+ } else if ((ret = ffio_ensure_seekback(pb, size - offset)) < 0) {
+ av_log(s, AV_LOG_WARNING, "Could not ensure seekback, will not loop\n");
+ ctx->num_play = 1;
+ }
+ }
+ if ((ctx->num_play == 1 || !acTL_found) &&
+ ((ret = ffio_ensure_seekback(pb, 4 /* len */ + 4 /* tag */)) < 0))
+ goto fail;
+
+ len = avio_rb32(pb);
+ if (len > 0x7fffffff) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ tag = avio_rl32(pb);
+ switch (tag) {
+ case MKTAG('a', 'c', 'T', 'L'):
+ if ((ret = avio_seek(pb, -8, SEEK_CUR)) < 0 ||
+ (ret = append_extradata(st->codec, pb, len + 12)) < 0)
+ goto fail;
+ acTL_found = 1;
+ ctx->num_frames = AV_RB32(st->codec->extradata + ret + 8);
+ ctx->num_play = AV_RB32(st->codec->extradata + ret + 12);
+ av_log(s, AV_LOG_DEBUG, "num_frames: %"PRIu32", num_play: %"PRIu32"\n",
+ ctx->num_frames, ctx->num_play);
+ break;
+ case MKTAG('f', 'c', 'T', 'L'):
+ if (!acTL_found) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ if ((ret = avio_seek(pb, -8, SEEK_CUR)) < 0)
+ goto fail;
+ return 0;
+ default:
+ if ((ret = avio_seek(pb, -8, SEEK_CUR)) < 0 ||
+ (ret = append_extradata(st->codec, pb, len + 12)) < 0)
+ goto fail;
+ }
+ }
+
+fail:
+ if (st->codec->extradata_size) {
+ av_freep(&st->codec->extradata);
+ st->codec->extradata_size = 0;
+ }
+ return ret;
+}
+
+static int decode_fctl_chunk(AVFormatContext *s, APNGDemuxContext *ctx, AVPacket *pkt)
+{
+ uint32_t sequence_number, width, height, x_offset, y_offset;
+ uint16_t delay_num, delay_den;
+ uint8_t dispose_op, blend_op;
+
+ sequence_number = avio_rb32(s->pb);
+ width = avio_rb32(s->pb);
+ height = avio_rb32(s->pb);
+ x_offset = avio_rb32(s->pb);
+ y_offset = avio_rb32(s->pb);
+ delay_num = avio_rb16(s->pb);
+ delay_den = avio_rb16(s->pb);
+ dispose_op = avio_r8(s->pb);
+ blend_op = avio_r8(s->pb);
+ avio_skip(s->pb, 4); /* crc */
+
+ /* default is hundredths of seconds */
+ if (!delay_den)
+ delay_den = 100;
+ if (!delay_num || delay_den / delay_num > ctx->max_fps) {
+ delay_num = 1;
+ delay_den = ctx->default_fps;
+ }
+ ctx->pkt_duration = av_rescale_q(delay_num,
+ (AVRational){ 1, delay_den },
+ s->streams[0]->time_base);
+
+ av_log(s, AV_LOG_DEBUG, "%s: "
+ "sequence_number: %"PRId32", "
+ "width: %"PRIu32", "
+ "height: %"PRIu32", "
+ "x_offset: %"PRIu32", "
+ "y_offset: %"PRIu32", "
+ "delay_num: %"PRIu16", "
+ "delay_den: %"PRIu16", "
+ "dispose_op: %d, "
+ "blend_op: %d\n",
+ __FUNCTION__,
+ sequence_number,
+ width,
+ height,
+ x_offset,
+ y_offset,
+ delay_num,
+ delay_den,
+ dispose_op,
+ blend_op);
+
+ if (width != s->streams[0]->codec->width ||
+ height != s->streams[0]->codec->height ||
+ x_offset != 0 ||
+ y_offset != 0) {
+ if (sequence_number == 0 ||
+ x_offset >= s->streams[0]->codec->width ||
+ width > s->streams[0]->codec->width - x_offset ||
+ y_offset >= s->streams[0]->codec->height ||
+ height > s->streams[0]->codec->height - y_offset)
+ return AVERROR_INVALIDDATA;
+ ctx->is_key_frame = 0;
+ } else {
+ if (sequence_number == 0 && dispose_op == APNG_DISPOSE_OP_PREVIOUS)
+ dispose_op = APNG_DISPOSE_OP_BACKGROUND;
+ ctx->is_key_frame = dispose_op == APNG_DISPOSE_OP_BACKGROUND ||
+ blend_op == APNG_BLEND_OP_SOURCE;
+ }
+
+ return 0;
+}
+
+static int apng_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ APNGDemuxContext *ctx = s->priv_data;
+ int ret;
+ int64_t size;
+ AVIOContext *pb = s->pb;
+ uint32_t len, tag;
+
+ /*
+ * fcTL chunk length, in bytes:
+ * 4 (length)
+ * 4 (tag)
+ * 26 (actual chunk)
+ * 4 (crc) bytes
+ * and needed next:
+ * 4 (length)
+ * 4 (tag (must be fdAT or IDAT))
+ */
+ /* if num_play is not 1, then the seekback is already guaranteed */
+ if (ctx->num_play == 1 && (ret = ffio_ensure_seekback(pb, 46)) < 0)
+ return ret;
+
+ len = avio_rb32(pb);
+ tag = avio_rl32(pb);
+ switch (tag) {
+ case MKTAG('f', 'c', 'T', 'L'):
+ if (len != 26)
+ return AVERROR_INVALIDDATA;
+
+ if ((ret = decode_fctl_chunk(s, ctx, pkt)) < 0)
+ return ret;
+
+ /* fcTL must precede fdAT or IDAT */
+ len = avio_rb32(pb);
+ tag = avio_rl32(pb);
+ if (len > 0x7fffffff ||
+ tag != MKTAG('f', 'd', 'A', 'T') &&
+ tag != MKTAG('I', 'D', 'A', 'T'))
+ return AVERROR_INVALIDDATA;
+
+ size = 38 /* fcTL */ + 8 /* len, tag */ + len + 4 /* crc */;
+ if (size > INT_MAX)
+ return AVERROR(EINVAL);
+
+ if ((ret = avio_seek(pb, -46, SEEK_CUR)) < 0 ||
+ (ret = av_append_packet(pb, pkt, size)) < 0)
+ return ret;
+
+ if (ctx->num_play == 1 && (ret = ffio_ensure_seekback(pb, 8)) < 0)
+ return ret;
+
+ len = avio_rb32(pb);
+ tag = avio_rl32(pb);
+ while (tag &&
+ tag != MKTAG('f', 'c', 'T', 'L') &&
+ tag != MKTAG('I', 'E', 'N', 'D')) {
+ if (len > 0x7fffffff)
+ return AVERROR_INVALIDDATA;
+ if ((ret = avio_seek(pb, -8, SEEK_CUR)) < 0 ||
+ (ret = av_append_packet(pb, pkt, len + 12)) < 0)
+ return ret;
+ if (ctx->num_play == 1 && (ret = ffio_ensure_seekback(pb, 8)) < 0)
+ return ret;
+ len = avio_rb32(pb);
+ tag = avio_rl32(pb);
+ }
+ if ((ret = avio_seek(pb, -8, SEEK_CUR)) < 0)
+ return ret;
+
+ if (ctx->is_key_frame)
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ pkt->pts = ctx->pkt_pts;
+ pkt->duration = ctx->pkt_duration;
+ ctx->pkt_pts += ctx->pkt_duration;
+ return ret;
+ case MKTAG('I', 'E', 'N', 'D'):
+ ctx->cur_loop++;
+ if (ctx->ignore_loop || ctx->num_play >= 1 && ctx->cur_loop == ctx->num_play) {
+ avio_seek(pb, -8, SEEK_CUR);
+ return AVERROR_EOF;
+ }
+ if ((ret = avio_seek(pb, s->streams[0]->codec->extradata_size + 8, SEEK_SET)) < 0)
+ return ret;
+ return 0;
+ default:
+ {
+ char tag_buf[32];
+
+ av_get_codec_tag_string(tag_buf, sizeof(tag_buf), tag);
+ avpriv_request_sample(s, "In-stream tag=%s (0x%08X) len=%"PRIu32, tag_buf, tag, len);
+ avio_skip(pb, len + 4);
+ }
+ }
+
+ /* Handle the unsupported yet cases */
+ return AVERROR_PATCHWELCOME;
+}
+
+static const AVOption options[] = {
+ { "ignore_loop", "ignore loop setting" , offsetof(APNGDemuxContext, ignore_loop),
+ AV_OPT_TYPE_INT, { .i64 = 1 } , 0, 1 , AV_OPT_FLAG_DECODING_PARAM },
+ { "max_fps" , "maximum framerate (0 is no limit)" , offsetof(APNGDemuxContext, max_fps),
+ AV_OPT_TYPE_INT, { .i64 = DEFAULT_APNG_FPS }, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
+ { "default_fps", "default framerate (0 is as fast as possible)", offsetof(APNGDemuxContext, default_fps),
+ AV_OPT_TYPE_INT, { .i64 = DEFAULT_APNG_FPS }, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+static const AVClass demuxer_class = {
+ .class_name = "APNG demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+ .category = AV_CLASS_CATEGORY_DEMUXER,
+};
+
+AVInputFormat ff_apng_demuxer = {
+ .name = "apng",
+ .long_name = NULL_IF_CONFIG_SMALL("Animated Portable Network Graphics"),
+ .priv_data_size = sizeof(APNGDemuxContext),
+ .read_probe = apng_probe,
+ .read_header = apng_read_header,
+ .read_packet = apng_read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+ .priv_class = &demuxer_class,
+};
diff --git a/libavformat/aqtitledec.c b/libavformat/aqtitledec.c
new file mode 100644
index 0000000000..95087665aa
--- /dev/null
+++ b/libavformat/aqtitledec.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2012 Clément Bœsch
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * AQTitle subtitles format demuxer
+ *
+ * @see http://web.archive.org/web/20070210095721/http://www.volny.cz/aberka/czech/aqt.html
+ * @see https://trac.annodex.net/wiki/AQTitle
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "subtitles.h"
+#include "libavutil/opt.h"
+
+typedef struct {
+ const AVClass *class;
+ FFDemuxSubtitlesQueue q;
+ AVRational frame_rate;
+} AQTitleContext;
+
+static int aqt_probe(AVProbeData *p)
+{
+ int frame;
+ const char *ptr = p->buf;
+
+ if (sscanf(ptr, "-->> %d", &frame) == 1)
+ return AVPROBE_SCORE_EXTENSION;
+ return 0;
+}
+
+static int aqt_read_header(AVFormatContext *s)
+{
+ AQTitleContext *aqt = s->priv_data;
+ AVStream *st = avformat_new_stream(s, NULL);
+ int new_event = 1;
+ int64_t pos = 0, frame = AV_NOPTS_VALUE;
+ AVPacket *sub = NULL;
+
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 64, aqt->frame_rate.den, aqt->frame_rate.num);
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_TEXT;
+
+ while (!avio_feof(s->pb)) {
+ char line[4096];
+ int len = ff_get_line(s->pb, line, sizeof(line));
+
+ if (!len)
+ break;
+
+ line[strcspn(line, "\r\n")] = 0;
+
+ if (sscanf(line, "-->> %"SCNd64, &frame) == 1) {
+ new_event = 1;
+ pos = avio_tell(s->pb);
+ if (sub) {
+ sub->duration = frame - sub->pts;
+ sub = NULL;
+ }
+ } else if (*line) {
+ if (!new_event) {
+ sub = ff_subtitles_queue_insert(&aqt->q, "\n", 1, 1);
+ if (!sub)
+ return AVERROR(ENOMEM);
+ }
+ sub = ff_subtitles_queue_insert(&aqt->q, line, strlen(line), !new_event);
+ if (!sub)
+ return AVERROR(ENOMEM);
+ if (new_event) {
+ sub->pts = frame;
+ sub->duration = -1;
+ sub->pos = pos;
+ }
+ new_event = 0;
+ }
+ }
+
+ ff_subtitles_queue_finalize(&aqt->q);
+ return 0;
+}
+
+static int aqt_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AQTitleContext *aqt = s->priv_data;
+ return ff_subtitles_queue_read_packet(&aqt->q, pkt);
+}
+
+static int aqt_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ AQTitleContext *aqt = s->priv_data;
+ return ff_subtitles_queue_seek(&aqt->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int aqt_read_close(AVFormatContext *s)
+{
+ AQTitleContext *aqt = s->priv_data;
+ ff_subtitles_queue_clean(&aqt->q);
+ return 0;
+}
+
+#define OFFSET(x) offsetof(AQTitleContext, x)
+#define SD AV_OPT_FLAG_SUBTITLE_PARAM|AV_OPT_FLAG_DECODING_PARAM
+static const AVOption aqt_options[] = {
+ { "subfps", "set the movie frame rate", OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, {.dbl=25}, 0, INT_MAX, SD },
+ { NULL }
+};
+
+static const AVClass aqt_class = {
+ .class_name = "aqtdec",
+ .item_name = av_default_item_name,
+ .option = aqt_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_aqtitle_demuxer = {
+ .name = "aqtitle",
+ .long_name = NULL_IF_CONFIG_SMALL("AQTitle subtitles"),
+ .priv_data_size = sizeof(AQTitleContext),
+ .read_probe = aqt_probe,
+ .read_header = aqt_read_header,
+ .read_packet = aqt_read_packet,
+ .read_seek2 = aqt_read_seek,
+ .read_close = aqt_read_close,
+ .extensions = "aqt",
+ .priv_class = &aqt_class,
+};
diff --git a/libavformat/asf.c b/libavformat/asf.c
index ec34b5010d..80d24dbf43 100644
--- a/libavformat/asf.c
+++ b/libavformat/asf.c
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2000, 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -159,7 +159,6 @@ const AVMetadataConv ff_asf_metadata_conv[] = {
{ "WM/Publisher", "publisher" },
{ "WM/Tool", "encoder" },
{ "WM/TrackNumber", "track" },
- { "WM/Track", "track" },
{ "WM/MediaStationCallSign", "service_provider" },
{ "WM/MediaStationName", "service_name" },
// { "Year" , "date" }, TODO: conversion year<->date
diff --git a/libavformat/asf.h b/libavformat/asf.h
index 2f6722adca..0c9598a8d8 100644
--- a/libavformat/asf.h
+++ b/libavformat/asf.h
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2000, 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -28,14 +28,22 @@
#define PACKET_SIZE 3200
+typedef struct ASFPayload {
+ uint8_t type;
+ uint16_t size;
+} ASFPayload;
+
typedef struct ASFStream {
int num;
unsigned char seq;
/* use for reading */
AVPacket pkt;
int frag_offset;
+ int packet_obj_size;
int timestamp;
int64_t duration;
+ int skip_to_key;
+ int pkt_clean;
int ds_span; /* descrambling */
int ds_packet_size;
@@ -47,6 +55,9 @@ typedef struct ASFStream {
int palette_changed;
uint32_t palette[256];
+
+ int payload_ext_ct;
+ ASFPayload payload[8];
} ASFStream;
typedef struct ASFMainHeader {
diff --git a/libavformat/asfcrypt.c b/libavformat/asfcrypt.c
index c261475848..a402758d0a 100644
--- a/libavformat/asfcrypt.c
+++ b/libavformat/asfcrypt.c
@@ -3,20 +3,20 @@
* Copyright (c) 2007 Reimar Doeffinger
* This is a rewrite of code contained in freeme/freeme2
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/asfcrypt.h b/libavformat/asfcrypt.h
index 53388b426e..8b80d63c5d 100644
--- a/libavformat/asfcrypt.h
+++ b/libavformat/asfcrypt.h
@@ -2,20 +2,20 @@
* ASF decryption
* Copyright (c) 2007 Reimar Doeffinger
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/asfdec.c b/libavformat/asfdec.c
index 8522ce4555..d5facddef1 100644
--- a/libavformat/asfdec.c
+++ b/libavformat/asfdec.c
@@ -2,20 +2,20 @@
* ASF compatible demuxer
* Copyright (c) 2000, 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -69,8 +69,8 @@ typedef struct ASFContext {
unsigned int packet_frag_offset;
unsigned int packet_frag_size;
int64_t packet_frag_timestamp;
+ int ts_is_pts;
int packet_multi_size;
- int packet_obj_size;
int packet_time_delta;
int packet_time_start;
int64_t packet_pos;
@@ -98,12 +98,8 @@ static const AVClass asf_class = {
#include <assert.h>
#define ASF_MAX_STREAMS 127
-#define FRAME_HEADER_SIZE 17
-// Fix Me! FRAME_HEADER_SIZE may be different.
-
-static const ff_asf_guid index_guid = {
- 0x90, 0x08, 0x00, 0x33, 0xb1, 0xe5, 0xcf, 0x11, 0x89, 0xf4, 0x00, 0xa0, 0xc9, 0x03, 0x49, 0xcb
-};
+#define FRAME_HEADER_SIZE 16
+// Fix Me! FRAME_HEADER_SIZE may be different. (17 is known to be too large)
#ifdef DEBUG
static const ff_asf_guid stream_bitrate_guid = { /* (http://get.to/sdp) */
@@ -129,7 +125,7 @@ static void print_guid(ff_asf_guid *g)
else PRINT_IF_GUID(g, ff_asf_codec_comment_header);
else PRINT_IF_GUID(g, ff_asf_codec_comment1_header);
else PRINT_IF_GUID(g, ff_asf_data_header);
- else PRINT_IF_GUID(g, index_guid);
+ else PRINT_IF_GUID(g, ff_asf_simple_index_header);
else PRINT_IF_GUID(g, ff_asf_head1_guid);
else PRINT_IF_GUID(g, ff_asf_head2_guid);
else PRINT_IF_GUID(g, ff_asf_my_guid);
@@ -273,7 +269,7 @@ static void get_id3_tag(AVFormatContext *s, int len)
{
ID3v2ExtraMeta *id3v2_extra_meta = NULL;
- ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);
+ ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, len);
if (id3v2_extra_meta)
ff_id3v2_parse_apic(s, &id3v2_extra_meta);
ff_id3v2_free_extra_meta(&id3v2_extra_meta);
@@ -283,16 +279,20 @@ static void get_tag(AVFormatContext *s, const char *key, int type, int len, int
{
char *value;
int64_t off = avio_tell(s->pb);
+#define LEN 22
- if ((unsigned)len >= (UINT_MAX - 1) / 2)
+ if ((unsigned)len >= (UINT_MAX - LEN) / 2)
return;
- value = av_malloc(2 * len + 1);
+ value = av_malloc(2 * len + LEN);
if (!value)
goto finish;
if (type == 0) { // UTF16-LE
avio_get_str16le(s->pb, len, value, 2 * len + 1);
+ } else if (type == -1) { // ASCII
+ avio_read(s->pb, value, len);
+ value[len]=0;
} else if (type == 1) { // byte array
if (!strcmp(key, "WM/Picture")) { // handle cover art
asf_read_picture(s, len);
@@ -304,7 +304,7 @@ static void get_tag(AVFormatContext *s, const char *key, int type, int len, int
goto finish;
} else if (type > 1 && type <= 5) { // boolean or DWORD or QWORD or WORD
uint64_t num = get_value(s->pb, type, type2_size);
- snprintf(value, len, "%"PRIu64, num);
+ snprintf(value, LEN, "%"PRIu64, num);
} else if (type == 6) { // (don't) handle GUID
av_log(s, AV_LOG_DEBUG, "Unsupported GUID value in tag %s.\n", key);
goto finish;
@@ -369,17 +369,13 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size)
if (!st)
return AVERROR(ENOMEM);
avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
- asf_st = av_mallocz(sizeof(ASFStream));
- if (!asf_st)
- return AVERROR(ENOMEM);
- st->priv_data = asf_st;
- st->start_time = 0;
start_time = asf->hdr.preroll;
- asf_st->stream_language_index = 128; // invalid stream index means no language info
-
if (!(asf->hdr.flags & 0x01)) { // if we aren't streaming...
- st->duration = asf->hdr.play_time /
+ int64_t fsize = avio_size(pb);
+ if (fsize <= 0 || (int64_t)asf->hdr.file_size <= 0 ||
+ FFABS(fsize - (int64_t)asf->hdr.file_size) / (float)FFMIN(fsize, asf->hdr.file_size) < 0.05)
+ st->duration = asf->hdr.play_time /
(10000000 / 1000) - start_time;
}
ff_get_guid(pb, &g);
@@ -407,6 +403,7 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size)
st->id = avio_rl16(pb) & 0x7f; /* stream id */
// mapping of asf ID to AV stream ID;
asf->asfid2avid[st->id] = s->nb_streams - 1;
+ asf_st = &asf->streams[st->id];
avio_rl32(pb);
@@ -426,13 +423,13 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size)
st->codec->codec_type = type;
if (type == AVMEDIA_TYPE_AUDIO) {
- int ret = ff_get_wav_header(pb, st->codec, type_specific_size);
+ int ret = ff_get_wav_header(pb, st->codec, type_specific_size, 0);
if (ret < 0)
return ret;
if (is_dvr_ms_audio) {
// codec_id and codec_tag are unreliable in dvr_ms
// files. Set them later by probing stream.
- st->codec->codec_id = AV_CODEC_ID_PROBE;
+ st->request_probe = 1;
st->codec->codec_tag = 0;
}
if (st->codec->codec_id == AV_CODEC_ID_AAC)
@@ -469,9 +466,11 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size)
tag1 = avio_rl32(pb);
avio_skip(pb, 20);
if (sizeX > 40) {
- st->codec->extradata_size = sizeX - 40;
+ st->codec->extradata_size = ffio_limit(pb, sizeX - 40);
st->codec->extradata = av_mallocz(st->codec->extradata_size +
FF_INPUT_BUFFER_PADDING_SIZE);
+ if (!st->codec->extradata)
+ return AVERROR(ENOMEM);
avio_read(pb, st->codec->extradata, st->codec->extradata_size);
}
@@ -504,6 +503,8 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size)
}
if (st->codec->codec_id == AV_CODEC_ID_H264)
st->need_parsing = AVSTREAM_PARSE_FULL_ONCE;
+ if (st->codec->codec_id == AV_CODEC_ID_MPEG4)
+ st->need_parsing = AVSTREAM_PARSE_FULL_ONCE;
}
pos2 = avio_tell(pb);
avio_skip(pb, size - (pos2 - pos1 + 24));
@@ -540,8 +541,10 @@ static int asf_read_ext_stream_properties(AVFormatContext *s, int64_t size)
stream_ct = avio_rl16(pb); // stream-name-count
payload_ext_ct = avio_rl16(pb); // payload-extension-system-count
- if (stream_num < 128)
+ if (stream_num < 128) {
asf->stream_bitrates[stream_num] = leak_rate;
+ asf->streams[stream_num].payload_ext_ct = 0;
+ }
for (i = 0; i < stream_ct; i++) {
avio_rl16(pb);
@@ -550,10 +553,18 @@ static int asf_read_ext_stream_properties(AVFormatContext *s, int64_t size)
}
for (i = 0; i < payload_ext_ct; i++) {
+ int size;
ff_get_guid(pb, &g);
- avio_skip(pb, 2);
+ size = avio_rl16(pb);
ext_len = avio_rl32(pb);
avio_skip(pb, ext_len);
+ if (stream_num < 128 && i < FF_ARRAY_ELEMS(asf->streams[stream_num].payload)) {
+ ASFPayload *p = &asf->streams[stream_num].payload[i];
+ p->type = g[0];
+ p->size = size;
+ av_log(s, AV_LOG_DEBUG, "Payload extension %x %d\n", g[0], p->size );
+ asf->streams[stream_num].payload_ext_ct ++;
+ }
}
return 0;
@@ -717,12 +728,16 @@ static int asf_read_header(AVFormatContext *s)
ff_get_guid(pb, &g);
if (ff_guidcmp(&g, &ff_asf_header))
- return -1;
+ return AVERROR_INVALIDDATA;
avio_rl64(pb);
avio_rl32(pb);
avio_r8(pb);
avio_r8(pb);
memset(&asf->asfid2avid, -1, sizeof(asf->asfid2avid));
+
+ for (i = 0; i<128; i++)
+ asf->streams[i].stream_language_index = 128; // invalid stream index means no language info
+
for (;;) {
uint64_t gpos = avio_tell(pb);
ff_get_guid(pb, &g);
@@ -739,7 +754,7 @@ static int asf_read_header(AVFormatContext *s)
break;
}
if (gsize < 24)
- return -1;
+ return AVERROR_INVALIDDATA;
if (!ff_guidcmp(&g, &ff_asf_file_header)) {
int ret = asf_read_file_properties(s, gsize);
if (ret < 0)
@@ -770,19 +785,35 @@ static int asf_read_header(AVFormatContext *s)
continue;
} else if (!ff_guidcmp(&g, &ff_asf_marker_header)) {
asf_read_marker(s, gsize);
- } else if (pb->eof_reached) {
- return -1;
+ } else if (avio_feof(pb)) {
+ return AVERROR_EOF;
} else {
if (!s->keylen) {
if (!ff_guidcmp(&g, &ff_asf_content_encryption)) {
+ unsigned int len;
+ int ret;
+ 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");
+
+ if ((ret = av_get_packet(pb, &pkt, len)) < 0)
+ return ret;
+ av_hex_dump_log(s, AV_LOG_DEBUG, pkt.data, pkt.size);
+ av_free_packet(&pkt);
+ len= avio_rl32(pb);
+ get_tag(s, "ASF_Protection_Type", -1, len, 32);
+ len= avio_rl32(pb);
+ get_tag(s, "ASF_Key_ID", -1, len, 32);
+ len= avio_rl32(pb);
+ get_tag(s, "ASF_License_URL", -1, len, 32);
} else if (!ff_guidcmp(&g, &ff_asf_ext_content_encryption)) {
av_log(s, AV_LOG_WARNING,
"Ext DRM protected stream detected, decoding will likely fail!\n");
+ av_dict_set(&s->metadata, "encryption", "ASF Extended Content Encryption", 0);
} else if (!ff_guidcmp(&g, &ff_asf_digital_signature)) {
- av_log(s, AV_LOG_WARNING,
- "Digital signature detected, decoding will likely fail!\n");
+ av_log(s, AV_LOG_INFO, "Digital signature detected!\n");
}
}
}
@@ -796,8 +827,8 @@ static int asf_read_header(AVFormatContext *s)
avio_rl64(pb);
avio_r8(pb);
avio_r8(pb);
- if (pb->eof_reached)
- return -1;
+ if (avio_feof(pb))
+ return AVERROR_EOF;
asf->data_offset = avio_tell(pb);
asf->packet_size_left = 0;
@@ -896,20 +927,20 @@ static int asf_get_packet(AVFormatContext *s, AVIOContext *pb)
* the stream. */
if (pb->error == AVERROR(EAGAIN))
return AVERROR(EAGAIN);
- if (!pb->eof_reached)
+ if (!avio_feof(pb))
av_log(s, AV_LOG_ERROR,
"ff asf bad header %x at:%"PRId64"\n", c, avio_tell(pb));
}
if ((c & 0x8f) == 0x82) {
if (d || e) {
- if (!pb->eof_reached)
+ if (!avio_feof(pb))
av_log(s, AV_LOG_ERROR, "ff asf bad non zero\n");
- return -1;
+ return AVERROR_INVALIDDATA;
}
c = avio_r8(pb);
d = avio_r8(pb);
rsize += 3;
- } else if (!pb->eof_reached) {
+ } else if(!avio_feof(pb)) {
avio_seek(pb, -1, SEEK_CUR); // FIXME
}
@@ -925,12 +956,12 @@ static int asf_get_packet(AVFormatContext *s, AVIOContext *pb)
av_log(s, AV_LOG_ERROR,
"invalid packet_length %"PRIu32" at:%"PRId64"\n",
packet_length, avio_tell(pb));
- return -1;
+ return AVERROR_INVALIDDATA;
}
if (padsize >= packet_length) {
av_log(s, AV_LOG_ERROR,
"invalid padsize %"PRIu32" at:%"PRId64"\n", padsize, avio_tell(pb));
- return -1;
+ return AVERROR_INVALIDDATA;
}
asf->packet_timestamp = avio_rl32(pb);
@@ -950,7 +981,7 @@ static int asf_get_packet(AVFormatContext *s, AVIOContext *pb)
av_log(s, AV_LOG_ERROR,
"invalid packet header length %d for pktlen %"PRIu32"-%"PRIu32" at %"PRId64"\n",
rsize, packet_length, padsize, avio_tell(pb));
- return -1;
+ return AVERROR_INVALIDDATA;
}
asf->packet_size_left = packet_length - padsize - rsize;
if (packet_length < asf->hdr.min_pktsize)
@@ -968,13 +999,16 @@ static int asf_get_packet(AVFormatContext *s, AVIOContext *pb)
static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb)
{
ASFContext *asf = s->priv_data;
+ ASFStream *asfst;
int rsize = 1;
int num = avio_r8(pb);
- int64_t ts0;
+ int i;
+ int64_t ts0, ts1 av_unused;
asf->packet_segments--;
asf->packet_key_frame = num >> 7;
asf->stream_index = asf->asfid2avid[num & 0x7f];
+ asfst = &asf->streams[num & 0x7f];
// sequence should be ignored!
DO_2BITS(asf->packet_property >> 4, asf->packet_seq, 0);
DO_2BITS(asf->packet_property >> 2, asf->packet_frag_offset, 0);
@@ -982,26 +1016,63 @@ static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb)
av_dlog(asf, "key:%d stream:%d seq:%d offset:%d replic_size:%d\n",
asf->packet_key_frame, asf->stream_index, asf->packet_seq,
asf->packet_frag_offset, asf->packet_replic_size);
+ if (rsize+(int64_t)asf->packet_replic_size > asf->packet_size_left) {
+ av_log(s, AV_LOG_ERROR, "packet_replic_size %d is invalid\n", asf->packet_replic_size);
+ return AVERROR_INVALIDDATA;
+ }
if (asf->packet_replic_size >= 8) {
- asf->packet_obj_size = avio_rl32(pb);
- if (asf->packet_obj_size >= (1 << 24) || asf->packet_obj_size <= 0) {
+ int64_t end = avio_tell(pb) + asf->packet_replic_size;
+ AVRational aspect;
+ asfst->packet_obj_size = avio_rl32(pb);
+ if (asfst->packet_obj_size >= (1 << 24) || asfst->packet_obj_size <= 0) {
av_log(s, AV_LOG_ERROR, "packet_obj_size invalid\n");
- return -1;
+ asfst->packet_obj_size = 0;
+ return AVERROR_INVALIDDATA;
}
asf->packet_frag_timestamp = avio_rl32(pb); // timestamp
- if (asf->packet_replic_size >= 8 + 38 + 4) {
- avio_skip(pb, 10);
- ts0 = avio_rl64(pb);
- avio_skip(pb, 8);
- avio_skip(pb, 12);
- avio_rl32(pb);
- avio_skip(pb, asf->packet_replic_size - 8 - 38 - 4);
- if (ts0 != -1)
- asf->packet_frag_timestamp = ts0 / 10000;
- else
- asf->packet_frag_timestamp = AV_NOPTS_VALUE;
- } else
- avio_skip(pb, asf->packet_replic_size - 8);
+
+ for (i = 0; i < asfst->payload_ext_ct; i++) {
+ ASFPayload *p = &asfst->payload[i];
+ int size = p->size;
+ int64_t payend;
+ if (size == 0xFFFF)
+ size = avio_rl16(pb);
+ payend = avio_tell(pb) + size;
+ if (payend > end) {
+ av_log(s, AV_LOG_ERROR, "too long payload\n");
+ break;
+ }
+ switch (p->type) {
+ case 0x50:
+// duration = avio_rl16(pb);
+ break;
+ case 0x54:
+ aspect.num = avio_r8(pb);
+ aspect.den = avio_r8(pb);
+ if (aspect.num > 0 && aspect.den > 0 && asf->stream_index >= 0) {
+ s->streams[asf->stream_index]->sample_aspect_ratio = aspect;
+ }
+ break;
+ case 0x2A:
+ avio_skip(pb, 8);
+ ts0 = avio_rl64(pb);
+ ts1 = avio_rl64(pb);
+ if (ts0!= -1) asf->packet_frag_timestamp = ts0/10000;
+ else asf->packet_frag_timestamp = AV_NOPTS_VALUE;
+ asf->ts_is_pts = 1;
+ break;
+ case 0x5B:
+ case 0xB7:
+ case 0xCC:
+ case 0xC0:
+ case 0xA0:
+ //unknown
+ break;
+ }
+ avio_seek(pb, payend, SEEK_SET);
+ }
+
+ avio_seek(pb, end, SEEK_SET);
rsize += asf->packet_replic_size; // FIXME - check validity
} else if (asf->packet_replic_size == 1) {
// multipacket - frag_offset is beginning timestamp
@@ -1014,18 +1085,18 @@ static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb)
} else if (asf->packet_replic_size != 0) {
av_log(s, AV_LOG_ERROR, "unexpected packet_replic_size of %d\n",
asf->packet_replic_size);
- return -1;
+ return AVERROR_INVALIDDATA;
}
if (asf->packet_flags & 0x01) {
DO_2BITS(asf->packet_segsizetype >> 6, asf->packet_frag_size, 0); // 0 is illegal
if (rsize > asf->packet_size_left) {
av_log(s, AV_LOG_ERROR, "packet_replic_size is invalid\n");
- return -1;
+ return AVERROR_INVALIDDATA;
} else if (asf->packet_frag_size > asf->packet_size_left - rsize) {
if (asf->packet_frag_size > asf->packet_size_left - rsize + asf->packet_padsize) {
av_log(s, AV_LOG_ERROR, "packet_frag_size is invalid (%d-%d)\n",
asf->packet_size_left, rsize);
- return -1;
+ return AVERROR_INVALIDDATA;
} else {
int diff = asf->packet_frag_size - (asf->packet_size_left - rsize);
asf->packet_size_left += diff;
@@ -1033,16 +1104,12 @@ static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb)
}
}
} else {
- if (rsize > asf->packet_size_left) {
- av_log(s, AV_LOG_ERROR, "packet_replic_size is invalid\n");
- return -1;
- }
asf->packet_frag_size = asf->packet_size_left - rsize;
}
if (asf->packet_replic_size == 1) {
asf->packet_multi_size = asf->packet_frag_size;
if (asf->packet_multi_size > asf->packet_size_left)
- return -1;
+ return AVERROR_INVALIDDATA;
}
asf->packet_size_left -= rsize;
@@ -1064,12 +1131,10 @@ static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt)
ASFStream *asf_st = 0;
for (;;) {
int ret;
-
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR_EOF;
-
if (asf->packet_size_left < FRAME_HEADER_SIZE ||
- asf->packet_segments < 1) {
+ asf->packet_segments < 1 && asf->packet_time_start == 0) {
int ret = asf->packet_size_left + asf->packet_padsize;
assert(ret >= 0);
@@ -1084,13 +1149,13 @@ static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt)
}
if (asf->packet_time_start == 0) {
if (asf_read_frame_header(s, pb) < 0) {
- asf->packet_segments = 0;
+ asf->packet_time_start = asf->packet_segments = 0;
continue;
}
if (asf->stream_index < 0 ||
s->streams[asf->stream_index]->discard >= AVDISCARD_ALL ||
(!asf->packet_key_frame &&
- s->streams[asf->stream_index]->discard >= AVDISCARD_NONKEY)) {
+ (s->streams[asf->stream_index]->discard >= AVDISCARD_NONKEY || asf->streams[s->streams[asf->stream_index]->id].skip_to_key))) {
asf->packet_time_start = 0;
/* unhandled packet (should not happen) */
avio_skip(pb, asf->packet_frag_size);
@@ -1100,7 +1165,8 @@ static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt)
asf->packet_frag_size);
continue;
}
- asf->asf_st = s->streams[asf->stream_index]->priv_data;
+ asf->asf_st = &asf->streams[s->streams[asf->stream_index]->id];
+ asf->asf_st->skip_to_key = 0;
}
asf_st = asf->asf_st;
av_assert0(asf_st);
@@ -1119,41 +1185,41 @@ static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt)
// frag_offset is here used as the beginning timestamp
asf->packet_frag_timestamp = asf->packet_time_start;
asf->packet_time_start += asf->packet_time_delta;
- asf->packet_obj_size = asf->packet_frag_size = avio_r8(pb);
+ asf_st->packet_obj_size = asf->packet_frag_size = avio_r8(pb);
asf->packet_size_left--;
asf->packet_multi_size--;
- if (asf->packet_multi_size < asf->packet_obj_size) {
+ if (asf->packet_multi_size < asf_st->packet_obj_size) {
asf->packet_time_start = 0;
avio_skip(pb, asf->packet_multi_size);
asf->packet_size_left -= asf->packet_multi_size;
continue;
}
- asf->packet_multi_size -= asf->packet_obj_size;
- }
- if (asf_st->frag_offset + asf->packet_frag_size <= asf_st->pkt.size &&
- asf_st->frag_offset + asf->packet_frag_size > asf->packet_obj_size) {
- av_log(s, AV_LOG_INFO, "ignoring invalid packet_obj_size (%d %d %d %d)\n",
- asf_st->frag_offset, asf->packet_frag_size,
- asf->packet_obj_size, asf_st->pkt.size);
- asf->packet_obj_size = asf_st->pkt.size;
+ asf->packet_multi_size -= asf_st->packet_obj_size;
}
- if (asf_st->pkt.size != asf->packet_obj_size ||
+ if (asf_st->pkt.size != asf_st->packet_obj_size ||
// FIXME is this condition sufficient?
asf_st->frag_offset + asf->packet_frag_size > asf_st->pkt.size) {
+ int ret;
+
if (asf_st->pkt.data) {
av_log(s, AV_LOG_INFO,
"freeing incomplete packet size %d, new %d\n",
- asf_st->pkt.size, asf->packet_obj_size);
+ asf_st->pkt.size, asf_st->packet_obj_size);
asf_st->frag_offset = 0;
av_free_packet(&asf_st->pkt);
}
/* new packet */
- av_new_packet(&asf_st->pkt, asf->packet_obj_size);
+ if ((ret = av_new_packet(&asf_st->pkt, asf_st->packet_obj_size)) < 0)
+ return ret;
asf_st->seq = asf->packet_seq;
- asf_st->pkt.dts = asf->packet_frag_timestamp - asf->hdr.preroll;
+ if (asf->ts_is_pts) {
+ asf_st->pkt.pts = asf->packet_frag_timestamp - asf->hdr.preroll;
+ } else
+ asf_st->pkt.dts = asf->packet_frag_timestamp - asf->hdr.preroll;
asf_st->pkt.stream_index = asf->stream_index;
asf_st->pkt.pos = asf_st->packet_pos = asf->packet_pos;
+ asf_st->pkt_clean = 0;
if (asf_st->pkt.data && asf_st->palette_changed) {
uint8_t *pal;
@@ -1170,7 +1236,7 @@ static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt)
asf->stream_index, asf->packet_key_frame,
asf_st->pkt.flags & AV_PKT_FLAG_KEY,
s->streams[asf->stream_index]->codec->codec_type == AVMEDIA_TYPE_AUDIO,
- asf->packet_obj_size);
+ asf_st->packet_obj_size);
if (s->streams[asf->stream_index]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
asf->packet_key_frame = 1;
if (asf->packet_key_frame)
@@ -1194,6 +1260,11 @@ static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt)
continue;
}
+ if (asf->packet_frag_offset != asf_st->frag_offset && !asf_st->pkt_clean) {
+ memset(asf_st->pkt.data + asf_st->frag_offset, 0, asf_st->pkt.size - asf_st->frag_offset);
+ asf_st->pkt_clean = 1;
+ }
+
ret = avio_read(pb, asf_st->pkt.data + asf->packet_frag_offset,
asf->packet_frag_size);
if (ret != asf->packet_frag_size) {
@@ -1311,7 +1382,6 @@ static void asf_reset_header(AVFormatContext *s)
int i;
asf->packet_size_left = 0;
- asf->packet_segments = 0;
asf->packet_flags = 0;
asf->packet_property = 0;
asf->packet_timestamp = 0;
@@ -1325,21 +1395,34 @@ static void asf_reset_header(AVFormatContext *s)
asf->packet_frag_size = 0;
asf->packet_frag_timestamp = 0;
asf->packet_multi_size = 0;
- asf->packet_obj_size = 0;
asf->packet_time_delta = 0;
asf->packet_time_start = 0;
- for (i = 0; i < s->nb_streams; i++) {
- asf_st = s->streams[i]->priv_data;
- if (!asf_st)
- continue;
+ for (i = 0; i < 128; i++) {
+ asf_st = &asf->streams[i];
av_free_packet(&asf_st->pkt);
+ asf_st->packet_obj_size = 0;
asf_st->frag_offset = 0;
asf_st->seq = 0;
}
asf->asf_st = NULL;
}
+static void skip_to_key(AVFormatContext *s)
+{
+ ASFContext *asf = s->priv_data;
+ int i;
+
+ for (i = 0; i < 128; i++) {
+ int j = asf->asfid2avid[i];
+ ASFStream *asf_st = &asf->streams[i];
+ if (j < 0 || s->streams[j]->codec->codec_type != AVMEDIA_TYPE_VIDEO)
+ continue;
+
+ asf_st->skip_to_key = 1;
+ }
+}
+
static int asf_read_close(AVFormatContext *s)
{
asf_reset_header(s);
@@ -1350,6 +1433,7 @@ static int asf_read_close(AVFormatContext *s)
static int64_t asf_read_pts(AVFormatContext *s, int stream_index,
int64_t *ppos, int64_t pos_limit)
{
+ ASFContext *asf = s->priv_data;
AVPacket pkt1, *pkt = &pkt1;
ASFStream *asf_st;
int64_t pts;
@@ -1365,11 +1449,13 @@ static int64_t asf_read_pts(AVFormatContext *s, int stream_index,
s->packet_size * s->packet_size +
s->internal->data_offset;
*ppos = pos;
- avio_seek(s->pb, pos, SEEK_SET);
+ if (avio_seek(s->pb, pos, SEEK_SET) < 0)
+ return AV_NOPTS_VALUE;
+ ff_read_frame_flush(s);
asf_reset_header(s);
for (;;) {
- if (asf_read_packet(s, pkt) < 0) {
+ if (av_read_frame(s, pkt) < 0) {
av_log(s, AV_LOG_INFO, "asf_read_pts failed\n");
return AV_NOPTS_VALUE;
}
@@ -1380,8 +1466,7 @@ static int64_t asf_read_pts(AVFormatContext *s, int stream_index,
if (pkt->flags & AV_PKT_FLAG_KEY) {
i = pkt->stream_index;
- asf_st = s->streams[i]->priv_data;
- av_assert0(asf_st);
+ asf_st = &asf->streams[s->streams[i]->id];
// assert((asf_st->packet_pos - s->data_offset) % s->packet_size == 0);
pos = asf_st->packet_pos;
@@ -1404,17 +1489,20 @@ static int asf_build_simple_index(AVFormatContext *s, int stream_index)
ff_asf_guid g;
ASFContext *asf = s->priv_data;
int64_t current_pos = avio_tell(s->pb);
- int i, ret = 0;
+ int ret = 0;
+
+ if((ret = avio_seek(s->pb, asf->data_object_offset + asf->data_object_size, SEEK_SET)) < 0) {
+ return ret;
+ }
- avio_seek(s->pb, asf->data_object_offset + asf->data_object_size, SEEK_SET);
if ((ret = ff_get_guid(s->pb, &g)) < 0)
goto end;
/* the data object can be followed by other top-level objects,
* skip them until the simple index object is reached */
- while (ff_guidcmp(&g, &index_guid)) {
+ while (ff_guidcmp(&g, &ff_asf_simple_index_header)) {
int64_t gsize = avio_rl64(s->pb);
- if (gsize < 24 || s->pb->eof_reached) {
+ if (gsize < 24 || avio_feof(s->pb)) {
goto end;
}
avio_skip(s->pb, gsize - 24);
@@ -1425,6 +1513,7 @@ static int asf_build_simple_index(AVFormatContext *s, int stream_index)
{
int64_t itime, last_pos = -1;
int pct, ict;
+ int i;
int64_t av_unused gsize = avio_rl64(s->pb);
if ((ret = ff_get_guid(s->pb, &g)) < 0)
goto end;
@@ -1448,11 +1537,12 @@ static int asf_build_simple_index(AVFormatContext *s, int stream_index)
last_pos = pos;
}
}
- asf->index_read = ict > 0;
+ asf->index_read = ict > 1;
}
end:
- if (s->pb->eof_reached)
- ret = 0;
+// if (avio_feof(s->pb)) {
+// ret = 0;
+// }
avio_seek(s->pb, current_pos, SEEK_SET);
return ret;
}
@@ -1462,8 +1552,7 @@ static int asf_read_seek(AVFormatContext *s, int stream_index,
{
ASFContext *asf = s->priv_data;
AVStream *st = s->streams[stream_index];
- int64_t pos;
- int index, ret = 0;
+ int ret = 0;
if (s->packet_size <= 0)
return -1;
@@ -1484,19 +1573,24 @@ static int asf_read_seek(AVFormatContext *s, int stream_index,
return 0;
}
- if (!asf->index_read)
+ if (!asf->index_read) {
ret = asf_build_simple_index(s, stream_index);
+ if (ret < 0)
+ asf->index_read = -1;
+ }
- if (!ret && asf->index_read && st->index_entries) {
- index = av_index_search_timestamp(st, pts, flags);
+ if (asf->index_read > 0 && st->index_entries) {
+ int index = av_index_search_timestamp(st, pts, flags);
if (index >= 0) {
/* find the position */
- pos = st->index_entries[index].pos;
+ uint64_t pos = st->index_entries[index].pos;
/* do the seek */
av_log(s, AV_LOG_DEBUG, "SEEKTO: %"PRId64"\n", pos);
- avio_seek(s->pb, pos, SEEK_SET);
+ if(avio_seek(s->pb, pos, SEEK_SET) < 0)
+ return -1;
asf_reset_header(s);
+ skip_to_key(s);
return 0;
}
}
@@ -1504,6 +1598,7 @@ static int asf_read_seek(AVFormatContext *s, int stream_index,
if (ff_seek_frame_binary(s, stream_index, pts, flags) < 0)
return -1;
asf_reset_header(s);
+ skip_to_key(s);
return 0;
}
diff --git a/libavformat/asfenc.c b/libavformat/asfenc.c
index beacd73459..f4f64a62e1 100644
--- a/libavformat/asfenc.c
+++ b/libavformat/asfenc.c
@@ -2,23 +2,24 @@
* ASF muxer
* Copyright (c) 2000, 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/avassert.h"
#include "libavutil/dict.h"
#include "libavutil/mathematics.h"
#include "avformat.h"
@@ -27,12 +28,9 @@
#include "riff.h"
#include "asf.h"
-#undef NDEBUG
-#include <assert.h>
-
-
#define ASF_INDEXED_INTERVAL 10000000
-#define ASF_INDEX_BLOCK 600
+#define ASF_INDEX_BLOCK (1<<9)
+#define ASF_PAYLOADS_PER_PACKET 63
#define ASF_PACKET_ERROR_CORRECTION_DATA_SIZE 0x2
#define ASF_PACKET_ERROR_CORRECTION_FLAGS \
@@ -195,36 +193,33 @@ typedef struct ASFContext {
/* packet filling */
unsigned char multi_payloads_present;
int packet_size_left;
- int packet_timestamp_start;
- int packet_timestamp_end;
+ int64_t packet_timestamp_start;
+ int64_t packet_timestamp_end;
unsigned int packet_nb_payloads;
uint8_t packet_buf[PACKET_SIZE];
AVIOContext pb;
/* only for reading */
uint64_t data_offset; ///< beginning of the first data packet
- int64_t last_indexed_pts;
ASFIndex *index_ptr;
- uint32_t nb_index_count;
uint32_t nb_index_memory_alloc;
uint16_t maximum_packet;
+ uint32_t next_packet_number;
+ uint16_t next_packet_count;
+ uint64_t next_packet_offset;
+ int next_start_sec;
+ int end_sec;
} ASFContext;
static const AVCodecTag codec_asf_bmp_tags[] = {
- { AV_CODEC_ID_MPEG4, MKTAG('M', 'P', '4', 'S') },
{ AV_CODEC_ID_MPEG4, MKTAG('M', '4', 'S', '2') },
+ { AV_CODEC_ID_MPEG4, MKTAG('M', 'P', '4', 'S') },
{ AV_CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', '4', '3') },
{ AV_CODEC_ID_NONE, 0 },
};
#define PREROLL_TIME 3100
-static void put_guid(AVIOContext *s, const ff_asf_guid *g)
-{
- assert(sizeof(*g) == 16);
- avio_write(s, *g, sizeof(*g));
-}
-
static void put_str16(AVIOContext *s, const char *tag)
{
int len;
@@ -245,7 +240,7 @@ static int64_t put_header(AVIOContext *pb, const ff_asf_guid *g)
int64_t pos;
pos = avio_tell(pb);
- put_guid(pb, g);
+ ff_put_guid(pb, g);
avio_wl64(pb, 24);
return pos;
}
@@ -293,7 +288,7 @@ static int32_t get_send_time(ASFContext *asf, int64_t pres_time, uint64_t *offse
int i;
int32_t send_time = 0;
*offset = asf->data_offset + DATA_HEADER_SIZE;
- for (i = 0; i < asf->nb_index_count; i++) {
+ for (i = 0; i < asf->next_start_sec; i++) {
if (pres_time <= asf->index_ptr[i].send_time)
break;
send_time = asf->index_ptr[i].send_time;
@@ -311,7 +306,7 @@ static int asf_write_markers(AVFormatContext *s)
AVRational scale = {1, 10000000};
int64_t hpos = put_header(pb, &ff_asf_marker_header);
- put_guid(pb, &ff_asf_reserved_4); // ASF spec mandates this reserved value
+ ff_put_guid(pb, &ff_asf_reserved_4);// ASF spec mandates this reserved value
avio_wl32(pb, s->nb_chapters); // markers count
avio_wl16(pb, 0); // ASF spec mandates 0 for this
avio_wl16(pb, 0); // name length 0, no name given
@@ -354,7 +349,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
AVIOContext *pb = s->pb;
AVDictionaryEntry *tags[5];
int header_size, n, extra_size, extra_size2, wav_extra_size, file_time;
- int has_title;
+ int has_title, has_aspect_ratio = 0;
int metadata_count;
AVCodecContext *enc;
int64_t header_offset, cur_pos, hpos;
@@ -380,13 +375,17 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
avpriv_set_pts_info(s->streams[n], 32, 1, 1000); /* 32 bit pts in ms */
bit_rate += enc->bit_rate;
+ if ( enc->codec_type == AVMEDIA_TYPE_VIDEO
+ && enc->sample_aspect_ratio.num > 0
+ && enc->sample_aspect_ratio.den > 0)
+ has_aspect_ratio++;
}
if (asf->is_streamed) {
put_chunk(s, 0x4824, 0, 0xc00); /* start of stream (length will be patched later) */
}
- put_guid(pb, &ff_asf_header);
+ ff_put_guid(pb, &ff_asf_header);
avio_wl64(pb, -1); /* header length, will be patched after */
avio_wl32(pb, 3 + has_title + !!metadata_count + s->nb_streams); /* number of chunks in header */
avio_w8(pb, 1); /* ??? */
@@ -395,7 +394,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
/* file header */
header_offset = avio_tell(pb);
hpos = put_header(pb, &ff_asf_file_header);
- put_guid(pb, &ff_asf_my_guid);
+ ff_put_guid(pb, &ff_asf_my_guid);
avio_wl64(pb, file_size);
file_time = 0;
avio_wl64(pb, unix_to_file_time(file_time));
@@ -406,14 +405,46 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
avio_wl32(pb, (asf->is_streamed || !pb->seekable) ? 3 : 2); /* ??? */
avio_wl32(pb, s->packet_size); /* packet size */
avio_wl32(pb, s->packet_size); /* packet size */
- avio_wl32(pb, bit_rate); /* Nominal data rate in bps */
+ avio_wl32(pb, bit_rate ? bit_rate : -1); /* Maximum data rate in bps */
end_header(pb, hpos);
/* unknown headers */
hpos = put_header(pb, &ff_asf_head1_guid);
- put_guid(pb, &ff_asf_head2_guid);
- avio_wl32(pb, 6);
- avio_wl16(pb, 0);
+ ff_put_guid(pb, &ff_asf_head2_guid);
+ avio_wl16(pb, 6);
+ if (has_aspect_ratio) {
+ int64_t hpos2;
+ avio_wl32(pb, 26 + has_aspect_ratio * 84);
+ hpos2 = put_header(pb, &ff_asf_metadata_header);
+ avio_wl16(pb, 2 * has_aspect_ratio);
+ for (n = 0; n < s->nb_streams; n++) {
+ enc = s->streams[n]->codec;
+ if ( enc->codec_type == AVMEDIA_TYPE_VIDEO
+ && enc->sample_aspect_ratio.num > 0
+ && enc->sample_aspect_ratio.den > 0) {
+ AVRational sar = enc->sample_aspect_ratio;
+ avio_wl16(pb, 0);
+ // the stream number is set like this below
+ avio_wl16(pb, n + 1);
+ avio_wl16(pb, 26); // name_len
+ avio_wl16(pb, 3); // value_type
+ avio_wl32(pb, 4); // value_len
+ avio_put_str16le(pb, "AspectRatioX");
+ avio_wl32(pb, sar.num);
+ avio_wl16(pb, 0);
+ // the stream number is set like this below
+ avio_wl16(pb, n + 1);
+ avio_wl16(pb, 26); // name_len
+ avio_wl16(pb, 3); // value_type
+ avio_wl32(pb, 4); // value_len
+ avio_put_str16le(pb, "AspectRatioY");
+ avio_wl32(pb, sar.den);
+ }
+ }
+ end_header(pb, hpos2);
+ } else {
+ avio_wl32(pb, 0);
+ }
end_header(pb, hpos);
/* title and other infos */
@@ -450,7 +481,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
/* chapters using ASF markers */
if (!asf->is_streamed && s->nb_chapters) {
int ret;
- if (ret = asf_write_markers(s))
+ if ((ret = asf_write_markers(s)) < 0)
return ret;
}
/* stream headers */
@@ -460,7 +491,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
enc = s->streams[n]->codec;
asf->streams[n].num = n + 1;
- asf->streams[n].seq = 0;
+ asf->streams[n].seq = 1;
switch (enc->codec_type) {
case AVMEDIA_TYPE_AUDIO:
@@ -478,11 +509,11 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
hpos = put_header(pb, &ff_asf_stream_header);
if (enc->codec_type == AVMEDIA_TYPE_AUDIO) {
- put_guid(pb, &ff_asf_audio_stream);
- put_guid(pb, &ff_asf_audio_conceal_spread);
+ ff_put_guid(pb, &ff_asf_audio_stream);
+ ff_put_guid(pb, &ff_asf_audio_conceal_spread);
} else {
- put_guid(pb, &ff_asf_video_stream);
- put_guid(pb, &ff_asf_video_conceal_none);
+ ff_put_guid(pb, &ff_asf_video_stream);
+ ff_put_guid(pb, &ff_asf_video_conceal_none);
}
avio_wl64(pb, 0); /* ??? */
es_pos = avio_tell(pb);
@@ -493,7 +524,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
if (enc->codec_type == AVMEDIA_TYPE_AUDIO) {
/* WAVEFORMATEX header */
- int wavsize = ff_put_wav_header(pb, enc);
+ int wavsize = ff_put_wav_header(pb, enc, FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX);
if (wavsize < 0)
return -1;
@@ -521,7 +552,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
avio_wl16(pb, 40 + enc->extradata_size); /* size */
/* BITMAPINFOHEADER header */
- ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 1);
+ ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 1, 0);
}
end_header(pb, hpos);
}
@@ -529,7 +560,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
/* media comments */
hpos = put_header(pb, &ff_asf_codec_comment_header);
- put_guid(pb, &ff_asf_codec_comment1_header);
+ ff_put_guid(pb, &ff_asf_codec_comment1_header);
avio_wl32(pb, s->nb_streams);
for (n = 0; n < s->nb_streams; n++) {
const AVCodecDescriptor *codec_desc;
@@ -603,9 +634,9 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
/* movie chunk, followed by packets of packet_size */
asf->data_offset = cur_pos;
- put_guid(pb, &ff_asf_data_header);
+ ff_put_guid(pb, &ff_asf_data_header);
avio_wl64(pb, data_chunk_size);
- put_guid(pb, &ff_asf_my_guid);
+ ff_put_guid(pb, &ff_asf_my_guid);
avio_wl64(pb, asf->nb_packets); /* nb packets */
avio_w8(pb, 1); /* ??? */
avio_w8(pb, 1); /* ??? */
@@ -617,12 +648,11 @@ static int asf_write_header(AVFormatContext *s)
ASFContext *asf = s->priv_data;
s->packet_size = PACKET_SIZE;
+ s->max_interleave_delta = 0;
asf->nb_packets = 0;
- asf->last_indexed_pts = 0;
asf->index_ptr = av_malloc(sizeof(ASFIndex) * ASF_INDEX_BLOCK);
asf->nb_index_memory_alloc = ASF_INDEX_BLOCK;
- asf->nb_index_count = 0;
asf->maximum_packet = 0;
/* the data-chunk-size has to be 50 (DATA_HEADER_SIZE), which is
@@ -641,6 +671,9 @@ static int asf_write_header(AVFormatContext *s)
ffio_init_context(&asf->pb, asf->packet_buf, s->packet_size, 1,
NULL, NULL, NULL, NULL);
+ if (s->avoid_negative_ts < 0)
+ s->avoid_negative_ts = 1;
+
return 0;
}
@@ -667,7 +700,7 @@ static int put_payload_parsing_info(AVFormatContext *s,
padsize -= PACKET_HEADER_MIN_SIZE;
if (asf->multi_payloads_present)
padsize--;
- assert(padsize >= 0);
+ av_assert0(padsize >= 0);
avio_w8(pb, ASF_PACKET_ERROR_CORRECTION_FLAGS);
for (i = 0; i < ASF_PACKET_ERROR_CORRECTION_DATA_SIZE; i++)
@@ -706,20 +739,19 @@ static void flush_packet(AVFormatContext *s)
ASFContext *asf = s->priv_data;
int packet_hdr_size, packet_filled_size;
- assert(asf->packet_timestamp_end >= asf->packet_timestamp_start);
+ av_assert0(asf->packet_timestamp_end >= asf->packet_timestamp_start);
if (asf->is_streamed)
put_chunk(s, 0x4424, s->packet_size, 0);
packet_hdr_size = put_payload_parsing_info(s,
asf->packet_timestamp_start,
- asf->packet_timestamp_end -
- asf->packet_timestamp_start,
+ asf->packet_timestamp_end - asf->packet_timestamp_start,
asf->packet_nb_payloads,
asf->packet_size_left);
packet_filled_size = PACKET_SIZE - asf->packet_size_left;
- assert(packet_hdr_size <= asf->packet_size_left);
+ av_assert0(packet_hdr_size <= asf->packet_size_left);
memset(asf->packet_buf + packet_filled_size, 0, asf->packet_size_left);
avio_write(s->pb, asf->packet_buf, s->packet_size - packet_hdr_size);
@@ -734,7 +766,7 @@ static void flush_packet(AVFormatContext *s)
}
static void put_payload_header(AVFormatContext *s, ASFStream *stream,
- int presentation_time, int m_obj_size,
+ int64_t presentation_time, int m_obj_size,
int m_obj_offset, int payload_len, int flags)
{
ASFContext *asf = s->priv_data;
@@ -757,7 +789,7 @@ static void put_payload_header(AVFormatContext *s, ASFStream *stream,
avio_w8(pb, ASF_PAYLOAD_REPLICATED_DATA_LENGTH);
avio_wl32(pb, m_obj_size); // Replicated Data - Media Object Size
- avio_wl32(pb, presentation_time); // Replicated Data - Presentation Time
+ avio_wl32(pb, (uint32_t) presentation_time); // Replicated Data - Presentation Time
if (asf->multi_payloads_present) {
avio_wl16(pb, payload_len); // payload length
@@ -765,7 +797,7 @@ static void put_payload_header(AVFormatContext *s, ASFStream *stream,
}
static void put_frame(AVFormatContext *s, ASFStream *stream, AVStream *avst,
- int timestamp, const uint8_t *buf,
+ int64_t timestamp, const uint8_t *buf,
int m_obj_size, int flags)
{
ASFContext *asf = s->priv_data;
@@ -823,20 +855,65 @@ static void put_frame(AVFormatContext *s, ASFStream *stream, AVStream *avst,
flush_packet(s);
else if (asf->packet_size_left <= (PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS + PACKET_HEADER_MIN_SIZE + 1))
flush_packet(s);
+ else if (asf->packet_nb_payloads == ASF_PAYLOADS_PER_PACKET)
+ flush_packet(s);
}
stream->seq++;
}
+static int update_index(AVFormatContext *s, int start_sec,
+ uint32_t packet_number, uint16_t packet_count,
+ uint64_t packet_offset)
+{
+ ASFContext *asf = s->priv_data;
+
+ if (start_sec > asf->next_start_sec) {
+ int i;
+
+ if (!asf->next_start_sec) {
+ asf->next_packet_number = packet_number;
+ asf->next_packet_count = packet_count;
+ asf->next_packet_offset = packet_offset;
+ }
+
+ if (start_sec > asf->nb_index_memory_alloc) {
+ int err;
+ asf->nb_index_memory_alloc = (start_sec + ASF_INDEX_BLOCK) & ~(ASF_INDEX_BLOCK - 1);
+ if ((err = av_reallocp_array(&asf->index_ptr,
+ asf->nb_index_memory_alloc,
+ sizeof(*asf->index_ptr))) < 0) {
+ asf->nb_index_memory_alloc = 0;
+ return err;
+ }
+ }
+ for (i = asf->next_start_sec; i < start_sec; i++) {
+ asf->index_ptr[i].packet_number = asf->next_packet_number;
+ asf->index_ptr[i].packet_count = asf->next_packet_count;
+ asf->index_ptr[i].send_time = asf->next_start_sec * INT64_C(10000000);
+ asf->index_ptr[i].offset = asf->next_packet_offset;
+
+ }
+ }
+ asf->maximum_packet = FFMAX(asf->maximum_packet, packet_count);
+ asf->next_packet_number = packet_number;
+ asf->next_packet_count = packet_count;
+ asf->next_packet_offset = packet_offset;
+ asf->next_start_sec = start_sec;
+
+ return 0;
+}
+
static int asf_write_packet(AVFormatContext *s, AVPacket *pkt)
{
ASFContext *asf = s->priv_data;
AVIOContext *pb = s->pb;
ASFStream *stream;
- int64_t duration;
AVCodecContext *codec;
- int64_t packet_st, pts;
- int start_sec, i;
+ uint32_t packet_number;
+ int64_t pts;
+ int start_sec;
int flags = pkt->flags;
+ int ret;
uint64_t offset = avio_tell(pb);
codec = s->streams[pkt->stream_index]->codec;
@@ -846,53 +923,38 @@ static int asf_write_packet(AVFormatContext *s, AVPacket *pkt)
flags &= ~AV_PKT_FLAG_KEY;
pts = (pkt->pts != AV_NOPTS_VALUE) ? pkt->pts : pkt->dts;
- assert(pts != AV_NOPTS_VALUE);
- duration = pts * 10000;
- asf->duration = FFMAX(asf->duration, duration + pkt->duration * 10000);
+ av_assert0(pts != AV_NOPTS_VALUE);
+ pts *= 10000;
+ asf->duration = FFMAX(asf->duration, pts + pkt->duration * 10000);
- packet_st = asf->nb_packets;
+ packet_number = asf->nb_packets;
put_frame(s, stream, s->streams[pkt->stream_index],
pkt->dts, pkt->data, pkt->size, flags);
+ start_sec = (int)((PREROLL_TIME * 10000 + pts + ASF_INDEXED_INTERVAL - 1)
+ / ASF_INDEXED_INTERVAL);
+
/* check index */
if ((!asf->is_streamed) && (flags & AV_PKT_FLAG_KEY)) {
- start_sec = (int)(duration / INT64_C(10000000));
- if (start_sec != (int)(asf->last_indexed_pts / INT64_C(10000000))) {
- for (i = asf->nb_index_count; i < start_sec; i++) {
- if (i >= asf->nb_index_memory_alloc) {
- int err;
- asf->nb_index_memory_alloc += ASF_INDEX_BLOCK;
- if ((err = av_reallocp_array(&asf->index_ptr,
- asf->nb_index_memory_alloc,
- sizeof(*asf->index_ptr))) < 0) {
- asf->nb_index_memory_alloc = 0;
- return err;
- }
- }
- // store
- asf->index_ptr[i].packet_number = (uint32_t)packet_st;
- asf->index_ptr[i].packet_count = (uint16_t)(asf->nb_packets - packet_st);
- asf->index_ptr[i].send_time = start_sec * INT64_C(10000000);
- asf->index_ptr[i].offset = offset;
- asf->maximum_packet = FFMAX(asf->maximum_packet,
- (uint16_t)(asf->nb_packets - packet_st));
- }
- asf->nb_index_count = start_sec;
- asf->last_indexed_pts = duration;
- }
+ uint16_t packet_count = asf->nb_packets - packet_number;
+ ret = update_index(s, start_sec, packet_number, packet_count, offset);
+ if (ret < 0)
+ return ret;
}
+ asf->end_sec = start_sec;
+
return 0;
}
-static int asf_write_index(AVFormatContext *s, ASFIndex *index,
+static int asf_write_index(AVFormatContext *s, const ASFIndex *index,
uint16_t max, uint32_t count)
{
AVIOContext *pb = s->pb;
int i;
- put_guid(pb, &ff_asf_simple_index_header);
+ ff_put_guid(pb, &ff_asf_simple_index_header);
avio_wl64(pb, 24 + 16 + 8 + 4 + 4 + (4 + 2) * count);
- put_guid(pb, &ff_asf_my_guid);
+ ff_put_guid(pb, &ff_asf_my_guid);
avio_wl64(pb, ASF_INDEXED_INTERVAL);
avio_wl32(pb, max);
avio_wl32(pb, count);
@@ -908,6 +970,7 @@ static int asf_write_trailer(AVFormatContext *s)
{
ASFContext *asf = s->priv_data;
int64_t file_size, data_size;
+ int ret;
/* flush the current packet */
if (asf->pb.buf_ptr > asf->pb.buffer)
@@ -915,8 +978,11 @@ static int asf_write_trailer(AVFormatContext *s)
/* write index */
data_size = avio_tell(s->pb);
- if ((!asf->is_streamed) && (asf->nb_index_count != 0))
- asf_write_index(s, asf->index_ptr, asf->maximum_packet, asf->nb_index_count);
+ if (!asf->is_streamed && asf->next_start_sec) {
+ if ((ret = update_index(s, asf->end_sec + 1, 0, 0, 0)) < 0)
+ return ret;
+ asf_write_index(s, asf->index_ptr, asf->maximum_packet, asf->next_start_sec);
+ }
avio_flush(s->pb);
if (asf->is_streamed || !s->pb->seekable) {
@@ -928,7 +994,7 @@ static int asf_write_trailer(AVFormatContext *s)
asf_write_header1(s, file_size, data_size - asf->data_offset);
}
- av_free(asf->index_ptr);
+ av_freep(&asf->index_ptr);
return 0;
}
@@ -939,7 +1005,7 @@ AVOutputFormat ff_asf_muxer = {
.mime_type = "video/x-ms-asf",
.extensions = "asf,wmv,wma",
.priv_data_size = sizeof(ASFContext),
- .audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_MP2,
+ .audio_codec = AV_CODEC_ID_WMAV2,
.video_codec = AV_CODEC_ID_MSMPEG4V3,
.write_header = asf_write_header,
.write_packet = asf_write_packet,
@@ -958,7 +1024,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 08c1222264..c62e76f0dd 100644
--- a/libavformat/assdec.c
+++ b/libavformat/assdec.c
@@ -1,224 +1,187 @@
/*
* SSA/ASS demuxer
* Copyright (c) 2008 Michael Niedermayer
+ * Copyright (c) 2014 Clément Bœsch
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdint.h>
-#include "libavutil/mathematics.h"
-
#include "avformat.h"
#include "internal.h"
-
-#define MAX_LINESIZE 2000
+#include "subtitles.h"
+#include "libavcodec/internal.h"
+#include "libavutil/bprint.h"
typedef struct ASSContext {
- uint8_t *event_buffer;
- uint8_t **event;
- unsigned int event_count;
- unsigned int event_index;
+ FFDemuxSubtitlesQueue q;
+ unsigned readorder;
} ASSContext;
-static int probe(AVProbeData *p)
+static int ass_probe(AVProbeData *p)
{
- const char *header = "[Script Info]";
+ char buf[13];
+ FFTextReader tr;
+ ff_text_init_buf(&tr, p->buf, p->buf_size);
- if (!memcmp(p->buf, header, strlen(header)) ||
- !memcmp(p->buf + 3, header, strlen(header)))
+ ff_text_read(&tr, buf, sizeof(buf));
+
+ if (!memcmp(buf, "[Script Info]", 13))
return AVPROBE_SCORE_MAX;
return 0;
}
-static int read_close(AVFormatContext *s)
+static int ass_read_close(AVFormatContext *s)
{
ASSContext *ass = s->priv_data;
-
- av_freep(&ass->event_buffer);
- av_freep(&ass->event);
-
+ ff_subtitles_queue_clean(&ass->q);
return 0;
}
-static int64_t get_pts(const uint8_t *p)
+static int read_dialogue(ASSContext *ass, AVBPrint *dst, const uint8_t *p,
+ int64_t *start, int *duration)
{
- int hour, min, sec, hsec;
-
- if (sscanf(p, "%*[^,],%d:%d:%d%*c%d", &hour, &min, &sec, &hsec) != 4)
- return AV_NOPTS_VALUE;
-
- av_dlog(NULL, "%d %d %d %d [%s]\n", hour, min, sec, hsec, p);
-
- min += 60 * hour;
- sec += 60 * min;
-
- return sec * 100 + hsec;
+ int pos = 0;
+ int64_t end;
+ int hh1, mm1, ss1, ms1;
+ int hh2, mm2, ss2, ms2;
+
+ if (sscanf(p, "Dialogue: %*[^,],%d:%d:%d%*c%d,%d:%d:%d%*c%d,%n",
+ &hh1, &mm1, &ss1, &ms1,
+ &hh2, &mm2, &ss2, &ms2, &pos) >= 8 && pos > 0) {
+
+ /* This is not part of the sscanf itself in order to handle an actual
+ * number (which would be the Layer) or the form "Marked=N" (which is
+ * the old SSA field, now replaced by Layer, and will lead to Layer
+ * being 0 here). */
+ const int layer = atoi(p + 10);
+
+ end = (hh2*3600LL + mm2*60LL + ss2) * 100LL + ms2;
+ *start = (hh1*3600LL + mm1*60LL + ss1) * 100LL + ms1;
+ *duration = end - *start;
+
+ av_bprint_clear(dst);
+ av_bprintf(dst, "%u,%d,%s", ass->readorder++, layer, p + pos);
+
+ /* right strip the buffer */
+ while (dst->len > 0 &&
+ dst->str[dst->len - 1] == '\r' ||
+ dst->str[dst->len - 1] == '\n')
+ dst->str[--dst->len] = 0;
+ return 0;
+ }
+ return -1;
}
-static int event_cmp(const void *_a, const void *_b)
+static int64_t get_line(AVBPrint *buf, FFTextReader *tr)
{
- const uint8_t *const *a = _a, *const *b = _b;
- return get_pts(*a) - get_pts(*b);
+ int64_t pos = ff_text_pos(tr);
+
+ av_bprint_clear(buf);
+ for (;;) {
+ char c = ff_text_r8(tr);
+ if (!c)
+ break;
+ av_bprint_chars(buf, c, 1);
+ if (c == '\n')
+ break;
+ }
+ return pos;
}
-static int read_header(AVFormatContext *s)
+static int ass_read_header(AVFormatContext *s)
{
- int i, len, header_remaining;
ASSContext *ass = s->priv_data;
- AVIOContext *pb = s->pb;
+ AVBPrint header, line, rline;
+ int res = 0;
AVStream *st;
- int allocated[2] = { 0 };
- uint8_t *p, **dst[2] = { 0 };
- int pos[2] = { 0 };
+ FFTextReader tr;
+ ff_text_init_avio(s, &tr, s->pb);
st = avformat_new_stream(s, NULL);
if (!st)
- return -1;
+ return AVERROR(ENOMEM);
avpriv_set_pts_info(st, 64, 1, 100);
st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
- st->codec->codec_id = AV_CODEC_ID_SSA;
+ st->codec->codec_id = AV_CODEC_ID_ASS;
- header_remaining = INT_MAX;
- dst[0] = &st->codec->extradata;
- dst[1] = &ass->event_buffer;
- while (!pb->eof_reached) {
- uint8_t line[MAX_LINESIZE];
+ av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED);
+ av_bprint_init(&line, 0, AV_BPRINT_SIZE_UNLIMITED);
+ av_bprint_init(&rline, 0, AV_BPRINT_SIZE_UNLIMITED);
- len = ff_get_line(pb, line, sizeof(line));
+ for (;;) {
+ int64_t pos = get_line(&line, &tr);
+ int64_t ts_start = AV_NOPTS_VALUE;
+ int duration = -1;
+ AVPacket *sub;
- if (!memcmp(line, "[Events]", 8))
- header_remaining = 2;
- else if (line[0] == '[')
- header_remaining = INT_MAX;
+ if (!line.str[0]) // EOF
+ break;
- i = header_remaining == 0;
-
- if (i && get_pts(line) == AV_NOPTS_VALUE)
+ if (read_dialogue(ass, &rline, line.str, &ts_start, &duration) < 0) {
+ av_bprintf(&header, "%s", line.str);
continue;
-
- p = av_fast_realloc(*(dst[i]), &allocated[i], pos[i] + MAX_LINESIZE);
- if (!p)
- goto fail;
- *(dst[i]) = p;
- memcpy(p + pos[i], line, len + 1);
- pos[i] += len;
- if (i)
- ass->event_count++;
- else
- header_remaining--;
- }
- st->codec->extradata_size = pos[0];
-
- if (ass->event_count >= UINT_MAX / sizeof(*ass->event))
- goto fail;
-
- ass->event = av_malloc(ass->event_count * sizeof(*ass->event));
- p = ass->event_buffer;
- for (i = 0; i < ass->event_count; i++) {
- ass->event[i] = p;
- while (*p && *p != '\n')
- p++;
- p++;
+ }
+ sub = ff_subtitles_queue_insert(&ass->q, rline.str, rline.len, 0);
+ if (!sub) {
+ res = AVERROR(ENOMEM);
+ goto end;
+ }
+ sub->pos = pos;
+ sub->pts = ts_start;
+ sub->duration = duration;
}
- qsort(ass->event, ass->event_count, sizeof(*ass->event), event_cmp);
-
- return 0;
+ res = avpriv_bprint_to_extradata(st->codec, &header);
+ if (res < 0)
+ goto end;
-fail:
- read_close(s);
+ ff_subtitles_queue_finalize(&ass->q);
- return -1;
+end:
+ av_bprint_finalize(&header, NULL);
+ av_bprint_finalize(&line, NULL);
+ av_bprint_finalize(&rline, NULL);
+ return res;
}
-static int read_packet(AVFormatContext *s, AVPacket *pkt)
+static int ass_read_packet(AVFormatContext *s, AVPacket *pkt)
{
ASSContext *ass = s->priv_data;
- uint8_t *p, *end;
- int ret;
-
- if (ass->event_index >= ass->event_count)
- return AVERROR(EIO);
-
- p = ass->event[ass->event_index];
-
- end = strchr(p, '\n');
- ret = av_new_packet(pkt, end ? end - p + 1 : strlen(p));
- if (ret < 0)
- return ret;
- pkt->flags |= AV_PKT_FLAG_KEY;
- pkt->pos = p - ass->event_buffer + s->streams[0]->codec->extradata_size;
- pkt->pts = pkt->dts = get_pts(p);
- memcpy(pkt->data, p, pkt->size);
-
- ass->event_index++;
-
- return 0;
+ return ff_subtitles_queue_read_packet(&ass->q, pkt);
}
-static int read_seek2(AVFormatContext *s, int stream_index,
- int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+static int ass_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
{
ASSContext *ass = s->priv_data;
-
- if (flags & AVSEEK_FLAG_BYTE) {
- return AVERROR(ENOSYS);
- } else if (flags & AVSEEK_FLAG_FRAME) {
- if (ts < 0 || ts >= ass->event_count)
- return AVERROR(ERANGE);
- ass->event_index = ts;
- } else {
- int i, idx = -1;
- int64_t min_ts_diff = INT64_MAX;
- if (stream_index == -1) {
- AVRational time_base = s->streams[0]->time_base;
- ts = av_rescale_q(ts, AV_TIME_BASE_Q, time_base);
- min_ts = av_rescale_rnd(min_ts, time_base.den,
- time_base.num * (int64_t) AV_TIME_BASE,
- AV_ROUND_UP);
- max_ts = av_rescale_rnd(max_ts, time_base.den,
- time_base.num * (int64_t) AV_TIME_BASE,
- AV_ROUND_DOWN);
- }
- /* TODO: ass->event[] is sorted by pts so we could do a binary search */
- for (i = 0; i < ass->event_count; i++) {
- int64_t pts = get_pts(ass->event[i]);
- int64_t ts_diff = FFABS(pts - ts);
- if (pts >= min_ts && pts <= max_ts && ts_diff < min_ts_diff) {
- min_ts_diff = ts_diff;
- idx = i;
- }
- }
- if (idx < 0)
- return AVERROR(ERANGE);
- ass->event_index = idx;
- }
- return 0;
+ return ff_subtitles_queue_seek(&ass->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
}
AVInputFormat ff_ass_demuxer = {
.name = "ass",
.long_name = NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"),
.priv_data_size = sizeof(ASSContext),
- .read_probe = probe,
- .read_header = read_header,
- .read_packet = read_packet,
- .read_close = read_close,
- .read_seek2 = read_seek2,
+ .read_probe = ass_probe,
+ .read_header = ass_read_header,
+ .read_packet = ass_read_packet,
+ .read_close = ass_read_close,
+ .read_seek2 = ass_read_seek,
};
diff --git a/libavformat/assenc.c b/libavformat/assenc.c
index 751485dfb5..079991d14e 100644
--- a/libavformat/assenc.c
+++ b/libavformat/assenc.c
@@ -2,76 +2,214 @@
* SSA/ASS muxer
* Copyright (c) 2008 Michael Niedermayer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/avstring.h"
#include "avformat.h"
+#include "internal.h"
+
+#include "libavutil/opt.h"
+
+typedef struct DialogueLine {
+ int readorder;
+ char *line;
+ struct DialogueLine *prev, *next;
+} DialogueLine;
typedef struct ASSContext{
- unsigned int extra_index;
+ const AVClass *class;
+ int write_ts; // 0: ssa (timing in payload), 1: ass (matroska like)
+ int expected_readorder;
+ DialogueLine *dialogue_cache;
+ DialogueLine *last_added_dialogue;
+ int cache_size;
+ int ssa_mode;
+ int ignore_readorder;
}ASSContext;
static int write_header(AVFormatContext *s)
{
ASSContext *ass = s->priv_data;
AVCodecContext *avctx= s->streams[0]->codec;
- uint8_t *last= NULL;
- if(s->nb_streams != 1 || avctx->codec_id != AV_CODEC_ID_SSA){
+ if (s->nb_streams != 1 || (avctx->codec_id != AV_CODEC_ID_SSA &&
+ avctx->codec_id != AV_CODEC_ID_ASS)) {
av_log(s, AV_LOG_ERROR, "Exactly one ASS/SSA stream is needed.\n");
- return -1;
+ return AVERROR(EINVAL);
+ }
+ ass->write_ts = avctx->codec_id == AV_CODEC_ID_ASS;
+ avpriv_set_pts_info(s->streams[0], 64, 1, 100);
+ if (avctx->extradata_size > 0) {
+ avio_write(s->pb, avctx->extradata, avctx->extradata_size);
+ if (avctx->extradata[avctx->extradata_size - 1] != '\n')
+ avio_write(s->pb, "\r\n", 2);
+ ass->ssa_mode = !strstr(avctx->extradata, "\n[V4+ Styles]");
+ if (!strstr(avctx->extradata, "\n[Events]"))
+ avio_printf(s->pb, "[Events]\r\nFormat: %s, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n",
+ ass->ssa_mode ? "Marked" : "Layer");
}
+ avio_flush(s->pb);
+
+ return 0;
+}
+
+static void purge_dialogues(AVFormatContext *s, int force)
+{
+ int n = 0;
+ ASSContext *ass = s->priv_data;
+ DialogueLine *dialogue = ass->dialogue_cache;
- while(ass->extra_index < avctx->extradata_size){
- uint8_t *p = avctx->extradata + ass->extra_index;
- uint8_t *end= strchr(p, '\n');
- if(!end) end= avctx->extradata + avctx->extradata_size;
- else end++;
+ while (dialogue && (dialogue->readorder == ass->expected_readorder || force)) {
+ DialogueLine *next = dialogue->next;
+ if (dialogue->readorder != ass->expected_readorder) {
+ av_log(s, AV_LOG_WARNING, "ReadOrder gap found between %d and %d\n",
+ ass->expected_readorder, dialogue->readorder);
+ ass->expected_readorder = dialogue->readorder;
+ }
+ avio_printf(s->pb, "Dialogue: %s\r\n", dialogue->line);
+ if (dialogue == ass->last_added_dialogue)
+ ass->last_added_dialogue = next;
+ av_freep(&dialogue->line);
+ av_free(dialogue);
+ if (next)
+ next->prev = NULL;
+ dialogue = ass->dialogue_cache = next;
+ ass->expected_readorder++;
+ n++;
+ }
+ ass->cache_size -= n;
+ if (n > 1)
+ av_log(s, AV_LOG_DEBUG, "wrote %d ASS lines, cached dialogues: %d, waiting for event id %d\n",
+ n, ass->cache_size, ass->expected_readorder);
+}
- avio_write(s->pb, p, end-p);
- ass->extra_index += end-p;
+static void insert_dialogue(ASSContext *ass, DialogueLine *dialogue)
+{
+ DialogueLine *cur, *next = NULL, *prev = NULL;
- if(last && !memcmp(last, "[Events]", 8))
- break;
- last=p;
+ /* from the last added to the end of the list */
+ if (ass->last_added_dialogue) {
+ for (cur = ass->last_added_dialogue; cur; cur = cur->next) {
+ if (cur->readorder > dialogue->readorder)
+ break;
+ prev = cur;
+ next = cur->next;
+ }
}
- avio_flush(s->pb);
+ /* from the beginning to the last one added */
+ if (!prev) {
+ next = ass->dialogue_cache;
+ for (cur = next; cur != ass->last_added_dialogue; cur = cur->next) {
+ if (cur->readorder > dialogue->readorder)
+ break;
+ prev = cur;
+ next = cur->next;
+ }
+ }
- return 0;
+ if (prev) {
+ prev->next = dialogue;
+ dialogue->prev = prev;
+ } else {
+ dialogue->prev = ass->dialogue_cache;
+ ass->dialogue_cache = dialogue;
+ }
+ if (next) {
+ next->prev = dialogue;
+ dialogue->next = next;
+ }
+ ass->cache_size++;
+ ass->last_added_dialogue = dialogue;
}
static int write_packet(AVFormatContext *s, AVPacket *pkt)
{
- avio_write(s->pb, pkt->data, pkt->size);
+ ASSContext *ass = s->priv_data;
+
+ if (ass->write_ts) {
+ long int layer;
+ char *p = pkt->data;
+ int64_t start = pkt->pts;
+ int64_t end = start + pkt->duration;
+ int hh1, mm1, ss1, ms1;
+ int hh2, mm2, ss2, ms2;
+ DialogueLine *dialogue = av_mallocz(sizeof(*dialogue));
+
+ if (!dialogue)
+ return AVERROR(ENOMEM);
+
+ dialogue->readorder = strtol(p, &p, 10);
+ if (dialogue->readorder < ass->expected_readorder)
+ av_log(s, AV_LOG_WARNING, "Unexpected ReadOrder %d\n",
+ dialogue->readorder);
+ if (*p == ',')
+ p++;
+
+ if (ass->ssa_mode && !strncmp(p, "Marked=", 7))
+ p += 7;
+
+ layer = strtol(p, &p, 10);
+ if (*p == ',')
+ p++;
+ hh1 = (int)(start / 360000); mm1 = (int)(start / 6000) % 60;
+ hh2 = (int)(end / 360000); mm2 = (int)(end / 6000) % 60;
+ ss1 = (int)(start / 100) % 60; ms1 = (int)(start % 100);
+ ss2 = (int)(end / 100) % 60; ms2 = (int)(end % 100);
+ if (hh1 > 9) hh1 = 9, mm1 = 59, ss1 = 59, ms1 = 99;
+ if (hh2 > 9) hh2 = 9, mm2 = 59, ss2 = 59, ms2 = 99;
+
+ dialogue->line = av_asprintf("%s%ld,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s",
+ ass->ssa_mode ? "Marked=" : "",
+ layer, hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, p);
+ if (!dialogue->line) {
+ av_free(dialogue);
+ return AVERROR(ENOMEM);
+ }
+ insert_dialogue(ass, dialogue);
+ purge_dialogues(s, ass->ignore_readorder);
+ } else {
+ avio_write(s->pb, pkt->data, pkt->size);
+ }
+
return 0;
}
static int write_trailer(AVFormatContext *s)
{
- ASSContext *ass = s->priv_data;
- AVCodecContext *avctx= s->streams[0]->codec;
-
- avio_write(s->pb, avctx->extradata + ass->extra_index,
- avctx->extradata_size - ass->extra_index);
-
+ purge_dialogues(s, 1);
return 0;
}
+#define OFFSET(x) offsetof(ASSContext, x)
+#define E AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ { "ignore_readorder", "write events immediately, even if they're out-of-order", OFFSET(ignore_readorder), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
+ { NULL },
+};
+
+static const AVClass ass_class = {
+ .class_name = "ass muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVOutputFormat ff_ass_muxer = {
.name = "ass",
.long_name = NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"),
@@ -82,5 +220,6 @@ AVOutputFormat ff_ass_muxer = {
.write_header = write_header,
.write_packet = write_packet,
.write_trailer = write_trailer,
- .flags = AVFMT_GLOBALHEADER | AVFMT_NOTIMESTAMPS,
+ .flags = AVFMT_GLOBALHEADER | AVFMT_NOTIMESTAMPS | AVFMT_TS_NONSTRICT,
+ .priv_class = &ass_class,
};
diff --git a/libavformat/ast.c b/libavformat/ast.c
new file mode 100644
index 0000000000..9de74aae69
--- /dev/null
+++ b/libavformat/ast.c
@@ -0,0 +1,29 @@
+/*
+ * AST common code
+ * Copyright (c) 2012 James Almer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "internal.h"
+
+const AVCodecTag ff_codec_ast_tags[] = {
+ { AV_CODEC_ID_ADPCM_AFC, 0 },
+ { AV_CODEC_ID_PCM_S16BE_PLANAR, 1 },
+ { AV_CODEC_ID_NONE, 0 },
+};
diff --git a/libavformat/ast.h b/libavformat/ast.h
new file mode 100644
index 0000000000..4a399ea410
--- /dev/null
+++ b/libavformat/ast.h
@@ -0,0 +1,30 @@
+/*
+ * AST common code
+ * Copyright (c) 2012 James Almer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_AST_H
+#define AVFORMAT_AST_H
+
+#include "avformat.h"
+#include "internal.h"
+
+extern const AVCodecTag ff_codec_ast_tags[];
+
+#endif /* AVFORMAT_AST_H */
diff --git a/libavformat/astdec.c b/libavformat/astdec.c
new file mode 100644
index 0000000000..92c208d044
--- /dev/null
+++ b/libavformat/astdec.c
@@ -0,0 +1,122 @@
+/*
+ * AST demuxer
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/channel_layout.h"
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "internal.h"
+#include "ast.h"
+
+static int ast_probe(AVProbeData *p)
+{
+ if (AV_RL32(p->buf) != MKTAG('S','T','R','M'))
+ return 0;
+
+ if (!AV_RB16(p->buf + 10) ||
+ !AV_RB16(p->buf + 12) || AV_RB16(p->buf + 12) > 256 ||
+ !AV_RB32(p->buf + 16) || AV_RB32(p->buf + 16) > 8*48000)
+ return AVPROBE_SCORE_MAX / 8;
+
+ return AVPROBE_SCORE_MAX / 3 * 2;
+}
+
+static int ast_read_header(AVFormatContext *s)
+{
+ int depth;
+ AVStream *st;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avio_skip(s->pb, 8);
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->codec_id = ff_codec_get_id(ff_codec_ast_tags, avio_rb16(s->pb));
+
+ depth = avio_rb16(s->pb);
+ if (depth != 16) {
+ avpriv_request_sample(s, "depth %d", depth);
+ return AVERROR_INVALIDDATA;
+ }
+
+ st->codec->channels = avio_rb16(s->pb);
+ if (!st->codec->channels)
+ return AVERROR_INVALIDDATA;
+
+ if (st->codec->channels == 2)
+ st->codec->channel_layout = AV_CH_LAYOUT_STEREO;
+ else if (st->codec->channels == 4)
+ st->codec->channel_layout = AV_CH_LAYOUT_4POINT0;
+
+ avio_skip(s->pb, 2);
+ st->codec->sample_rate = avio_rb32(s->pb);
+ if (st->codec->sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+ st->start_time = 0;
+ st->duration = avio_rb32(s->pb);
+ avio_skip(s->pb, 40);
+ avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+
+ return 0;
+}
+
+static int ast_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ uint32_t type, size;
+ int64_t pos;
+ int ret;
+
+ if (avio_feof(s->pb))
+ return AVERROR_EOF;
+
+ pos = avio_tell(s->pb);
+ type = avio_rl32(s->pb);
+ size = avio_rb32(s->pb);
+ if (size > INT_MAX / s->streams[0]->codec->channels)
+ return AVERROR_INVALIDDATA;
+
+ size *= s->streams[0]->codec->channels;
+ if ((ret = avio_skip(s->pb, 24)) < 0) // padding
+ return ret;
+
+ if (type == MKTAG('B','L','C','K')) {
+ ret = av_get_packet(s->pb, pkt, size);
+ pkt->stream_index = 0;
+ pkt->pos = pos;
+ } else {
+ av_log(s, AV_LOG_ERROR, "unknown chunk %x\n", type);
+ avio_skip(s->pb, size);
+ ret = AVERROR_INVALIDDATA;
+ }
+
+ return ret;
+}
+
+AVInputFormat ff_ast_demuxer = {
+ .name = "ast",
+ .long_name = NULL_IF_CONFIG_SMALL("AST (Audio Stream)"),
+ .read_probe = ast_probe,
+ .read_header = ast_read_header,
+ .read_packet = ast_read_packet,
+ .extensions = "ast",
+ .flags = AVFMT_GENERIC_INDEX,
+ .codec_tag = (const AVCodecTag* const []){ff_codec_ast_tags, 0},
+};
diff --git a/libavformat/astenc.c b/libavformat/astenc.c
new file mode 100644
index 0000000000..cf7a12c95d
--- /dev/null
+++ b/libavformat/astenc.c
@@ -0,0 +1,214 @@
+/*
+ * AST muxer
+ * Copyright (c) 2012 James Almer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "avio_internal.h"
+#include "internal.h"
+#include "ast.h"
+#include "libavutil/mathematics.h"
+#include "libavutil/opt.h"
+
+typedef struct ASTMuxContext {
+ AVClass *class;
+ int64_t size;
+ int64_t samples;
+ int64_t loopstart;
+ int64_t loopend;
+ int fbs;
+} ASTMuxContext;
+
+#define CHECK_LOOP(type) \
+ if (ast->loop ## type > 0) { \
+ ast->loop ## type = av_rescale_rnd(ast->loop ## type, enc->sample_rate, 1000, AV_ROUND_DOWN); \
+ if (ast->loop ## type < 0 || ast->loop ## type > UINT_MAX) { \
+ av_log(s, AV_LOG_ERROR, "Invalid loop" #type " value\n"); \
+ return AVERROR(EINVAL); \
+ } \
+ }
+
+static int ast_write_header(AVFormatContext *s)
+{
+ ASTMuxContext *ast = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVCodecContext *enc;
+ unsigned int codec_tag;
+
+ if (s->nb_streams == 1) {
+ enc = s->streams[0]->codec;
+ } else {
+ av_log(s, AV_LOG_ERROR, "only one stream is supported\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (enc->codec_id == AV_CODEC_ID_ADPCM_AFC) {
+ av_log(s, AV_LOG_ERROR, "muxing ADPCM AFC is not implemented\n");
+ return AVERROR_PATCHWELCOME;
+ }
+
+ codec_tag = ff_codec_get_tag(ff_codec_ast_tags, enc->codec_id);
+ if (!codec_tag) {
+ av_log(s, AV_LOG_ERROR, "unsupported codec\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (ast->loopend > 0 && ast->loopstart >= ast->loopend) {
+ av_log(s, AV_LOG_ERROR, "loopend can't be less or equal to loopstart\n");
+ return AVERROR(EINVAL);
+ }
+
+ /* Convert milliseconds to samples */
+ CHECK_LOOP(start)
+ CHECK_LOOP(end)
+
+ ffio_wfourcc(pb, "STRM");
+
+ ast->size = avio_tell(pb);
+ avio_wb32(pb, 0); /* File size minus header */
+ avio_wb16(pb, codec_tag);
+ avio_wb16(pb, 16); /* Bit depth */
+ avio_wb16(pb, enc->channels);
+ avio_wb16(pb, 0); /* Loop flag */
+ avio_wb32(pb, enc->sample_rate);
+
+ ast->samples = avio_tell(pb);
+ avio_wb32(pb, 0); /* Number of samples */
+ avio_wb32(pb, 0); /* Loopstart */
+ avio_wb32(pb, 0); /* Loopend */
+ avio_wb32(pb, 0); /* Size of first block */
+
+ /* Unknown */
+ avio_wb32(pb, 0);
+ avio_wl32(pb, 0x7F);
+ avio_wb64(pb, 0);
+ avio_wb64(pb, 0);
+ avio_wb32(pb, 0);
+
+ avio_flush(pb);
+
+ return 0;
+}
+
+static int ast_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVIOContext *pb = s->pb;
+ ASTMuxContext *ast = s->priv_data;
+ AVCodecContext *enc = s->streams[0]->codec;
+ int size = pkt->size / enc->channels;
+
+ if (s->streams[0]->nb_frames == 0)
+ ast->fbs = size;
+
+ ffio_wfourcc(pb, "BLCK");
+ avio_wb32(pb, size); /* Block size */
+
+ /* padding */
+ avio_wb64(pb, 0);
+ avio_wb64(pb, 0);
+ avio_wb64(pb, 0);
+
+ avio_write(pb, pkt->data, pkt->size);
+
+ return 0;
+}
+
+static int ast_write_trailer(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ ASTMuxContext *ast = s->priv_data;
+ AVCodecContext *enc = s->streams[0]->codec;
+ int64_t file_size = avio_tell(pb);
+ int64_t samples = (file_size - 64 - (32 * s->streams[0]->nb_frames)) / enc->block_align; /* PCM_S16BE_PLANAR */
+
+ av_log(s, AV_LOG_DEBUG, "total samples: %"PRId64"\n", samples);
+
+ if (s->pb->seekable) {
+ /* Number of samples */
+ avio_seek(pb, ast->samples, SEEK_SET);
+ avio_wb32(pb, samples);
+
+ /* Loopstart if provided */
+ if (ast->loopstart > 0) {
+ if (ast->loopstart >= samples) {
+ av_log(s, AV_LOG_WARNING, "Loopstart value is out of range and will be ignored\n");
+ ast->loopstart = -1;
+ avio_skip(pb, 4);
+ } else
+ avio_wb32(pb, ast->loopstart);
+ } else
+ avio_skip(pb, 4);
+
+ /* Loopend if provided. Otherwise number of samples again */
+ if (ast->loopend && ast->loopstart >= 0) {
+ if (ast->loopend > samples) {
+ av_log(s, AV_LOG_WARNING, "Loopend value is out of range and will be ignored\n");
+ ast->loopend = samples;
+ }
+ avio_wb32(pb, ast->loopend);
+ } else {
+ avio_wb32(pb, samples);
+ }
+
+ /* Size of first block */
+ avio_wb32(pb, ast->fbs);
+
+ /* File size minus header */
+ avio_seek(pb, ast->size, SEEK_SET);
+ avio_wb32(pb, file_size - 64);
+
+ /* Loop flag */
+ if (ast->loopstart >= 0) {
+ avio_skip(pb, 6);
+ avio_wb16(pb, 0xFFFF);
+ }
+
+ avio_seek(pb, file_size, SEEK_SET);
+ avio_flush(pb);
+ }
+ return 0;
+}
+
+#define OFFSET(obj) offsetof(ASTMuxContext, obj)
+static const AVOption options[] = {
+ { "loopstart", "Loopstart position in milliseconds.", OFFSET(loopstart), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "loopend", "Loopend position in milliseconds.", OFFSET(loopend), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { NULL },
+};
+
+static const AVClass ast_muxer_class = {
+ .class_name = "AST muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVOutputFormat ff_ast_muxer = {
+ .name = "ast",
+ .long_name = NULL_IF_CONFIG_SMALL("AST (Audio Stream)"),
+ .extensions = "ast",
+ .priv_data_size = sizeof(ASTMuxContext),
+ .audio_codec = AV_CODEC_ID_PCM_S16BE_PLANAR,
+ .video_codec = AV_CODEC_ID_NONE,
+ .write_header = ast_write_header,
+ .write_packet = ast_write_packet,
+ .write_trailer = ast_write_trailer,
+ .priv_class = &ast_muxer_class,
+ .codec_tag = (const AVCodecTag* const []){ff_codec_ast_tags, 0},
+};
diff --git a/libavformat/au.c b/libavformat/au.c
index e682cbf305..53702bcb42 100644
--- a/libavformat/au.c
+++ b/libavformat/au.c
@@ -4,20 +4,20 @@
*
* first version by Francois Revol <revol@free.fr>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -31,6 +31,12 @@
#include "internal.h"
#include "avio_internal.h"
#include "pcm.h"
+#include "libavutil/avassert.h"
+
+/* if we don't know the size in advance */
+#define AU_UNKNOWN_SIZE ((uint32_t)(~0))
+/* the specification requires an annotation field of at least eight bytes */
+#define AU_HEADER_SIZE (24+8)
static const AVCodecTag codec_au_tags[] = {
{ AV_CODEC_ID_PCM_MULAW, 1 },
@@ -40,7 +46,12 @@ static const AVCodecTag codec_au_tags[] = {
{ AV_CODEC_ID_PCM_S32BE, 5 },
{ AV_CODEC_ID_PCM_F32BE, 6 },
{ AV_CODEC_ID_PCM_F64BE, 7 },
+ { AV_CODEC_ID_ADPCM_G726LE, 23 },
+ { AV_CODEC_ID_ADPCM_G722,24 },
+ { AV_CODEC_ID_ADPCM_G726LE, 25 },
+ { AV_CODEC_ID_ADPCM_G726LE, 26 },
{ AV_CODEC_ID_PCM_ALAW, 27 },
+ { AV_CODEC_ID_ADPCM_G726LE, MKBETAG('7','2','6','2') },
{ AV_CODEC_ID_NONE, 0 },
};
@@ -59,7 +70,7 @@ static int au_probe(AVProbeData *p)
static int au_read_header(AVFormatContext *s)
{
- int size;
+ int size, data_size = 0;
unsigned int tag;
AVIOContext *pb = s->pb;
unsigned int id, channels, rate;
@@ -71,7 +82,12 @@ static int au_read_header(AVFormatContext *s)
if (tag != MKTAG('.', 's', 'n', 'd'))
return AVERROR_INVALIDDATA;
size = avio_rb32(pb); /* header size */
- avio_rb32(pb); /* data size */
+ data_size = avio_rb32(pb); /* data size in bytes */
+
+ if (data_size < 0 && data_size != AU_UNKNOWN_SIZE) {
+ av_log(s, AV_LOG_ERROR, "Invalid negative data size '%d' found\n", data_size);
+ return AVERROR_INVALIDDATA;
+ }
id = avio_rb32(pb);
rate = avio_rb32(pb);
@@ -90,7 +106,15 @@ static int au_read_header(AVFormatContext *s)
}
bps = av_get_bits_per_sample(codec);
- if (!bps) {
+ if (codec == AV_CODEC_ID_ADPCM_G726LE) {
+ if (id == MKBETAG('7','2','6','2')) {
+ bps = 2;
+ } else {
+ const uint8_t bpcss[] = {4, 0, 3, 5};
+ av_assert0(id >= 23 && id < 23 + 4);
+ bps = bpcss[id - 23];
+ }
+ } else if (!bps) {
avpriv_request_sample(s, "Unknown bits per sample");
return AVERROR_PATCHWELCOME;
}
@@ -113,8 +137,11 @@ static int au_read_header(AVFormatContext *s)
st->codec->codec_id = codec;
st->codec->channels = channels;
st->codec->sample_rate = rate;
+ st->codec->bits_per_coded_sample = bps;
st->codec->bit_rate = channels * rate * bps;
- st->codec->block_align = channels * bps >> 3;
+ st->codec->block_align = FFMAX(bps * st->codec->channels / 8, 1);
+ if (data_size != AU_UNKNOWN_SIZE)
+ st->duration = (((int64_t)data_size)<<3) / (st->codec->channels * (int64_t)bps);
st->start_time = 0;
avpriv_set_pts_info(st, 64, 1, rate);
@@ -122,27 +149,12 @@ static int au_read_header(AVFormatContext *s)
return 0;
}
-static int au_read_packet(AVFormatContext *s, AVPacket *pkt)
-{
- int ret;
-
- ret = av_get_packet(s->pb, pkt, BLOCK_SIZE *
- s->streams[0]->codec->block_align);
- if (ret < 0)
- return ret;
-
- pkt->stream_index = 0;
- pkt->duration = ret / s->streams[0]->codec->block_align;
-
- return 0;
-}
-
AVInputFormat ff_au_demuxer = {
.name = "au",
.long_name = NULL_IF_CONFIG_SMALL("Sun AU"),
.read_probe = au_probe,
.read_header = au_read_header,
- .read_packet = au_read_packet,
+ .read_packet = ff_pcm_read_packet,
.read_seek = ff_pcm_read_seek,
.codec_tag = (const AVCodecTag* const []) { codec_au_tags, 0 },
};
@@ -153,35 +165,29 @@ AVInputFormat ff_au_demuxer = {
#include "rawenc.h"
-/* if we don't know the size in advance */
-#define AU_UNKNOWN_SIZE ((uint32_t)(~0))
-
-/* AUDIO_FILE header */
-static int put_au_header(AVIOContext *pb, AVCodecContext *enc)
+static int au_write_header(AVFormatContext *s)
{
- if (!enc->codec_tag)
+ AVIOContext *pb = s->pb;
+ AVCodecContext *enc = s->streams[0]->codec;
+
+ if (s->nb_streams != 1) {
+ av_log(s, AV_LOG_ERROR, "only one stream is supported\n");
+ return AVERROR(EINVAL);
+ }
+
+ enc->codec_tag = ff_codec_get_tag(codec_au_tags, enc->codec_id);
+ if (!enc->codec_tag) {
+ av_log(s, AV_LOG_ERROR, "unsupported codec\n");
return AVERROR(EINVAL);
+ }
ffio_wfourcc(pb, ".snd"); /* magic number */
- avio_wb32(pb, 24); /* header size */
+ avio_wb32(pb, AU_HEADER_SIZE); /* header size */
avio_wb32(pb, AU_UNKNOWN_SIZE); /* data size */
avio_wb32(pb, enc->codec_tag); /* codec ID */
avio_wb32(pb, enc->sample_rate);
avio_wb32(pb, enc->channels);
-
- return 0;
-}
-
-static int au_write_header(AVFormatContext *s)
-{
- AVIOContext *pb = s->pb;
- int ret;
-
- s->priv_data = NULL;
-
- if ((ret = put_au_header(pb, s->streams[0]->codec)) < 0)
- return ret;
-
+ avio_wb64(pb, 0); /* annotation field */
avio_flush(pb);
return 0;
@@ -190,13 +196,12 @@ static int au_write_header(AVFormatContext *s)
static int au_write_trailer(AVFormatContext *s)
{
AVIOContext *pb = s->pb;
- int64_t file_size;
+ int64_t file_size = avio_tell(pb);
- if (s->pb->seekable) {
+ if (s->pb->seekable && file_size < INT32_MAX) {
/* update file size */
- file_size = avio_tell(pb);
avio_seek(pb, 8, SEEK_SET);
- avio_wb32(pb, (uint32_t)(file_size - 24));
+ avio_wb32(pb, (uint32_t)(file_size - AU_HEADER_SIZE));
avio_seek(pb, file_size, SEEK_SET);
avio_flush(pb);
}
diff --git a/libavformat/audiointerleave.c b/libavformat/audiointerleave.c
index ba78d4e988..b64010ff44 100644
--- a/libavformat/audiointerleave.c
+++ b/libavformat/audiointerleave.c
@@ -3,20 +3,20 @@
*
* Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -34,7 +34,7 @@ void ff_audio_interleave_close(AVFormatContext *s)
AudioInterleaveContext *aic = st->priv_data;
if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO)
- av_fifo_free(aic->fifo);
+ av_fifo_freep(&aic->fifo);
}
}
@@ -45,8 +45,12 @@ int ff_audio_interleave_init(AVFormatContext *s,
int i;
if (!samples_per_frame)
- return -1;
+ return AVERROR(EINVAL);
+ if (!time_base.num) {
+ av_log(s, AV_LOG_ERROR, "timebase not set for audio interleave\n");
+ return AVERROR(EINVAL);
+ }
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
AudioInterleaveContext *aic = st->priv_data;
@@ -56,14 +60,15 @@ int ff_audio_interleave_init(AVFormatContext *s,
av_get_bits_per_sample(st->codec->codec_id)) / 8;
if (!aic->sample_size) {
av_log(s, AV_LOG_ERROR, "could not compute sample size\n");
- return -1;
+ return AVERROR(EINVAL);
}
aic->samples_per_frame = samples_per_frame;
aic->samples = aic->samples_per_frame;
aic->time_base = time_base;
aic->fifo_size = 100* *aic->samples;
- aic->fifo= av_fifo_alloc(100 * *aic->samples);
+ if (!(aic->fifo= av_fifo_alloc_array(100, *aic->samples)))
+ return AVERROR(ENOMEM);
}
}
@@ -110,7 +115,7 @@ int ff_audio_rechunk_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt
unsigned new_size = av_fifo_size(aic->fifo) + pkt->size;
if (new_size > aic->fifo_size) {
if (av_fifo_realloc2(aic->fifo, new_size) < 0)
- return -1;
+ return AVERROR(ENOMEM);
aic->fifo_size = new_size;
}
av_fifo_generic_write(aic->fifo, pkt->data, pkt->size, NULL);
@@ -128,9 +133,12 @@ int ff_audio_rechunk_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt
AVStream *st = s->streams[i];
if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
AVPacket new_pkt;
- while (interleave_new_audio_packet(s, &new_pkt, i, flush))
+ while ((ret = interleave_new_audio_packet(s, &new_pkt, i, flush)) > 0) {
if ((ret = ff_interleave_add_packet(s, &new_pkt, compare_ts)) < 0)
return ret;
+ }
+ if (ret < 0)
+ return ret;
}
}
diff --git a/libavformat/audiointerleave.h b/libavformat/audiointerleave.h
index 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 e639ed5593..9d843e0ca4 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
*/
@@ -180,7 +180,7 @@ int ff_avc_write_annexb_extradata(const uint8_t *in, uint8_t **buf, int *size)
if (11 + sps_size + pps_size > *size)
return AVERROR_INVALIDDATA;
out_size = 8 + sps_size + pps_size;
- out = av_mallocz(out_size);
+ out = av_mallocz(out_size + FF_INPUT_BUFFER_PADDING_SIZE);
if (!out)
return AVERROR(ENOMEM);
AV_WB32(&out[0], 0x00000001);
diff --git a/libavformat/avc.h b/libavformat/avc.h
index 2442e5fff6..c5e80ff650 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 0994c8075e..3007f814ce 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -1,20 +1,20 @@
/*
* copyright (c) 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -50,7 +50,7 @@
*
* Main lavf structure used for both muxing and demuxing is AVFormatContext,
* which exports all information about the file being read or written. As with
- * most Libav structures, its size is not part of public ABI, so it cannot be
+ * most Libavformat structures, its size is not part of public ABI, so it cannot be
* allocated on stack or directly with av_malloc(). To create an
* AVFormatContext, use avformat_alloc_context() (some functions, like
* avformat_open_input() might do that for you).
@@ -261,6 +261,8 @@
struct AVFormatContext;
+struct AVDeviceInfoList;
+struct AVDeviceCapabilitiesQuery;
/**
* @defgroup metadata_api Public Metadata API
@@ -272,7 +274,7 @@ struct AVFormatContext;
*
* Metadata is exported or set as pairs of key/value strings in the 'metadata'
* fields of the AVFormatContext, AVStream, AVChapter and AVProgram structs
- * using the @ref lavu_dict "AVDictionary" API. Like all strings in Libav,
+ * using the @ref lavu_dict "AVDictionary" API. Like all strings in FFmpeg,
* metadata is assumed to be UTF-8 encoded Unicode. Note that metadata
* exported by demuxers isn't checked to be valid UTF-8 in most cases.
*
@@ -399,6 +401,9 @@ typedef struct AVProbeData {
const char *mime_type; /**< mime_type, when known. */
} AVProbeData;
+#define AVPROBE_SCORE_RETRY (AVPROBE_SCORE_MAX/4)
+#define AVPROBE_SCORE_STREAM_RETRY (AVPROBE_SCORE_MAX/4-1)
+
#define AVPROBE_SCORE_EXTENSION 50 ///< score for file extension
#define AVPROBE_SCORE_MIME 75 ///< score for file mime type
#define AVPROBE_SCORE_MAX 100 ///< maximum score
@@ -422,14 +427,24 @@ typedef struct AVProbeData {
#define AVFMT_NOGENSEARCH 0x4000 /**< Format does not allow to fall back on generic search */
#define AVFMT_NO_BYTE_SEEK 0x8000 /**< Format does not allow seeking by bytes */
#define AVFMT_ALLOW_FLUSH 0x10000 /**< Format allows flushing. If not set, the muxer will not receive a NULL packet in the write_packet function. */
-#define AVFMT_TS_NONSTRICT 0x20000 /**< Format does not require strictly
+#if LIBAVFORMAT_VERSION_MAJOR <= 54
+#define AVFMT_TS_NONSTRICT 0x8020000 //we try to be compatible to the ABIs of ffmpeg and major forks
+#else
+#define AVFMT_TS_NONSTRICT 0x20000
+#endif
+ /**< Format does not require strictly
increasing timestamps, but they must
still be monotonic */
#define AVFMT_TS_NEGATIVE 0x40000 /**< Format allows muxing negative
timestamps. If not set the timestamp
will be shifted in av_write_frame and
av_interleaved_write_frame so they
- start from 0. */
+ start from 0.
+ The user or muxer can override this through
+ AVFormatContext.avoid_negative_ts
+ */
+
+#define AVFMT_SEEK_TO_PTS 0x4000000 /**< Seeking is based on PTS */
/**
* @addtogroup lavf_encoding
@@ -499,8 +514,44 @@ typedef struct AVOutputFormat {
*
* @return 1 if the codec is supported, 0 if it is not.
* A negative number if unknown.
+ * MKTAG('A', 'P', 'I', 'C') if the codec is only supported as AV_DISPOSITION_ATTACHED_PIC
*/
int (*query_codec)(enum AVCodecID id, int std_compliance);
+
+ void (*get_output_timestamp)(struct AVFormatContext *s, int stream,
+ int64_t *dts, int64_t *wall);
+ /**
+ * Allows sending messages from application to device.
+ */
+ int (*control_message)(struct AVFormatContext *s, int type,
+ void *data, size_t data_size);
+
+ /**
+ * Write an uncoded AVFrame.
+ *
+ * See av_write_uncoded_frame() for details.
+ *
+ * The library will free *frame afterwards, but the muxer can prevent it
+ * by setting the pointer to NULL.
+ */
+ int (*write_uncoded_frame)(struct AVFormatContext *, int stream_index,
+ AVFrame **frame, unsigned flags);
+ /**
+ * Returns device list with it properties.
+ * @see avdevice_list_devices() for more details.
+ */
+ int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);
+ /**
+ * Initialize device capabilities submodule.
+ * @see avdevice_capabilities_create() for more details.
+ */
+ int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
+ /**
+ * Free device capabilities submodule.
+ * @see avdevice_capabilities_free() for more details.
+ */
+ int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
+ enum AVCodecID data_codec; /**< default data codec */
} AVOutputFormat;
/**
* @}
@@ -527,7 +578,7 @@ typedef struct AVInputFormat {
/**
* Can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS,
* AVFMT_GENERIC_INDEX, AVFMT_TS_DISCONT, AVFMT_NOBINSEARCH,
- * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK.
+ * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS.
*/
int flags;
@@ -636,6 +687,24 @@ typedef struct AVInputFormat {
* Active streams are all streams that have AVStream.discard < AVDISCARD_ALL.
*/
int (*read_seek2)(struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags);
+
+ /**
+ * Returns device list with it properties.
+ * @see avdevice_list_devices() for more details.
+ */
+ int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);
+
+ /**
+ * Initialize device capabilities submodule.
+ * @see avdevice_capabilities_create() for more details.
+ */
+ int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
+
+ /**
+ * Free device capabilities submodule.
+ * @see avdevice_capabilities_free() for more details.
+ */
+ int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
} AVInputFormat;
/**
* @}
@@ -647,11 +716,19 @@ enum AVStreamParseType {
AVSTREAM_PARSE_HEADERS, /**< Only parse headers, do not repack. */
AVSTREAM_PARSE_TIMESTAMPS, /**< full parsing and interpolation of timestamps for frames not starting on a packet boundary */
AVSTREAM_PARSE_FULL_ONCE, /**< full parsing and repack of the first frame only, only implemented for H.264 currently */
+ AVSTREAM_PARSE_FULL_RAW=MKTAG(0,'R','A','W'), /**< full parsing and repack with timestamp and position generation by parser for raw
+ this assumes that each packet in the file contains no demuxer level headers and
+ just codec level data, otherwise position generation would fail */
};
typedef struct AVIndexEntry {
int64_t pos;
- int64_t timestamp;
+ int64_t timestamp; /**<
+ * Timestamp in AVStream.time_base units, preferably the time from which on correctly decoded frames are available
+ * when seeking to this entry. That means preferable PTS on keyframe based formats.
+ * But demuxers can choose to store a different timestamp, if it is more convenient for the implementation or nothing better
+ * is known
+ */
#define AVINDEX_KEYFRAME 0x0001
int flags:2;
int size:30; //Yeah, trying to keep the size of this small to reduce memory requirements (it is 24 vs. 32 bytes due to possible 8-byte alignment).
@@ -683,6 +760,20 @@ typedef struct AVIndexEntry {
#define AV_DISPOSITION_ATTACHED_PIC 0x0400
/**
+ * To specify text track kind (different from subtitles default).
+ */
+#define AV_DISPOSITION_CAPTIONS 0x10000
+#define AV_DISPOSITION_DESCRIPTIONS 0x20000
+#define AV_DISPOSITION_METADATA 0x40000
+
+/**
+ * Options for behavior on timestamp wrap detection.
+ */
+#define AV_PTS_WRAP_IGNORE 0 ///< ignore the wrap
+#define AV_PTS_WRAP_ADD_OFFSET 1 ///< add the format specific offset on wrap detection
+#define AV_PTS_WRAP_SUB_OFFSET -1 ///< subtract the format specific offset on wrap detection
+
+/**
* Stream structure.
* New fields can be added to the end with minor version bumps.
* Removal, reordering and changes to existing fields require a major
@@ -734,10 +825,12 @@ typedef struct AVStream {
AVRational time_base;
/**
- * Decoding: pts of the first frame of the stream, in stream time base.
+ * Decoding: pts of the first frame of the stream in presentation order, in stream time base.
* Only set this if you are absolutely 100% sure that the value you set
* it to really is the pts of the first frame.
* This may be undefined (AV_NOPTS_VALUE).
+ * @note The ASF header does NOT contain a correct start_time the ASF
+ * demuxer must NOT set this.
*/
int64_t start_time;
@@ -796,6 +889,8 @@ typedef struct AVStream {
* - muxing: May be set by the caller before avformat_write_header().
*
* Freed by libavformat in avformat_free_context().
+ *
+ * @see av_format_inject_global_side_data()
*/
AVPacketSideData *side_data;
/**
@@ -822,11 +917,25 @@ typedef struct AVStream {
/**
* Stream information used internally by av_find_stream_info()
*/
-#define MAX_STD_TIMEBASES (60*12+5)
+#define MAX_STD_TIMEBASES (30*12+7+6)
struct {
- int nb_decoded_frames;
+ int64_t last_dts;
+ int64_t duration_gcd;
+ int duration_count;
+ int64_t rfps_duration_sum;
+ double (*duration_error)[2][MAX_STD_TIMEBASES];
+ int64_t codec_info_duration;
+ int64_t codec_info_duration_fields;
+
+ /**
+ * 0 -> decoder has not been searched for yet.
+ * >0 -> decoder found
+ * <0 -> decoder with codec_id == -found_decoder has not been found
+ */
int found_decoder;
+ int64_t last_duration;
+
/**
* Those are used for average framerate estimation.
*/
@@ -840,6 +949,13 @@ typedef struct AVStream {
int pts_wrap_bits; /**< number of bits in pts (used for wrapping control) */
// Timestamp generation support:
+ /**
+ * Timestamp corresponding to the last dts sync point.
+ *
+ * Initialized when AVCodecParserContext.dts_sync_point >= 0 and
+ * a DTS is received from the underlying container. Otherwise set to
+ * AV_NOPTS_VALUE by default.
+ */
int64_t first_dts;
int64_t cur_dts;
int64_t last_IP_pts;
@@ -872,8 +988,144 @@ typedef struct AVStream {
support seeking natively. */
int nb_index_entries;
unsigned int index_entries_allocated_size;
+
+ /**
+ * Real base framerate of the stream.
+ * This is the lowest framerate with which all timestamps can be
+ * represented accurately (it is the least common multiple of all
+ * framerates in the stream). Note, this value is just a guess!
+ * For example, if the time base is 1/90000 and all frames have either
+ * approximately 3600 or 1800 timer ticks, then r_frame_rate will be 50/1.
+ *
+ * Code outside avformat should access this field using:
+ * av_stream_get/set_r_frame_rate(stream)
+ */
+ AVRational r_frame_rate;
+
+ /**
+ * Stream Identifier
+ * This is the MPEG-TS stream identifier +1
+ * 0 means unknown
+ */
+ int stream_identifier;
+
+ int64_t interleaver_chunk_size;
+ int64_t interleaver_chunk_duration;
+
+ /**
+ * stream probing state
+ * -1 -> probing finished
+ * 0 -> no probing requested
+ * rest -> perform probing with request_probe being the minimum score to accept.
+ * NOT PART OF PUBLIC API
+ */
+ int request_probe;
+ /**
+ * Indicates that everything up to the next keyframe
+ * should be discarded.
+ */
+ int skip_to_keyframe;
+
+ /**
+ * Number of samples to skip at the start of the frame decoded from the next packet.
+ */
+ int skip_samples;
+
+ /**
+ * If not 0, the first audio sample that should be discarded from the stream.
+ * This is broken by design (needs global sample count), but can't be
+ * avoided for broken by design formats such as mp3 with ad-hoc gapless
+ * audio support.
+ */
+ int64_t first_discard_sample;
+
+ /**
+ * The sample after last sample that is intended to be discarded after
+ * first_discard_sample. Works on frame boundaries only. Used to prevent
+ * early EOF if the gapless info is broken (considered concatenated mp3s).
+ */
+ int64_t last_discard_sample;
+
+ /**
+ * Number of internally decoded frames, used internally in libavformat, do not access
+ * its lifetime differs from info which is why it is not in that structure.
+ */
+ int nb_decoded_frames;
+
+ /**
+ * Timestamp offset added to timestamps before muxing
+ * NOT PART OF PUBLIC API
+ */
+ int64_t mux_ts_offset;
+
+ /**
+ * Internal data to check for wrapping of the time stamp
+ */
+ int64_t pts_wrap_reference;
+
+ /**
+ * Options for behavior, when a wrap is detected.
+ *
+ * Defined by AV_PTS_WRAP_ values.
+ *
+ * If correction is enabled, there are two possibilities:
+ * If the first time stamp is near the wrap point, the wrap offset
+ * will be subtracted, which will create negative time stamps.
+ * Otherwise the offset will be added.
+ */
+ int pts_wrap_behavior;
+
+ /**
+ * Internal data to prevent doing update_initial_durations() twice
+ */
+ int update_initial_durations_done;
+
+ /**
+ * Internal data to generate dts from pts
+ */
+ int64_t pts_reorder_error[MAX_REORDER_DELAY+1];
+ uint8_t pts_reorder_error_count[MAX_REORDER_DELAY+1];
+
+ /**
+ * Internal data to analyze DTS and detect faulty mpeg streams
+ */
+ int64_t last_dts_for_order_check;
+ uint8_t dts_ordered;
+ uint8_t dts_misordered;
+
+ /**
+ * Internal data to inject global side data
+ */
+ int inject_global_side_data;
+
+ /**
+ * String containing paris of key and values describing recommended encoder configuration.
+ * Paris are separated by ','.
+ * Keys are separated from values by '='.
+ */
+ char *recommended_encoder_configuration;
+
+ /**
+ * display aspect ratio (0 if unknown)
+ * - encoding: unused
+ * - decoding: Set by libavformat to calculate sample_aspect_ratio internally
+ */
+ AVRational display_aspect_ratio;
} AVStream;
+AVRational av_stream_get_r_frame_rate(const AVStream *s);
+void av_stream_set_r_frame_rate(AVStream *s, AVRational r);
+struct AVCodecParserContext *av_stream_get_parser(const AVStream *s);
+char* av_stream_get_recommended_encoder_configuration(const AVStream *s);
+void av_stream_set_recommended_encoder_configuration(AVStream *s, char *configuration);
+
+/**
+ * Returns the pts of the last muxed packet + its duration
+ *
+ * the retuned value is undefined when used with a demuxer.
+ */
+int64_t av_stream_get_end_pts(const AVStream *st);
+
#define AV_PROGRAM_RUNNING 1
/**
@@ -889,6 +1141,23 @@ typedef struct AVProgram {
unsigned int *stream_index;
unsigned int nb_stream_indexes;
AVDictionary *metadata;
+
+ int program_num;
+ int pmt_pid;
+ int pcr_pid;
+
+ /*****************************************************************
+ * All fields below this line are not part of the public API. They
+ * may not be used outside of libavformat and can be changed and
+ * removed at will.
+ * New public fields should be added right above.
+ *****************************************************************
+ */
+ int64_t start_time;
+ int64_t end_time;
+
+ int64_t pts_wrap_reference; ///< reference dts for wrap detection
+ int pts_wrap_behavior; ///< behavior on wrap detection
} AVProgram;
#define AVFMTCTX_NOHEADER 0x0001 /**< signal that no header is present
@@ -901,6 +1170,24 @@ typedef struct AVChapter {
AVDictionary *metadata;
} AVChapter;
+
+/**
+ * Callback used by devices to communicate with application.
+ */
+typedef int (*av_format_control_message)(struct AVFormatContext *s, int type,
+ void *data, size_t data_size);
+
+
+/**
+ * The duration of a video can be estimated through various ways, and this enum can be used
+ * to know how the duration was estimated.
+ */
+enum AVDurationEstimationMethod {
+ AVFMT_DURATION_FROM_PTS, ///< Duration accurately estimated from PTSes
+ AVFMT_DURATION_FROM_STREAM, ///< Duration estimated from a stream with a known duration
+ AVFMT_DURATION_FROM_BITRATE ///< Duration estimated from bitrate (less accurate)
+};
+
typedef struct AVFormatInternal AVFormatInternal;
/**
@@ -1011,7 +1298,7 @@ typedef struct AVFormatContext {
/**
* Total stream bitrate in bit/s, 0 if not
* available. Never set it directly if the file_size and the
- * duration are known as Libav can compute it automatically.
+ * duration are known as FFmpeg can compute it automatically.
*/
int bit_rate;
@@ -1040,19 +1327,20 @@ typedef struct AVFormatContext {
* This flag is mainly intended for testing.
*/
#define AVFMT_FLAG_BITEXACT 0x0400
+#define AVFMT_FLAG_MP4A_LATM 0x8000 ///< Enable RTP MP4A-LATM payload
+#define AVFMT_FLAG_SORT_DTS 0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down)
+#define AVFMT_FLAG_PRIV_OPT 0x20000 ///< Enable use of private options by delaying codec open (this could be made default once all code is converted)
+#define AVFMT_FLAG_KEEP_SIDE_DATA 0x40000 ///< Don't merge side data but keep it separate.
/**
- * Maximum size of the data read from input for determining
- * the input container format.
- * Demuxing only, set by the caller before avformat_open_input().
+ * @deprecated deprecated in favor of probesize2
*/
unsigned int probesize;
/**
- * Maximum duration (in AV_TIME_BASE units) of the data read
- * from input in avformat_find_stream_info().
- * Demuxing only, set by the caller before avformat_find_stream_info().
+ * @deprecated deprecated in favor of max_analyze_duration2
*/
+ attribute_deprecated
int max_analyze_duration;
const uint8_t *key;
@@ -1125,7 +1413,12 @@ typedef struct AVFormatContext {
* Start time of the stream in real world time, in microseconds
* since the Unix epoch (00:00 1st January 1970). That is, pts=0 in the
* stream was captured at this real world time.
- * Muxing only, set by the caller before avformat_write_header().
+ * - muxing: Set by the caller before avformat_write_header(). If set to
+ * either 0 or AV_NOPTS_VALUE, then the current wall-time will
+ * be used.
+ * - demuxing: Set by libavformat. AV_NOPTS_VALUE if unknown. Note that
+ * the value may become known after some number of frames
+ * have been received.
*/
int64_t start_time_realtime;
@@ -1201,7 +1494,7 @@ typedef struct AVFormatContext {
/**
* Avoid negative timestamps during muxing.
* Any value of the AVFMT_AVOID_NEG_TS_* constants.
- * Note, this only works when using av_interleaved_write_frame.
+ * Note, this only works when using av_interleaved_write_frame. (interleave_packet_per_dts is in use)
* - muxing: Set by user
* - demuxing: unused
*/
@@ -1211,12 +1504,252 @@ typedef struct AVFormatContext {
#define AVFMT_AVOID_NEG_TS_MAKE_ZERO 2 ///< Shift timestamps so that they start at 0
/**
+ * 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;
+
+ /**
+ * avio flags, used to force AVIO_FLAG_DIRECT.
+ * - encoding: unused
+ * - decoding: Set by user via AVOptions (NO direct access)
+ */
+ int avio_flags;
+
+ /**
+ * The duration field can be estimated through various ways, and this field can be used
+ * to know how the duration was estimated.
+ * - encoding: unused
+ * - decoding: Read by user via AVOptions (NO direct access)
+ */
+ enum AVDurationEstimationMethod duration_estimation_method;
+
+ /**
+ * Skip initial bytes when opening stream
+ * - encoding: unused
+ * - decoding: Set by user via AVOptions (NO direct access)
+ */
+ int64_t skip_initial_bytes;
+
+ /**
+ * Correct single timestamp overflows
+ * - encoding: unused
+ * - decoding: Set by user via AVOptions (NO direct access)
+ */
+ unsigned int correct_ts_overflow;
+
+ /**
+ * Force seeking to any (also non key) frames.
+ * - encoding: unused
+ * - decoding: Set by user via AVOptions (NO direct access)
+ */
+ int seek2any;
+
+ /**
+ * Flush the I/O context after each packet.
+ * - encoding: Set by user via AVOptions (NO direct access)
+ * - decoding: unused
+ */
+ int flush_packets;
+
+ /**
+ * format probing score.
+ * The maximal score is AVPROBE_SCORE_MAX, its set when the demuxer probes
+ * the format.
+ * - encoding: unused
+ * - decoding: set by avformat, read by user via av_format_get_probe_score() (NO direct access)
+ */
+ int probe_score;
+
+ /**
+ * number of bytes to read maximally to identify format.
+ * - encoding: unused
+ * - decoding: set by user through AVOPtions (NO direct access)
+ */
+ int format_probesize;
+
+ /**
+ * ',' separated list of allowed decoders.
+ * If NULL then all are allowed
+ * - encoding: unused
+ * - decoding: set by user through AVOptions (NO direct access)
+ */
+ char *codec_whitelist;
+
+ /**
+ * ',' separated list of allowed demuxers.
+ * If NULL then all are allowed
+ * - encoding: unused
+ * - decoding: set by user through AVOptions (NO direct access)
+ */
+ char *format_whitelist;
+
+ /**
* An opaque field for libavformat internal usage.
* Must not be accessed in any way by callers.
*/
AVFormatInternal *internal;
+
+ /**
+ * IO repositioned flag.
+ * This is set by avformat when the underlaying IO context read pointer
+ * is repositioned, for example when doing byte based seeking.
+ * Demuxers can use the flag to detect such changes.
+ */
+ int io_repositioned;
+
+ /**
+ * Forced video codec.
+ * This allows forcing a specific decoder, even when there are multiple with
+ * the same codec_id.
+ * Demuxing: Set by user via av_format_set_video_codec (NO direct access).
+ */
+ AVCodec *video_codec;
+
+ /**
+ * Forced audio codec.
+ * This allows forcing a specific decoder, even when there are multiple with
+ * the same codec_id.
+ * Demuxing: Set by user via av_format_set_audio_codec (NO direct access).
+ */
+ AVCodec *audio_codec;
+
+ /**
+ * Forced subtitle codec.
+ * This allows forcing a specific decoder, even when there are multiple with
+ * the same codec_id.
+ * Demuxing: Set by user via av_format_set_subtitle_codec (NO direct access).
+ */
+ AVCodec *subtitle_codec;
+
+ /**
+ * Forced data codec.
+ * This allows forcing a specific decoder, even when there are multiple with
+ * the same codec_id.
+ * Demuxing: Set by user via av_format_set_data_codec (NO direct access).
+ */
+ AVCodec *data_codec;
+
+ /**
+ * Number of bytes to be written as padding in a metadata header.
+ * Demuxing: Unused.
+ * Muxing: Set by user via av_format_set_metadata_header_padding.
+ */
+ int metadata_header_padding;
+
+ /**
+ * User data.
+ * This is a place for some private data of the user.
+ * Mostly usable with control_message_cb or any future callbacks in device's context.
+ */
+ void *opaque;
+
+ /**
+ * Callback used by devices to communicate with application.
+ */
+ av_format_control_message control_message_cb;
+
+ /**
+ * Output timestamp offset, in microseconds.
+ * Muxing: set by user via AVOptions (NO direct access)
+ */
+ int64_t output_ts_offset;
+
+ /**
+ * Maximum duration (in AV_TIME_BASE units) of the data read
+ * from input in avformat_find_stream_info().
+ * Demuxing only, set by the caller before avformat_find_stream_info()
+ * via AVOptions (NO direct access).
+ * Can be set to 0 to let avformat choose using a heuristic.
+ */
+ int64_t max_analyze_duration2;
+
+ /**
+ * Maximum size of the data read from input for determining
+ * the input container format.
+ * Demuxing only, set by the caller before avformat_open_input()
+ * via AVOptions (NO direct access).
+ */
+ int64_t probesize2;
+
+ /**
+ * dump format separator.
+ * can be ", " or "\n " or anything else
+ * Code outside libavformat should access this field using AVOptions
+ * (NO direct access).
+ * - muxing: Set by user.
+ * - demuxing: Set by user.
+ */
+ uint8_t *dump_separator;
+
+ /**
+ * Forced Data codec_id.
+ * Demuxing: Set by user.
+ */
+ enum AVCodecID data_codec_id;
} AVFormatContext;
+int av_format_get_probe_score(const AVFormatContext *s);
+AVCodec * av_format_get_video_codec(const AVFormatContext *s);
+void av_format_set_video_codec(AVFormatContext *s, AVCodec *c);
+AVCodec * av_format_get_audio_codec(const AVFormatContext *s);
+void av_format_set_audio_codec(AVFormatContext *s, AVCodec *c);
+AVCodec * av_format_get_subtitle_codec(const AVFormatContext *s);
+void av_format_set_subtitle_codec(AVFormatContext *s, AVCodec *c);
+AVCodec * av_format_get_data_codec(const AVFormatContext *s);
+void av_format_set_data_codec(AVFormatContext *s, AVCodec *c);
+int av_format_get_metadata_header_padding(const AVFormatContext *s);
+void av_format_set_metadata_header_padding(AVFormatContext *s, int c);
+void * av_format_get_opaque(const AVFormatContext *s);
+void av_format_set_opaque(AVFormatContext *s, void *opaque);
+av_format_control_message av_format_get_control_message_cb(const AVFormatContext *s);
+void av_format_set_control_message_cb(AVFormatContext *s, av_format_control_message callback);
+
+/**
+ * This function will cause global side data to be injected in the next packet
+ * of each stream as well as after any subsequent seek.
+ */
+void av_format_inject_global_side_data(AVFormatContext *s);
+
+/**
+ * Returns the method used to set ctx->duration.
+ *
+ * @return AVFMT_DURATION_FROM_PTS, AVFMT_DURATION_FROM_STREAM, or AVFMT_DURATION_FROM_BITRATE.
+ */
+enum AVDurationEstimationMethod av_fmt_ctx_get_duration_estimation_method(const AVFormatContext* ctx);
+
typedef struct AVPacketList {
AVPacket pkt;
struct AVPacketList *next;
@@ -1254,7 +1787,6 @@ const char *avformat_license(void);
*
* @see av_register_input_format()
* @see av_register_output_format()
- * @see av_register_protocol()
*/
void av_register_all(void);
@@ -1320,6 +1852,9 @@ const AVClass *avformat_get_class(void);
*
* When muxing, should be called by the user before avformat_write_header().
*
+ * User is required to call avcodec_close() and avformat_free_context() to
+ * clean up the allocation by avformat_new_stream().
+ *
* @param s media file handle
* @param c If non-NULL, the AVCodecContext corresponding to the new stream
* will be initialized to use this codec. This is needed for e.g. codec-specific
@@ -1348,6 +1883,25 @@ AVProgram *av_new_program(AVFormatContext *s, int id);
/**
+ * 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
* @{
*/
@@ -1381,6 +1935,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
@@ -1392,9 +1955,17 @@ AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score
* @param logctx the log context
* @param offset the offset within the bytestream to probe from
* @param max_probe_size the maximum probe buffer size (zero for default)
- * @return 0 in case of success, a negative value corresponding to an
+ * @return the score in case of success, a negative value corresponding to an
+ * the maximal score is AVPROBE_SCORE_MAX
* AVERROR code otherwise
*/
+int av_probe_input_buffer2(AVIOContext *pb, AVInputFormat **fmt,
+ const char *filename, void *logctx,
+ unsigned int offset, unsigned int max_probe_size);
+
+/**
+ * Like av_probe_input_buffer2() but returns 0 on success
+ */
int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt,
const char *filename, void *logctx,
unsigned int offset, unsigned int max_probe_size);
@@ -1420,6 +1991,9 @@ 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);
+
/**
* Read packets of a media file to get stream information. This
* is useful for file formats with no headers such as MPEG. This
@@ -1444,6 +2018,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.
@@ -1530,6 +2116,7 @@ int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,
* or if stream_index is -1, in AV_TIME_BASE units.
* If flags contain AVSEEK_FLAG_ANY, then non-keyframes are treated as
* keyframes (this may not be supported by all demuxers).
+ * If flags contain AVSEEK_FLAG_BACKWARD, it is ignored.
*
* @param s media file handle
* @param stream_index index of the stream which is used as time base reference
@@ -1661,6 +2248,44 @@ int av_write_frame(AVFormatContext *s, AVPacket *pkt);
int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);
/**
+ * Write a uncoded frame to an output media file.
+ *
+ * The frame must be correctly interleaved according to the container
+ * specification; if not, then av_interleaved_write_frame() must be used.
+ *
+ * See av_interleaved_write_frame() for details.
+ */
+int av_write_uncoded_frame(AVFormatContext *s, int stream_index,
+ AVFrame *frame);
+
+/**
+ * Write a uncoded frame to an output media file.
+ *
+ * If the muxer supports it, this function allows to write an AVFrame
+ * structure directly, without encoding it into a packet.
+ * It is mostly useful for devices and similar special muxers that use raw
+ * video or PCM data and will not serialize it into a byte stream.
+ *
+ * To test whether it is possible to use it with a given muxer and stream,
+ * use av_write_uncoded_frame_query().
+ *
+ * The caller gives up ownership of the frame and must not access it
+ * afterwards.
+ *
+ * @return >=0 for success, a negative code on error
+ */
+int av_interleaved_write_uncoded_frame(AVFormatContext *s, int stream_index,
+ AVFrame *frame);
+
+/**
+ * Test whether a muxer supports uncoded frame.
+ *
+ * @return >=0 if an uncoded frame can be written to that muxer and stream,
+ * <0 if not
+ */
+int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index);
+
+/**
* Write the stream trailer to an output media file and free the
* file private data.
*
@@ -1695,6 +2320,25 @@ enum AVCodecID av_guess_codec(AVOutputFormat *fmt, const char *short_name,
enum AVMediaType type);
/**
+ * Get timing information for the data currently output.
+ * The exact meaning of "currently output" depends on the format.
+ * It is mostly relevant for devices that have an internal buffer and/or
+ * work in real time.
+ * @param s media file handle
+ * @param stream stream in the media file
+ * @param[out] dts DTS of the last packet output for the stream, in stream
+ * time_base units
+ * @param[out] wall absolute time when that packet whas output,
+ * in microsecond
+ * @return 0 if OK, AVERROR(ENOSYS) if the format does not support it
+ * Note: some formats or devices may not allow to measure dts and wall
+ * atomically.
+ */
+int av_get_output_timestamp(struct AVFormatContext *s, int stream,
+ int64_t *dts, int64_t *wall);
+
+
+/**
* @}
*/
@@ -1741,7 +2385,7 @@ void av_hex_dump_log(void *avcl, int level, const uint8_t *buf, int size);
* @param dump_payload True if the payload must be displayed, too.
* @param st AVStream that the packet belongs to
*/
-void av_pkt_dump2(FILE *f, AVPacket *pkt, int dump_payload, AVStream *st);
+void av_pkt_dump2(FILE *f, const AVPacket *pkt, int dump_payload, const AVStream *st);
/**
@@ -1755,8 +2399,8 @@ void av_pkt_dump2(FILE *f, AVPacket *pkt, int dump_payload, AVStream *st);
* @param dump_payload True if the payload must be displayed, too.
* @param st AVStream that the packet belongs to
*/
-void av_pkt_dump_log2(void *avcl, int level, AVPacket *pkt, int dump_payload,
- AVStream *st);
+void av_pkt_dump_log2(void *avcl, int level, const AVPacket *pkt, int dump_payload,
+ const AVStream *st);
/**
* Get the AVCodecID for the given codec tag tag.
@@ -1778,6 +2422,18 @@ enum AVCodecID av_codec_get_id(const struct AVCodecTag * const *tags, unsigned i
*/
unsigned int av_codec_get_tag(const struct AVCodecTag * const *tags, enum AVCodecID id);
+/**
+ * Get the codec tag for the given codec id.
+ *
+ * @param tags list of supported codec_id - codec_tag pairs, as stored
+ * in AVInputFormat.codec_tag and AVOutputFormat.codec_tag
+ * @param id codec id that should be searched for in the list
+ * @param tag A pointer to the found tag
+ * @return 0 if id was not found in tags, > 0 if it was found
+ */
+int av_codec_get_tag2(const struct AVCodecTag * const *tags, enum AVCodecID id,
+ unsigned int *tag);
+
int av_find_default_stream_index(AVFormatContext *s);
/**
@@ -1836,9 +2492,9 @@ void av_url_split(char *proto, int proto_size,
* codec and time base.
*
* @param ic the context to analyze
- * @param index the index to print, if you have multiple inputs or outputs
+ * @param index index of the stream to dump information about
* @param url the URL to print, such as source or destination file
- * @param is_output whether the context is input or ouput
+ * @param is_output Select whether the specified context is an input(0) or output(1)
*/
void av_dump_format(AVFormatContext *ic,
int index,
@@ -1930,10 +2586,67 @@ const struct AVCodecTag *avformat_get_riff_video_tags(void);
*/
const struct AVCodecTag *avformat_get_riff_audio_tags(void);
/**
+ * @return the table mapping MOV FourCCs for video to libavcodec AVCodecID.
+ */
+const struct AVCodecTag *avformat_get_mov_video_tags(void);
+/**
+ * @return the table mapping MOV FourCCs for audio to AVCodecID.
+ */
+const struct AVCodecTag *avformat_get_mov_audio_tags(void);
+
+/**
* @}
*/
/**
+ * Guess the sample aspect ratio of a frame, based on both the stream and the
+ * frame aspect ratio.
+ *
+ * Since the frame aspect ratio is set by the codec but the stream aspect ratio
+ * is set by the demuxer, these two may not be equal. This function tries to
+ * return the value that you should use if you would like to display the frame.
+ *
+ * Basic logic is to use the stream aspect ratio if it is set to something sane
+ * otherwise use the frame aspect ratio. This way a container setting, which is
+ * usually easy to modify can override the coded value in the frames.
+ *
+ * @param format the format context which the stream is part of
+ * @param stream the stream which the frame is part of
+ * @param frame the frame with the aspect ratio to be determined
+ * @return the guessed (valid) sample_aspect_ratio, 0/1 if no idea
+ */
+AVRational av_guess_sample_aspect_ratio(AVFormatContext *format, AVStream *stream, AVFrame *frame);
+
+/**
+ * Guess the frame rate, based on both the container and codec information.
+ *
+ * @param ctx the format context which the stream is part of
+ * @param stream the stream which the frame is part of
+ * @param frame the frame for which the frame rate should be determined, may be NULL
+ * @return the guessed (valid) frame rate, 0/1 if no idea
+ */
+AVRational av_guess_frame_rate(AVFormatContext *ctx, AVStream *stream, AVFrame *frame);
+
+/**
+ * Check if the stream st contained in s is matched by the stream specifier
+ * spec.
+ *
+ * See the "stream specifiers" chapter in the documentation for the syntax
+ * of spec.
+ *
+ * @return >0 if st is matched by spec;
+ * 0 if st is not matched by spec;
+ * AVERROR code if spec is invalid
+ *
+ * @note A stream specifier can match several streams in the format.
+ */
+int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st,
+ const char *spec);
+
+int avformat_queue_attached_pictures(AVFormatContext *s);
+
+
+/**
* @}
*/
diff --git a/libavformat/avformatres.rc b/libavformat/avformatres.rc
new file mode 100644
index 0000000000..ffe61e0d80
--- /dev/null
+++ b/libavformat/avformatres.rc
@@ -0,0 +1,55 @@
+/*
+ * Windows resource file for libavformat
+ *
+ * Copyright (C) 2012 James Almer
+ * Copyright (C) 2013 Tiancheng "Timothy" Gu
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <windows.h>
+#include "libavformat/version.h"
+#include "libavutil/ffversion.h"
+#include "config.h"
+
+1 VERSIONINFO
+FILEVERSION LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO, 0
+PRODUCTVERSION LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO, 0
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_DLL
+{
+ BLOCK "StringFileInfo"
+ {
+ BLOCK "040904B0"
+ {
+ VALUE "CompanyName", "FFmpeg Project"
+ VALUE "FileDescription", "FFmpeg container format library"
+ VALUE "FileVersion", AV_STRINGIFY(LIBAVFORMAT_VERSION)
+ VALUE "InternalName", "libavformat"
+ VALUE "LegalCopyright", "Copyright (C) 2000-" AV_STRINGIFY(CONFIG_THIS_YEAR) " FFmpeg Project"
+ VALUE "OriginalFilename", "avformat" BUILDSUF "-" AV_STRINGIFY(LIBAVFORMAT_VERSION_MAJOR) SLIBSUF
+ VALUE "ProductName", "FFmpeg"
+ VALUE "ProductVersion", FFMPEG_VERSION
+ }
+ }
+
+ BLOCK "VarFileInfo"
+ {
+ VALUE "Translation", 0x0409, 0x04B0
+ }
+}
diff --git a/libavformat/avi.h b/libavformat/avi.h
index 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 20dc6e1525..54c87a2559 100644
--- a/libavformat/avidec.c
+++ b/libavformat/avidec.c
@@ -2,27 +2,29 @@
* AVI demuxer
* Copyright (c) 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <inttypes.h>
+#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/bswap.h"
+#include "libavutil/opt.h"
#include "libavutil/dict.h"
#include "libavutil/internal.h"
#include "libavutil/intreadwrite.h"
@@ -32,9 +34,8 @@
#include "dv.h"
#include "internal.h"
#include "riff.h"
-
-#undef NDEBUG
-#include <assert.h>
+#include "libavcodec/bytestream.h"
+#include "libavcodec/exif.h"
typedef struct AVIStream {
int64_t frame_offset; /* current frame (video) or byte (audio) counter
@@ -59,12 +60,16 @@ typedef struct AVIStream {
AVFormatContext *sub_ctx;
AVPacket sub_pkt;
uint8_t *sub_buffer;
+
+ int64_t seek_pos;
} AVIStream;
typedef struct AVIContext {
+ const AVClass *class;
int64_t riff_end;
int64_t movi_end;
int64_t fsize;
+ int64_t io_fsize;
int64_t movi_list;
int64_t last_pkt_pos;
int index_loaded;
@@ -73,9 +78,26 @@ typedef struct AVIContext {
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' },
@@ -94,8 +116,8 @@ static int avi_load_index(AVFormatContext *s);
static int guess_ni_flag(AVFormatContext *s);
#define print_tag(str, tag, size) \
- av_dlog(NULL, "%s: tag=%c%c%c%c size=0x%x\n", \
- str, tag & 0xff, \
+ av_dlog(NULL, "pos:%"PRIX64" %s: tag=%c%c%c%c size=0x%x\n", \
+ avio_tell(pb), str, tag & 0xff, \
(tag >> 8) & 0xff, \
(tag >> 16) & 0xff, \
(tag >> 24) & 0xff, \
@@ -105,7 +127,7 @@ static inline int get_duration(AVIStream *ast, int len)
{
if (ast->sample_size)
return len;
- else if (ast->dshow_block_align)
+ else if (ast->dshow_block_align > 1)
return (len + ast->dshow_block_align - 1) / ast->dshow_block_align;
else
return 1;
@@ -195,14 +217,15 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num)
int key = len >= 0;
len &= 0x7FFFFFFF;
- av_dlog(s, "pos:%"PRId64", len:%X\n", pos, len);
-
- if (pb->eof_reached)
+#ifdef DEBUG_SEEK
+ av_log(s, AV_LOG_ERROR, "pos:%"PRId64", len:%X\n", pos, len);
+#endif
+ if (avio_feof(pb))
return AVERROR_INVALIDDATA;
if (last_pos == pos || pos == base - 8)
avi->non_interleaved = 1;
- if (last_pos != pos && (len || !ast->sample_size))
+ if (last_pos != pos && len)
av_add_index_entry(st, pos, ast->cum_len, len, 0,
key ? AVINDEX_KEYFRAME : 0);
@@ -215,7 +238,7 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num)
avio_rl32(pb); /* size */
duration = avio_rl32(pb);
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR_INVALIDDATA;
pos = avio_tell(pb);
@@ -225,16 +248,21 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num)
return AVERROR_INVALIDDATA;
}
- avio_seek(pb, offset + 8, SEEK_SET);
+ if (avio_seek(pb, offset + 8, SEEK_SET) < 0)
+ return -1;
avi->odml_depth++;
read_braindead_odml_indx(s, frame_num);
avi->odml_depth--;
frame_num += duration;
- avio_seek(pb, pos, SEEK_SET);
+ if (avio_seek(pb, pos, SEEK_SET) < 0) {
+ av_log(s, AV_LOG_ERROR, "Failed to restore position after reading index\n");
+ return -1;
+ }
+
}
}
- avi->index_loaded = 1;
+ avi->index_loaded = 2;
return 0;
}
@@ -325,6 +353,7 @@ static void avi_read_nikon(AVFormatContext *s, uint64_t end)
uint16_t size = avio_rl16(s->pb);
const char *name = NULL;
char buffer[64] = { 0 };
+ size = FFMIN(size, tag_end - avio_tell(s->pb));
size -= avio_read(s->pb, buffer,
FFMIN(size, sizeof(buffer) - 1));
switch (tag) {
@@ -353,6 +382,84 @@ static void avi_read_nikon(AVFormatContext *s, uint64_t end)
}
}
+static int avi_extract_stream_metadata(AVStream *st)
+{
+ GetByteContext gb;
+ uint8_t *data = st->codec->extradata;
+ int data_size = st->codec->extradata_size;
+ int tag, offset;
+
+ if (!data || data_size < 8) {
+ return AVERROR_INVALIDDATA;
+ }
+
+ bytestream2_init(&gb, data, data_size);
+
+ tag = bytestream2_get_le32(&gb);
+
+ switch (tag) {
+ case MKTAG('A', 'V', 'I', 'F'):
+ // skip 4 byte padding
+ bytestream2_skip(&gb, 4);
+ offset = bytestream2_tell(&gb);
+ bytestream2_init(&gb, data + offset, data_size - offset);
+
+ // decode EXIF tags from IFD, AVI is always little-endian
+ return avpriv_exif_decode_ifd(st->codec, &gb, 1, 0, &st->metadata);
+ break;
+ case MKTAG('C', 'A', 'S', 'I'):
+ avpriv_request_sample(st->codec, "RIFF stream data tag type CASI (%u)", tag);
+ break;
+ case MKTAG('Z', 'o', 'r', 'a'):
+ avpriv_request_sample(st->codec, "RIFF stream data tag type Zora (%u)", tag);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int calculate_bitrate(AVFormatContext *s)
+{
+ AVIContext *avi = s->priv_data;
+ int i, j;
+ int64_t lensum = 0;
+ int64_t maxpos = 0;
+
+ for (i = 0; i<s->nb_streams; i++) {
+ int64_t len = 0;
+ AVStream *st = s->streams[i];
+
+ if (!st->nb_index_entries)
+ continue;
+
+ for (j = 0; j < st->nb_index_entries; j++)
+ len += st->index_entries[j].size;
+ maxpos = FFMAX(maxpos, st->index_entries[j-1].pos);
+ lensum += len;
+ }
+ if (maxpos < avi->io_fsize*9/10) // index does not cover the whole file
+ return 0;
+ if (lensum*9/10 > maxpos || lensum < maxpos*9/10) // frame sum and filesize mismatch
+ return 0;
+
+ for (i = 0; i<s->nb_streams; i++) {
+ int64_t len = 0;
+ AVStream *st = s->streams[i];
+ int64_t duration;
+
+ for (j = 0; j < st->nb_index_entries; j++)
+ len += st->index_entries[j].size;
+
+ if (st->nb_index_entries < 2 || st->codec->bit_rate > 0)
+ continue;
+ duration = st->index_entries[j-1].timestamp - st->index_entries[0].timestamp;
+ st->codec->bit_rate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
+ }
+ return 1;
+}
+
static int avi_read_header(AVFormatContext *s)
{
AVIContext *avi = s->priv_data;
@@ -367,6 +474,7 @@ static int avi_read_header(AVFormatContext *s)
int amv_file_format = 0;
uint64_t list_end = 0;
int ret;
+ AVDictionaryEntry *dict_entry;
avi->stream_index = -1;
@@ -374,7 +482,9 @@ static int avi_read_header(AVFormatContext *s)
if (ret < 0)
return ret;
- avi->fsize = avio_size(pb);
+ av_log(avi, AV_LOG_DEBUG, "use odml:%d\n", avi->use_odml);
+
+ avi->io_fsize = avi->fsize = avio_size(pb);
if (avi->fsize <= 0 || avi->fsize < avi->riff_end)
avi->fsize = avi->riff_end == 8 ? INT64_MAX : avi->riff_end;
@@ -383,7 +493,7 @@ static int avi_read_header(AVFormatContext *s)
codec_type = -1;
frame_period = 0;
for (;;) {
- if (pb->eof_reached)
+ if (avio_feof(pb))
goto fail;
tag = avio_rl32(pb);
size = avio_rl32(pb);
@@ -431,7 +541,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;
@@ -487,6 +597,8 @@ static int avi_read_header(AVFormatContext *s)
ast = s->streams[0]->priv_data;
av_freep(&s->streams[0]->codec->extradata);
av_freep(&s->streams[0]->codec);
+ if (s->streams[0]->info)
+ av_freep(&s->streams[0]->info->duration_error);
av_freep(&s->streams[0]->info);
av_freep(&s->streams[0]);
s->nb_streams = 0;
@@ -515,7 +627,7 @@ static int avi_read_header(AVFormatContext *s)
break;
}
- assert(stream_index < s->nb_streams);
+ av_assert0(stream_index < s->nb_streams);
ast->handler = handler;
avio_rl32(pb); /* flags */
@@ -546,6 +658,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");
+ ast->cum_len = 0;
+ }
ast->sample_size = avio_rl32(pb); /* sample ssize */
ast->cum_len *= FFMAX(1, ast->sample_size);
av_dlog(s, "%"PRIu32" %"PRIu32" %d\n",
@@ -556,6 +672,7 @@ static int avi_read_header(AVFormatContext *s)
codec_type = AVMEDIA_TYPE_VIDEO;
ast->sample_size = 0;
+ st->avg_frame_rate = av_inv_q(st->time_base);
break;
case MKTAG('a', 'u', 'd', 's'):
codec_type = AVMEDIA_TYPE_AUDIO;
@@ -567,23 +684,34 @@ static int avi_read_header(AVFormatContext *s)
codec_type = AVMEDIA_TYPE_DATA;
break;
default:
- av_log(s, AV_LOG_ERROR, "unknown stream type %X\n", tag1);
- goto fail;
+ av_log(s, AV_LOG_INFO, "unknown stream type %X\n", tag1);
}
- if (ast->sample_size == 0)
+ if (ast->sample_size == 0) {
st->duration = st->nb_frames;
+ if (st->duration > 0 && avi->io_fsize > 0 && avi->riff_end > avi->io_fsize) {
+ av_log(s, AV_LOG_DEBUG, "File is truncated adjusting duration\n");
+ st->duration = av_rescale(st->duration, avi->io_fsize, avi->riff_end);
+ }
+ }
ast->frame_offset = ast->cum_len;
avio_skip(pb, size - 12 * 4);
break;
case MKTAG('s', 't', 'r', 'f'):
/* stream header */
+ if (!size)
+ break;
if (stream_index >= (unsigned)s->nb_streams || avi->dv_demux) {
avio_skip(pb, size);
} else {
uint64_t cur_pos = avio_tell(pb);
+ unsigned esize;
if (cur_pos < list_end)
size = FFMIN(size, list_end - cur_pos);
st = s->streams[stream_index];
+ if (st->codec->codec_type != AVMEDIA_TYPE_UNKNOWN) {
+ avio_skip(pb, size);
+ break;
+ }
switch (codec_type) {
case AVMEDIA_TYPE_VIDEO:
if (amv_file_format) {
@@ -594,7 +722,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')) {
@@ -604,17 +732,13 @@ static int avi_read_header(AVFormatContext *s)
break;
}
- if (size > 10 * 4 && size < (1 << 30)) {
- st->codec->extradata_size = size - 10 * 4;
- st->codec->extradata = av_malloc(st->codec->extradata_size +
- FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata) {
- st->codec->extradata_size = 0;
+ if (size > 10 * 4 && size < (1 << 30) && size < avi->fsize) {
+ if (esize == size-1 && (esize&1)) {
+ st->codec->extradata_size = esize - 10 * 4;
+ } else
+ st->codec->extradata_size = size - 10 * 4;
+ if (ff_get_extradata(st->codec, pb, st->codec->extradata_size) < 0)
return AVERROR(ENOMEM);
- }
- avio_read(pb,
- st->codec->extradata,
- st->codec->extradata_size);
}
// FIXME: check if the encoder really did this correctly
@@ -624,7 +748,7 @@ static int avi_read_header(AVFormatContext *s)
/* Extract palette from extradata if bpp <= 8.
* This code assumes that extradata contains only palette.
* This is true for all paletted codecs implemented in
- * Libav. */
+ * FFmpeg. */
if (st->codec->extradata_size &&
(st->codec->bits_per_coded_sample <= 8)) {
int pal_size = (1 << st->codec->bits_per_coded_sample) << 2;
@@ -633,12 +757,12 @@ static int avi_read_header(AVFormatContext *s)
pal_size = FFMIN(pal_size, st->codec->extradata_size);
pal_src = st->codec->extradata +
st->codec->extradata_size - pal_size;
-#if HAVE_BIGENDIAN
+ /* Exclude the "BottomUp" field from the palette */
+ if (pal_src - st->codec->extradata >= 9 &&
+ !memcmp(st->codec->extradata + st->codec->extradata_size - 9, "BottomUp", 9))
+ pal_src -= 9;
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;
}
@@ -656,11 +780,8 @@ static int avi_read_header(AVFormatContext *s)
ast->handler == MKTAG('X', 'V', 'I', 'D'))
st->codec->codec_tag = MKTAG('X', 'V', 'I', 'D');
- // Support "Resolution 1:1" for Avid AVI Codec
- if (tag1 == MKTAG('A', 'V', 'R', 'n') &&
- st->codec->extradata_size >= 31 &&
- !memcmp(&st->codec->extradata[28], "1:1", 3))
- st->codec->codec_id = AV_CODEC_ID_RAWVIDEO;
+ if (st->codec->codec_tag == MKTAG('V', 'S', 'S', 'H'))
+ st->need_parsing = AVSTREAM_PARSE_FULL;
if (st->codec->codec_tag == 0 && st->codec->height > 0 &&
st->codec->extradata_size < 1U << 30) {
@@ -679,7 +800,7 @@ static int avi_read_header(AVFormatContext *s)
// avio_skip(pb, size - 5 * 4);
break;
case AVMEDIA_TYPE_AUDIO:
- ret = ff_get_wav_header(pb, st->codec, size);
+ ret = ff_get_wav_header(pb, st->codec, size, 0);
if (ret < 0)
return ret;
ast->dshow_block_align = st->codec->block_align;
@@ -710,15 +831,27 @@ static int avi_read_header(AVFormatContext *s)
if (ast->handler == AV_RL32("Axan")) {
st->codec->codec_id = AV_CODEC_ID_XAN_DPCM;
st->codec->codec_tag = 0;
+ ast->dshow_block_align = 0;
}
if (amv_file_format) {
st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_AMV;
ast->dshow_block_align = 0;
}
+ if (st->codec->codec_id == AV_CODEC_ID_AAC && ast->dshow_block_align <= 4 && ast->dshow_block_align) {
+ av_log(s, AV_LOG_DEBUG, "overriding invalid dshow_block_align of %d\n", ast->dshow_block_align);
+ ast->dshow_block_align = 0;
+ }
+ if (st->codec->codec_id == AV_CODEC_ID_AAC && ast->dshow_block_align == 1024 && ast->sample_size == 1024 ||
+ st->codec->codec_id == AV_CODEC_ID_AAC && ast->dshow_block_align == 4096 && ast->sample_size == 4096 ||
+ st->codec->codec_id == AV_CODEC_ID_MP3 && ast->dshow_block_align == 1152 && ast->sample_size == 1152) {
+ av_log(s, AV_LOG_DEBUG, "overriding sample_size\n");
+ ast->sample_size = 0;
+ }
break;
case AVMEDIA_TYPE_SUBTITLE:
st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
- st->codec->codec_id = AV_CODEC_ID_PROBE;
+ st->request_probe= 1;
+ avio_skip(pb, size);
break;
default:
st->codec->codec_type = AVMEDIA_TYPE_DATA;
@@ -729,9 +862,35 @@ static int avi_read_header(AVFormatContext *s)
}
}
break;
+ case MKTAG('s', 't', 'r', 'd'):
+ if (stream_index >= (unsigned)s->nb_streams
+ || s->streams[stream_index]->codec->extradata_size
+ || s->streams[stream_index]->codec->codec_tag == MKTAG('H','2','6','4')) {
+ avio_skip(pb, size);
+ } else {
+ uint64_t cur_pos = avio_tell(pb);
+ if (cur_pos < list_end)
+ size = FFMIN(size, list_end - cur_pos);
+ st = s->streams[stream_index];
+
+ if (size<(1<<30)) {
+ if (ff_get_extradata(st->codec, pb, size) < 0)
+ return AVERROR(ENOMEM);
+ }
+
+ if (st->codec->extradata_size & 1) //FIXME check if the encoder really did this correctly
+ avio_r8(pb);
+
+ ret = avi_extract_stream_metadata(st);
+ if (ret < 0) {
+ av_log(s, AV_LOG_WARNING, "could not decoding EXIF data in stream header.\n");
+ }
+ }
+ break;
case MKTAG('i', 'n', 'd', 'x'):
i = avio_tell(pb);
if (pb->seekable && !(s->flags & AVFMT_FLAG_IGNIDX) &&
+ avi->use_odml &&
read_braindead_odml_indx(s, 0) < 0 &&
(s->error_recognition & AV_EF_EXPLODE))
goto fail;
@@ -800,17 +959,32 @@ fail:
if (!avi->index_loaded && pb->seekable)
avi_load_index(s);
- avi->index_loaded = 1;
+ calculate_bitrate(s);
+ avi->index_loaded |= 1;
if ((ret = guess_ni_flag(s)) < 0)
return ret;
- avi->non_interleaved |= ret;
+ avi->non_interleaved |= ret | (s->flags & AVFMT_FLAG_SORT_DTS);
+
+ dict_entry = av_dict_get(s->metadata, "ISFT", NULL, 0);
+ if (dict_entry && !strcmp(dict_entry->value, "PotEncoder"))
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ if ( st->codec->codec_id == AV_CODEC_ID_MPEG1VIDEO
+ || st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO)
+ st->need_parsing = AVSTREAM_PARSE_FULL;
+ }
+
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
if (st->nb_index_entries)
break;
}
+ // DV-in-AVI cannot be non-interleaved, if set this must be
+ // a mis-detection.
+ if (avi->dv_demux)
+ avi->non_interleaved = 0;
if (i == s->nb_streams && avi->non_interleaved) {
av_log(s, AV_LOG_WARNING,
"Non-interleaved AVI without index, switching to interleaved\n");
@@ -828,15 +1002,17 @@ fail:
return 0;
}
-static int read_gab2_sub(AVStream *st, AVPacket *pkt)
+static int read_gab2_sub(AVFormatContext *s, AVStream *st, AVPacket *pkt)
{
if (pkt->size >= 7 &&
+ pkt->size < INT_MAX - AVPROBE_PADDING_SIZE &&
!strcmp(pkt->data, "GAB2") && AV_RL16(pkt->data + 5) == 2) {
uint8_t desc[256];
int score = AVPROBE_SCORE_EXTENSION, ret;
AVIStream *ast = st->priv_data;
AVInputFormat *sub_demuxer;
AVRational time_base;
+ int size;
AVIOContext *pb = avio_alloc_context(pkt->data + 7,
pkt->size - 7,
0, NULL, NULL, NULL, NULL);
@@ -854,15 +1030,25 @@ static int read_gab2_sub(AVStream *st, AVPacket *pkt)
avio_rl16(pb); /* flags? */
avio_rl32(pb); /* data size */
- pd = (AVProbeData) { .buf = pb->buf_ptr,
- .buf_size = pb->buf_end - pb->buf_ptr };
- if (!(sub_demuxer = av_probe_input_format2(&pd, 1, &score)))
+ size = pb->buf_end - pb->buf_ptr;
+ pd = (AVProbeData) { .buf = av_mallocz(size + AVPROBE_PADDING_SIZE),
+ .buf_size = size };
+ if (!pd.buf)
+ goto error;
+ memcpy(pd.buf, pb->buf_ptr, size);
+ sub_demuxer = av_probe_input_format2(&pd, 1, &score);
+ av_freep(&pd.buf);
+ if (!sub_demuxer)
goto error;
if (!(ast->sub_ctx = avformat_alloc_context()))
goto error;
ast->sub_ctx->pb = pb;
+
+ if (ff_copy_whitelists(ast->sub_ctx, s) < 0)
+ goto error;
+
if (!avformat_open_input(&ast->sub_ctx, "", sub_demuxer, NULL)) {
ff_read_packet(ast->sub_ctx, &ast->sub_pkt);
*st->codec = *ast->sub_ctx->streams[0]->codec;
@@ -875,6 +1061,7 @@ static int read_gab2_sub(AVStream *st, AVPacket *pkt)
return 1;
error:
+ av_freep(&ast->sub_ctx);
av_freep(&pb);
}
return 0;
@@ -914,7 +1101,7 @@ static AVStream *get_subtitle_pkt(AVFormatContext *s, AVStream *next_st,
return sub_st;
}
-static int get_stream_idx(int *d)
+static int get_stream_idx(const unsigned *d)
{
if (d[0] >= '0' && d[0] <= '9' &&
d[1] >= '0' && d[1] <= '9') {
@@ -924,6 +1111,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;
@@ -935,7 +1126,7 @@ static int avi_sync(AVFormatContext *s, int exit_early)
start_sync:
memset(d, -1, sizeof(d));
- for (i = sync = avio_tell(pb); !pb->eof_reached; i++) {
+ for (i = sync = avio_tell(pb); !avio_feof(pb); i++) {
int j;
for (j = 0; j < 7; j++)
@@ -947,7 +1138,7 @@ start_sync:
n = get_stream_idx(d + 2);
av_dlog(s, "%X %X %X %X %X %X %X %X %"PRId64" %u %d\n",
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], i, size, n);
- if (i + (uint64_t)size > avi->fsize || d[0] > 127)
+ if (i*(avi->io_fsize>0) + (uint64_t)size > avi->fsize || d[0] > 127)
continue;
// parse ix##
@@ -965,7 +1156,7 @@ start_sync:
goto start_sync;
}
- n = avi->dv_demux ? 0 : get_stream_idx(d);
+ n = get_stream_idx(d);
if (!((i - avi->last_pkt_pos) & 1) &&
get_stream_idx(d + 1) < s->nb_streams)
@@ -977,6 +1168,9 @@ start_sync:
goto start_sync;
}
+ if (avi->dv_demux && n != 0)
+ continue;
+
// parse ##dc/##wb
if (n < s->nb_streams) {
AVStream *st;
@@ -984,16 +1178,22 @@ start_sync:
st = s->streams[n];
ast = st->priv_data;
+ if (!ast) {
+ av_log(s, AV_LOG_WARNING, "Skipping foreign stream %d packet\n", n);
+ continue;
+ }
+
if (s->nb_streams >= 2) {
AVStream *st1 = s->streams[1];
AVIStream *ast1 = st1->priv_data;
// workaround for broken small-file-bug402.avi
- if (d[2] == 'w' && d[3] == 'b' && n == 0 &&
- st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
- st1->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
- ast->prefix == 'd' * 256 + 'c' &&
- (d[2] * 256 + d[3] == ast1->prefix ||
- !ast1->prefix_count)) {
+ if ( d[2] == 'w' && d[3] == 'b'
+ && n == 0
+ && st ->codec->codec_type == AVMEDIA_TYPE_VIDEO
+ && st1->codec->codec_type == AVMEDIA_TYPE_AUDIO
+ && ast->prefix == 'd'*256+'c'
+ && (d[2]*256+d[3] == ast1->prefix || !ast1->prefix_count)
+ ) {
n = 1;
st = st1;
ast = ast1;
@@ -1010,9 +1210,9 @@ start_sync:
|| st->discard >= AVDISCARD_ALL)) {
if (!exit_early) {
ast->frame_offset += get_duration(ast, size);
+ avio_skip(pb, size);
+ goto start_sync;
}
- avio_skip(pb, size);
- goto start_sync;
}
if (d[2] == 'p' && d[3] == 'c' && size <= 4 * 256 + 4) {
@@ -1023,7 +1223,7 @@ start_sync:
// b + (g << 8) + (r << 16);
for (; k <= last; k++)
- ast->pal[k] = avio_rb32(pb) >> 8;
+ ast->pal[k] = 0xFFU<<24 | avio_rb32(pb)>>8;
ast->has_pal = 1;
goto start_sync;
@@ -1045,7 +1245,7 @@ start_sync:
ast->packet_size = size + 8;
ast->remaining = size;
- if (size || !ast->sample_size) {
+ if (size) {
uint64_t pos = avio_tell(pb) - 8;
if (!st->index_entries || !st->nb_index_entries ||
st->index_entries[st->nb_index_entries - 1].pos < pos) {
@@ -1058,6 +1258,8 @@ start_sync:
}
}
+ if (pb->error)
+ return pb->error;
return AVERROR_EOF;
}
@@ -1114,10 +1316,7 @@ static int avi_read_packet(AVFormatContext *s, AVPacket *pkt)
return AVERROR_EOF;
best_ast = best_st->priv_data;
- best_ts = av_rescale_q(best_ts,
- (AVRational) { FFMAX(1, best_ast->sample_size),
- AV_TIME_BASE },
- best_st->time_base);
+ best_ts = best_ast->frame_offset;
if (best_ast->remaining) {
i = av_index_search_timestamp(best_st,
best_ts,
@@ -1132,15 +1331,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:
@@ -1167,8 +1369,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,
@@ -1190,7 +1393,7 @@ FF_DISABLE_DEPRECATION_WARNINGS
FF_ENABLE_DEPRECATION_WARNINGS
#endif
size = avpriv_dv_produce_packet(avi->dv_demux, pkt,
- pkt->data, pkt->size);
+ pkt->data, pkt->size, pkt->pos);
#if FF_API_DESTRUCT_PACKET
FF_DISABLE_DEPRECATION_WARNINGS
pkt->destruct = dstr;
@@ -1201,7 +1404,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
if (size < 0)
av_free_packet(pkt);
} else if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE &&
- !st->codec->codec_tag && read_gab2_sub(st, pkt)) {
+ !st->codec->codec_tag && read_gab2_sub(s, st, pkt)) {
ast->frame_offset++;
avi->stream_index = -1;
ast->remaining = 0;
@@ -1225,17 +1428,34 @@ FF_ENABLE_DEPRECATION_WARNINGS
size);
pkt->stream_index = avi->stream_index;
- if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && st->index_entries) {
AVIndexEntry *e;
int index;
- assert(st->index_entries);
- index = av_index_search_timestamp(st, ast->frame_offset, 0);
+ index = av_index_search_timestamp(st, ast->frame_offset, AVSEEK_FLAG_ANY);
e = &st->index_entries[index];
- if (index >= 0 && e->timestamp == ast->frame_offset)
+ if (index >= 0 && e->timestamp == ast->frame_offset) {
+ if (index == st->nb_index_entries-1) {
+ int key=1;
+ int i;
+ uint32_t state=-1;
+ for (i=0; i<FFMIN(size,256); i++) {
+ if (st->codec->codec_id == AV_CODEC_ID_MPEG4) {
+ if (state == 0x1B6) {
+ key= !(pkt->data[i]&0xC0);
+ break;
+ }
+ }else
+ break;
+ state= (state<<8) + pkt->data[i];
+ }
+ if (!key)
+ e->flags &= ~AVINDEX_KEYFRAME;
+ }
if (e->flags & AVINDEX_KEYFRAME)
pkt->flags |= AV_PKT_FLAG_KEY;
+ }
} else {
pkt->flags |= AV_PKT_FLAG_KEY;
}
@@ -1247,6 +1467,22 @@ FF_ENABLE_DEPRECATION_WARNINGS
ast->packet_size = 0;
}
+ if (!avi->non_interleaved && pkt->pos >= 0 && ast->seek_pos > pkt->pos) {
+ av_free_packet(pkt);
+ goto resync;
+ }
+ ast->seek_pos= 0;
+
+ if (!avi->non_interleaved && st->nb_index_entries>1 && avi->index_loaded>1) {
+ int64_t dts= av_rescale_q(pkt->dts, st->time_base, AV_TIME_BASE_Q);
+
+ if (avi->dts_max - dts > 2*AV_TIME_BASE) {
+ avi->non_interleaved= 1;
+ av_log(s, AV_LOG_INFO, "Switching to NI mode, due to poor interleaving\n");
+ }else if (avi->dts_max < dts)
+ avi->dts_max = dts;
+ }
+
return 0;
}
@@ -1266,7 +1502,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)
@@ -1279,8 +1517,16 @@ static int avi_read_idx1(AVFormatContext *s, int size)
avi->stream_index = -1;
avio_seek(pb, idx1_pos, SEEK_SET);
+ if (s->nb_streams == 1 && s->streams[0]->codec->codec_tag == AV_RL32("MMES")) {
+ first_packet_pos = 0;
+ data_offset = avi->movi_list;
+ }
+
/* Read the entries and sort them in each stream component. */
for (i = 0; i < nb_index_entries; i++) {
+ if (avio_feof(pb))
+ return -1;
+
tag = avio_rl32(pb);
flags = avio_rl32(pb);
pos = avio_rl32(pb);
@@ -1295,7 +1541,7 @@ static int avi_read_idx1(AVFormatContext *s, int size)
st = s->streams[index];
ast = st->priv_data;
- if (first_packet && first_packet_pos && len) {
+ if (first_packet && first_packet_pos) {
data_offset = first_packet_pos - pos;
first_packet = 0;
}
@@ -1303,16 +1549,25 @@ static int avi_read_idx1(AVFormatContext *s, int size)
av_dlog(s, "%d cum_len=%"PRId64"\n", len, ast->cum_len);
- if (pb->eof_reached)
- return AVERROR_INVALIDDATA;
-
+ // even if we have only a single stream, we should
+ // switch to non-interleaved to get correct timestamps
if (last_pos == pos)
avi->non_interleaved = 1;
- else if (len || !ast->sample_size)
+ if (last_idx != pos && len) {
av_add_index_entry(st, pos, ast->cum_len, len, 0,
(flags & AVIIF_INDEX) ? AVINDEX_KEYFRAME : 0);
+ last_idx= pos;
+ }
ast->cum_len += get_duration(ast, len);
last_pos = pos;
+ anykey |= flags&AVIIF_INDEX;
+ }
+ if (!anykey) {
+ for (index = 0; index < s->nb_streams; index++) {
+ st = s->streams[index];
+ if (st->nb_index_entries)
+ st->index_entries[0].flags |= AVINDEX_KEYFRAME;
+ }
}
return 0;
}
@@ -1326,7 +1581,6 @@ static int check_stream_max_drift(AVFormatContext *s)
int *idx = av_mallocz_array(s->nb_streams, sizeof(*idx));
if (!idx)
return AVERROR(ENOMEM);
-
for (min_pos = pos = 0; min_pos != INT64_MAX; pos = min_pos + 1LU) {
int64_t max_dts = INT64_MIN / 2;
int64_t min_dts = INT64_MAX / 2;
@@ -1417,16 +1671,19 @@ static int avi_load_index(AVFormatContext *s)
AVIOContext *pb = s->pb;
uint32_t tag, size;
int64_t pos = avio_tell(pb);
+ int64_t next;
int ret = -1;
if (avio_seek(pb, avi->movi_end, SEEK_SET) < 0)
goto the_end; // maybe truncated file
av_dlog(s, "movi_end=0x%"PRIx64"\n", avi->movi_end);
for (;;) {
- if (pb->eof_reached)
- break;
tag = avio_rl32(pb);
size = avio_rl32(pb);
+ if (avio_feof(pb))
+ break;
+ next = avio_tell(pb) + size + (size & 1);
+
av_dlog(s, "tag=%c%c%c%c size=0x%x\n",
tag & 0xff,
(tag >> 8) & 0xff,
@@ -1436,12 +1693,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
}
@@ -1466,7 +1728,7 @@ static int avi_read_seek(AVFormatContext *s, int stream_index,
AVIContext *avi = s->priv_data;
AVStream *st;
int i, index;
- int64_t pos;
+ int64_t pos, pos_min;
AVIStream *ast;
/* Does not matter which stream is requested dv in avi has the
@@ -1478,16 +1740,23 @@ static int avi_read_seek(AVFormatContext *s, int stream_index,
if (!avi->index_loaded) {
/* we only load the index on demand */
avi_load_index(s);
- avi->index_loaded = 1;
+ avi->index_loaded |= 1;
}
+ av_assert0(stream_index >= 0);
st = s->streams[stream_index];
ast = st->priv_data;
index = av_index_search_timestamp(st,
timestamp * FFMAX(ast->sample_size, 1),
flags);
- if (index < 0)
+ if (index < 0) {
+ if (st->nb_index_entries > 0)
+ av_log(s, AV_LOG_DEBUG, "Failed to find timestamp %"PRId64 " in index %"PRId64 " .. %"PRId64 "\n",
+ timestamp * FFMAX(ast->sample_size, 1),
+ st->index_entries[0].timestamp,
+ st->index_entries[st->nb_index_entries - 1].timestamp);
return AVERROR_INVALIDDATA;
+ }
/* find the position */
pos = st->index_entries[index].pos;
@@ -1501,15 +1770,18 @@ static int avi_read_seek(AVFormatContext *s, int stream_index,
/* offsets. Calling with other stream indexes should have failed */
/* the av_index_search_timestamp call above. */
+ if (avio_seek(s->pb, pos, SEEK_SET) < 0)
+ return -1;
+
/* Feed the DV video stream version of the timestamp to the */
/* DV demux so it can synthesize correct timestamps. */
ff_dv_offset_reset(avi->dv_demux, timestamp);
- avio_seek(s->pb, pos, SEEK_SET);
avi->stream_index = -1;
return 0;
}
+ pos_min = pos;
for (i = 0; i < s->nb_streams; i++) {
AVStream *st2 = s->streams[i];
AVIStream *ast2 = st2->priv_data;
@@ -1525,35 +1797,46 @@ static int avi_read_seek(AVFormatContext *s, int stream_index,
if (st2->nb_index_entries <= 0)
continue;
-// assert(st2->codec->block_align);
- assert((int64_t)st2->time_base.num * ast2->rate ==
- (int64_t)st2->time_base.den * ast2->scale);
+// av_assert1(st2->codec->block_align);
+ av_assert0(fabs(av_q2d(st2->time_base) - ast2->scale / (double)ast2->rate) < av_q2d(st2->time_base) * 0.00000001);
index = av_index_search_timestamp(st2,
av_rescale_q(timestamp,
st->time_base,
st2->time_base) *
FFMAX(ast2->sample_size, 1),
- flags | AVSEEK_FLAG_BACKWARD);
+ flags |
+ AVSEEK_FLAG_BACKWARD |
+ (st2->codec->codec_type != AVMEDIA_TYPE_VIDEO ? AVSEEK_FLAG_ANY : 0));
if (index < 0)
index = 0;
+ ast2->seek_pos = st2->index_entries[index].pos;
+ pos_min = FFMIN(pos_min,ast2->seek_pos);
+ }
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st2 = s->streams[i];
+ AVIStream *ast2 = st2->priv_data;
- if (!avi->non_interleaved) {
- while (index > 0 && st2->index_entries[index].pos > pos)
- index--;
- while (index + 1 < st2->nb_index_entries &&
- st2->index_entries[index].pos < pos)
- index++;
- }
+ if (ast2->sub_ctx || st2->nb_index_entries <= 0)
+ continue;
- av_dlog(s, "%"PRId64" %d %"PRId64"\n",
- timestamp, index, st2->index_entries[index].timestamp);
- /* extract the current frame number */
+ index = av_index_search_timestamp(
+ st2,
+ av_rescale_q(timestamp, st->time_base, st2->time_base) * FFMAX(ast2->sample_size, 1),
+ flags | AVSEEK_FLAG_BACKWARD | (st2->codec->codec_type != AVMEDIA_TYPE_VIDEO ? AVSEEK_FLAG_ANY : 0));
+ if (index < 0)
+ index = 0;
+ while (!avi->non_interleaved && index>0 && st2->index_entries[index-1].pos >= pos_min)
+ index--;
ast2->frame_offset = st2->index_entries[index].timestamp;
}
/* do the seek */
- avio_seek(s->pb, pos, SEEK_SET);
+ if (avio_seek(s->pb, pos_min, SEEK_SET) < 0) {
+ av_log(s, AV_LOG_ERROR, "Seek failed\n");
+ return -1;
+ }
avi->stream_index = -1;
+ avi->dts_max = INT_MIN;
return 0;
}
@@ -1570,12 +1853,12 @@ static int avi_read_close(AVFormatContext *s)
av_freep(&ast->sub_ctx->pb);
avformat_close_input(&ast->sub_ctx);
}
- av_free(ast->sub_buffer);
+ av_freep(&ast->sub_buffer);
av_free_packet(&ast->sub_pkt);
}
}
- av_free(avi->dv_demux);
+ av_freep(&avi->dv_demux);
return 0;
}
@@ -1586,8 +1869,8 @@ static int avi_probe(AVProbeData *p)
/* check file header */
for (i = 0; avi_headers[i][0]; i++)
- if (!memcmp(p->buf, avi_headers[i], 4) &&
- !memcmp(p->buf + 8, avi_headers[i] + 4, 4))
+ if (AV_RL32(p->buf ) == AV_RL32(avi_headers[i] ) &&
+ AV_RL32(p->buf + 8) == AV_RL32(avi_headers[i] + 4))
return AVPROBE_SCORE_MAX;
return 0;
@@ -1603,4 +1886,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 9c2936af60..53c2fe7eac 100644
--- a/libavformat/avienc.c
+++ b/libavformat/avienc.c
@@ -2,29 +2,39 @@
* AVI muxer
* Copyright (c) 2000 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+
+//#define DEBUG
+
#include "avformat.h"
#include "internal.h"
#include "avi.h"
#include "avio_internal.h"
#include "riff.h"
+#include "mpegts.h"
+#include "libavformat/avlanguage.h"
+#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
+#include "libavutil/avassert.h"
+#include "libavutil/timestamp.h"
+#include "libavutil/pixdesc.h"
+#include "libavcodec/raw.h"
/*
* TODO:
@@ -52,14 +62,19 @@ typedef struct AVIContext {
typedef struct AVIStream {
int64_t frames_hdr_strm;
- int audio_strm_length;
+ int64_t audio_strm_length;
int packet_count;
int entry;
+ int max_size;
+
+ int64_t last_dts;
AVIIndex indexes;
} AVIStream;
-static inline AVIIentry *avi_get_ientry(AVIIndex *idx, int ent_id)
+static int avi_write_packet(AVFormatContext *s, AVPacket *pkt);
+
+static inline AVIIentry *avi_get_ientry(const AVIIndex *idx, int ent_id)
{
int cl = ent_id / AVI_INDEX_CLUSTER_SIZE;
int id = ent_id % AVI_INDEX_CLUSTER_SIZE;
@@ -117,7 +132,7 @@ static int avi_write_counters(AVFormatContext *s, int riff_id)
for (n = 0; n < s->nb_streams; n++) {
AVIStream *avist = s->streams[n]->priv_data;
- assert(avist->frames_hdr_strm);
+ av_assert0(avist->frames_hdr_strm);
stream = s->streams[n]->codec;
avio_seek(pb, avist->frames_hdr_strm, SEEK_SET);
ff_parse_specific_params(s->streams[n], &au_byterate, &au_ssize, &au_scale);
@@ -129,7 +144,7 @@ static int avi_write_counters(AVFormatContext *s, int riff_id)
nb_frames = FFMAX(nb_frames, avist->packet_count);
}
if (riff_id == 1) {
- assert(avi->frames_hdr_all);
+ av_assert0(avi->frames_hdr_all);
avio_seek(pb, avi->frames_hdr_all, SEEK_SET);
avio_wl32(pb, nb_frames);
}
@@ -147,11 +162,12 @@ static int avi_write_header(AVFormatContext *s)
AVStream *video_st = NULL;
int64_t list1, list2, strh, strf;
AVDictionaryEntry *t = NULL;
+ int padding;
if (s->nb_streams > AVI_MAX_STREAM_COUNT) {
av_log(s, AV_LOG_ERROR, "AVI does not support >%d streams\n",
AVI_MAX_STREAM_COUNT);
- return -1;
+ return AVERROR(EINVAL);
}
for (n = 0; n < s->nb_streams; n++) {
@@ -254,9 +270,18 @@ static int avi_write_header(AVFormatContext *s)
ff_parse_specific_params(st, &au_byterate, &au_ssize, &au_scale);
+ if ( enc->codec_type == AVMEDIA_TYPE_VIDEO
+ && enc->codec_id != AV_CODEC_ID_XSUB
+ && au_byterate > 1000LL*au_scale) {
+ au_byterate = 600;
+ au_scale = 1;
+ }
+ avpriv_set_pts_info(st, 64, au_scale, au_byterate);
+ if (enc->codec_id == AV_CODEC_ID_XSUB)
+ au_scale = au_byterate = 0;
+
avio_wl32(pb, au_scale); /* scale */
avio_wl32(pb, au_byterate); /* rate */
- avpriv_set_pts_info(st, 64, au_scale, au_byterate);
avio_wl32(pb, 0); /* start */
/* remember this offset to fill later */
@@ -267,7 +292,7 @@ static int avi_write_header(AVFormatContext *s)
else
avio_wl32(pb, 0); /* length, XXX: filled later */
- /* suggested buffer size */ //FIXME set at the end to largest chunk
+ /* suggested buffer size, is set to largest chunk size in avi_write_trailer */
if (enc->codec_type == AVMEDIA_TYPE_VIDEO)
avio_wl32(pb, 1024 * 1024);
else if (enc->codec_type == AVMEDIA_TYPE_AUDIO)
@@ -282,6 +307,9 @@ static int avi_write_header(AVFormatContext *s)
ff_end_tag(pb, strh);
if (enc->codec_type != AVMEDIA_TYPE_DATA) {
+ int ret;
+ enum AVPixelFormat pix_fmt;
+
strf = ff_start_tag(pb, "strf");
switch (enc->codec_type) {
case AVMEDIA_TYPE_SUBTITLE:
@@ -290,20 +318,47 @@ static int avi_write_header(AVFormatContext *s)
if (enc->codec_id != AV_CODEC_ID_XSUB)
break;
case AVMEDIA_TYPE_VIDEO:
- ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 0);
+ /* WMP expects RGB 5:5:5 rawvideo in avi to have bpp set to 16. */
+ if ( !enc->codec_tag
+ && enc->codec_id == AV_CODEC_ID_RAWVIDEO
+ && enc->pix_fmt == AV_PIX_FMT_RGB555LE
+ && enc->bits_per_coded_sample == 15)
+ enc->bits_per_coded_sample = 16;
+ ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 0, 0);
+ pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_avi,
+ enc->bits_per_coded_sample);
+ if ( !enc->codec_tag
+ && enc->codec_id == AV_CODEC_ID_RAWVIDEO
+ && enc->pix_fmt != pix_fmt
+ && enc->pix_fmt != AV_PIX_FMT_NONE)
+ av_log(s, AV_LOG_ERROR, "%s rawvideo cannot be written to avi, output file will be unreadable\n",
+ av_get_pix_fmt_name(enc->pix_fmt));
break;
case AVMEDIA_TYPE_AUDIO:
- if (ff_put_wav_header(pb, enc) < 0)
- return -1;
+ if ((ret = ff_put_wav_header(pb, enc, 0)) < 0)
+ return ret;
break;
default:
- return -1;
+ av_log(s, AV_LOG_ERROR,
+ "Invalid or not supported codec type '%s' found in the input\n",
+ (char *)av_x_if_null(av_get_media_type_string(enc->codec_type), "?"));
+ return AVERROR(EINVAL);
}
ff_end_tag(pb, strf);
if ((t = av_dict_get(st->metadata, "title", NULL, 0))) {
ff_riff_write_info_tag(s->pb, "strn", t->value);
t = NULL;
}
+ if (enc->codec_id == AV_CODEC_ID_XSUB
+ && (t = av_dict_get(s->streams[i]->metadata, "language", NULL, 0))) {
+ const char* langstr = av_convert_lang_to(t->value, AV_LANG_ISO639_1);
+ t = NULL;
+ if (langstr) {
+ char* str = av_asprintf("Subtitle - %s-xx;02", langstr);
+ ff_riff_write_info_tag(s->pb, "strn", str);
+ av_free(str);
+ }
+ }
}
if (pb->seekable) {
@@ -381,11 +436,18 @@ static int avi_write_header(AVFormatContext *s)
ff_riff_write_info(s);
+
+ padding = s->metadata_header_padding;
+ if (padding < 0)
+ padding = 1016;
+
/* some padding for easier tag editing */
- list2 = ff_start_tag(pb, "JUNK");
- for (i = 0; i < 1016; i += 4)
- avio_wl32(pb, 0);
- ff_end_tag(pb, list2);
+ if (padding) {
+ list2 = ff_start_tag(pb, "JUNK");
+ for (i = padding; i > 0; i -= 4)
+ avio_wl32(pb, 0);
+ ff_end_tag(pb, list2);
+ }
avi->movi_list = ff_start_tag(pb, "LIST");
ffio_wfourcc(pb, "movi");
@@ -403,10 +465,13 @@ static int avi_write_ix(AVFormatContext *s)
char ix_tag[] = "ix00";
int i, j;
- assert(pb->seekable);
+ av_assert0(pb->seekable);
- if (avi->riff_id > AVI_MASTER_INDEX_SIZE)
- return -1;
+ if (avi->riff_id > AVI_MASTER_INDEX_SIZE) {
+ av_log(s, AV_LOG_ERROR, "Invalid riff index %d > %d\n",
+ avi->riff_id, AVI_MASTER_INDEX_SIZE);
+ return AVERROR(EINVAL);
+ }
for (i = 0; i < s->nb_streams; i++) {
AVIStream *avist = s->streams[i]->priv_data;
@@ -504,27 +569,56 @@ static int avi_write_idx1(AVFormatContext *s)
return 0;
}
-static int avi_write_packet(AVFormatContext *s, AVPacket *pkt)
+static int write_skip_frames(AVFormatContext *s, int stream_index, int64_t dts)
{
- unsigned char tag[5];
- unsigned int flags = 0;
- const int stream_index = pkt->stream_index;
- int size = pkt->size;
- AVIContext *avi = s->priv_data;
- AVIOContext *pb = s->pb;
AVIStream *avist = s->streams[stream_index]->priv_data;
AVCodecContext *enc = s->streams[stream_index]->codec;
- while (enc->block_align == 0 && pkt->dts != AV_NOPTS_VALUE &&
- pkt->dts > avist->packet_count) {
+ av_dlog(s, "dts:%s packet_count:%d stream_index:%d\n", av_ts2str(dts), avist->packet_count, stream_index);
+ while (enc->block_align == 0 && dts != AV_NOPTS_VALUE &&
+ dts > avist->packet_count && enc->codec_id != AV_CODEC_ID_XSUB && avist->packet_count) {
AVPacket empty_packet;
+ if (dts - avist->packet_count > 60000) {
+ av_log(s, AV_LOG_ERROR, "Too large number of skipped frames %"PRId64" > 60000\n", dts - avist->packet_count);
+ return AVERROR(EINVAL);
+ }
+
av_init_packet(&empty_packet);
empty_packet.size = 0;
empty_packet.data = NULL;
empty_packet.stream_index = stream_index;
avi_write_packet(s, &empty_packet);
+ av_dlog(s, "dup dts:%s packet_count:%d\n", av_ts2str(dts), avist->packet_count);
}
+
+ return 0;
+}
+
+static int avi_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ unsigned char tag[5];
+ unsigned int flags = 0;
+ const int stream_index = pkt->stream_index;
+ int size = pkt->size;
+ AVIContext *avi = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVIStream *avist = s->streams[stream_index]->priv_data;
+ AVCodecContext *enc = s->streams[stream_index]->codec;
+ int ret;
+
+ if (enc->codec_id == AV_CODEC_ID_H264 && enc->codec_tag == MKTAG('H','2','6','4') && pkt->size) {
+ ret = ff_check_h264_startcode(s, s->streams[stream_index], pkt);
+ if (ret < 0)
+ return ret;
+ }
+
+ if ((ret = write_skip_frames(s, stream_index, pkt->dts)) < 0)
+ return ret;
+
+ if (pkt->dts != AV_NOPTS_VALUE)
+ avist->last_dts = pkt->dts + pkt->duration;
+
avist->packet_count++;
// Make sure to put an OpenDML chunk when the file size exceeds the limits
@@ -547,27 +641,27 @@ static int avi_write_packet(AVFormatContext *s, AVPacket *pkt)
avist->audio_strm_length += size;
if (s->pb->seekable) {
- int err;
AVIIndex *idx = &avist->indexes;
int cl = idx->entry / AVI_INDEX_CLUSTER_SIZE;
int id = idx->entry % AVI_INDEX_CLUSTER_SIZE;
if (idx->ents_allocated <= idx->entry) {
- if ((err = av_reallocp(&idx->cluster,
- (cl + 1) * sizeof(*idx->cluster))) < 0) {
+ idx->cluster = av_realloc_f(idx->cluster, sizeof(void*), cl+1);
+ if (!idx->cluster) {
idx->ents_allocated = 0;
idx->entry = 0;
- return err;
+ return AVERROR(ENOMEM);
}
idx->cluster[cl] =
av_malloc(AVI_INDEX_CLUSTER_SIZE * sizeof(AVIIentry));
if (!idx->cluster[cl])
- return -1;
+ return AVERROR(ENOMEM);
idx->ents_allocated += AVI_INDEX_CLUSTER_SIZE;
}
idx->cluster[cl][id].flags = flags;
idx->cluster[cl][id].pos = avio_tell(pb) - avi->movi_list;
idx->cluster[cl][id].len = size;
+ avist->max_size = FFMAX(avist->max_size, size);
idx->entry++;
}
@@ -588,6 +682,11 @@ static int avi_write_trailer(AVFormatContext *s)
int i, j, n, nb_frames;
int64_t file_size;
+ for (i = 0; i < s->nb_streams; i++) {
+ AVIStream *avist = s->streams[i]->priv_data;
+ write_skip_frames(s, i, avist->last_dts);
+ }
+
if (pb->seekable) {
if (avi->riff_id == 1) {
ff_end_tag(pb, avi->movi_list);
@@ -626,9 +725,13 @@ static int avi_write_trailer(AVFormatContext *s)
for (i = 0; i < s->nb_streams; i++) {
AVIStream *avist = s->streams[i]->priv_data;
for (j = 0; j < avist->indexes.ents_allocated / AVI_INDEX_CLUSTER_SIZE; j++)
- av_free(avist->indexes.cluster[j]);
+ av_freep(&avist->indexes.cluster[j]);
av_freep(&avist->indexes.cluster);
avist->indexes.ents_allocated = avist->indexes.entry = 0;
+ if (pb->seekable) {
+ avio_seek(pb, avist->frames_hdr_strm + 4, SEEK_SET);
+ avio_wl32(pb, avist->max_size);
+ }
}
return res;
diff --git a/libavformat/avio.c b/libavformat/avio.c
index f68994cef1..326bb0aa78 100644
--- a/libavformat/avio.c
+++ b/libavformat/avio.c
@@ -2,20 +2,20 @@
* unbuffered I/O
* Copyright (c) 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -116,6 +116,16 @@ static int url_alloc_for_protocol(URLContext **puc, struct URLProtocol *up,
if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init())
return AVERROR(EIO);
#endif
+ if ((flags & AVIO_FLAG_READ) && !up->url_read) {
+ av_log(NULL, AV_LOG_ERROR,
+ "Impossible to open the '%s' protocol for reading\n", up->name);
+ return AVERROR(EIO);
+ }
+ if ((flags & AVIO_FLAG_WRITE) && !up->url_write) {
+ av_log(NULL, AV_LOG_ERROR,
+ "Impossible to open the '%s' protocol for writing\n", up->name);
+ return AVERROR(EIO);
+ }
uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1);
if (!uc) {
err = AVERROR(ENOMEM);
@@ -135,8 +145,33 @@ static int url_alloc_for_protocol(URLContext **puc, struct URLProtocol *up,
goto fail;
}
if (up->priv_data_class) {
+ int proto_len= strlen(up->name);
+ char *start = strchr(uc->filename, ',');
*(const AVClass **)uc->priv_data = up->priv_data_class;
av_opt_set_defaults(uc->priv_data);
+ if(!strncmp(up->name, uc->filename, proto_len) && uc->filename + proto_len == start){
+ int ret= 0;
+ char *p= start;
+ char sep= *++p;
+ char *key, *val;
+ p++;
+ while(ret >= 0 && (key= strchr(p, sep)) && p<key && (val = strchr(key+1, sep))){
+ *val= *key= 0;
+ ret= av_opt_set(uc->priv_data, p, key+1, 0);
+ if (ret == AVERROR_OPTION_NOT_FOUND)
+ av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p);
+ *val= *key= sep;
+ p= val+1;
+ }
+ if(ret<0 || p!=key){
+ av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start);
+ av_freep(&uc->priv_data);
+ av_freep(&uc);
+ err = AVERROR(EINVAL);
+ goto fail;
+ }
+ memmove(start, key+1, strlen(key));
+ }
}
}
if (int_cb)
@@ -180,31 +215,54 @@ int ffurl_connect(URLContext *uc, AVDictionary **options)
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
"0123456789+-."
-int ffurl_alloc(URLContext **puc, const char *filename, int flags,
- const AVIOInterruptCB *int_cb)
+static struct URLProtocol *url_find_protocol(const char *filename)
{
URLProtocol *up = NULL;
char proto_str[128], proto_nested[128], *ptr;
size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
- if (filename[proto_len] != ':' || is_dos_path(filename))
+ if (filename[proto_len] != ':' &&
+ (filename[proto_len] != ',' || !strchr(filename + proto_len + 1, ':')) ||
+ is_dos_path(filename))
strcpy(proto_str, "file");
else
av_strlcpy(proto_str, filename,
FFMIN(proto_len + 1, sizeof(proto_str)));
+ if ((ptr = strchr(proto_str, ',')))
+ *ptr = '\0';
av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
if ((ptr = strchr(proto_nested, '+')))
*ptr = '\0';
while (up = ffurl_protocol_next(up)) {
if (!strcmp(proto_str, up->name))
- return url_alloc_for_protocol(puc, up, filename, flags, int_cb);
+ break;
if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
!strcmp(proto_nested, up->name))
- return url_alloc_for_protocol(puc, up, filename, flags, int_cb);
+ break;
+ }
+
+ return up;
+}
+
+int ffurl_alloc(URLContext **puc, const char *filename, int flags,
+ const AVIOInterruptCB *int_cb)
+{
+ URLProtocol *p = NULL;
+
+ if (!first_protocol) {
+ av_log(NULL, AV_LOG_WARNING, "No URL Protocols are registered. "
+ "Missing call to av_register_all()?\n");
}
+
+ p = url_find_protocol(filename);
+ if (p)
+ return url_alloc_for_protocol(puc, p, filename, flags, int_cb);
+
*puc = NULL;
+ if (av_strstart(filename, "https:", NULL))
+ av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile with openssl or gnutls enabled.\n");
return AVERROR_PROTOCOL_NOT_FOUND;
}
@@ -212,11 +270,13 @@ int ffurl_open(URLContext **puc, const char *filename, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options)
{
int ret = ffurl_alloc(puc, filename, flags, int_cb);
- if (ret)
+ if (ret < 0)
return ret;
if (options && (*puc)->prot->priv_data_class &&
(ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
goto fail;
+ if ((ret = av_opt_set_dict(*puc, options)) < 0)
+ goto fail;
ret = ffurl_connect(*puc, options);
if (!ret)
return 0;
@@ -234,9 +294,12 @@ static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,
{
int ret, len;
int fast_retries = 5;
+ int64_t wait_since = 0;
len = 0;
while (len < size_min) {
+ if (ff_check_interrupt(&h->interrupt_callback))
+ return AVERROR_EXIT;
ret = transfer_func(h, buf + len, size - len);
if (ret == AVERROR(EINTR))
continue;
@@ -244,17 +307,22 @@ static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,
return ret;
if (ret == AVERROR(EAGAIN)) {
ret = 0;
- if (fast_retries)
+ if (fast_retries) {
fast_retries--;
- else
+ } else {
+ if (h->rw_timeout) {
+ if (!wait_since)
+ wait_since = av_gettime_relative();
+ else if (av_gettime_relative() > wait_since + h->rw_timeout)
+ return AVERROR(EIO);
+ }
av_usleep(1000);
+ }
} else if (ret < 1)
return (ret < 0 && ret != AVERROR_EOF) ? ret : len;
if (ret)
fast_retries = FFMAX(fast_retries, 2);
len += ret;
- if (ff_check_interrupt(&h->interrupt_callback))
- return AVERROR_EXIT;
}
return len;
}
@@ -281,7 +349,7 @@ int ffurl_write(URLContext *h, const unsigned char *buf, int size)
if (h->max_packet_size && size > h->max_packet_size)
return AVERROR(EIO);
- return retry_transfer_wrapper(h, buf, size, size, h->prot->url_write);
+ return retry_transfer_wrapper(h, (unsigned char *)buf, size, size, (void*)h->prot->url_write);
}
int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
@@ -294,8 +362,9 @@ int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
return ret;
}
-int ffurl_close(URLContext *h)
+int ffurl_closep(URLContext **hh)
{
+ URLContext *h= *hh;
int ret = 0;
if (!h)
return 0; /* can happen when ffurl_open fails */
@@ -309,17 +378,30 @@ int ffurl_close(URLContext *h)
if (h->prot->priv_data_size) {
if (h->prot->priv_data_class)
av_opt_free(h->priv_data);
- av_free(h->priv_data);
+ av_freep(&h->priv_data);
}
- av_free(h);
+ av_freep(hh);
return ret;
}
+int ffurl_close(URLContext *h)
+{
+ return ffurl_closep(&h);
+}
+
+
+const char *avio_find_protocol_name(const char *url)
+{
+ URLProtocol *p = url_find_protocol(url);
+
+ return p ? p->name : NULL;
+}
+
int avio_check(const char *url, int flags)
{
URLContext *h;
int ret = ffurl_alloc(&h, url, flags, NULL);
- if (ret)
+ if (ret < 0)
return ret;
if (h->prot->url_check) {
diff --git a/libavformat/avio.h b/libavformat/avio.h
index 3360e8296e..b9b4017fb3 100644
--- a/libavformat/avio.h
+++ b/libavformat/avio.h
@@ -1,20 +1,20 @@
/*
* copyright (c) 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef AVFORMAT_AVIO_H
@@ -115,11 +115,58 @@ typedef struct AVIOContext {
* A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable.
*/
int seekable;
+
+ /**
+ * max filesize, used to limit allocations
+ * This field is internal to libavformat and access from outside is not allowed.
+ */
+ int64_t maxsize;
+
+ /**
+ * avio_read and avio_write should if possible be satisfied directly
+ * instead of going through a buffer, and avio_seek will always
+ * call the underlying seek function directly.
+ */
+ int direct;
+
+ /**
+ * Bytes read statistic
+ * This field is internal to libavformat and access from outside is not allowed.
+ */
+ int64_t bytes_read;
+
+ /**
+ * seek statistic
+ * This field is internal to libavformat and access from outside is not allowed.
+ */
+ int seek_count;
+
+ /**
+ * writeout statistic
+ * This field is internal to libavformat and access from outside is not allowed.
+ */
+ int writeout_count;
+
+ /**
+ * Original buffer size
+ * used internally after probing and ensure seekback to reset the buffer size
+ * This field is internal to libavformat and access from outside is not allowed.
+ */
+ int orig_buffer_size;
} AVIOContext;
/* unbuffered I/O */
/**
+ * Return the name of the protocol that will handle the passed URL.
+ *
+ * NULL is returned if no protocol could be found for the given URL.
+ *
+ * @return Name of the protocol or NULL.
+ */
+const char *avio_find_protocol_name(const char *url);
+
+/**
* Return AVIO_FLAG_* access flags corresponding to the access permissions
* of the resource in url, or a negative value corresponding to an
* AVERROR code in case of failure. The returned access flags are
@@ -139,6 +186,9 @@ int avio_check(const char *url, int flags);
*
* @param buffer Memory block for input/output operations via AVIOContext.
* The buffer must be allocated with av_malloc() and friends.
+ * It may be freed and replaced with a new buffer by libavformat.
+ * AVIOContext.buffer holds the buffer currently in use,
+ * which must be later freed with av_free().
* @param buffer_size The buffer size is very important for performance.
* For protocols with fixed blocksize it should be set to this blocksize.
* For others a typical size is a cache page, e.g. 4kb.
@@ -146,6 +196,7 @@ int avio_check(const char *url, int flags);
* @param opaque An opaque pointer to user-specific data.
* @param read_packet A function for refilling the buffer, may be NULL.
* @param write_packet A function for writing the buffer contents, may be NULL.
+ * The function may not change the input buffers content.
* @param seek A function for seeking to specified byte position, may be NULL.
*
* @return Allocated AVIOContext or NULL on failure.
@@ -191,8 +242,8 @@ int avio_put_str16le(AVIOContext *s, const char *str);
/**
* Oring this flag as into the "whence" parameter to a seek function causes it to
- * seek by any means (like reopening and linear reading) or other normally unreasonble
- * means that can be extreemly slow.
+ * seek by any means (like reopening and linear reading) or other normally unreasonable
+ * means that can be extremely slow.
* This may be ignored by the seek code.
*/
#define AVSEEK_FORCE 0x20000
@@ -207,10 +258,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 +275,34 @@ static av_always_inline int64_t avio_tell(AVIOContext *s)
*/
int64_t avio_size(AVIOContext *s);
+/**
+ * feof() equivalent for AVIOContext.
+ * @return non zero if and only if end of file
+ */
+int avio_feof(AVIOContext *s);
+#if FF_API_URL_FEOF
+/**
+ * @deprecated use avio_feof()
+ */
+attribute_deprecated
+int url_feof(AVIOContext *s);
+#endif
+
/** @warning currently size is limited */
int avio_printf(AVIOContext *s, const char *fmt, ...) av_printf_format(2, 3);
+/**
+ * Force flushing of buffered data.
+ *
+ * For write streams, force the buffered data to be immediately written to the output,
+ * without to wait to fill the internal buffer.
+ *
+ * For read streams, discard all currently buffered data, and advance the
+ * reported file position to that of the underlying stream. This does not
+ * read new data, and does not perform any seeks.
+ */
void avio_flush(AVIOContext *s);
-
/**
* Read size bytes from AVIOContext into buf.
* @return number of bytes read or AVERROR
@@ -311,6 +381,14 @@ int avio_get_str16be(AVIOContext *pb, int maxlen, char *buf, int buflen);
#define AVIO_FLAG_NONBLOCK 8
/**
+ * Use direct mode.
+ * avio_read and avio_write should if possible be satisfied directly
+ * instead of going through a buffer, and avio_seek will always
+ * call the underlying seek function directly.
+ */
+#define AVIO_FLAG_DIRECT 0x8000
+
+/**
* Create and initialize a AVIOContext for accessing the
* resource indicated by url.
* @note When the resource indicated by url has been opened in
@@ -321,7 +399,7 @@ int avio_get_str16be(AVIOContext *pb, int maxlen, char *buf, int buflen);
* @param url resource to access
* @param flags flags which control how the resource indicated by url
* is to be opened
- * @return 0 in case of success, a negative value corresponding to an
+ * @return >= 0 in case of success, a negative value corresponding to an
* AVERROR code in case of failure
*/
int avio_open(AVIOContext **s, const char *url, int flags);
@@ -341,7 +419,7 @@ int avio_open(AVIOContext **s, const char *url, int flags);
* @param options A dictionary filled with protocol-private options. On return
* this parameter will be destroyed and replaced with a dict containing options
* that were not found. May be NULL.
- * @return 0 in case of success, a negative value corresponding to an
+ * @return >= 0 in case of success, a negative value corresponding to an
* AVERROR code in case of failure
*/
int avio_open2(AVIOContext **s, const char *url, int flags,
@@ -423,17 +501,28 @@ int avio_pause(AVIOContext *h, int pause);
* If stream_index is (-1) the timestamp should be in AV_TIME_BASE
* units from the beginning of the presentation.
* If a stream_index >= 0 is used and the protocol does not support
- * seeking based on component streams, the call will fail with ENOTSUP.
+ * seeking based on component streams, the call will fail.
* @param timestamp timestamp in AVStream.time_base units
* or if there is no stream specified then in AV_TIME_BASE units.
* @param flags Optional combination of AVSEEK_FLAG_BACKWARD, AVSEEK_FLAG_BYTE
* and AVSEEK_FLAG_ANY. The protocol may silently ignore
* AVSEEK_FLAG_BACKWARD and AVSEEK_FLAG_ANY, but AVSEEK_FLAG_BYTE will
- * fail with ENOTSUP if used and not supported.
+ * fail if used and not supported.
* @return >= 0 on success
* @see AVInputFormat::read_seek
*/
int64_t avio_seek_time(AVIOContext *h, int stream_index,
int64_t timestamp, int flags);
+/* Avoid a warning. The header can not be included because it breaks c++. */
+struct AVBPrint;
+
+/**
+ * Read contents of h into print buffer, up to max_size bytes, or up to EOF.
+ *
+ * @return 0 for success (max_size bytes read or EOF reached), negative error
+ * code otherwise
+ */
+int avio_read_to_bprint(AVIOContext *h, struct AVBPrint *pb, size_t max_size);
+
#endif /* AVFORMAT_AVIO_H */
diff --git a/libavformat/avio_internal.h b/libavformat/avio_internal.h
index a8bcadd38b..5864048811 100644
--- a/libavformat/avio_internal.h
+++ b/libavformat/avio_internal.h
@@ -1,19 +1,19 @@
/*
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -78,16 +78,27 @@ static av_always_inline void ffio_wfourcc(AVIOContext *pb, const uint8_t *s)
* @param s The read-only AVIOContext to rewind
* @param buf The probe buffer containing the first buf_size bytes of the file
* @param buf_size The size of buf
- * @return 0 in case of success, a negative value corresponding to an
+ * @return >= 0 in case of success, a negative value corresponding to an
* AVERROR code in case of failure
*/
-int ffio_rewind_with_probe_data(AVIOContext *s, unsigned char *buf, int buf_size);
+int ffio_rewind_with_probe_data(AVIOContext *s, unsigned char **buf, int buf_size);
uint64_t ffio_read_varlen(AVIOContext *bc);
/** @warning must be called before any I/O */
int ffio_set_buf_size(AVIOContext *s, int buf_size);
+/**
+ * Ensures that the requested seekback buffer size will be available
+ *
+ * Will ensure that when reading sequentially up to buf_size, seeking
+ * within the current pos and pos+buf_size is possible.
+ * Once the stream position moves outside this window this guarantee is lost.
+ */
+int ffio_ensure_seekback(AVIOContext *s, int64_t buf_size);
+
+int ffio_limit(AVIOContext *s, int size);
+
void ffio_init_checksum(AVIOContext *s,
unsigned long (*update_checksum)(unsigned long c, const uint8_t *p, unsigned int len),
unsigned long checksum);
@@ -116,7 +127,7 @@ int ffio_open_dyn_packet_buf(AVIOContext **s, int max_packet_size);
*
* @param s Used to return the pointer to the created AVIOContext.
* In case of failure the pointed to value is set to NULL.
- * @return 0 in case of success, a negative value corresponding to an
+ * @return >= 0 in case of success, a negative value corresponding to an
* AVERROR code in case of failure
*/
int ffio_fdopen(AVIOContext **s, URLContext *h);
diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c
index 5f848b20f9..3d77a7ff25 100644
--- a/libavformat/aviobuf.c
+++ b/libavformat/aviobuf.c
@@ -2,28 +2,30 @@
* buffered I/O
* Copyright (c) 2000,2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/bprint.h"
#include "libavutil/crc.h"
#include "libavutil/dict.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
#include "libavutil/opt.h"
+#include "libavutil/avassert.h"
#include "avformat.h"
#include "avio.h"
#include "avio_internal.h"
@@ -77,9 +79,11 @@ int ffio_init_context(AVIOContext *s,
int64_t (*seek)(void *opaque, int64_t offset, int whence))
{
s->buffer = buffer;
+ s->orig_buffer_size =
s->buffer_size = buffer_size;
s->buf_ptr = buffer;
s->opaque = opaque;
+ s->direct = 0;
url_resetbuf(s, write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ);
@@ -121,28 +125,36 @@ AVIOContext *avio_alloc_context(
return s;
}
-static void flush_buffer(AVIOContext *s)
+static void writeout(AVIOContext *s, const uint8_t *data, int len)
{
- 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;
- }
+ if (s->write_packet && !s->error) {
+ int ret = s->write_packet(s->opaque, (uint8_t *)data, len);
+ if (ret < 0) {
+ s->error = ret;
}
+ }
+ s->writeout_count ++;
+ s->pos += len;
+}
+
+static void flush_buffer(AVIOContext *s)
+{
+ if (s->write_flag && s->buf_ptr > s->buffer) {
+ 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;
+ if (!s->write_flag)
+ s->buf_end = s->buffer;
}
void avio_w8(AVIOContext *s, int b)
{
+ av_assert2(b>=-128 && b<=255);
*s->buf_ptr++ = b;
if (s->buf_ptr >= s->buf_end)
flush_buffer(s);
@@ -164,6 +176,11 @@ void ffio_fill(AVIOContext *s, int b, int count)
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
{
+ if (s->direct && !s->update_checksum) {
+ avio_flush(s);
+ writeout(s, buf, size);
+ return;
+ }
while (size > 0) {
int len = FFMIN(s->buf_end - s->buf_ptr, size);
memcpy(s->buf_ptr, buf, len);
@@ -188,12 +205,14 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
int64_t offset1;
int64_t pos;
int force = whence & AVSEEK_FORCE;
+ int buffer_size;
whence &= ~AVSEEK_FORCE;
if(!s)
return AVERROR(EINVAL);
- pos = s->pos - (s->write_flag ? 0 : (s->buf_end - s->buffer));
+ buffer_size = s->buf_end - s->buffer;
+ pos = s->pos - (s->write_flag ? 0 : buffer_size);
if (whence != SEEK_CUR && whence != SEEK_SET)
return AVERROR(EINVAL);
@@ -204,23 +223,38 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
return offset1;
offset += offset1;
}
+ if (offset < 0)
+ return AVERROR(EINVAL);
+
offset1 = offset - pos;
- if (!s->must_flush &&
- offset1 >= 0 && offset1 < (s->buf_end - s->buffer)) {
+ if (!s->must_flush && (!s->direct || !s->seek) &&
+ offset1 >= 0 && offset1 <= buffer_size - s->write_flag) {
/* can do the seek inside the buffer */
s->buf_ptr = s->buffer + offset1;
} else if ((!s->seekable ||
offset1 <= s->buf_end + SHORT_SEEK_THRESHOLD - s->buffer) &&
!s->write_flag && offset1 >= 0 &&
+ (!s->direct || !s->seek) &&
(whence != SEEK_END || force)) {
while(s->pos < offset && !s->eof_reached)
fill_buffer(s);
if (s->eof_reached)
return AVERROR_EOF;
s->buf_ptr = s->buf_end + offset - s->pos;
- } else {
+ } else if(!s->write_flag && offset1 < 0 && -offset1 < buffer_size>>1 && s->seek && offset > 0) {
int64_t res;
+ pos -= FFMIN(buffer_size>>1, pos);
+ if ((res = s->seek(s->opaque, pos, SEEK_SET)) < 0)
+ return res;
+ s->buf_end =
+ s->buf_ptr = s->buffer;
+ s->pos = pos;
+ s->eof_reached = 0;
+ fill_buffer(s);
+ return avio_seek(s, offset, SEEK_SET | force);
+ } else {
+ int64_t res;
if (s->write_flag) {
flush_buffer(s);
s->must_flush = 1;
@@ -229,6 +263,7 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
return AVERROR(EPIPE);
if ((res = s->seek(s->opaque, offset, SEEK_SET)) < 0)
return res;
+ s->seek_count ++;
if (!s->write_flag)
s->buf_end = s->buffer;
s->buf_ptr = s->buffer;
@@ -238,6 +273,11 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
return offset;
}
+int64_t avio_skip(AVIOContext *s, int64_t offset)
+{
+ return avio_seek(s, offset, SEEK_CUR);
+}
+
int64_t avio_size(AVIOContext *s)
{
int64_t size;
@@ -257,20 +297,38 @@ int64_t avio_size(AVIOContext *s)
return size;
}
+int avio_feof(AVIOContext *s)
+{
+ if(!s)
+ return 0;
+ if(s->eof_reached){
+ s->eof_reached=0;
+ fill_buffer(s);
+ }
+ return s->eof_reached;
+}
+
+#if FF_API_URL_FEOF
+int url_feof(AVIOContext *s)
+{
+ return avio_feof(s);
+}
+#endif
+
void avio_wl32(AVIOContext *s, unsigned int val)
{
- avio_w8(s, val);
- avio_w8(s, val >> 8);
- avio_w8(s, val >> 16);
- avio_w8(s, val >> 24);
+ avio_w8(s, (uint8_t) val );
+ avio_w8(s, (uint8_t)(val >> 8 ));
+ avio_w8(s, (uint8_t)(val >> 16));
+ avio_w8(s, val >> 24 );
}
void avio_wb32(AVIOContext *s, unsigned int val)
{
- avio_w8(s, val >> 24);
- avio_w8(s, val >> 16);
- avio_w8(s, val >> 8);
- avio_w8(s, val);
+ avio_w8(s, val >> 24 );
+ avio_w8(s, (uint8_t)(val >> 16));
+ avio_w8(s, (uint8_t)(val >> 8 ));
+ avio_w8(s, (uint8_t) val );
}
int avio_put_str(AVIOContext *s, const char *str)
@@ -288,15 +346,22 @@ int avio_put_str16le(AVIOContext *s, const char *str)
{
const uint8_t *q = str;
int ret = 0;
+ int err = 0;
while (*q) {
uint32_t ch;
uint16_t tmp;
- GET_UTF8(ch, *q++, break;)
+ GET_UTF8(ch, *q++, goto invalid;)
PUT_UTF16(ch, tmp, avio_wl16(s, tmp); ret += 2;)
+ continue;
+invalid:
+ av_log(s, AV_LOG_ERROR, "Invaid UTF8 sequence in avio_put_str16le\n");
+ err = AVERROR(EINVAL);
}
avio_wl16(s, 0);
+ if (err)
+ return err;
ret += 2;
return ret;
}
@@ -316,7 +381,7 @@ void ff_put_v(AVIOContext *bc, uint64_t val)
int i = ff_get_v_length(val);
while (--i > 0)
- avio_w8(bc, 128 | (val >> (7 * i)));
+ avio_w8(bc, 128 | (uint8_t)(val >> (7*i)));
avio_w8(bc, val & 127);
}
@@ -335,38 +400,37 @@ void avio_wb64(AVIOContext *s, uint64_t val)
void avio_wl16(AVIOContext *s, unsigned int val)
{
- avio_w8(s, val);
- avio_w8(s, val >> 8);
+ avio_w8(s, (uint8_t)val);
+ avio_w8(s, (int)val >> 8);
}
void avio_wb16(AVIOContext *s, unsigned int val)
{
- avio_w8(s, val >> 8);
- avio_w8(s, val);
+ avio_w8(s, (int)val >> 8);
+ avio_w8(s, (uint8_t)val);
}
void avio_wl24(AVIOContext *s, unsigned int val)
{
avio_wl16(s, val & 0xffff);
- avio_w8(s, val >> 16);
+ avio_w8(s, (int)val >> 16);
}
void avio_wb24(AVIOContext *s, unsigned int val)
{
- avio_wb16(s, val >> 8);
- avio_w8(s, val);
+ avio_wb16(s, (int)val >> 8);
+ avio_w8(s, (uint8_t)val);
}
/* Input stream */
static void fill_buffer(AVIOContext *s)
{
- uint8_t *dst = !s->max_packet_size &&
- s->buf_end - s->buffer < s->buffer_size ?
- s->buf_end : s->buffer;
- int len = s->buffer_size - (dst - s->buffer);
int max_buffer_size = s->max_packet_size ?
s->max_packet_size : IO_BUFFER_SIZE;
+ uint8_t *dst = s->buf_end - s->buffer + max_buffer_size < s->buffer_size ?
+ s->buf_end : s->buffer;
+ int len = s->buffer_size - (dst - s->buffer);
/* can't fill the buffer without read_packet, just set EOF if appropriate */
if (!s->read_packet && s->buf_ptr >= s->buf_end)
@@ -384,11 +448,14 @@ static void fill_buffer(AVIOContext *s)
}
/* make buffer smaller in case it ended up large after probing */
- if (s->buffer_size > max_buffer_size) {
- ffio_set_buf_size(s, max_buffer_size);
+ if (s->read_packet && s->orig_buffer_size && s->buffer_size > s->orig_buffer_size) {
+ if (dst == s->buffer) {
+ ffio_set_buf_size(s, s->orig_buffer_size);
- s->checksum_ptr = dst = s->buffer;
- len = s->buffer_size;
+ s->checksum_ptr = dst = s->buffer;
+ }
+ av_assert0(len >= s->orig_buffer_size);
+ len = s->orig_buffer_size;
}
if (s->read_packet)
@@ -405,6 +472,7 @@ static void fill_buffer(AVIOContext *s)
s->pos += len;
s->buf_ptr = dst;
s->buf_end = dst + len;
+ s->bytes_read += len;
}
}
@@ -459,7 +527,7 @@ int avio_read(AVIOContext *s, unsigned char *buf, int size)
if (len > size)
len = size;
if (len == 0 || s->write_flag) {
- if(size > s->buffer_size && !s->update_checksum){
+ if((s->direct || size > s->buffer_size) && !s->update_checksum){
if(s->read_packet)
len = s->read_packet(s->opaque, buf, size);
if (len <= 0) {
@@ -471,6 +539,7 @@ int avio_read(AVIOContext *s, unsigned char *buf, int size)
break;
} else {
s->pos += len;
+ s->bytes_read += len;
size -= len;
buf += len;
s->buf_ptr = s->buffer;
@@ -490,8 +559,8 @@ int avio_read(AVIOContext *s, unsigned char *buf, int size)
}
}
if (size1 == size) {
- if (s->error) return s->error;
- if (s->eof_reached) return AVERROR_EOF;
+ if (s->error) return s->error;
+ if (avio_feof(s)) return AVERROR_EOF;
}
return size1 - size;
}
@@ -539,8 +608,8 @@ int ffio_read_partial(AVIOContext *s, unsigned char *buf, int size)
memcpy(buf, s->buf_ptr, len);
s->buf_ptr += len;
if (!len) {
- if (s->error) return s->error;
- if (s->eof_reached) return AVERROR_EOF;
+ if (s->error) return s->error;
+ if (avio_feof(s)) return AVERROR_EOF;
}
return len;
}
@@ -609,7 +678,9 @@ int ff_get_line(AVIOContext *s, char *buf, int maxlen)
c = avio_r8(s);
if (c && i < maxlen-1)
buf[i++] = c;
- } while (c != '\n' && c);
+ } while (c != '\n' && c != '\r' && c);
+ if (c == '\r' && avio_r8(s) != '\n' && !avio_feof(s))
+ avio_skip(s, -1);
buf[i] = 0;
return i;
@@ -692,11 +763,12 @@ int ffio_fdopen(AVIOContext **s, URLContext *h)
return AVERROR(ENOMEM);
*s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h,
- ffurl_read, ffurl_write, ffurl_seek);
+ (void*)ffurl_read, (void*)ffurl_write, (void*)ffurl_seek);
if (!*s) {
av_free(buffer);
return AVERROR(ENOMEM);
}
+ (*s)->direct = h->flags & AVIO_FLAG_DIRECT;
(*s)->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL;
(*s)->max_packet_size = max_packet_size;
if(h->prot) {
@@ -707,6 +779,32 @@ int ffio_fdopen(AVIOContext **s, URLContext *h)
return 0;
}
+int ffio_ensure_seekback(AVIOContext *s, int64_t buf_size)
+{
+ uint8_t *buffer;
+ int max_buffer_size = s->max_packet_size ?
+ s->max_packet_size : IO_BUFFER_SIZE;
+ int filled = s->buf_end - s->buffer;
+
+ buf_size += s->buf_ptr - s->buffer + max_buffer_size;
+
+ if (buf_size < filled || s->seekable)
+ return 0;
+ av_assert0(!s->write_flag);
+
+ buffer = av_malloc(buf_size);
+ if (!buffer)
+ return AVERROR(ENOMEM);
+
+ memcpy(buffer, s->buffer, filled);
+ av_free(s->buffer);
+ s->buf_ptr = buffer + (s->buf_ptr - s->buffer);
+ s->buf_end = buffer + (s->buf_end - s->buffer);
+ s->buffer = buffer;
+ s->buffer_size = buf_size;
+ return 0;
+}
+
int ffio_set_buf_size(AVIOContext *s, int buf_size)
{
uint8_t *buffer;
@@ -716,6 +814,7 @@ int ffio_set_buf_size(AVIOContext *s, int buf_size)
av_free(s->buffer);
s->buffer = buffer;
+ s->orig_buffer_size =
s->buffer_size = buf_size;
s->buf_ptr = buffer;
url_resetbuf(s, s->write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ);
@@ -724,7 +823,7 @@ int ffio_set_buf_size(AVIOContext *s, int buf_size)
static int url_resetbuf(AVIOContext *s, int flags)
{
- assert(flags == AVIO_FLAG_WRITE || flags == AVIO_FLAG_READ);
+ av_assert1(flags == AVIO_FLAG_WRITE || flags == AVIO_FLAG_READ);
if (flags & AVIO_FLAG_WRITE) {
s->buf_end = s->buffer + s->buffer_size;
@@ -736,27 +835,32 @@ static int url_resetbuf(AVIOContext *s, int flags)
return 0;
}
-int ffio_rewind_with_probe_data(AVIOContext *s, unsigned char *buf, int buf_size)
+int ffio_rewind_with_probe_data(AVIOContext *s, unsigned char **bufp, int buf_size)
{
int64_t buffer_start;
int buffer_size;
int overlap, new_size, alloc_size;
+ uint8_t *buf = *bufp;
- if (s->write_flag)
+ if (s->write_flag) {
+ av_freep(bufp);
return AVERROR(EINVAL);
+ }
buffer_size = s->buf_end - s->buffer;
/* the buffers must touch or overlap */
- if ((buffer_start = s->pos - buffer_size) > buf_size)
+ if ((buffer_start = s->pos - buffer_size) > buf_size) {
+ av_freep(bufp);
return AVERROR(EINVAL);
+ }
overlap = buf_size - buffer_start;
new_size = buf_size + buffer_size - overlap;
alloc_size = FFMAX(s->buffer_size, new_size);
if (alloc_size > buf_size)
- if (!(buf = av_realloc(buf, alloc_size)))
+ if (!(buf = (*bufp) = av_realloc_f(buf, 1, alloc_size)))
return AVERROR(ENOMEM);
if (new_size > buf_size) {
@@ -807,6 +911,10 @@ int avio_close(AVIOContext *s)
avio_flush(s);
h = s->opaque;
av_freep(&s->buffer);
+ if (s->write_flag)
+ av_log(s, AV_LOG_DEBUG, "Statistics: %d seeks, %d writeouts\n", s->seek_count, s->writeout_count);
+ else
+ av_log(s, AV_LOG_DEBUG, "Statistics: %"PRId64" bytes read, %d seeks\n", s->bytes_read, s->seek_count);
av_free(s);
return ffurl_close(h);
}
@@ -858,6 +966,24 @@ int64_t avio_seek_time(AVIOContext *s, int stream_index,
return ret;
}
+int avio_read_to_bprint(AVIOContext *h, AVBPrint *pb, size_t max_size)
+{
+ int ret;
+ char buf[1024];
+ while (max_size) {
+ ret = avio_read(h, buf, FFMIN(max_size, sizeof(buf)));
+ if (ret == AVERROR_EOF)
+ return 0;
+ if (ret <= 0)
+ return ret;
+ av_bprint_append_data(pb, buf, ret);
+ if (!av_bprint_is_complete(pb))
+ return AVERROR(ENOMEM);
+ max_size -= ret;
+ }
+ return 0;
+}
+
/* output in a dynamic buffer */
typedef struct DynBuffer {
diff --git a/libavformat/avisynth.c b/libavformat/avisynth.c
index c66f65d838..fbd4a8d4b0 100644
--- a/libavformat/avisynth.c
+++ b/libavformat/avisynth.c
@@ -2,20 +2,19 @@
* AviSynth/AvxSynth support
* Copyright (c) 2012 AvxSynth Team.
*
- * This file is part of Libav.
- *
- * Libav is free software; you can redistribute it and/or
+ * This file is part of FFmpeg
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -27,25 +26,17 @@
/* Enable function pointer definitions for runtime loading. */
#define AVSC_NO_DECLSPEC
-/* Platform-specific directives for AviSynth vs AvxSynth.
- *
- * avisynth_c.h needs to be the one provided with x264, as
- * the one in AviSynth's CVS hasn't been updated to support
- * 2.6's extra colorspaces. A temporary source of that header,
- * installable from a GNU-style Makefile is available from
- * github.com/qyot27/avisynth_headers -- AvxSynth doesn't
- * require this kind of special treatment because like any
- * standard *nix application, it installs its headers
- * alongside its libs. */
+/* Platform-specific directives for AviSynth vs AvxSynth. */
#ifdef _WIN32
#include <windows.h>
#undef EXTERN_C
- #include <avisynth/avisynth_c.h>
+ #include "compat/avisynth/avisynth_c.h"
+ #include "compat/avisynth/avisynth_c_25.h"
#define AVISYNTH_LIB "avisynth"
#define USING_AVISYNTH
#else
#include <dlfcn.h>
- #include <avxsynth/avxsynth_c.h>
+ #include "compat/avisynth/avxsynth_c.h"
#if defined (__APPLE__)
#define AVISYNTH_LIB "libavxsynth.dylib"
#else
@@ -393,19 +384,6 @@ static int avisynth_open_file(AVFormatContext *s)
avs->clip = avs_library.avs_take_clip(val, avs->env);
avs->vi = avs_library.avs_get_video_info(avs->clip);
-#ifdef USING_AVISYNTH
- /* libav only supports AviSynth 2.6 on Windows. Since AvxSynth
- * identifies itself as interface version 3 like 2.5.8, this
- * needs to be special-cased. */
-
- if (avs_library.avs_get_version(avs->clip) == 3) {
- av_log(s, AV_LOG_ERROR,
- "AviSynth 2.5.8 not supported. Please upgrade to 2.6.\n");
- ret = AVERROR_UNKNOWN;
- goto fail;
- }
-#endif
-
/* Release the AVS_Value as it will go out of scope. */
avs_library.avs_release_value(val);
@@ -502,8 +480,18 @@ static int avisynth_read_packet_video(AVFormatContext *s, AVPacket *pkt,
src_p = avs_get_read_ptr_p(frame, plane);
pitch = avs_get_pitch_p(frame, plane);
+#ifdef USING_AVISYNTH
+ if (avs_library.avs_get_version(avs->clip) == 3) {
+ rowsize = avs_get_row_size_p_25(frame, plane);
+ planeheight = avs_get_height_p_25(frame, plane);
+ } else {
+ rowsize = avs_get_row_size_p(frame, plane);
+ planeheight = avs_get_height_p(frame, plane);
+ }
+#else
rowsize = avs_get_row_size_p(frame, plane);
planeheight = avs_get_height_p(frame, plane);
+#endif
/* Flip RGB video. */
if (avs_is_rgb24(avs->vi) || avs_is_rgb(avs->vi)) {
diff --git a/libavformat/avlanguage.c b/libavformat/avlanguage.c
index e606ef22f9..39f2560d94 100644
--- a/libavformat/avlanguage.c
+++ b/libavformat/avlanguage.c
@@ -1,20 +1,20 @@
/*
* Cyril Comparon, Larbi Joubala, Resonate-MP4 2009
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/avlanguage.h b/libavformat/avlanguage.h
index 2ec3e2d372..7fb8968810 100644
--- a/libavformat/avlanguage.h
+++ b/libavformat/avlanguage.h
@@ -1,20 +1,20 @@
/*
* Cyril Comparon, Larbi Joubala, Resonate-MP4 2009
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/avr.c b/libavformat/avr.c
new file mode 100644
index 0000000000..a33134eed5
--- /dev/null
+++ b/libavformat/avr.c
@@ -0,0 +1,99 @@
+/*
+ * AVR demuxer
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "internal.h"
+#include "pcm.h"
+
+static int avr_probe(AVProbeData *p)
+{
+ if (AV_RL32(p->buf) != MKTAG('2', 'B', 'I', 'T'))
+ return 0;
+
+ if (!AV_RB16(p->buf+12) || AV_RB16(p->buf+12) > 256) // channels
+ return AVPROBE_SCORE_EXTENSION/2;
+ if (AV_RB16(p->buf+14) > 256) // bps
+ return AVPROBE_SCORE_EXTENSION/2;
+
+ return AVPROBE_SCORE_EXTENSION;
+}
+
+static int avr_read_header(AVFormatContext *s)
+{
+ uint16_t chan, sign, bps;
+ AVStream *st;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+
+ avio_skip(s->pb, 4); // magic
+ avio_skip(s->pb, 8); // sample_name
+
+ chan = avio_rb16(s->pb);
+ if (!chan) {
+ st->codec->channels = 1;
+ } else if (chan == 0xFFFFu) {
+ st->codec->channels = 2;
+ } else {
+ avpriv_request_sample(s, "chan %d", chan);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ st->codec->bits_per_coded_sample = bps = avio_rb16(s->pb);
+
+ sign = avio_rb16(s->pb);
+
+ avio_skip(s->pb, 2); // loop
+ avio_skip(s->pb, 2); // midi
+ avio_skip(s->pb, 1); // replay speed
+
+ st->codec->sample_rate = avio_rb24(s->pb);
+ avio_skip(s->pb, 4 * 3);
+ avio_skip(s->pb, 2 * 3);
+ avio_skip(s->pb, 20);
+ avio_skip(s->pb, 64);
+
+ st->codec->codec_id = ff_get_pcm_codec_id(bps, 0, 1, sign);
+ if (st->codec->codec_id == AV_CODEC_ID_NONE) {
+ avpriv_request_sample(s, "Bps %d and sign %d", bps, sign);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ st->codec->block_align = bps * st->codec->channels / 8;
+
+ avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+ return 0;
+}
+
+AVInputFormat ff_avr_demuxer = {
+ .name = "avr",
+ .long_name = NULL_IF_CONFIG_SMALL("AVR (Audio Visual Research)"),
+ .read_probe = avr_probe,
+ .read_header = avr_read_header,
+ .read_packet = ff_pcm_read_packet,
+ .read_seek = ff_pcm_read_seek,
+ .extensions = "avr",
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/avs.c b/libavformat/avs.c
index d8042c51f9..b699dbf9ba 100644
--- a/libavformat/avs.c
+++ b/libavformat/avs.c
@@ -2,20 +2,20 @@
* AVS demuxer.
* Copyright (c) 2006 Aurelien Jacobs <aurel@gnuage.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -52,7 +52,7 @@ static int avs_probe(AVProbeData * p)
if (d[0] == 'w' && d[1] == 'W' && d[2] == 0x10 && d[3] == 0)
/* Ensure the buffer probe scores higher than the extension probe.
* This avoids problems with misdetection as AviSynth scripts. */
- return AVPROBE_SCORE_EXTENSION + 1;
+ return AVPROBE_SCORE_EXTENSION + 5;
return 0;
}
@@ -190,6 +190,9 @@ static int avs_read_packet(AVFormatContext * s, AVPacket * pkt)
avs->st_video->codec->height = avs->height;
avs->st_video->codec->bits_per_coded_sample=avs->bits_per_sample;
avs->st_video->nb_frames = avs->nb_frames;
+#if FF_API_R_FRAME_RATE
+ avs->st_video->r_frame_rate =
+#endif
avs->st_video->avg_frame_rate = (AVRational){avs->fps, 1};
}
return avs_read_video_packet(s, pkt, type, sub_type, size,
diff --git a/libavformat/bethsoftvid.c b/libavformat/bethsoftvid.c
index 135a3c491a..40a425271d 100644
--- a/libavformat/bethsoftvid.c
+++ b/libavformat/bethsoftvid.c
@@ -2,20 +2,20 @@
* Bethsoft VID format Demuxer
* Copyright (c) 2007 Nicholas Tung
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -61,6 +61,9 @@ static int vid_probe(AVProbeData *p)
if (AV_RL32(p->buf) != MKTAG('V', 'I', 'D', 0))
return 0;
+ if (p->buf[4] != 2)
+ return AVPROBE_SCORE_MAX / 4;
+
return AVPROBE_SCORE_MAX;
}
@@ -190,9 +193,11 @@ static int read_frame(BVID_DemuxContext *vid, AVIOContext *pb, AVPacket *pkt,
BVID_PALETTE_SIZE);
if (!pdata) {
ret = AVERROR(ENOMEM);
+ av_log(s, AV_LOG_ERROR, "Failed to allocate palette side data\n");
goto fail;
}
memcpy(pdata, vid->palette, BVID_PALETTE_SIZE);
+
av_freep(&vid->palette);
}
@@ -211,8 +216,8 @@ static int vid_read_packet(AVFormatContext *s,
int audio_length;
int ret_value;
- if(vid->is_finished || pb->eof_reached)
- return AVERROR(EIO);
+ if(vid->is_finished || avio_feof(pb))
+ return AVERROR_EOF;
block_type = avio_r8(pb);
switch(block_type){
diff --git a/libavformat/bfi.c b/libavformat/bfi.c
index 19060e760f..cc7f4948c9 100644
--- a/libavformat/bfi.c
+++ b/libavformat/bfi.c
@@ -2,20 +2,20 @@
* Brute Force & Ignorance (BFI) demuxer
* Copyright (c) 2008 Sisir Koppaka
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -81,6 +81,8 @@ static int bfi_read_header(AVFormatContext * s)
/*Load the palette to extradata */
avio_skip(pb, 8);
vstream->codec->extradata = av_malloc(768);
+ if (!vstream->codec->extradata)
+ return AVERROR(ENOMEM);
vstream->codec->extradata_size = 768;
avio_read(pb, vstream->codec->extradata,
vstream->codec->extradata_size);
@@ -92,6 +94,8 @@ static int bfi_read_header(AVFormatContext * s)
vstream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
vstream->codec->codec_id = AV_CODEC_ID_BFI;
vstream->codec->pix_fmt = AV_PIX_FMT_PAL8;
+ vstream->nb_frames =
+ vstream->duration = bfi->nframes;
/* Set up the audio codec now... */
astream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
@@ -112,15 +116,15 @@ static int bfi_read_packet(AVFormatContext * s, AVPacket * pkt)
BFIContext *bfi = s->priv_data;
AVIOContext *pb = s->pb;
int ret, audio_offset, video_offset, chunk_size, audio_size = 0;
- if (bfi->nframes == 0 || pb->eof_reached) {
- return AVERROR(EIO);
+ if (bfi->nframes == 0 || avio_feof(pb)) {
+ return AVERROR_EOF;
}
/* If all previous chunks were completely read, then find a new one... */
if (!bfi->avflag) {
uint32_t state = 0;
while(state != MKTAG('S','A','V','I')){
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR(EIO);
state = 256*state + avio_r8(pb);
}
diff --git a/libavformat/bink.c b/libavformat/bink.c
index 3224790485..92ce26198d 100644
--- a/libavformat/bink.c
+++ b/libavformat/bink.c
@@ -3,20 +3,20 @@
* Copyright (c) 2008-2010 Peter Ross (pross@xvid.org)
* Copyright (c) 2009 Daniel Verkamp (daniel@drv.nu)
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -61,8 +61,10 @@ static int probe(AVProbeData *p)
{
const uint8_t *b = p->buf;
- if ( b[0] == 'B' && b[1] == 'I' && b[2] == 'K' &&
- (b[3] == 'b' || b[3] == 'f' || b[3] == 'g' || b[3] == 'h' || b[3] == 'i') &&
+ if (((b[0] == 'B' && b[1] == 'I' && b[2] == 'K' &&
+ (b[3] == 'b' || b[3] == 'f' || b[3] == 'g' || b[3] == 'h' || b[3] == 'i')) ||
+ (b[0] == 'K' && b[1] == 'B' && b[2] == '2' && /* Bink 2 */
+ (b[3] == 'a' || b[3] == 'd' || b[3] == 'f' || b[3] == 'g'))) &&
AV_RL32(b+8) > 0 && // num_frames
AV_RL32(b+20) > 0 && AV_RL32(b+20) <= BINK_MAX_WIDTH &&
AV_RL32(b+24) > 0 && AV_RL32(b+24) <= BINK_MAX_HEIGHT &&
@@ -81,6 +83,7 @@ static int read_header(AVFormatContext *s)
uint32_t pos, next_pos;
uint16_t flags;
int keyframe;
+ int ret;
vst = avformat_new_stream(s, NULL);
if (!vst)
@@ -120,11 +123,14 @@ static int read_header(AVFormatContext *s)
vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
vst->codec->codec_id = AV_CODEC_ID_BINKVIDEO;
- vst->codec->extradata = av_mallocz(4 + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!vst->codec->extradata)
+
+ if ((vst->codec->codec_tag & 0xFFFFFF) == MKTAG('K', 'B', '2', 0)) {
+ av_log(s, AV_LOG_WARNING, "Bink 2 video is not implemented\n");
+ vst->codec->codec_id = AV_CODEC_ID_NONE;
+ }
+
+ if (ff_get_extradata(vst->codec, pb, 4) < 0)
return AVERROR(ENOMEM);
- vst->codec->extradata_size = 4;
- avio_read(pb, vst->codec->extradata, 4);
bink->num_audio_tracks = avio_rl32(pb);
@@ -156,10 +162,8 @@ static int read_header(AVFormatContext *s)
ast->codec->channels = 1;
ast->codec->channel_layout = AV_CH_LAYOUT_MONO;
}
- ast->codec->extradata = av_mallocz(4 + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!ast->codec->extradata)
+ if (ff_alloc_extradata(ast->codec, 4))
return AVERROR(ENOMEM);
- ast->codec->extradata_size = 4;
AV_WL32(ast->codec->extradata, vst->codec->codec_tag);
}
@@ -185,11 +189,12 @@ static int read_header(AVFormatContext *s)
av_log(s, AV_LOG_ERROR, "invalid frame index table\n");
return AVERROR(EIO);
}
- av_add_index_entry(vst, pos, i, next_pos - pos, 0,
- keyframe ? AVINDEX_KEYFRAME : 0);
+ if ((ret = av_add_index_entry(vst, pos, i, next_pos - pos, 0,
+ keyframe ? AVINDEX_KEYFRAME : 0)) < 0)
+ return ret;
}
- avio_skip(pb, 4);
+ avio_seek(pb, vst->index_entries[0].pos, SEEK_SET);
bink->current_track = -1;
return 0;
@@ -206,7 +211,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt)
AVStream *st = s->streams[0]; // stream 0 is video stream with index
if (bink->video_pts >= st->duration)
- return AVERROR(EIO);
+ return AVERROR_EOF;
index_entry = av_index_search_timestamp(st, bink->video_pts,
AVSEEK_FLAG_ANY);
@@ -288,4 +293,5 @@ AVInputFormat ff_bink_demuxer = {
.read_header = read_header,
.read_packet = read_packet,
.read_seek = read_seek,
+ .flags = AVFMT_SHOW_IDS,
};
diff --git a/libavformat/bintext.c b/libavformat/bintext.c
new file mode 100644
index 0000000000..217ea49247
--- /dev/null
+++ b/libavformat/bintext.c
@@ -0,0 +1,388 @@
+/*
+ * Binary text demuxer
+ * eXtended BINary text (XBIN) demuxer
+ * Artworx Data Format demuxer
+ * iCEDraw File demuxer
+ * Copyright (c) 2010 Peter Ross <pross@xvid.org>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Binary text demuxer
+ * eXtended BINary text (XBIN) demuxer
+ * Artworx Data Format demuxer
+ * iCEDraw File demuxer
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
+#include "avformat.h"
+#include "internal.h"
+#include "sauce.h"
+#include "libavcodec/bintext.h"
+
+typedef struct {
+ const AVClass *class;
+ int chars_per_frame; /**< characters to send decoder per frame;
+ set by private options as characters per second, and then
+ converted to characters per frame at runtime */
+ int width, height; /**< video size (WxH pixels) (private option) */
+ AVRational framerate; /**< frames per second (private option) */
+ uint64_t fsize; /**< file size less metadata buffer */
+} BinDemuxContext;
+
+static AVStream * init_stream(AVFormatContext *s)
+{
+ BinDemuxContext *bin = s->priv_data;
+ AVStream *st = avformat_new_stream(s, NULL);
+ if (!st)
+ return NULL;
+ st->codec->codec_tag = 0;
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+
+ if (!bin->width) {
+ st->codec->width = (80<<3);
+ st->codec->height = (25<<4);
+ }
+
+ avpriv_set_pts_info(st, 60, bin->framerate.den, bin->framerate.num);
+
+ /* simulate tty display speed */
+ bin->chars_per_frame = av_clip(av_q2d(st->time_base) * bin->chars_per_frame, 1, INT_MAX);
+
+ return st;
+}
+
+#if CONFIG_BINTEXT_DEMUXER | CONFIG_ADF_DEMUXER | CONFIG_IDF_DEMUXER
+/**
+ * Given filesize and width, calculate height (assume font_height of 16)
+ */
+static void calculate_height(AVCodecContext *avctx, uint64_t fsize)
+{
+ avctx->height = (fsize / ((avctx->width>>3)*2)) << 4;
+}
+#endif
+
+#if CONFIG_BINTEXT_DEMUXER
+static const uint8_t next_magic[]={
+ 0x1A, 0x1B, '[', '0', ';', '3', '0', ';', '4', '0', 'm', 'N', 'E', 'X', 'T', 0x00
+};
+
+static int next_tag_read(AVFormatContext *avctx, uint64_t *fsize)
+{
+ AVIOContext *pb = avctx->pb;
+ char buf[36];
+ int len;
+ uint64_t start_pos = avio_size(pb) - 256;
+
+ avio_seek(pb, start_pos, SEEK_SET);
+ if (avio_read(pb, buf, sizeof(next_magic)) != sizeof(next_magic))
+ return -1;
+ if (memcmp(buf, next_magic, sizeof(next_magic)))
+ return -1;
+ if (avio_r8(pb) != 0x01)
+ return -1;
+
+ *fsize -= 256;
+
+#define GET_EFI2_META(name,size) \
+ len = avio_r8(pb); \
+ if (len < 1 || len > size) \
+ return -1; \
+ if (avio_read(pb, buf, size) == size && *buf) { \
+ buf[len] = 0; \
+ av_dict_set(&avctx->metadata, name, buf, 0); \
+ }
+
+ GET_EFI2_META("filename", 12)
+ GET_EFI2_META("author", 20)
+ GET_EFI2_META("publisher", 20)
+ GET_EFI2_META("title", 35)
+
+ return 0;
+}
+
+static void predict_width(AVCodecContext *avctx, uint64_t fsize, int got_width)
+{
+ /** attempt to guess width */
+ if (!got_width)
+ avctx->width = fsize > 4000 ? (160<<3) : (80<<3);
+}
+
+static int bintext_read_header(AVFormatContext *s)
+{
+ BinDemuxContext *bin = s->priv_data;
+ AVIOContext *pb = s->pb;
+
+ AVStream *st = init_stream(s);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codec->codec_id = AV_CODEC_ID_BINTEXT;
+
+ if (ff_alloc_extradata(st->codec, 2))
+ return AVERROR(ENOMEM);
+ st->codec->extradata[0] = 16;
+ st->codec->extradata[1] = 0;
+
+ if (pb->seekable) {
+ int got_width = 0;
+ bin->fsize = avio_size(pb);
+ if (ff_sauce_read(s, &bin->fsize, &got_width, 0) < 0)
+ next_tag_read(s, &bin->fsize);
+ if (!bin->width) {
+ predict_width(st->codec, bin->fsize, got_width);
+ calculate_height(st->codec, bin->fsize);
+ }
+ avio_seek(pb, 0, SEEK_SET);
+ }
+ return 0;
+}
+#endif /* CONFIG_BINTEXT_DEMUXER */
+
+#if CONFIG_XBIN_DEMUXER
+static int xbin_probe(AVProbeData *p)
+{
+ const uint8_t *d = p->buf;
+
+ if (AV_RL32(d) == MKTAG('X','B','I','N') && d[4] == 0x1A &&
+ AV_RL16(d+5) > 0 && AV_RL16(d+5) <= 160 &&
+ d[9] > 0 && d[9] <= 32)
+ return AVPROBE_SCORE_MAX;
+ return 0;
+}
+
+static int xbin_read_header(AVFormatContext *s)
+{
+ BinDemuxContext *bin = s->priv_data;
+ AVIOContext *pb = s->pb;
+ char fontheight, flags;
+
+ AVStream *st = init_stream(s);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avio_skip(pb, 5);
+ st->codec->width = avio_rl16(pb)<<3;
+ st->codec->height = avio_rl16(pb);
+ fontheight = avio_r8(pb);
+ st->codec->height *= fontheight;
+ flags = avio_r8(pb);
+
+ st->codec->extradata_size = 2;
+ if ((flags & BINTEXT_PALETTE))
+ st->codec->extradata_size += 48;
+ if ((flags & BINTEXT_FONT))
+ st->codec->extradata_size += fontheight * (flags & 0x10 ? 512 : 256);
+ st->codec->codec_id = flags & 4 ? AV_CODEC_ID_XBIN : AV_CODEC_ID_BINTEXT;
+
+ if (ff_alloc_extradata(st->codec, st->codec->extradata_size))
+ return AVERROR(ENOMEM);
+ st->codec->extradata[0] = fontheight;
+ st->codec->extradata[1] = flags;
+ if (avio_read(pb, st->codec->extradata + 2, st->codec->extradata_size - 2) < 0)
+ return AVERROR(EIO);
+
+ if (pb->seekable) {
+ bin->fsize = avio_size(pb) - 9 - st->codec->extradata_size;
+ ff_sauce_read(s, &bin->fsize, NULL, 0);
+ avio_seek(pb, 9 + st->codec->extradata_size, SEEK_SET);
+ }
+
+ return 0;
+}
+#endif /* CONFIG_XBIN_DEMUXER */
+
+#if CONFIG_ADF_DEMUXER
+static int adf_read_header(AVFormatContext *s)
+{
+ BinDemuxContext *bin = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVStream *st;
+
+ if (avio_r8(pb) != 1)
+ return AVERROR_INVALIDDATA;
+
+ st = init_stream(s);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codec->codec_id = AV_CODEC_ID_BINTEXT;
+
+ if (ff_alloc_extradata(st->codec, 2 + 48 + 4096))
+ return AVERROR(ENOMEM);
+ st->codec->extradata[0] = 16;
+ st->codec->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT;
+
+ if (avio_read(pb, st->codec->extradata + 2, 24) < 0)
+ return AVERROR(EIO);
+ avio_skip(pb, 144);
+ if (avio_read(pb, st->codec->extradata + 2 + 24, 24) < 0)
+ return AVERROR(EIO);
+ if (avio_read(pb, st->codec->extradata + 2 + 48, 4096) < 0)
+ return AVERROR(EIO);
+
+ if (pb->seekable) {
+ int got_width = 0;
+ bin->fsize = avio_size(pb) - 1 - 192 - 4096;
+ st->codec->width = 80<<3;
+ ff_sauce_read(s, &bin->fsize, &got_width, 0);
+ if (!bin->width)
+ calculate_height(st->codec, bin->fsize);
+ avio_seek(pb, 1 + 192 + 4096, SEEK_SET);
+ }
+ return 0;
+}
+#endif /* CONFIG_ADF_DEMUXER */
+
+#if CONFIG_IDF_DEMUXER
+static const uint8_t idf_magic[] = {
+ 0x04, 0x31, 0x2e, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x15, 0x00
+};
+
+static int idf_probe(AVProbeData *p)
+{
+ if (p->buf_size < sizeof(idf_magic))
+ return 0;
+ if (!memcmp(p->buf, idf_magic, sizeof(idf_magic)))
+ return AVPROBE_SCORE_MAX;
+ return 0;
+}
+
+static int idf_read_header(AVFormatContext *s)
+{
+ BinDemuxContext *bin = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVStream *st;
+ int got_width = 0;
+
+ if (!pb->seekable)
+ return AVERROR(EIO);
+
+ st = init_stream(s);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codec->codec_id = AV_CODEC_ID_IDF;
+
+ if (ff_alloc_extradata(st->codec, 2 + 48 + 4096))
+ return AVERROR(ENOMEM);
+ st->codec->extradata[0] = 16;
+ st->codec->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT;
+
+ avio_seek(pb, avio_size(pb) - 4096 - 48, SEEK_SET);
+
+ if (avio_read(pb, st->codec->extradata + 2 + 48, 4096) < 0)
+ return AVERROR(EIO);
+ if (avio_read(pb, st->codec->extradata + 2, 48) < 0)
+ return AVERROR(EIO);
+
+ bin->fsize = avio_size(pb) - 12 - 4096 - 48;
+ ff_sauce_read(s, &bin->fsize, &got_width, 0);
+ if (!bin->width)
+ calculate_height(st->codec, bin->fsize);
+ avio_seek(pb, 12, SEEK_SET);
+ return 0;
+}
+#endif /* CONFIG_IDF_DEMUXER */
+
+static int read_packet(AVFormatContext *s,
+ AVPacket *pkt)
+{
+ BinDemuxContext *bin = s->priv_data;
+
+ if (bin->fsize > 0) {
+ if (av_get_packet(s->pb, pkt, bin->fsize) < 0)
+ return AVERROR(EIO);
+ bin->fsize = -1; /* done */
+ } else if (!bin->fsize) {
+ if (avio_feof(s->pb))
+ return AVERROR(EIO);
+ if (av_get_packet(s->pb, pkt, bin->chars_per_frame) < 0)
+ return AVERROR(EIO);
+ } else {
+ return AVERROR(EIO);
+ }
+
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ return 0;
+}
+
+#define OFFSET(x) offsetof(BinDemuxContext, x)
+static const AVOption options[] = {
+ { "linespeed", "set simulated line speed (bytes per second)", OFFSET(chars_per_frame), AV_OPT_TYPE_INT, {.i64 = 6000}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM},
+ { "video_size", "set video size, such as 640x480 or hd720.", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM },
+ { "framerate", "set framerate (frames per second)", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+#define CLASS(name) \
+(const AVClass[1]){{ \
+ .class_name = name, \
+ .item_name = av_default_item_name, \
+ .option = options, \
+ .version = LIBAVUTIL_VERSION_INT, \
+}}
+
+#if CONFIG_BINTEXT_DEMUXER
+AVInputFormat ff_bintext_demuxer = {
+ .name = "bin",
+ .long_name = NULL_IF_CONFIG_SMALL("Binary text"),
+ .priv_data_size = sizeof(BinDemuxContext),
+ .read_header = bintext_read_header,
+ .read_packet = read_packet,
+ .extensions = "bin",
+ .priv_class = CLASS("Binary text demuxer"),
+};
+#endif
+
+#if CONFIG_XBIN_DEMUXER
+AVInputFormat ff_xbin_demuxer = {
+ .name = "xbin",
+ .long_name = NULL_IF_CONFIG_SMALL("eXtended BINary text (XBIN)"),
+ .priv_data_size = sizeof(BinDemuxContext),
+ .read_probe = xbin_probe,
+ .read_header = xbin_read_header,
+ .read_packet = read_packet,
+ .priv_class = CLASS("eXtended BINary text (XBIN) demuxer"),
+};
+#endif
+
+#if CONFIG_ADF_DEMUXER
+AVInputFormat ff_adf_demuxer = {
+ .name = "adf",
+ .long_name = NULL_IF_CONFIG_SMALL("Artworx Data Format"),
+ .priv_data_size = sizeof(BinDemuxContext),
+ .read_header = adf_read_header,
+ .read_packet = read_packet,
+ .extensions = "adf",
+ .priv_class = CLASS("Artworx Data Format demuxer"),
+};
+#endif
+
+#if CONFIG_IDF_DEMUXER
+AVInputFormat ff_idf_demuxer = {
+ .name = "idf",
+ .long_name = NULL_IF_CONFIG_SMALL("iCE Draw File"),
+ .priv_data_size = sizeof(BinDemuxContext),
+ .read_probe = idf_probe,
+ .read_header = idf_read_header,
+ .read_packet = read_packet,
+ .extensions = "idf",
+ .priv_class = CLASS("iCE Draw File demuxer"),
+};
+#endif
diff --git a/libavformat/bit.c b/libavformat/bit.c
new file mode 100644
index 0000000000..7b807b9bc1
--- /dev/null
+++ b/libavformat/bit.c
@@ -0,0 +1,156 @@
+/*
+ * G.729 bit format muxer and demuxer
+ * Copyright (c) 2007-2008 Vladimir Voroshilov
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "avformat.h"
+#include "internal.h"
+#include "libavcodec/get_bits.h"
+#include "libavcodec/put_bits.h"
+
+#define MAX_FRAME_SIZE 10
+
+#define SYNC_WORD 0x6b21
+#define BIT_0 0x7f
+#define BIT_1 0x81
+
+static int probe(AVProbeData *p)
+{
+ int i, j;
+
+ if(p->buf_size < 0x40)
+ return 0;
+
+ for(i=0; i+3<p->buf_size && i< 10*0x50; ){
+ if(AV_RL16(&p->buf[0]) != SYNC_WORD)
+ return 0;
+ j=AV_RL16(&p->buf[2]);
+ if(j!=0x40 && j!=0x50)
+ return 0;
+ i+=j;
+ }
+ return AVPROBE_SCORE_EXTENSION;
+}
+
+static int read_header(AVFormatContext *s)
+{
+ AVStream* st;
+
+ st=avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->codec_id=AV_CODEC_ID_G729;
+ st->codec->sample_rate=8000;
+ st->codec->block_align = 16;
+ st->codec->channels=1;
+
+ avpriv_set_pts_info(st, 64, 1, 100);
+ return 0;
+}
+
+static int read_packet(AVFormatContext *s,
+ AVPacket *pkt)
+{
+ AVIOContext *pb = s->pb;
+ PutBitContext pbo;
+ uint16_t buf[8 * MAX_FRAME_SIZE + 2];
+ int packet_size;
+ uint16_t* src=buf;
+ int i, j, ret;
+ int64_t pos= avio_tell(pb);
+
+ if(avio_feof(pb))
+ return AVERROR_EOF;
+
+ avio_rl16(pb); // sync word
+ packet_size = avio_rl16(pb) / 8;
+ if(packet_size > MAX_FRAME_SIZE)
+ return AVERROR_INVALIDDATA;
+
+ ret = avio_read(pb, (uint8_t*)buf, (8 * packet_size) * sizeof(uint16_t));
+ if(ret<0)
+ return ret;
+ if(ret != 8 * packet_size * sizeof(uint16_t))
+ return AVERROR(EIO);
+
+ if (av_new_packet(pkt, packet_size) < 0)
+ return AVERROR(ENOMEM);
+
+ init_put_bits(&pbo, pkt->data, packet_size);
+ for(j=0; j < packet_size; j++)
+ for(i=0; i<8;i++)
+ put_bits(&pbo,1, AV_RL16(src++) == BIT_1 ? 1 : 0);
+
+ flush_put_bits(&pbo);
+
+ pkt->duration=1;
+ pkt->pos = pos;
+ return 0;
+}
+
+AVInputFormat ff_bit_demuxer = {
+ .name = "bit",
+ .long_name = NULL_IF_CONFIG_SMALL("G.729 BIT file format"),
+ .read_probe = probe,
+ .read_header = read_header,
+ .read_packet = read_packet,
+ .extensions = "bit",
+};
+
+#if CONFIG_MUXERS
+static int write_header(AVFormatContext *s)
+{
+ AVCodecContext *enc = s->streams[0]->codec;
+
+ enc->codec_id = AV_CODEC_ID_G729;
+ enc->channels = 1;
+ enc->bits_per_coded_sample = 16;
+ enc->block_align = (enc->bits_per_coded_sample * enc->channels) >> 3;
+
+ return 0;
+}
+
+static int write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVIOContext *pb = s->pb;
+ GetBitContext gb;
+ int i;
+
+ avio_wl16(pb, SYNC_WORD);
+ avio_wl16(pb, 8 * 10);
+
+ init_get_bits(&gb, pkt->data, 8*10);
+ for(i=0; i< 8 * 10; i++)
+ avio_wl16(pb, get_bits1(&gb) ? BIT_1 : BIT_0);
+
+ return 0;
+}
+
+AVOutputFormat ff_bit_muxer = {
+ .name = "bit",
+ .long_name = NULL_IF_CONFIG_SMALL("G.729 BIT file format"),
+ .mime_type = "audio/bit",
+ .extensions = "bit",
+ .audio_codec = AV_CODEC_ID_G729,
+ .video_codec = AV_CODEC_ID_NONE,
+ .write_header = write_header,
+ .write_packet = write_packet,
+};
+#endif
diff --git a/libavformat/bluray.c b/libavformat/bluray.c
new file mode 100644
index 0000000000..d2c57aab9c
--- /dev/null
+++ b/libavformat/bluray.c
@@ -0,0 +1,235 @@
+/*
+ * BluRay (libbluray) protocol
+ *
+ * Copyright (c) 2012 Petri Hintukainen <phintuka <at> users.sourceforge.net>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <libbluray/bluray.h>
+
+#include "libavutil/avstring.h"
+#include "libavformat/avformat.h"
+#include "libavformat/url.h"
+#include "libavutil/opt.h"
+
+#define BLURAY_PROTO_PREFIX "bluray:"
+#define MIN_PLAYLIST_LENGTH 180 /* 3 min */
+
+typedef struct {
+ const AVClass *class;
+
+ BLURAY *bd;
+
+ int playlist;
+ int angle;
+ int chapter;
+ /*int region;*/
+} BlurayContext;
+
+#define OFFSET(x) offsetof(BlurayContext, x)
+static const AVOption options[] = {
+{"playlist", "", OFFSET(playlist), AV_OPT_TYPE_INT, { .i64=-1 }, -1, 99999, AV_OPT_FLAG_DECODING_PARAM },
+{"angle", "", OFFSET(angle), AV_OPT_TYPE_INT, { .i64=0 }, 0, 0xfe, AV_OPT_FLAG_DECODING_PARAM },
+{"chapter", "", OFFSET(chapter), AV_OPT_TYPE_INT, { .i64=1 }, 1, 0xfffe, AV_OPT_FLAG_DECODING_PARAM },
+/*{"region", "bluray player region code (1 = region A, 2 = region B, 4 = region C)", OFFSET(region), AV_OPT_TYPE_INT, { .i64=0 }, 0, 3, AV_OPT_FLAG_DECODING_PARAM },*/
+{NULL}
+};
+
+static const AVClass bluray_context_class = {
+ .class_name = "bluray",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+
+static int check_disc_info(URLContext *h)
+{
+ BlurayContext *bd = h->priv_data;
+ const BLURAY_DISC_INFO *disc_info;
+
+ disc_info = bd_get_disc_info(bd->bd);
+ if (!disc_info) {
+ av_log(h, AV_LOG_ERROR, "bd_get_disc_info() failed\n");
+ return -1;
+ }
+
+ if (!disc_info->bluray_detected) {
+ av_log(h, AV_LOG_ERROR, "BluRay disc not detected\n");
+ return -1;
+ }
+
+ /* AACS */
+ if (disc_info->aacs_detected && !disc_info->aacs_handled) {
+ if (!disc_info->libaacs_detected) {
+ av_log(h, AV_LOG_ERROR,
+ "Media stream encrypted with AACS, install and configure libaacs\n");
+ } else {
+ av_log(h, AV_LOG_ERROR, "Your libaacs can't decrypt this media\n");
+ }
+ return -1;
+ }
+
+ /* BD+ */
+ if (disc_info->bdplus_detected && !disc_info->bdplus_handled) {
+ /*
+ if (!disc_info->libbdplus_detected) {
+ av_log(h, AV_LOG_ERROR,
+ "Media stream encrypted with BD+, install and configure libbdplus");
+ } else {
+ */
+ av_log(h, AV_LOG_ERROR, "Unable to decrypt BD+ encrypted media\n");
+ /*}*/
+ return -1;
+ }
+
+ return 0;
+}
+
+static int bluray_close(URLContext *h)
+{
+ BlurayContext *bd = h->priv_data;
+ if (bd->bd) {
+ bd_close(bd->bd);
+ }
+
+ return 0;
+}
+
+static int bluray_open(URLContext *h, const char *path, int flags)
+{
+ BlurayContext *bd = h->priv_data;
+ int num_title_idx;
+ const char *diskname = path;
+
+ av_strstart(path, BLURAY_PROTO_PREFIX, &diskname);
+
+ bd->bd = bd_open(diskname, NULL);
+ if (!bd->bd) {
+ av_log(h, AV_LOG_ERROR, "bd_open() failed\n");
+ return AVERROR(EIO);
+ }
+
+ /* check if disc can be played */
+ if (check_disc_info(h) < 0) {
+ return AVERROR(EIO);
+ }
+
+ /* setup player registers */
+ /* region code has no effect without menus
+ if (bd->region > 0 && bd->region < 5) {
+ av_log(h, AV_LOG_INFO, "setting region code to %d (%c)\n", bd->region, 'A' + (bd->region - 1));
+ bd_set_player_setting(bd->bd, BLURAY_PLAYER_SETTING_REGION_CODE, bd->region);
+ }
+ */
+
+ /* load title list */
+ num_title_idx = bd_get_titles(bd->bd, TITLES_RELEVANT, MIN_PLAYLIST_LENGTH);
+ av_log(h, AV_LOG_INFO, "%d usable playlists:\n", num_title_idx);
+ if (num_title_idx < 1) {
+ return AVERROR(EIO);
+ }
+
+ /* if playlist was not given, select longest playlist */
+ if (bd->playlist < 0) {
+ uint64_t duration = 0;
+ int i;
+ for (i = 0; i < num_title_idx; i++) {
+ BLURAY_TITLE_INFO *info = bd_get_title_info(bd->bd, i, 0);
+
+ av_log(h, AV_LOG_INFO, "playlist %05d.mpls (%d:%02d:%02d)\n",
+ info->playlist,
+ ((int)(info->duration / 90000) / 3600),
+ ((int)(info->duration / 90000) % 3600) / 60,
+ ((int)(info->duration / 90000) % 60));
+
+ if (info->duration > duration) {
+ bd->playlist = info->playlist;
+ duration = info->duration;
+ }
+
+ bd_free_title_info(info);
+ }
+ av_log(h, AV_LOG_INFO, "selected %05d.mpls\n", bd->playlist);
+ }
+
+ /* select playlist */
+ if (bd_select_playlist(bd->bd, bd->playlist) <= 0) {
+ av_log(h, AV_LOG_ERROR, "bd_select_playlist(%05d.mpls) failed\n", bd->playlist);
+ return AVERROR(EIO);
+ }
+
+ /* select angle */
+ if (bd->angle >= 0) {
+ bd_select_angle(bd->bd, bd->angle);
+ }
+
+ /* select chapter */
+ if (bd->chapter > 1) {
+ bd_seek_chapter(bd->bd, bd->chapter - 1);
+ }
+
+ return 0;
+}
+
+static int bluray_read(URLContext *h, unsigned char *buf, int size)
+{
+ BlurayContext *bd = h->priv_data;
+ int len;
+
+ if (!bd || !bd->bd) {
+ return AVERROR(EFAULT);
+ }
+
+ len = bd_read(bd->bd, buf, size);
+
+ return len;
+}
+
+static int64_t bluray_seek(URLContext *h, int64_t pos, int whence)
+{
+ BlurayContext *bd = h->priv_data;
+
+ if (!bd || !bd->bd) {
+ return AVERROR(EFAULT);
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ case SEEK_CUR:
+ case SEEK_END:
+ return bd_seek(bd->bd, pos);
+
+ case AVSEEK_SIZE:
+ return bd_get_title_size(bd->bd);
+ }
+
+ av_log(h, AV_LOG_ERROR, "Unsupported whence operation %d\n", whence);
+ return AVERROR(EINVAL);
+}
+
+
+URLProtocol ff_bluray_protocol = {
+ .name = "bluray",
+ .url_close = bluray_close,
+ .url_open = bluray_open,
+ .url_read = bluray_read,
+ .url_seek = bluray_seek,
+ .priv_data_size = sizeof(BlurayContext),
+ .priv_data_class = &bluray_context_class,
+};
diff --git a/libavformat/bmv.c b/libavformat/bmv.c
index b5572aff37..f7a6068af3 100644
--- a/libavformat/bmv.c
+++ b/libavformat/bmv.c
@@ -2,20 +2,20 @@
* Discworld II BMV demuxer
* Copyright (c) 2011 Konstantin Shishkov.
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/boadec.c b/libavformat/boadec.c
new file mode 100644
index 0000000000..be003e59c1
--- /dev/null
+++ b/libavformat/boadec.c
@@ -0,0 +1,79 @@
+/*
+ * Black ops audio demuxer
+ * Copyright (c) 2013 Michael Niedermayer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "internal.h"
+
+static int probe(AVProbeData *p)
+{
+ if (p->buf_size < 2096)
+ return 0;
+ if ( AV_RL32(p->buf ) != 1
+ || AV_RL32(p->buf + 8) > 100000
+ || AV_RL32(p->buf + 12) > 8
+ || AV_RL32(p->buf + 16) != 2096
+ ||!AV_RL32(p->buf + 21)
+ || AV_RL16(p->buf + 25) != 2096
+ || AV_RL32(p->buf + 48) % AV_RL32(p->buf + 21)
+ )
+ return 0;
+ return AVPROBE_SCORE_EXTENSION;
+}
+
+
+static int read_header(AVFormatContext *s)
+{
+ AVStream *st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->codec_id = AV_CODEC_ID_ADPCM_MS;
+
+ avio_rl32(s->pb);
+ avio_rl32(s->pb);
+ st->codec->sample_rate = avio_rl32(s->pb);
+ st->codec->channels = avio_rl32(s->pb);
+ s->internal->data_offset = avio_rl32(s->pb);
+ avio_r8(s->pb);
+ st->codec->block_align = st->codec->channels * avio_rl32(s->pb);
+
+ avio_seek(s->pb, s->internal->data_offset, SEEK_SET);
+
+ return 0;
+}
+
+static int read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVStream *st = s->streams[0];
+
+ return av_get_packet(s->pb, pkt, st->codec->block_align);
+}
+
+AVInputFormat ff_boa_demuxer = {
+ .name = "boa",
+ .long_name = NULL_IF_CONFIG_SMALL("Black Ops Audio"),
+ .read_probe = probe,
+ .read_header = read_header,
+ .read_packet = read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/brstm.c b/libavformat/brstm.c
new file mode 100644
index 0000000000..19a4a2a96b
--- /dev/null
+++ b/libavformat/brstm.c
@@ -0,0 +1,297 @@
+/*
+ * BRSTM demuxer
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "libavcodec/bytestream.h"
+#include "avformat.h"
+#include "internal.h"
+
+typedef struct BRSTMDemuxContext {
+ uint32_t block_size;
+ uint32_t block_count;
+ uint32_t current_block;
+ uint32_t samples_per_block;
+ uint32_t last_block_used_bytes;
+ uint8_t *table;
+ uint8_t *adpc;
+} BRSTMDemuxContext;
+
+static int probe(AVProbeData *p)
+{
+ if (AV_RL32(p->buf) == MKTAG('R','S','T','M') &&
+ (AV_RL16(p->buf + 4) == 0xFFFE ||
+ AV_RL16(p->buf + 4) == 0xFEFF))
+ return AVPROBE_SCORE_MAX / 3 * 2;
+ return 0;
+}
+
+static int read_close(AVFormatContext *s)
+{
+ BRSTMDemuxContext *b = s->priv_data;
+
+ av_freep(&b->table);
+ av_freep(&b->adpc);
+
+ return 0;
+}
+
+static int read_header(AVFormatContext *s)
+{
+ BRSTMDemuxContext *b = s->priv_data;
+ int bom, major, minor, codec, chunk;
+ int64_t pos, h1offset, toffset;
+ uint32_t size, start, asize;
+ AVStream *st;
+ int ret = AVERROR_EOF;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+
+ avio_skip(s->pb, 4);
+
+ bom = avio_rb16(s->pb);
+ if (bom != 0xFEFF && bom != 0xFFFE) {
+ av_log(s, AV_LOG_ERROR, "invalid byte order: %X\n", bom);
+ return AVERROR_INVALIDDATA;
+ }
+ if (bom == 0xFFFE) {
+ avpriv_request_sample(s, "little endian byte order");
+ return AVERROR_PATCHWELCOME;
+ }
+
+ major = avio_r8(s->pb);
+ minor = avio_r8(s->pb);
+ avio_skip(s->pb, 4); // size of file
+ size = avio_rb16(s->pb);
+ if (size < 14)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(s->pb, size - 14);
+ pos = avio_tell(s->pb);
+ if (avio_rl32(s->pb) != MKTAG('H','E','A','D'))
+ return AVERROR_INVALIDDATA;
+ size = avio_rb32(s->pb);
+ if (size < 256)
+ return AVERROR_INVALIDDATA;
+ avio_skip(s->pb, 4); // unknown
+ h1offset = avio_rb32(s->pb);
+ if (h1offset > size)
+ return AVERROR_INVALIDDATA;
+ avio_skip(s->pb, 12);
+ toffset = avio_rb32(s->pb) + 16LL;
+ if (toffset > size)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(s->pb, pos + h1offset + 8 - avio_tell(s->pb));
+ codec = avio_r8(s->pb);
+
+ switch (codec) {
+ case 0: codec = AV_CODEC_ID_PCM_S8_PLANAR; break;
+ case 1: codec = AV_CODEC_ID_PCM_S16BE_PLANAR; break;
+ case 2: codec = AV_CODEC_ID_ADPCM_THP; break;
+ default:
+ avpriv_request_sample(s, "codec %d", codec);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ avio_skip(s->pb, 1); // loop flag
+ st->codec->codec_id = codec;
+ st->codec->channels = avio_r8(s->pb);
+ if (!st->codec->channels)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(s->pb, 1); // padding
+ st->codec->sample_rate = avio_rb16(s->pb);
+ if (!st->codec->sample_rate)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(s->pb, 2); // padding
+ avio_skip(s->pb, 4); // loop start sample
+ st->start_time = 0;
+ st->duration = avio_rb32(s->pb);
+ avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+
+ start = avio_rb32(s->pb);
+ b->current_block = 0;
+ b->block_count = avio_rb32(s->pb);
+ if (b->block_count > UINT16_MAX) {
+ av_log(s, AV_LOG_WARNING, "too many blocks: %u\n", b->block_count);
+ return AVERROR_INVALIDDATA;
+ }
+
+ b->block_size = avio_rb32(s->pb);
+ if (b->block_size > UINT16_MAX / st->codec->channels)
+ return AVERROR_INVALIDDATA;
+ b->block_size *= st->codec->channels;
+
+ b->samples_per_block = avio_rb32(s->pb);
+ b->last_block_used_bytes = avio_rb32(s->pb);
+ if (b->last_block_used_bytes > UINT16_MAX / st->codec->channels)
+ return AVERROR_INVALIDDATA;
+ b->last_block_used_bytes *= st->codec->channels;
+
+ avio_skip(s->pb, 4); // last block samples
+ avio_skip(s->pb, 4); // last block size
+
+ if (codec == AV_CODEC_ID_ADPCM_THP) {
+ int ch;
+
+ avio_skip(s->pb, pos + toffset - avio_tell(s->pb));
+ toffset = avio_rb32(s->pb) + 16LL;
+ if (toffset > size)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(s->pb, pos + toffset - avio_tell(s->pb));
+ b->table = av_mallocz(32 * st->codec->channels);
+ if (!b->table)
+ return AVERROR(ENOMEM);
+
+ for (ch = 0; ch < st->codec->channels; ch++) {
+ if (avio_read(s->pb, b->table + ch * 32, 32) != 32) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ avio_skip(s->pb, 24);
+ }
+ }
+
+ if (size < (avio_tell(s->pb) - pos)) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ avio_skip(s->pb, size - (avio_tell(s->pb) - pos));
+
+ while (!avio_feof(s->pb)) {
+ chunk = avio_rl32(s->pb);
+ size = avio_rb32(s->pb);
+ if (size < 8) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ size -= 8;
+ switch (chunk) {
+ case MKTAG('A','D','P','C'):
+ if (codec != AV_CODEC_ID_ADPCM_THP)
+ goto skip;
+
+ asize = b->block_count * st->codec->channels * 4;
+ if (size < asize) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ if (b->adpc) {
+ av_log(s, AV_LOG_WARNING, "skipping additional ADPC chunk\n");
+ goto skip;
+ } else {
+ b->adpc = av_mallocz(asize);
+ if (!b->adpc) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ avio_read(s->pb, b->adpc, asize);
+ avio_skip(s->pb, size - asize);
+ }
+ break;
+ case MKTAG('D','A','T','A'):
+ if ((start < avio_tell(s->pb)) ||
+ (!b->adpc && codec == AV_CODEC_ID_ADPCM_THP)) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ avio_skip(s->pb, start - avio_tell(s->pb));
+
+ if (major != 1 || minor)
+ avpriv_request_sample(s, "Version %d.%d", major, minor);
+
+ return 0;
+ default:
+ av_log(s, AV_LOG_WARNING, "skipping unknown chunk: %X\n", chunk);
+skip:
+ avio_skip(s->pb, size);
+ }
+ }
+
+fail:
+ read_close(s);
+
+ return ret;
+}
+
+static int read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecContext *codec = s->streams[0]->codec;
+ BRSTMDemuxContext *b = s->priv_data;
+ uint32_t samples, size;
+ int ret;
+
+ if (avio_feof(s->pb))
+ return AVERROR_EOF;
+ b->current_block++;
+ if (b->current_block == b->block_count) {
+ size = b->last_block_used_bytes;
+ samples = size / (8 * codec->channels) * 14;
+ } else if (b->current_block < b->block_count) {
+ size = b->block_size;
+ samples = b->samples_per_block;
+ } else {
+ return AVERROR_EOF;
+ }
+
+ if (codec->codec_id == AV_CODEC_ID_ADPCM_THP) {
+ uint8_t *dst;
+
+ if (av_new_packet(pkt, 8 + (32 + 4) * codec->channels + size) < 0)
+ return AVERROR(ENOMEM);
+ dst = pkt->data;
+ bytestream_put_be32(&dst, size);
+ bytestream_put_be32(&dst, samples);
+ bytestream_put_buffer(&dst, b->table, 32 * codec->channels);
+ bytestream_put_buffer(&dst, b->adpc + 4 * codec->channels *
+ (b->current_block - 1), 4 * codec->channels);
+
+ ret = avio_read(s->pb, dst, size);
+ if (ret != size)
+ av_free_packet(pkt);
+ pkt->duration = samples;
+ } else {
+ ret = av_get_packet(s->pb, pkt, size);
+ }
+
+ pkt->stream_index = 0;
+
+ if (ret != size)
+ ret = AVERROR(EIO);
+
+ return ret;
+}
+
+AVInputFormat ff_brstm_demuxer = {
+ .name = "brstm",
+ .long_name = NULL_IF_CONFIG_SMALL("BRSTM (Binary Revolution Stream)"),
+ .priv_data_size = sizeof(BRSTMDemuxContext),
+ .read_probe = probe,
+ .read_header = read_header,
+ .read_packet = read_packet,
+ .read_close = read_close,
+ .extensions = "brstm",
+};
diff --git a/libavformat/c93.c b/libavformat/c93.c
index fdb7a87a2a..d67afcfb4b 100644
--- a/libavformat/c93.c
+++ b/libavformat/c93.c
@@ -2,20 +2,20 @@
* Interplay C93 demuxer
* Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -133,7 +133,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt)
}
if (c93->current_frame >= br->frames) {
if (c93->current_block >= 511 || !br[1].length)
- return AVERROR(EIO);
+ return AVERROR_EOF;
br++;
c93->current_block++;
c93->current_frame = 0;
diff --git a/libavformat/cache.c b/libavformat/cache.c
new file mode 100644
index 0000000000..26e68c5130
--- /dev/null
+++ b/libavformat/cache.c
@@ -0,0 +1,322 @@
+/*
+ * Input cache protocol.
+ * Copyright (c) 2011,2014 Michael Niedermayer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Based on file.c by Fabrice Bellard
+ */
+
+/**
+ * @TODO
+ * support keeping files
+ * support filling with a background thread
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/file.h"
+#include "libavutil/opt.h"
+#include "libavutil/tree.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 CacheEntry {
+ int64_t logical_pos;
+ int64_t physical_pos;
+ int size;
+} CacheEntry;
+
+typedef struct Context {
+ AVClass *class;
+ int fd;
+ struct AVTreeNode *root;
+ int64_t logical_pos;
+ int64_t cache_pos;
+ int64_t inner_pos;
+ int64_t end;
+ int is_true_eof;
+ URLContext *inner;
+ int64_t cache_hit, cache_miss;
+ int read_ahead_limit;
+} Context;
+
+static int cmp(void *key, const void *node)
+{
+ return (*(int64_t *) key) - ((const CacheEntry *) node)->logical_pos;
+}
+
+static int cache_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
+{
+ 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, options);
+}
+
+static int add_entry(URLContext *h, const unsigned char *buf, int size)
+{
+ Context *c= h->priv_data;
+ int64_t pos = -1;
+ int ret;
+ CacheEntry *entry = NULL, *next[2] = {NULL, NULL};
+ CacheEntry *entry_ret;
+ struct AVTreeNode *node = NULL;
+
+ //FIXME avoid lseek
+ pos = lseek(c->fd, 0, SEEK_END);
+ if (pos < 0) {
+ ret = AVERROR(errno);
+ av_log(h, AV_LOG_ERROR, "seek in cache failed\n");
+ goto fail;
+ }
+ c->cache_pos = pos;
+
+ ret = write(c->fd, buf, size);
+ if (ret < 0) {
+ ret = AVERROR(errno);
+ av_log(h, AV_LOG_ERROR, "write in cache failed\n");
+ goto fail;
+ }
+ c->cache_pos += ret;
+
+ entry = av_tree_find(c->root, &c->logical_pos, cmp, (void**)next);
+
+ if (!entry)
+ entry = next[0];
+
+ if (!entry ||
+ entry->logical_pos + entry->size != c->logical_pos ||
+ entry->physical_pos + entry->size != pos
+ ) {
+ entry = av_malloc(sizeof(*entry));
+ node = av_tree_node_alloc();
+ if (!entry || !node) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ entry->logical_pos = c->logical_pos;
+ entry->physical_pos = pos;
+ entry->size = ret;
+
+ entry_ret = av_tree_insert(&c->root, entry, cmp, &node);
+ if (entry_ret && entry_ret != entry) {
+ ret = -1;
+ av_log(h, AV_LOG_ERROR, "av_tree_insert failed\n");
+ goto fail;
+ }
+ } else
+ entry->size += ret;
+
+ return 0;
+fail:
+ //we could truncate the file to pos here if pos >=0 but ftruncate isnt available in VS so
+ //for simplicty we just leave the file a bit larger
+ av_free(entry);
+ av_free(node);
+ return ret;
+}
+
+static int cache_read(URLContext *h, unsigned char *buf, int size)
+{
+ Context *c= h->priv_data;
+ CacheEntry *entry, *next[2] = {NULL, NULL};
+ int r;
+
+ entry = av_tree_find(c->root, &c->logical_pos, cmp, (void**)next);
+
+ if (!entry)
+ entry = next[0];
+
+ if (entry) {
+ int64_t in_block_pos = c->logical_pos - entry->logical_pos;
+ av_assert0(entry->logical_pos <= c->logical_pos);
+ if (in_block_pos < entry->size) {
+ int64_t physical_target = entry->physical_pos + in_block_pos;
+
+ if (c->cache_pos != physical_target) {
+ r = lseek(c->fd, physical_target, SEEK_SET);
+ } else
+ r = c->cache_pos;
+
+ if (r >= 0) {
+ c->cache_pos = r;
+ r = read(c->fd, buf, FFMIN(size, entry->size - in_block_pos));
+ }
+
+ if (r > 0) {
+ c->cache_pos += r;
+ c->logical_pos += r;
+ c->cache_hit ++;
+ return r;
+ }
+ }
+ }
+
+ // Cache miss or some kind of fault with the cache
+
+ if (c->logical_pos != c->inner_pos) {
+ r = ffurl_seek(c->inner, c->logical_pos, SEEK_SET);
+ if (r<0) {
+ av_log(h, AV_LOG_ERROR, "Failed to perform internal seek\n");
+ return r;
+ }
+ c->inner_pos = r;
+ }
+
+ r = ffurl_read(c->inner, buf, size);
+ if (r == 0 && size>0) {
+ c->is_true_eof = 1;
+ av_assert0(c->end >= c->logical_pos);
+ }
+ if (r<=0)
+ return r;
+ c->inner_pos += r;
+
+ c->cache_miss ++;
+
+ add_entry(h, buf, r);
+ c->logical_pos += r;
+ c->end = FFMAX(c->end, c->logical_pos);
+
+ return r;
+}
+
+static int64_t cache_seek(URLContext *h, int64_t pos, int whence)
+{
+ Context *c= h->priv_data;
+ int64_t ret;
+
+ if (whence == AVSEEK_SIZE) {
+ pos= ffurl_seek(c->inner, pos, whence);
+ if(pos <= 0){
+ pos= ffurl_seek(c->inner, -1, SEEK_END);
+ if (ffurl_seek(c->inner, c->inner_pos, SEEK_SET) < 0)
+ av_log(h, AV_LOG_ERROR, "Inner protocol failed to seekback end : %"PRId64"\n", pos);
+ }
+ if (pos > 0)
+ c->is_true_eof = 1;
+ c->end = FFMAX(c->end, pos);
+ return pos;
+ }
+
+ if (whence == SEEK_CUR) {
+ whence = SEEK_SET;
+ pos += c->logical_pos;
+ } else if (whence == SEEK_END && c->is_true_eof) {
+resolve_eof:
+ whence = SEEK_SET;
+ pos += c->end;
+ }
+
+ if (whence == SEEK_SET && pos >= 0 && pos < c->end) {
+ //Seems within filesize, assume it will not fail.
+ c->logical_pos = pos;
+ return pos;
+ }
+
+ //cache miss
+ ret= ffurl_seek(c->inner, pos, whence);
+ if ((whence == SEEK_SET && pos >= c->logical_pos ||
+ whence == SEEK_END && pos <= 0) && ret < 0) {
+ if ( (whence == SEEK_SET && c->read_ahead_limit >= pos - c->logical_pos)
+ || c->read_ahead_limit < 0) {
+ uint8_t tmp[32768];
+ while (c->logical_pos < pos || whence == SEEK_END) {
+ int size = sizeof(tmp);
+ if (whence == SEEK_SET)
+ size = FFMIN(sizeof(tmp), pos - c->logical_pos);
+ ret = cache_read(h, tmp, size);
+ if (ret == 0 && whence == SEEK_END) {
+ av_assert0(c->is_true_eof);
+ goto resolve_eof;
+ }
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ return c->logical_pos;
+ }
+ }
+
+ if (ret >= 0) {
+ c->logical_pos = ret;
+ c->end = FFMAX(c->end, ret);
+ }
+
+ return ret;
+}
+
+static int cache_close(URLContext *h)
+{
+ Context *c= h->priv_data;
+
+ av_log(h, AV_LOG_INFO, "Statistics, cache hits:%"PRId64" cache misses:%"PRId64"\n",
+ c->cache_hit, c->cache_miss);
+
+ close(c->fd);
+ ffurl_close(c->inner);
+ av_tree_destroy(c->root);
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(Context, x)
+#define D AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption options[] = {
+ { "read_ahead_limit", "Amount in bytes that may be read ahead when seeking isnt supported, -1 for unlimited", OFFSET(read_ahead_limit), AV_OPT_TYPE_INT, { .i64 = 65536 }, -1, INT_MAX, D },
+ {NULL},
+};
+
+static const AVClass cache_context_class = {
+ .class_name = "Cache",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+URLProtocol ff_cache_protocol = {
+ .name = "cache",
+ .url_open2 = cache_open,
+ .url_read = cache_read,
+ .url_seek = cache_seek,
+ .url_close = cache_close,
+ .priv_data_size = sizeof(Context),
+ .priv_data_class = &cache_context_class,
+};
diff --git a/libavformat/caf.c b/libavformat/caf.c
index cf128d5624..c1ecc94425 100644
--- a/libavformat/caf.c
+++ b/libavformat/caf.c
@@ -2,20 +2,20 @@
* CAF common code
* Copyright (c) 2007 Justin Ruggles
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -32,27 +32,35 @@
* Known codec tags for CAF
*/
const AVCodecTag ff_codec_caf_tags[] = {
- { AV_CODEC_ID_AAC, MKBETAG('a','a','c',' ') },
- { AV_CODEC_ID_AC3, MKBETAG('a','c','-','3') },
- { AV_CODEC_ID_ALAC, MKBETAG('a','l','a','c') },
+ { AV_CODEC_ID_AAC, MKTAG('a','a','c',' ') },
+ { AV_CODEC_ID_AC3, MKTAG('a','c','-','3') },
+ { AV_CODEC_ID_ADPCM_IMA_QT, MKTAG('i','m','a','4') },
+ { AV_CODEC_ID_ADPCM_IMA_WAV, MKTAG('m','s', 0, 17 ) },
+ { AV_CODEC_ID_ADPCM_MS, MKTAG('m','s', 0, 2 ) },
+ { AV_CODEC_ID_ALAC, MKTAG('a','l','a','c') },
+ { AV_CODEC_ID_AMR_NB, MKTAG('s','a','m','r') },
/* FIXME: use DV demuxer, as done in MOV */
- /*{ AV_CODEC_ID_DVAUDIO, MKBETAG('v','d','v','a') },*/
- /*{ AV_CODEC_ID_DVAUDIO, MKBETAG('d','v','c','a') },*/
- { AV_CODEC_ID_ADPCM_IMA_QT, MKBETAG('i','m','a','4') },
- { AV_CODEC_ID_MACE3, MKBETAG('M','A','C','3') },
- { AV_CODEC_ID_MACE6, MKBETAG('M','A','C','6') },
- { AV_CODEC_ID_MP3, MKBETAG('.','m','p','3') },
- { AV_CODEC_ID_MP2, MKBETAG('.','m','p','2') },
- { AV_CODEC_ID_MP1, MKBETAG('.','m','p','1') },
- { AV_CODEC_ID_PCM_ALAW, MKBETAG('a','l','a','w') },
- { AV_CODEC_ID_PCM_MULAW, MKBETAG('u','l','a','w') },
- { AV_CODEC_ID_QCELP, MKBETAG('Q','c','l','p') },
- { AV_CODEC_ID_QDM2, MKBETAG('Q','D','M','2') },
- { AV_CODEC_ID_QDM2, MKBETAG('Q','D','M','C') },
+ /*{ AV_CODEC_ID_DVAUDIO, MKTAG('v','d','v','a') },*/
+ /*{ AV_CODEC_ID_DVAUDIO, MKTAG('d','v','c','a') },*/
+ { AV_CODEC_ID_GSM, MKTAG('a','g','s','m') },
+ { AV_CODEC_ID_GSM_MS, MKTAG('m','s', 0, '1') },
+ { AV_CODEC_ID_ILBC, MKTAG('i','l','b','c') },
+ { AV_CODEC_ID_MACE3, MKTAG('M','A','C','3') },
+ { AV_CODEC_ID_MACE6, MKTAG('M','A','C','6') },
+ { AV_CODEC_ID_MP1, MKTAG('.','m','p','1') },
+ { AV_CODEC_ID_MP2, MKTAG('.','m','p','2') },
+ { AV_CODEC_ID_MP3, MKTAG('.','m','p','3') },
+ { AV_CODEC_ID_MP3, MKTAG('m','s', 0 ,'U') },
+ { AV_CODEC_ID_PCM_ALAW, MKTAG('a','l','a','w') },
+ { AV_CODEC_ID_PCM_MULAW, MKTAG('u','l','a','w') },
+ { AV_CODEC_ID_QCELP, MKTAG('Q','c','l','p') },
+ { AV_CODEC_ID_QDM2, MKTAG('Q','D','M','2') },
+ { AV_CODEC_ID_QDM2, MKTAG('Q','D','M','C') },
/* currently unsupported codecs */
- /*{ AC-3 over S/PDIF MKBETAG('c','a','c','3') },*/
- /*{ MPEG4CELP MKBETAG('c','e','l','p') },*/
- /*{ MPEG4HVXC MKBETAG('h','v','x','c') },*/
- /*{ MPEG4TwinVQ MKBETAG('t','w','v','q') },*/
+ /*{ AC-3 over S/PDIF MKTAG('c','a','c','3') },*/
+ /*{ MPEG4CELP MKTAG('c','e','l','p') },*/
+ /*{ MPEG4HVXC MKTAG('h','v','x','c') },*/
+ /*{ MPEG4TwinVQ MKTAG('t','w','v','q') },*/
{ AV_CODEC_ID_NONE, 0 },
};
+
diff --git a/libavformat/caf.h b/libavformat/caf.h
index 7ca4dc5c66..9c25f2c683 100644
--- a/libavformat/caf.h
+++ b/libavformat/caf.h
@@ -2,20 +2,20 @@
* CAF common code
* Copyright (c) 2007 Justin Ruggles
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/cafdec.c b/libavformat/cafdec.c
index a37ceaaebd..e31c0a52bc 100644
--- a/libavformat/cafdec.c
+++ b/libavformat/cafdec.c
@@ -3,20 +3,20 @@
* Copyright (c) 2007 Justin Ruggles
* Copyright (c) 2009 Peter Ross
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -71,7 +71,7 @@ static int read_desc_chunk(AVFormatContext *s)
/* parse format description */
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->sample_rate = av_int2double(avio_rb64(pb));
- st->codec->codec_tag = avio_rb32(pb);
+ st->codec->codec_tag = avio_rl32(pb);
flags = avio_rb32(pb);
caf->bytes_per_packet = avio_rb32(pb);
st->codec->block_align = caf->bytes_per_packet;
@@ -88,7 +88,7 @@ static int read_desc_chunk(AVFormatContext *s)
}
/* determine codec */
- if (st->codec->codec_tag == MKBETAG('l','p','c','m'))
+ if (st->codec->codec_tag == MKTAG('l','p','c','m'))
st->codec->codec_id = ff_mov_get_lpcm_codec_id(st->codec->bits_per_coded_sample, (flags ^ 0x2) | 0x4);
else
st->codec->codec_id = ff_codec_get_id(ff_codec_caf_tags, st->codec->codec_tag);
@@ -131,8 +131,7 @@ static int read_kuki_chunk(AVFormatContext *s, int64_t size)
}
avio_read(pb, preamble, ALAC_PREAMBLE);
- st->codec->extradata = av_mallocz(ALAC_HEADER + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
+ if (ff_alloc_extradata(st->codec, ALAC_HEADER))
return AVERROR(ENOMEM);
/* For the old style cookie, we skip 12 bytes, then read 36 bytes.
@@ -155,13 +154,9 @@ static int read_kuki_chunk(AVFormatContext *s, int64_t size)
avio_read(pb, &st->codec->extradata[24], ALAC_NEW_KUKI - 12);
avio_skip(pb, size - ALAC_NEW_KUKI);
}
- st->codec->extradata_size = ALAC_HEADER;
} else {
- st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
+ if (ff_get_extradata(st->codec, pb, size) < 0)
return AVERROR(ENOMEM);
- avio_read(pb, st->codec->extradata, size);
- st->codec->extradata_size = size;
}
return 0;
@@ -209,10 +204,10 @@ static void read_info_chunk(AVFormatContext *s, int64_t size)
AVIOContext *pb = s->pb;
unsigned int i;
unsigned int nb_entries = avio_rb32(pb);
- for (i = 0; i < nb_entries; i++) {
+ for (i = 0; i < nb_entries && !avio_feof(pb); i++) {
char key[32];
char value[1024];
- avio_get_str(pb, INT_MAX, key, sizeof(key));
+ avio_get_str(pb, INT_MAX, key, sizeof(key));
avio_get_str(pb, INT_MAX, value, sizeof(value));
av_dict_set(&s->metadata, key, value, 0);
}
@@ -225,7 +220,7 @@ static int read_header(AVFormatContext *s)
AVStream *st;
uint32_t tag = 0;
int found_data, ret;
- int64_t size;
+ int64_t size, pos;
avio_skip(pb, 8); /* magic, version, file flags */
@@ -245,7 +240,7 @@ static int read_header(AVFormatContext *s)
/* parse each chunk */
found_data = 0;
- while (!pb->eof_reached) {
+ while (!avio_feof(pb)) {
/* stop at data chunk if seeking is not supported or
data chunk size is unknown */
@@ -254,7 +249,8 @@ static int read_header(AVFormatContext *s)
tag = avio_rb32(pb);
size = avio_rb64(pb);
- if (pb->eof_reached)
+ pos = avio_tell(pb);
+ if (avio_feof(pb))
break;
switch (tag) {
@@ -291,15 +287,20 @@ static int read_header(AVFormatContext *s)
default:
#define _(x) ((x) >= ' ' ? (x) : ' ')
av_log(s, AV_LOG_WARNING,
- "skipping CAF chunk: %08"PRIX32" (%"PRIu32"%"PRIu32"%"PRIu32"%"PRIu32")\n",
- tag, _(tag>>24), _((tag>>16)&0xFF), _((tag>>8)&0xFF), _(tag&0xFF));
+ "skipping CAF chunk: %08"PRIX32" (%c%c%c%c), size %"PRId64"\n",
+ tag, _(tag>>24), _((tag>>16)&0xFF), _((tag>>8)&0xFF), _(tag&0xFF), size);
#undef _
case MKBETAG('f','r','e','e'):
if (size < 0)
return AVERROR_INVALIDDATA;
- avio_skip(pb, size);
break;
}
+
+ if (size > 0) {
+ if (pos > INT64_MAX - size)
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, FFMAX(0, pos + size - avio_tell(pb)));
+ }
}
if (!found_data)
@@ -308,7 +309,7 @@ static int read_header(AVFormatContext *s)
if (caf->bytes_per_packet > 0 && caf->frames_per_packet > 0) {
if (caf->data_size > 0)
st->nb_frames = (caf->data_size / caf->bytes_per_packet) * caf->frames_per_packet;
- } else if (st->nb_index_entries) {
+ } else if (st->nb_index_entries && st->duration > 0) {
st->codec->bit_rate = st->codec->sample_rate * caf->data_size * 8 /
st->duration;
} else {
@@ -337,13 +338,15 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt)
int res, pkt_size = 0, pkt_frames = 0;
int64_t left = CAF_MAX_PKT_SIZE;
- if (pb->eof_reached)
- return AVERROR(EIO);
+ if (avio_feof(pb))
+ return AVERROR_EOF;
/* don't read past end of data chunk */
if (caf->data_size > 0) {
left = (caf->data_start + caf->data_size) - avio_tell(pb);
- if (left <= 0)
+ if (!left)
+ return AVERROR_EOF;
+ if (left < 0)
return AVERROR(EIO);
}
@@ -394,7 +397,7 @@ static int read_seek(AVFormatContext *s, int stream_index,
if (caf->frames_per_packet > 0 && caf->bytes_per_packet > 0) {
/* calculate new byte position based on target frame position */
- pos = caf->bytes_per_packet * timestamp / caf->frames_per_packet;
+ pos = caf->bytes_per_packet * (timestamp / caf->frames_per_packet);
if (caf->data_size > 0)
pos = FFMIN(pos, caf->data_size);
packet_cnt = pos / caf->bytes_per_packet;
diff --git a/libavformat/cafenc.c b/libavformat/cafenc.c
new file mode 100644
index 0000000000..1708275e11
--- /dev/null
+++ b/libavformat/cafenc.c
@@ -0,0 +1,286 @@
+/*
+ * Core Audio Format muxer
+ * Copyright (c) 2011 Carl Eugen Hoyos
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "caf.h"
+#include "isom.h"
+#include "avio_internal.h"
+#include "libavutil/intfloat.h"
+#include "libavutil/dict.h"
+
+typedef struct {
+ int64_t data;
+ uint8_t *pkt_sizes;
+ int size_buffer_size;
+ int size_entries_used;
+ int packets;
+} CAFContext;
+
+static uint32_t codec_flags(enum AVCodecID codec_id) {
+ switch (codec_id) {
+ case AV_CODEC_ID_PCM_F32BE:
+ case AV_CODEC_ID_PCM_F64BE:
+ return 1; //< kCAFLinearPCMFormatFlagIsFloat
+ case AV_CODEC_ID_PCM_S16LE:
+ case AV_CODEC_ID_PCM_S24LE:
+ case AV_CODEC_ID_PCM_S32LE:
+ return 2; //< kCAFLinearPCMFormatFlagIsLittleEndian
+ case AV_CODEC_ID_PCM_F32LE:
+ case AV_CODEC_ID_PCM_F64LE:
+ return 3; //< kCAFLinearPCMFormatFlagIsFloat | kCAFLinearPCMFormatFlagIsLittleEndian
+ default:
+ return 0;
+ }
+}
+
+static uint32_t samples_per_packet(enum AVCodecID codec_id, int channels, int block_align) {
+ switch (codec_id) {
+ case AV_CODEC_ID_PCM_S8:
+ case AV_CODEC_ID_PCM_S16LE:
+ case AV_CODEC_ID_PCM_S16BE:
+ case AV_CODEC_ID_PCM_S24LE:
+ case AV_CODEC_ID_PCM_S24BE:
+ case AV_CODEC_ID_PCM_S32LE:
+ case AV_CODEC_ID_PCM_S32BE:
+ case AV_CODEC_ID_PCM_F32LE:
+ case AV_CODEC_ID_PCM_F32BE:
+ case AV_CODEC_ID_PCM_F64LE:
+ case AV_CODEC_ID_PCM_F64BE:
+ case AV_CODEC_ID_PCM_ALAW:
+ case AV_CODEC_ID_PCM_MULAW:
+ return 1;
+ case AV_CODEC_ID_MACE3:
+ case AV_CODEC_ID_MACE6:
+ return 6;
+ case AV_CODEC_ID_ADPCM_IMA_QT:
+ return 64;
+ case AV_CODEC_ID_AMR_NB:
+ case AV_CODEC_ID_GSM:
+ case AV_CODEC_ID_ILBC:
+ case AV_CODEC_ID_QCELP:
+ return 160;
+ case AV_CODEC_ID_GSM_MS:
+ return 320;
+ case AV_CODEC_ID_MP1:
+ return 384;
+ case AV_CODEC_ID_MP2:
+ case AV_CODEC_ID_MP3:
+ return 1152;
+ case AV_CODEC_ID_AC3:
+ return 1536;
+ case AV_CODEC_ID_QDM2:
+ return 2048 * channels;
+ case AV_CODEC_ID_ALAC:
+ return 4096;
+ case AV_CODEC_ID_ADPCM_IMA_WAV:
+ return (block_align - 4 * channels) * 8 / (4 * channels) + 1;
+ case AV_CODEC_ID_ADPCM_MS:
+ return (block_align - 7 * channels) * 2 / channels + 2;
+ default:
+ return 0;
+ }
+}
+
+static int caf_write_header(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ AVCodecContext *enc = s->streams[0]->codec;
+ CAFContext *caf = s->priv_data;
+ AVDictionaryEntry *t = NULL;
+ unsigned int codec_tag = ff_codec_get_tag(ff_codec_caf_tags, enc->codec_id);
+ int64_t chunk_size = 0;
+ int frame_size = enc->frame_size;
+
+ if (s->nb_streams != 1) {
+ av_log(s, AV_LOG_ERROR, "CAF files have exactly one stream\n");
+ return AVERROR(EINVAL);
+ }
+
+ switch (enc->codec_id) {
+ case AV_CODEC_ID_AAC:
+ av_log(s, AV_LOG_ERROR, "muxing codec currently unsupported\n");
+ return AVERROR_PATCHWELCOME;
+ }
+
+ switch (enc->codec_id) {
+ case AV_CODEC_ID_PCM_S8:
+ case AV_CODEC_ID_PCM_S16LE:
+ case AV_CODEC_ID_PCM_S16BE:
+ case AV_CODEC_ID_PCM_S24LE:
+ case AV_CODEC_ID_PCM_S24BE:
+ case AV_CODEC_ID_PCM_S32LE:
+ case AV_CODEC_ID_PCM_S32BE:
+ case AV_CODEC_ID_PCM_F32LE:
+ case AV_CODEC_ID_PCM_F32BE:
+ case AV_CODEC_ID_PCM_F64LE:
+ case AV_CODEC_ID_PCM_F64BE:
+ codec_tag = MKTAG('l','p','c','m');
+ }
+
+ if (!codec_tag) {
+ av_log(s, AV_LOG_ERROR, "unsupported codec\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (!enc->block_align && !pb->seekable) {
+ av_log(s, AV_LOG_ERROR, "Muxing variable packet size not supported on non seekable output\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (enc->codec_id != AV_CODEC_ID_MP3 || frame_size != 576)
+ frame_size = samples_per_packet(enc->codec_id, enc->channels, enc->block_align);
+
+ ffio_wfourcc(pb, "caff"); //< mFileType
+ avio_wb16(pb, 1); //< mFileVersion
+ avio_wb16(pb, 0); //< mFileFlags
+
+ ffio_wfourcc(pb, "desc"); //< Audio Description chunk
+ avio_wb64(pb, 32); //< mChunkSize
+ avio_wb64(pb, av_double2int(enc->sample_rate)); //< mSampleRate
+ avio_wl32(pb, codec_tag); //< mFormatID
+ avio_wb32(pb, codec_flags(enc->codec_id)); //< mFormatFlags
+ avio_wb32(pb, enc->block_align); //< mBytesPerPacket
+ avio_wb32(pb, frame_size); //< mFramesPerPacket
+ avio_wb32(pb, enc->channels); //< mChannelsPerFrame
+ avio_wb32(pb, av_get_bits_per_sample(enc->codec_id)); //< mBitsPerChannel
+
+ if (enc->channel_layout) {
+ ffio_wfourcc(pb, "chan");
+ avio_wb64(pb, 12);
+ ff_mov_write_chan(pb, enc->channel_layout);
+ }
+
+ if (enc->codec_id == AV_CODEC_ID_ALAC) {
+ ffio_wfourcc(pb, "kuki");
+ avio_wb64(pb, 12 + enc->extradata_size);
+ avio_write(pb, "\0\0\0\14frmaalac", 12);
+ avio_write(pb, enc->extradata, enc->extradata_size);
+ } else if (enc->codec_id == AV_CODEC_ID_AMR_NB) {
+ ffio_wfourcc(pb, "kuki");
+ avio_wb64(pb, 29);
+ avio_write(pb, "\0\0\0\14frmasamr", 12);
+ avio_wb32(pb, 0x11); /* size */
+ avio_write(pb, "samrFFMP", 8);
+ avio_w8(pb, 0); /* decoder version */
+
+ avio_wb16(pb, 0x81FF); /* Mode set (all modes for AMR_NB) */
+ avio_w8(pb, 0x00); /* Mode change period (no restriction) */
+ avio_w8(pb, 0x01); /* Frames per sample */
+ } else if (enc->codec_id == AV_CODEC_ID_QDM2) {
+ ffio_wfourcc(pb, "kuki");
+ avio_wb64(pb, enc->extradata_size);
+ avio_write(pb, enc->extradata, enc->extradata_size);
+ }
+
+ if (av_dict_count(s->metadata)) {
+ ffio_wfourcc(pb, "info"); //< Information chunk
+ while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) {
+ chunk_size += strlen(t->key) + strlen(t->value) + 2;
+ }
+ avio_wb64(pb, chunk_size + 4);
+ avio_wb32(pb, av_dict_count(s->metadata));
+ t = NULL;
+ while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) {
+ avio_put_str(pb, t->key);
+ avio_put_str(pb, t->value);
+ }
+ }
+
+ ffio_wfourcc(pb, "data"); //< Audio Data chunk
+ caf->data = avio_tell(pb);
+ avio_wb64(pb, -1); //< mChunkSize
+ avio_wb32(pb, 0); //< mEditCount
+
+ avio_flush(pb);
+ return 0;
+}
+
+static int caf_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ CAFContext *caf = s->priv_data;
+
+ avio_write(s->pb, pkt->data, pkt->size);
+ if (!s->streams[0]->codec->block_align) {
+ void *pkt_sizes = caf->pkt_sizes;
+ int i, alloc_size = caf->size_entries_used + 5;
+ if (alloc_size < 0) {
+ caf->pkt_sizes = NULL;
+ } else {
+ caf->pkt_sizes = av_fast_realloc(caf->pkt_sizes,
+ &caf->size_buffer_size,
+ alloc_size);
+ }
+ if (!caf->pkt_sizes) {
+ av_free(pkt_sizes);
+ return AVERROR(ENOMEM);
+ }
+ for (i = 4; i > 0; i--) {
+ unsigned top = pkt->size >> i * 7;
+ if (top)
+ caf->pkt_sizes[caf->size_entries_used++] = 128 | top;
+ }
+ caf->pkt_sizes[caf->size_entries_used++] = pkt->size & 127;
+ caf->packets++;
+ }
+ return 0;
+}
+
+static int caf_write_trailer(AVFormatContext *s)
+{
+ CAFContext *caf = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVCodecContext *enc = s->streams[0]->codec;
+
+ if (pb->seekable) {
+ int64_t file_size = avio_tell(pb);
+
+ avio_seek(pb, caf->data, SEEK_SET);
+ avio_wb64(pb, file_size - caf->data - 8);
+ avio_seek(pb, file_size, SEEK_SET);
+ if (!enc->block_align) {
+ ffio_wfourcc(pb, "pakt");
+ avio_wb64(pb, caf->size_entries_used + 24);
+ avio_wb64(pb, caf->packets); ///< mNumberPackets
+ avio_wb64(pb, caf->packets * samples_per_packet(enc->codec_id, enc->channels, enc->block_align)); ///< mNumberValidFrames
+ avio_wb32(pb, 0); ///< mPrimingFrames
+ avio_wb32(pb, 0); ///< mRemainderFrames
+ avio_write(pb, caf->pkt_sizes, caf->size_entries_used);
+ caf->size_buffer_size = 0;
+ }
+ avio_flush(pb);
+ }
+ av_freep(&caf->pkt_sizes);
+ return 0;
+}
+
+AVOutputFormat ff_caf_muxer = {
+ .name = "caf",
+ .long_name = NULL_IF_CONFIG_SMALL("Apple CAF (Core Audio Format)"),
+ .mime_type = "audio/x-caf",
+ .extensions = "caf",
+ .priv_data_size = sizeof(CAFContext),
+ .audio_codec = AV_CODEC_ID_PCM_S16BE,
+ .video_codec = AV_CODEC_ID_NONE,
+ .write_header = caf_write_header,
+ .write_packet = caf_write_packet,
+ .write_trailer = caf_write_trailer,
+ .codec_tag = (const AVCodecTag* const []){ff_codec_caf_tags, 0},
+};
diff --git a/libavformat/cavsvideodec.c b/libavformat/cavsvideodec.c
index fcca9e18b3..880f4ab534 100644
--- a/libavformat/cavsvideodec.c
+++ b/libavformat/cavsvideodec.c
@@ -2,20 +2,20 @@
* RAW Chinese AVS video demuxer
* Copyright (c) 2009 Stefan Gehrer <stefan.gehrer@gmx.de>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -61,7 +61,7 @@ static int cavsvideo_probe(AVProbeData *p)
}
}
if(seq && seq*9<=pic*10)
- return AVPROBE_SCORE_EXTENSION;
+ return AVPROBE_SCORE_EXTENSION+1;
return 0;
}
diff --git a/libavformat/cdg.c b/libavformat/cdg.c
index e699691925..baf37d4c6a 100644
--- a/libavformat/cdg.c
+++ b/libavformat/cdg.c
@@ -2,20 +2,20 @@
* CD Graphics Demuxer
* Copyright (c) 2009 Michael Tison
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -46,10 +46,11 @@ static int read_header(AVFormatContext *s)
avpriv_set_pts_info(vst, 32, 1, 300);
ret = avio_size(s->pb);
- if (ret < 0)
- return ret;
+ if (ret < 0) {
+ av_log(s, AV_LOG_WARNING, "Cannot calculate duration as file size cannot be determined\n");
+ } else
+ vst->duration = (ret * vst->time_base.den) / (CDG_PACKET_SIZE * 300);
- vst->duration = (ret * vst->time_base.den) / (CDG_PACKET_SIZE * 300);
return 0;
}
@@ -71,6 +72,12 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt)
}
pkt->stream_index = 0;
+ pkt->dts=
+ pkt->pts= pkt->pos / CDG_PACKET_SIZE;
+
+ if(ret>5 && (pkt->data[0]&0x3F) == 9 && (pkt->data[1]&0x3F)==1 && !(pkt->data[2+2+1] & 0x0F)){
+ pkt->flags = AV_PKT_FLAG_KEY;
+ }
return ret;
}
@@ -80,5 +87,6 @@ AVInputFormat ff_cdg_demuxer = {
.priv_data_size = sizeof(CDGContext),
.read_header = read_header,
.read_packet = read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
.extensions = "cdg",
};
diff --git a/libavformat/cdxl.c b/libavformat/cdxl.c
index a2cba526ca..f198bf50fa 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
*/
@@ -37,8 +37,51 @@ typedef struct CDXLDemuxContext {
uint8_t header[CDXL_HEADER_SIZE];
int video_stream_index;
int audio_stream_index;
+ int64_t filesize;
} CDXLDemuxContext;
+static int cdxl_read_probe(AVProbeData *p)
+{
+ int score = AVPROBE_SCORE_EXTENSION + 10;
+
+ if (p->buf_size < CDXL_HEADER_SIZE)
+ return 0;
+
+ /* reserved bytes should always be set to 0 */
+ if (AV_RN64(&p->buf[24]) || AV_RN16(&p->buf[10]))
+ return 0;
+
+ /* check type */
+ if (p->buf[0] != 1)
+ return 0;
+
+ /* check palette size */
+ if (AV_RB16(&p->buf[20]) > 512)
+ return 0;
+
+ /* check number of planes */
+ if (p->buf[18] || !p->buf[19])
+ return 0;
+
+ /* check widh and height */
+ if (!AV_RN16(&p->buf[14]) || !AV_RN16(&p->buf[16]))
+ return 0;
+
+ /* chunk size */
+ if (AV_RB32(&p->buf[2]) < AV_RB16(&p->buf[22]) + AV_RB16(&p->buf[20]) + CDXL_HEADER_SIZE)
+ return 0;
+
+ /* previous chunk size */
+ if (AV_RN32(&p->buf[6]))
+ score /= 2;
+
+ /* current frame number, usually starts from 1 */
+ if (AV_RB16(&p->buf[12]) != 1)
+ score /= 2;
+
+ return score;
+}
+
static int cdxl_read_header(AVFormatContext *s)
{
CDXLDemuxContext *cdxl = s->priv_data;
@@ -54,6 +97,8 @@ static int cdxl_read_header(AVFormatContext *s)
cdxl->video_stream_index = -1;
cdxl->audio_stream_index = -1;
+ cdxl->filesize = avio_size(s->pb);
+
s->ctx_flags |= AVFMTCTX_NOHEADER;
return 0;
@@ -66,9 +111,9 @@ static int cdxl_read_packet(AVFormatContext *s, AVPacket *pkt)
uint32_t current_size, video_size, image_size;
uint16_t audio_size, palette_size, width, height;
int64_t pos;
- int ret;
+ int frames, ret;
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR_EOF;
pos = avio_tell(pb);
@@ -85,6 +130,8 @@ static int cdxl_read_packet(AVFormatContext *s, AVPacket *pkt)
height = AV_RB16(&cdxl->header[16]);
palette_size = AV_RB16(&cdxl->header[20]);
audio_size = AV_RB16(&cdxl->header[22]);
+ if (FFALIGN(width, 16) * (uint64_t)height * cdxl->header[19] > INT_MAX)
+ return AVERROR_INVALIDDATA;
image_size = FFALIGN(width, 16) * height * cdxl->header[19] / 8;
video_size = palette_size + image_size;
@@ -133,6 +180,15 @@ static int cdxl_read_packet(AVFormatContext *s, AVPacket *pkt)
st->codec->codec_id = AV_CODEC_ID_CDXL;
st->codec->width = width;
st->codec->height = height;
+
+ if (audio_size + video_size && cdxl->filesize > 0) {
+ frames = cdxl->filesize / (audio_size + video_size);
+
+ if(cdxl->framerate)
+ st->duration = frames;
+ else
+ st->duration = frames * (int64_t)audio_size;
+ }
st->start_time = 0;
cdxl->video_stream_index = st->index;
if (cdxl->framerate)
@@ -180,6 +236,7 @@ AVInputFormat ff_cdxl_demuxer = {
.name = "cdxl",
.long_name = NULL_IF_CONFIG_SMALL("Commodore CDXL video"),
.priv_data_size = sizeof(CDXLDemuxContext),
+ .read_probe = cdxl_read_probe,
.read_header = cdxl_read_header,
.read_packet = cdxl_read_packet,
.extensions = "cdxl,xl",
diff --git a/libavformat/cinedec.c b/libavformat/cinedec.c
new file mode 100644
index 0000000000..632f46c454
--- /dev/null
+++ b/libavformat/cinedec.c
@@ -0,0 +1,325 @@
+/*
+ * Phantom Cine demuxer
+ * Copyright (c) 2010-2011 Peter Ross <pross@xvid.org>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Phantom Cine demuxer
+ * @author Peter Ross <pross@xvid.org>
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "libavcodec/bmp.h"
+#include "libavutil/intfloat.h"
+#include "avformat.h"
+#include "internal.h"
+
+typedef struct {
+ uint64_t pts;
+} CineDemuxContext;
+
+/** Compression */
+enum {
+ CC_RGB = 0, /**< Gray */
+ CC_LEAD = 1, /**< LEAD (M)JPEG */
+ CC_UNINT = 2 /**< Uninterpolated color image (CFA field indicates color ordering) */
+};
+
+/** Color Filter Array */
+enum {
+ CFA_NONE = 0, /**< GRAY */
+ CFA_VRI = 1, /**< GBRG/RGGB */
+ CFA_VRIV6 = 2, /**< BGGR/GRBG */
+ CFA_BAYER = 3, /**< GB/RG */
+ CFA_BAYERFLIP = 4, /**< RG/GB */
+
+ CFA_TLGRAY = 0x80000000,
+ CFA_TRGRAY = 0x40000000,
+ CFA_BLGRAY = 0x20000000,
+ CFA_BRGRAY = 0x10000000
+};
+
+static int cine_read_probe(AVProbeData *p)
+{
+ int HeaderSize;
+ if (p->buf[0] == 'C' && p->buf[1] == 'I' && // Type
+ (HeaderSize = AV_RL16(p->buf + 2)) >= 0x2C && // HeaderSize
+ AV_RL16(p->buf + 4) <= CC_UNINT && // Compression
+ AV_RL16(p->buf + 6) <= 1 && // Version
+ AV_RL32(p->buf + 20) && // ImageCount
+ AV_RL32(p->buf + 24) >= HeaderSize && // OffImageHeader
+ AV_RL32(p->buf + 28) >= HeaderSize && // OffSetup
+ AV_RL32(p->buf + 32) >= HeaderSize) // OffImageOffsets
+ return AVPROBE_SCORE_MAX;
+ return 0;
+}
+
+static int set_metadata_int(AVDictionary **dict, const char *key, int value, int allow_zero)
+{
+ if (value || allow_zero) {
+ return av_dict_set_int(dict, key, value, 0);
+ }
+ return 0;
+}
+
+static int set_metadata_float(AVDictionary **dict, const char *key, float value, int allow_zero)
+{
+ if (value != 0 || allow_zero) {
+ char tmp[64];
+ snprintf(tmp, sizeof(tmp), "%f", value);
+ return av_dict_set(dict, key, tmp, 0);
+ }
+ return 0;
+}
+
+static int cine_read_header(AVFormatContext *avctx)
+{
+ AVIOContext *pb = avctx->pb;
+ AVStream *st;
+ unsigned int version, compression, offImageHeader, offSetup, offImageOffsets, biBitCount, length, CFA;
+ int vflip;
+ char *description;
+ uint64_t i;
+
+ st = avformat_new_stream(avctx, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codec->codec_id = AV_CODEC_ID_RAWVIDEO;
+ st->codec->codec_tag = 0;
+
+ /* CINEFILEHEADER structure */
+ avio_skip(pb, 4); // Type, Headersize
+
+ compression = avio_rl16(pb);
+ version = avio_rl16(pb);
+ if (version != 1) {
+ avpriv_request_sample(avctx, "uknown version %i", version);
+ return AVERROR_INVALIDDATA;
+ }
+
+ avio_skip(pb, 12); // FirstMovieImage, TotalImageCount, FirstImageNumber
+
+ st->duration = avio_rl32(pb);
+ offImageHeader = avio_rl32(pb);
+ offSetup = avio_rl32(pb);
+ offImageOffsets = avio_rl32(pb);
+
+ avio_skip(pb, 8); // TriggerTime
+
+ /* BITMAPINFOHEADER structure */
+ avio_seek(pb, offImageHeader, SEEK_SET);
+ avio_skip(pb, 4); //biSize
+ st->codec->width = avio_rl32(pb);
+ st->codec->height = avio_rl32(pb);
+
+ if (avio_rl16(pb) != 1) // biPlanes
+ return AVERROR_INVALIDDATA;
+
+ biBitCount = avio_rl16(pb);
+ if (biBitCount != 8 && biBitCount != 16 && biBitCount != 24 && biBitCount != 48) {
+ avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
+ return AVERROR_INVALIDDATA;
+ }
+
+ switch (avio_rl32(pb)) {
+ case BMP_RGB:
+ vflip = 0;
+ break;
+ case 0x100: /* BI_PACKED */
+ st->codec->codec_tag = MKTAG('B', 'I', 'T', 0);
+ vflip = 1;
+ break;
+ default:
+ avpriv_request_sample(avctx, "unknown bitmap compression");
+ return AVERROR_INVALIDDATA;
+ }
+
+ avio_skip(pb, 4); // biSizeImage
+
+ /* parse SETUP structure */
+ avio_seek(pb, offSetup, SEEK_SET);
+ avio_skip(pb, 140); // FrameRatae16 .. descriptionOld
+ if (avio_rl16(pb) != 0x5453)
+ return AVERROR_INVALIDDATA;
+ length = avio_rl16(pb);
+ if (length < 0x163C) {
+ avpriv_request_sample(avctx, "short SETUP header");
+ return AVERROR_INVALIDDATA;
+ }
+
+ avio_skip(pb, 616); // Binning .. bFlipH
+ if (!avio_rl32(pb) ^ vflip) {
+ st->codec->extradata = av_strdup("BottomUp");
+ st->codec->extradata_size = 9;
+ }
+
+ avio_skip(pb, 4); // Grid
+
+ avpriv_set_pts_info(st, 64, 1, avio_rl32(pb));
+
+ avio_skip(pb, 20); // Shutter .. bEnableColor
+
+ set_metadata_int(&st->metadata, "camera_version", avio_rl32(pb), 0);
+ set_metadata_int(&st->metadata, "firmware_version", avio_rl32(pb), 0);
+ set_metadata_int(&st->metadata, "software_version", avio_rl32(pb), 0);
+ set_metadata_int(&st->metadata, "recording_timezone", avio_rl32(pb), 0);
+
+ CFA = avio_rl32(pb);
+
+ set_metadata_int(&st->metadata, "brightness", avio_rl32(pb), 1);
+ set_metadata_int(&st->metadata, "contrast", avio_rl32(pb), 1);
+ set_metadata_int(&st->metadata, "gamma", avio_rl32(pb), 1);
+
+ avio_skip(pb, 12 + 16); // Reserved1 .. AutoExpRect
+ set_metadata_float(&st->metadata, "wbgain[0].r", av_int2float(avio_rl32(pb)), 1);
+ set_metadata_float(&st->metadata, "wbgain[0].b", av_int2float(avio_rl32(pb)), 1);
+ avio_skip(pb, 36); // WBGain[1].. WBView
+
+ st->codec->bits_per_coded_sample = avio_rl32(pb);
+
+ if (compression == CC_RGB) {
+ if (biBitCount == 8) {
+ st->codec->pix_fmt = AV_PIX_FMT_GRAY8;
+ } else if (biBitCount == 16) {
+ st->codec->pix_fmt = AV_PIX_FMT_GRAY16LE;
+ } else if (biBitCount == 24) {
+ st->codec->pix_fmt = AV_PIX_FMT_BGR24;
+ } else if (biBitCount == 48) {
+ st->codec->pix_fmt = AV_PIX_FMT_BGR48LE;
+ } else {
+ avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
+ return AVERROR_INVALIDDATA;
+ }
+ } else if (compression == CC_UNINT) {
+ switch (CFA & 0xFFFFFF) {
+ case CFA_BAYER:
+ if (biBitCount == 8) {
+ st->codec->pix_fmt = AV_PIX_FMT_BAYER_GBRG8;
+ } else if (biBitCount == 16) {
+ st->codec->pix_fmt = AV_PIX_FMT_BAYER_GBRG16LE;
+ } else {
+ avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
+ return AVERROR_INVALIDDATA;
+ }
+ break;
+ case CFA_BAYERFLIP:
+ if (biBitCount == 8) {
+ st->codec->pix_fmt = AV_PIX_FMT_BAYER_RGGB8;
+ } else if (biBitCount == 16) {
+ st->codec->pix_fmt = AV_PIX_FMT_BAYER_RGGB16LE;
+ } else {
+ avpriv_request_sample(avctx, "unsupported biBitCount %i", biBitCount);
+ return AVERROR_INVALIDDATA;
+ }
+ break;
+ default:
+ avpriv_request_sample(avctx, "unsupported Color Field Array (CFA) %i", CFA & 0xFFFFFF);
+ return AVERROR_INVALIDDATA;
+ }
+ } else { //CC_LEAD
+ avpriv_request_sample(avctx, "unsupported compression %i", compression);
+ return AVERROR_INVALIDDATA;
+ }
+
+ avio_skip(pb, 668); // Conv8Min ... Sensor
+
+ set_metadata_int(&st->metadata, "shutter_ns", avio_rl32(pb), 0);
+
+ avio_skip(pb, 24); // EDRShutterNs ... ImHeightAcq
+
+#define DESCRIPTION_SIZE 4096
+ description = av_malloc(DESCRIPTION_SIZE + 1);
+ if (!description)
+ return AVERROR(ENOMEM);
+ i = avio_get_str(pb, DESCRIPTION_SIZE, description, DESCRIPTION_SIZE + 1);
+ if (i < DESCRIPTION_SIZE)
+ avio_skip(pb, DESCRIPTION_SIZE - i);
+ if (description[0])
+ av_dict_set(&st->metadata, "description", description, AV_DICT_DONT_STRDUP_VAL);
+ else
+ av_free(description);
+
+ avio_skip(pb, 1176); // RisingEdge ... cmUser
+
+ set_metadata_int(&st->metadata, "enable_crop", avio_rl32(pb), 1);
+ set_metadata_int(&st->metadata, "crop_left", avio_rl32(pb), 1);
+ set_metadata_int(&st->metadata, "crop_top", avio_rl32(pb), 1);
+ set_metadata_int(&st->metadata, "crop_right", avio_rl32(pb), 1);
+ set_metadata_int(&st->metadata, "crop_bottom", avio_rl32(pb), 1);
+
+ /* parse image offsets */
+ avio_seek(pb, offImageOffsets, SEEK_SET);
+ for (i = 0; i < st->duration; i++)
+ av_add_index_entry(st, avio_rl64(pb), i, 0, 0, AVINDEX_KEYFRAME);
+
+ return 0;
+}
+
+static int cine_read_packet(AVFormatContext *avctx, AVPacket *pkt)
+{
+ CineDemuxContext *cine = avctx->priv_data;
+ AVStream *st = avctx->streams[0];
+ AVIOContext *pb = avctx->pb;
+ int n, size, ret;
+
+ if (cine->pts >= st->duration)
+ return AVERROR_EOF;
+
+ avio_seek(pb, st->index_entries[cine->pts].pos, SEEK_SET);
+ n = avio_rl32(pb);
+ if (n < 8)
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, n - 8);
+ size = avio_rl32(pb);
+
+ ret = av_get_packet(pb, pkt, size);
+ if (ret < 0)
+ return ret;
+
+ pkt->pts = cine->pts++;
+ pkt->stream_index = 0;
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ return 0;
+}
+
+static int cine_read_seek(AVFormatContext *avctx, int stream_index, int64_t timestamp, int flags)
+{
+ CineDemuxContext *cine = avctx->priv_data;
+
+ if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE))
+ return AVERROR(ENOSYS);
+
+ if (!avctx->pb->seekable)
+ return AVERROR(EIO);
+
+ cine->pts = timestamp;
+ return 0;
+}
+
+AVInputFormat ff_cine_demuxer = {
+ .name = "cine",
+ .long_name = NULL_IF_CONFIG_SMALL("Phantom Cine"),
+ .priv_data_size = sizeof(CineDemuxContext),
+ .read_probe = cine_read_probe,
+ .read_header = cine_read_header,
+ .read_packet = cine_read_packet,
+ .read_seek = cine_read_seek,
+};
diff --git a/libavformat/concat.c b/libavformat/concat.c
index 2fb3ba9059..81fe97082c 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
*/
@@ -114,9 +114,10 @@ static av_cold int concat_open(URLContext *h, const char *uri, int flags)
if (err < 0)
concat_close(h);
- else if ((err = av_reallocp(&nodes, data->length * sizeof(*nodes))) < 0)
+ else if (!(nodes = av_realloc(nodes, data->length * sizeof(*nodes)))) {
concat_close(h);
- else
+ err = AVERROR(ENOMEM);
+ } else
data->nodes = nodes;
return err;
}
diff --git a/libavformat/concatdec.c b/libavformat/concatdec.c
new file mode 100644
index 0000000000..f07cfd7e8f
--- /dev/null
+++ b/libavformat/concatdec.c
@@ -0,0 +1,652 @@
+/*
+ * Copyright (c) 2012 Nicolas George
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/timestamp.h"
+#include "avformat.h"
+#include "internal.h"
+#include "url.h"
+
+typedef enum ConcatMatchMode {
+ MATCH_ONE_TO_ONE,
+ MATCH_EXACT_ID,
+} ConcatMatchMode;
+
+typedef struct ConcatStream {
+ AVBitStreamFilterContext *bsf;
+ int out_stream_index;
+} ConcatStream;
+
+typedef struct {
+ char *url;
+ int64_t start_time;
+ int64_t duration;
+ ConcatStream *streams;
+ int nb_streams;
+} ConcatFile;
+
+typedef struct {
+ AVClass *class;
+ ConcatFile *files;
+ ConcatFile *cur_file;
+ unsigned nb_files;
+ AVFormatContext *avf;
+ int safe;
+ int seekable;
+ ConcatMatchMode stream_match_mode;
+ unsigned auto_convert;
+} ConcatContext;
+
+static int concat_probe(AVProbeData *probe)
+{
+ return memcmp(probe->buf, "ffconcat version 1.0", 20) ?
+ 0 : AVPROBE_SCORE_MAX;
+}
+
+static char *get_keyword(uint8_t **cursor)
+{
+ char *ret = *cursor += strspn(*cursor, SPACE_CHARS);
+ *cursor += strcspn(*cursor, SPACE_CHARS);
+ if (**cursor) {
+ *((*cursor)++) = 0;
+ *cursor += strspn(*cursor, SPACE_CHARS);
+ }
+ return ret;
+}
+
+static int safe_filename(const char *f)
+{
+ const char *start = f;
+
+ for (; *f; f++) {
+ /* A-Za-z0-9_- */
+ if (!((unsigned)((*f | 32) - 'a') < 26 ||
+ (unsigned)(*f - '0') < 10 || *f == '_' || *f == '-')) {
+ if (f == start)
+ return 0;
+ else if (*f == '/')
+ start = f + 1;
+ else if (*f != '.')
+ return 0;
+ }
+ }
+ return 1;
+}
+
+#define FAIL(retcode) do { ret = (retcode); goto fail; } while(0)
+
+static int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile,
+ unsigned *nb_files_alloc)
+{
+ ConcatContext *cat = avf->priv_data;
+ ConcatFile *file;
+ char *url = NULL;
+ const char *proto;
+ size_t url_len, proto_len;
+ int ret;
+
+ if (cat->safe > 0 && !safe_filename(filename)) {
+ av_log(avf, AV_LOG_ERROR, "Unsafe file name '%s'\n", filename);
+ FAIL(AVERROR(EPERM));
+ }
+
+ proto = avio_find_protocol_name(filename);
+ proto_len = proto ? strlen(proto) : 0;
+ if (!memcmp(filename, proto, proto_len) &&
+ (filename[proto_len] == ':' || filename[proto_len] == ',')) {
+ url = filename;
+ filename = NULL;
+ } else {
+ url_len = strlen(avf->filename) + strlen(filename) + 16;
+ if (!(url = av_malloc(url_len)))
+ FAIL(AVERROR(ENOMEM));
+ ff_make_absolute_url(url, url_len, avf->filename, filename);
+ av_freep(&filename);
+ }
+
+ if (cat->nb_files >= *nb_files_alloc) {
+ size_t n = FFMAX(*nb_files_alloc * 2, 16);
+ ConcatFile *new_files;
+ if (n <= cat->nb_files || n > SIZE_MAX / sizeof(*cat->files) ||
+ !(new_files = av_realloc(cat->files, n * sizeof(*cat->files))))
+ FAIL(AVERROR(ENOMEM));
+ cat->files = new_files;
+ *nb_files_alloc = n;
+ }
+
+ file = &cat->files[cat->nb_files++];
+ memset(file, 0, sizeof(*file));
+ *rfile = file;
+
+ file->url = url;
+ file->start_time = AV_NOPTS_VALUE;
+ file->duration = AV_NOPTS_VALUE;
+
+ return 0;
+
+fail:
+ av_free(url);
+ av_free(filename);
+ return ret;
+}
+
+static int copy_stream_props(AVStream *st, AVStream *source_st)
+{
+ int ret;
+
+ if (st->codec->codec_id || !source_st->codec->codec_id) {
+ if (st->codec->extradata_size < source_st->codec->extradata_size) {
+ ret = ff_alloc_extradata(st->codec,
+ source_st->codec->extradata_size);
+ if (ret < 0)
+ return ret;
+ }
+ memcpy(st->codec->extradata, source_st->codec->extradata,
+ source_st->codec->extradata_size);
+ return 0;
+ }
+ if ((ret = avcodec_copy_context(st->codec, source_st->codec)) < 0)
+ return ret;
+ st->r_frame_rate = source_st->r_frame_rate;
+ st->avg_frame_rate = source_st->avg_frame_rate;
+ st->time_base = source_st->time_base;
+ st->sample_aspect_ratio = source_st->sample_aspect_ratio;
+ return 0;
+}
+
+static int detect_stream_specific(AVFormatContext *avf, int idx)
+{
+ ConcatContext *cat = avf->priv_data;
+ AVStream *st = cat->avf->streams[idx];
+ ConcatStream *cs = &cat->cur_file->streams[idx];
+ AVBitStreamFilterContext *bsf;
+
+ if (cat->auto_convert && st->codec->codec_id == AV_CODEC_ID_H264 &&
+ (st->codec->extradata_size < 4 || AV_RB32(st->codec->extradata) != 1)) {
+ av_log(cat->avf, AV_LOG_INFO,
+ "Auto-inserting h264_mp4toannexb bitstream filter\n");
+ if (!(bsf = av_bitstream_filter_init("h264_mp4toannexb"))) {
+ av_log(avf, AV_LOG_ERROR, "h264_mp4toannexb bitstream filter "
+ "required for H.264 streams\n");
+ return AVERROR_BSF_NOT_FOUND;
+ }
+ cs->bsf = bsf;
+ }
+ return 0;
+}
+
+static int match_streams_one_to_one(AVFormatContext *avf)
+{
+ ConcatContext *cat = avf->priv_data;
+ AVStream *st;
+ int i, ret;
+
+ for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) {
+ if (i < avf->nb_streams) {
+ st = avf->streams[i];
+ } else {
+ if (!(st = avformat_new_stream(avf, NULL)))
+ return AVERROR(ENOMEM);
+ }
+ if ((ret = copy_stream_props(st, cat->avf->streams[i])) < 0)
+ return ret;
+ cat->cur_file->streams[i].out_stream_index = i;
+ }
+ return 0;
+}
+
+static int match_streams_exact_id(AVFormatContext *avf)
+{
+ ConcatContext *cat = avf->priv_data;
+ AVStream *st;
+ int i, j, ret;
+
+ for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) {
+ st = cat->avf->streams[i];
+ for (j = 0; j < avf->nb_streams; j++) {
+ if (avf->streams[j]->id == st->id) {
+ av_log(avf, AV_LOG_VERBOSE,
+ "Match slave stream #%d with stream #%d id 0x%x\n",
+ i, j, st->id);
+ if ((ret = copy_stream_props(avf->streams[j], st)) < 0)
+ return ret;
+ cat->cur_file->streams[i].out_stream_index = j;
+ }
+ }
+ }
+ return 0;
+}
+
+static int match_streams(AVFormatContext *avf)
+{
+ ConcatContext *cat = avf->priv_data;
+ ConcatStream *map;
+ int i, ret;
+
+ if (cat->cur_file->nb_streams >= cat->avf->nb_streams)
+ return 0;
+ map = av_realloc(cat->cur_file->streams,
+ cat->avf->nb_streams * sizeof(*map));
+ if (!map)
+ return AVERROR(ENOMEM);
+ cat->cur_file->streams = map;
+ memset(map + cat->cur_file->nb_streams, 0,
+ (cat->avf->nb_streams - cat->cur_file->nb_streams) * sizeof(*map));
+
+ for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++)
+ map[i].out_stream_index = -1;
+ switch (cat->stream_match_mode) {
+ case MATCH_ONE_TO_ONE:
+ ret = match_streams_one_to_one(avf);
+ break;
+ case MATCH_EXACT_ID:
+ ret = match_streams_exact_id(avf);
+ break;
+ default:
+ ret = AVERROR_BUG;
+ }
+ if (ret < 0)
+ return ret;
+ for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++)
+ if ((ret = detect_stream_specific(avf, i)) < 0)
+ return ret;
+ cat->cur_file->nb_streams = cat->avf->nb_streams;
+ return 0;
+}
+
+static int open_file(AVFormatContext *avf, unsigned fileno)
+{
+ ConcatContext *cat = avf->priv_data;
+ ConcatFile *file = &cat->files[fileno];
+ int ret;
+
+ if (cat->avf)
+ avformat_close_input(&cat->avf);
+
+ cat->avf = avformat_alloc_context();
+ if (!cat->avf)
+ return AVERROR(ENOMEM);
+
+ cat->avf->interrupt_callback = avf->interrupt_callback;
+
+ if ((ret = ff_copy_whitelists(cat->avf, avf)) < 0)
+ return ret;
+
+ if ((ret = avformat_open_input(&cat->avf, file->url, NULL, NULL)) < 0 ||
+ (ret = avformat_find_stream_info(cat->avf, NULL)) < 0) {
+ av_log(avf, AV_LOG_ERROR, "Impossible to open '%s'\n", file->url);
+ avformat_close_input(&cat->avf);
+ return ret;
+ }
+ cat->cur_file = file;
+ if (file->start_time == AV_NOPTS_VALUE)
+ file->start_time = !fileno ? 0 :
+ cat->files[fileno - 1].start_time +
+ cat->files[fileno - 1].duration;
+ if ((ret = match_streams(avf)) < 0)
+ return ret;
+ return 0;
+}
+
+static int concat_read_close(AVFormatContext *avf)
+{
+ ConcatContext *cat = avf->priv_data;
+ unsigned i;
+
+ if (cat->avf)
+ avformat_close_input(&cat->avf);
+ for (i = 0; i < cat->nb_files; i++) {
+ av_freep(&cat->files[i].url);
+ av_freep(&cat->files[i].streams);
+ }
+ av_freep(&cat->files);
+ return 0;
+}
+
+static int concat_read_header(AVFormatContext *avf)
+{
+ ConcatContext *cat = avf->priv_data;
+ uint8_t buf[4096];
+ uint8_t *cursor, *keyword;
+ int ret, line = 0, i;
+ unsigned nb_files_alloc = 0;
+ ConcatFile *file = NULL;
+ int64_t time = 0;
+
+ while (1) {
+ if ((ret = ff_get_line(avf->pb, buf, sizeof(buf))) <= 0)
+ break;
+ line++;
+ cursor = buf;
+ keyword = get_keyword(&cursor);
+ if (!*keyword || *keyword == '#')
+ continue;
+
+ if (!strcmp(keyword, "file")) {
+ char *filename = av_get_token((const char **)&cursor, SPACE_CHARS);
+ if (!filename) {
+ av_log(avf, AV_LOG_ERROR, "Line %d: filename required\n", line);
+ FAIL(AVERROR_INVALIDDATA);
+ }
+ if ((ret = add_file(avf, filename, &file, &nb_files_alloc)) < 0)
+ goto fail;
+ } else if (!strcmp(keyword, "duration")) {
+ char *dur_str = get_keyword(&cursor);
+ int64_t dur;
+ if (!file) {
+ av_log(avf, AV_LOG_ERROR, "Line %d: duration without file\n",
+ line);
+ FAIL(AVERROR_INVALIDDATA);
+ }
+ if ((ret = av_parse_time(&dur, dur_str, 1)) < 0) {
+ av_log(avf, AV_LOG_ERROR, "Line %d: invalid duration '%s'\n",
+ line, dur_str);
+ goto fail;
+ }
+ file->duration = dur;
+ } else if (!strcmp(keyword, "stream")) {
+ if (!avformat_new_stream(avf, NULL))
+ FAIL(AVERROR(ENOMEM));
+ } else if (!strcmp(keyword, "exact_stream_id")) {
+ if (!avf->nb_streams) {
+ av_log(avf, AV_LOG_ERROR, "Line %d: exact_stream_id without stream\n",
+ line);
+ FAIL(AVERROR_INVALIDDATA);
+ }
+ avf->streams[avf->nb_streams - 1]->id =
+ strtol(get_keyword(&cursor), NULL, 0);
+ } else if (!strcmp(keyword, "ffconcat")) {
+ char *ver_kw = get_keyword(&cursor);
+ char *ver_val = get_keyword(&cursor);
+ if (strcmp(ver_kw, "version") || strcmp(ver_val, "1.0")) {
+ av_log(avf, AV_LOG_ERROR, "Line %d: invalid version\n", line);
+ FAIL(AVERROR_INVALIDDATA);
+ }
+ if (cat->safe < 0)
+ cat->safe = 1;
+ } else {
+ av_log(avf, AV_LOG_ERROR, "Line %d: unknown keyword '%s'\n",
+ line, keyword);
+ FAIL(AVERROR_INVALIDDATA);
+ }
+ }
+ if (ret < 0)
+ goto fail;
+ if (!cat->nb_files)
+ FAIL(AVERROR_INVALIDDATA);
+
+ for (i = 0; i < cat->nb_files; i++) {
+ if (cat->files[i].start_time == AV_NOPTS_VALUE)
+ cat->files[i].start_time = time;
+ else
+ time = cat->files[i].start_time;
+ if (cat->files[i].duration == AV_NOPTS_VALUE)
+ break;
+ time += cat->files[i].duration;
+ }
+ if (i == cat->nb_files) {
+ avf->duration = time;
+ cat->seekable = 1;
+ }
+
+ cat->stream_match_mode = avf->nb_streams ? MATCH_EXACT_ID :
+ MATCH_ONE_TO_ONE;
+ if ((ret = open_file(avf, 0)) < 0)
+ goto fail;
+ return 0;
+
+fail:
+ concat_read_close(avf);
+ return ret;
+}
+
+static int open_next_file(AVFormatContext *avf)
+{
+ ConcatContext *cat = avf->priv_data;
+ unsigned fileno = cat->cur_file - cat->files;
+
+ if (cat->cur_file->duration == AV_NOPTS_VALUE)
+ cat->cur_file->duration = cat->avf->duration;
+
+ if (++fileno >= cat->nb_files)
+ return AVERROR_EOF;
+ return open_file(avf, fileno);
+}
+
+static int filter_packet(AVFormatContext *avf, ConcatStream *cs, AVPacket *pkt)
+{
+ AVStream *st = avf->streams[cs->out_stream_index];
+ AVBitStreamFilterContext *bsf;
+ AVPacket pkt2;
+ int ret;
+
+ av_assert0(cs->out_stream_index >= 0);
+ for (bsf = cs->bsf; bsf; bsf = bsf->next) {
+ pkt2 = *pkt;
+ ret = av_bitstream_filter_filter(bsf, st->codec, NULL,
+ &pkt2.data, &pkt2.size,
+ pkt->data, pkt->size,
+ !!(pkt->flags & AV_PKT_FLAG_KEY));
+ if (ret < 0) {
+ av_packet_unref(pkt);
+ return ret;
+ }
+ av_assert0(pkt2.buf);
+ if (ret == 0 && pkt2.data != pkt->data) {
+ if ((ret = av_copy_packet(&pkt2, pkt)) < 0) {
+ av_free(pkt2.data);
+ return ret;
+ }
+ ret = 1;
+ }
+ if (ret > 0) {
+ av_free_packet(pkt);
+ pkt2.buf = av_buffer_create(pkt2.data, pkt2.size,
+ av_buffer_default_free, NULL, 0);
+ if (!pkt2.buf) {
+ av_free(pkt2.data);
+ return AVERROR(ENOMEM);
+ }
+ }
+ *pkt = pkt2;
+ }
+ return 0;
+}
+
+static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt)
+{
+ ConcatContext *cat = avf->priv_data;
+ int ret;
+ int64_t file_start_time, delta;
+ ConcatStream *cs;
+ AVStream *st;
+
+ if (!cat->avf)
+ return AVERROR(EIO);
+
+ while (1) {
+ ret = av_read_frame(cat->avf, pkt);
+ if (ret == AVERROR_EOF) {
+ if ((ret = open_next_file(avf)) < 0)
+ return ret;
+ continue;
+ }
+ if (ret < 0)
+ return ret;
+ if ((ret = match_streams(avf)) < 0) {
+ av_packet_unref(pkt);
+ return ret;
+ }
+ cs = &cat->cur_file->streams[pkt->stream_index];
+ if (cs->out_stream_index < 0) {
+ av_packet_unref(pkt);
+ continue;
+ }
+ pkt->stream_index = cs->out_stream_index;
+ break;
+ }
+ if ((ret = filter_packet(avf, cs, pkt)))
+ return ret;
+
+ st = cat->avf->streams[pkt->stream_index];
+ av_log(avf, AV_LOG_DEBUG, "file:%d stream:%d pts:%s pts_time:%s dts:%s dts_time:%s",
+ (unsigned)(cat->cur_file - cat->files), pkt->stream_index,
+ av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
+ av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
+
+ file_start_time = cat->avf->start_time;
+ if (file_start_time == AV_NOPTS_VALUE)
+ file_start_time = 0;
+ delta = av_rescale_q(cat->cur_file->start_time - file_start_time,
+ AV_TIME_BASE_Q,
+ cat->avf->streams[pkt->stream_index]->time_base);
+ if (pkt->pts != AV_NOPTS_VALUE)
+ pkt->pts += delta;
+ if (pkt->dts != AV_NOPTS_VALUE)
+ pkt->dts += delta;
+ av_log(avf, AV_LOG_DEBUG, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n",
+ av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
+ av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
+ return ret;
+}
+
+static void rescale_interval(AVRational tb_in, AVRational tb_out,
+ int64_t *min_ts, int64_t *ts, int64_t *max_ts)
+{
+ *ts = av_rescale_q (* ts, tb_in, tb_out);
+ *min_ts = av_rescale_q_rnd(*min_ts, tb_in, tb_out,
+ AV_ROUND_UP | AV_ROUND_PASS_MINMAX);
+ *max_ts = av_rescale_q_rnd(*max_ts, tb_in, tb_out,
+ AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX);
+}
+
+static int try_seek(AVFormatContext *avf, int stream,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ ConcatContext *cat = avf->priv_data;
+ int64_t t0 = cat->cur_file->start_time - cat->avf->start_time;
+
+ ts -= t0;
+ min_ts = min_ts == INT64_MIN ? INT64_MIN : min_ts - t0;
+ max_ts = max_ts == INT64_MAX ? INT64_MAX : max_ts - t0;
+ if (stream >= 0) {
+ if (stream >= cat->avf->nb_streams)
+ return AVERROR(EIO);
+ rescale_interval(AV_TIME_BASE_Q, cat->avf->streams[stream]->time_base,
+ &min_ts, &ts, &max_ts);
+ }
+ return avformat_seek_file(cat->avf, stream, min_ts, ts, max_ts, flags);
+}
+
+static int real_seek(AVFormatContext *avf, int stream,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ ConcatContext *cat = avf->priv_data;
+ int ret, left, right;
+
+ if (stream >= 0) {
+ if (stream >= avf->nb_streams)
+ return AVERROR(EINVAL);
+ rescale_interval(avf->streams[stream]->time_base, AV_TIME_BASE_Q,
+ &min_ts, &ts, &max_ts);
+ }
+
+ left = 0;
+ right = cat->nb_files;
+ while (right - left > 1) {
+ int mid = (left + right) / 2;
+ if (ts < cat->files[mid].start_time)
+ right = mid;
+ else
+ left = mid;
+ }
+
+ if ((ret = open_file(avf, left)) < 0)
+ return ret;
+
+ ret = try_seek(avf, stream, min_ts, ts, max_ts, flags);
+ if (ret < 0 &&
+ left < cat->nb_files - 1 &&
+ cat->files[left + 1].start_time < max_ts) {
+ if ((ret = open_file(avf, left + 1)) < 0)
+ return ret;
+ ret = try_seek(avf, stream, min_ts, ts, max_ts, flags);
+ }
+ return ret;
+}
+
+static int concat_seek(AVFormatContext *avf, int stream,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ ConcatContext *cat = avf->priv_data;
+ ConcatFile *cur_file_saved = cat->cur_file;
+ AVFormatContext *cur_avf_saved = cat->avf;
+ int ret;
+
+ if (!cat->seekable)
+ return AVERROR(ESPIPE); /* XXX: can we use it? */
+ if (flags & (AVSEEK_FLAG_BYTE | AVSEEK_FLAG_FRAME))
+ return AVERROR(ENOSYS);
+ cat->avf = NULL;
+ if ((ret = real_seek(avf, stream, min_ts, ts, max_ts, flags)) < 0) {
+ if (cat->avf)
+ avformat_close_input(&cat->avf);
+ cat->avf = cur_avf_saved;
+ cat->cur_file = cur_file_saved;
+ } else {
+ avformat_close_input(&cur_avf_saved);
+ }
+ return ret;
+}
+
+#define OFFSET(x) offsetof(ConcatContext, x)
+#define DEC AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption options[] = {
+ { "safe", "enable safe mode",
+ OFFSET(safe), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, DEC },
+ { "auto_convert", "automatically convert bitstream format",
+ OFFSET(auto_convert), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC },
+ { NULL }
+};
+
+static const AVClass concat_class = {
+ .class_name = "concat demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+
+AVInputFormat ff_concat_demuxer = {
+ .name = "concat",
+ .long_name = NULL_IF_CONFIG_SMALL("Virtual concatenation script"),
+ .priv_data_size = sizeof(ConcatContext),
+ .read_probe = concat_probe,
+ .read_header = concat_read_header,
+ .read_packet = concat_read_packet,
+ .read_close = concat_read_close,
+ .read_seek2 = concat_seek,
+ .priv_class = &concat_class,
+};
diff --git a/libavformat/crcenc.c b/libavformat/crcenc.c
index 925646bc5e..3fdfea5207 100644
--- a/libavformat/crcenc.c
+++ b/libavformat/crcenc.c
@@ -2,20 +2,20 @@
* CRC encoder (for codec/format testing)
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -59,7 +59,6 @@ static int crc_write_trailer(struct AVFormatContext *s)
AVOutputFormat ff_crc_muxer = {
.name = "crc",
.long_name = NULL_IF_CONFIG_SMALL("CRC testing"),
- .extensions = "",
.priv_data_size = sizeof(CRCState),
.audio_codec = AV_CODEC_ID_PCM_S16LE,
.video_codec = AV_CODEC_ID_RAWVIDEO,
diff --git a/libavformat/crypto.c b/libavformat/crypto.c
index 6b19b5ee23..f56ee4eb17 100644
--- a/libavformat/crypto.c
+++ b/libavformat/crypto.c
@@ -2,20 +2,20 @@
* Decryption protocol handler
* Copyright (c) 2011 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -41,14 +41,32 @@ typedef struct CryptoContext {
int keylen;
uint8_t *iv;
int ivlen;
- struct AVAES *aes;
+ uint8_t *decrypt_key;
+ int decrypt_keylen;
+ uint8_t *decrypt_iv;
+ int decrypt_ivlen;
+ uint8_t *encrypt_key;
+ int encrypt_keylen;
+ uint8_t *encrypt_iv;
+ int encrypt_ivlen;
+ struct AVAES *aes_decrypt;
+ struct AVAES *aes_encrypt;
+
+ uint8_t pad[BLOCKSIZE];
+ int pad_len;
+
} CryptoContext;
#define OFFSET(x) offsetof(CryptoContext, x)
#define D AV_OPT_FLAG_DECODING_PARAM
+#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
- {"key", "AES decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, .flags = D },
- {"iv", "AES decryption initialization vector", OFFSET(iv), AV_OPT_TYPE_BINARY, .flags = D },
+ {"key", "AES encryption/decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, .flags = D|E },
+ {"iv", "AES encryption/decryption initialization vector", OFFSET(iv), AV_OPT_TYPE_BINARY, .flags = D|E },
+ {"decryption_key", "AES decryption key", OFFSET(decrypt_key), AV_OPT_TYPE_BINARY, .flags = D },
+ {"decryption_iv", "AES decryption initialization vector", OFFSET(decrypt_iv), AV_OPT_TYPE_BINARY, .flags = D },
+ {"encryption_key", "AES encryption key", OFFSET(encrypt_key), AV_OPT_TYPE_BINARY, .flags = E },
+ {"encryption_iv", "AES encryption initialization vector", OFFSET(encrypt_iv), AV_OPT_TYPE_BINARY, .flags = E },
{ NULL }
};
@@ -59,7 +77,34 @@ static const AVClass crypto_class = {
.version = LIBAVUTIL_VERSION_INT,
};
-static int crypto_open(URLContext *h, const char *uri, int flags)
+static int set_aes_arg(CryptoContext *c, uint8_t **buf, int *buf_len,
+ uint8_t *default_buf, int default_buf_len,
+ const char *desc)
+{
+ if (!*buf_len) {
+ if (!default_buf_len) {
+ av_log(c, AV_LOG_ERROR, "%s not set\n", desc);
+ return AVERROR(EINVAL);
+ } else if (default_buf_len != BLOCKSIZE) {
+ av_log(c, AV_LOG_ERROR,
+ "invalid %s size (%d bytes, block size is %d)\n",
+ desc, default_buf_len, BLOCKSIZE);
+ return AVERROR(EINVAL);
+ }
+ *buf = av_memdup(default_buf, default_buf_len);
+ if (!*buf)
+ return AVERROR(ENOMEM);
+ *buf_len = default_buf_len;
+ } else if (*buf_len != BLOCKSIZE) {
+ av_log(c, AV_LOG_ERROR,
+ "invalid %s size (%d bytes, block size is %d)\n",
+ desc, *buf_len, BLOCKSIZE);
+ return AVERROR(EINVAL);
+ }
+ return 0;
+}
+
+static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary **options)
{
const char *nested_url;
int ret = 0;
@@ -72,28 +117,54 @@ static int crypto_open(URLContext *h, const char *uri, int flags)
goto err;
}
- if (c->keylen < BLOCKSIZE || c->ivlen < BLOCKSIZE) {
- av_log(h, AV_LOG_ERROR, "Key or IV not set\n");
- ret = AVERROR(EINVAL);
- goto err;
+ if (flags & AVIO_FLAG_READ) {
+ if ((ret = set_aes_arg(c, &c->decrypt_key, &c->decrypt_keylen,
+ c->key, c->keylen, "decryption key")) < 0)
+ goto err;
+ if ((ret = set_aes_arg(c, &c->decrypt_iv, &c->decrypt_ivlen,
+ c->iv, c->ivlen, "decryption IV")) < 0)
+ goto err;
}
+
if (flags & AVIO_FLAG_WRITE) {
- av_log(h, AV_LOG_ERROR, "Only decryption is supported currently\n");
- ret = AVERROR(ENOSYS);
- goto err;
+ if ((ret = set_aes_arg(c, &c->encrypt_key, &c->encrypt_keylen,
+ c->key, c->keylen, "encryption key")) < 0)
+ if (ret < 0)
+ goto err;
+ if ((ret = set_aes_arg(c, &c->encrypt_iv, &c->encrypt_ivlen,
+ c->iv, c->ivlen, "encryption IV")) < 0)
+ goto err;
}
- if ((ret = ffurl_open(&c->hd, nested_url, AVIO_FLAG_READ,
- &h->interrupt_callback, NULL)) < 0) {
- av_log(h, AV_LOG_ERROR, "Unable to open input\n");
+
+ if ((ret = ffurl_open(&c->hd, nested_url, flags,
+ &h->interrupt_callback, options)) < 0) {
+ av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", nested_url);
goto err;
}
- c->aes = av_aes_alloc();
- if (!c->aes) {
- ret = AVERROR(ENOMEM);
- goto err;
+
+ if (flags & AVIO_FLAG_READ) {
+ c->aes_decrypt = av_aes_alloc();
+ if (!c->aes_decrypt) {
+ ret = AVERROR(ENOMEM);
+ goto err;
+ }
+ ret = av_aes_init(c->aes_decrypt, c->decrypt_key, BLOCKSIZE*8, 1);
+ if (ret < 0)
+ goto err;
}
- av_aes_init(c->aes, c->key, 128, 1);
+ if (flags & AVIO_FLAG_WRITE) {
+ c->aes_encrypt = av_aes_alloc();
+ if (!c->aes_encrypt) {
+ ret = AVERROR(ENOMEM);
+ goto err;
+ }
+ ret = av_aes_init(c->aes_encrypt, c->encrypt_key, BLOCKSIZE*8, 0);
+ if (ret < 0)
+ goto err;
+ }
+
+ c->pad_len = 0;
h->is_streamed = 1;
@@ -131,8 +202,8 @@ retry:
return AVERROR_EOF;
if (!c->eof)
blocks--;
- av_aes_crypt(c->aes, c->outbuffer, c->inbuffer + c->indata_used, blocks,
- c->iv, 1);
+ av_aes_crypt(c->aes_decrypt, c->outbuffer, c->inbuffer + c->indata_used,
+ blocks, c->decrypt_iv, 1);
c->outdata = BLOCKSIZE * blocks;
c->outptr = c->outbuffer;
c->indata_used += BLOCKSIZE * blocks;
@@ -150,19 +221,73 @@ retry:
goto retry;
}
+static int crypto_write(URLContext *h, const unsigned char *buf, int size)
+{
+ CryptoContext *c = h->priv_data;
+ int total_size, blocks, pad_len, out_size;
+ uint8_t *out_buf;
+ int ret = 0;
+
+ total_size = size + c->pad_len;
+ pad_len = total_size % BLOCKSIZE;
+ out_size = total_size - pad_len;
+ blocks = out_size / BLOCKSIZE;
+
+ if (out_size) {
+ out_buf = av_malloc(out_size);
+ if (!out_buf)
+ return AVERROR(ENOMEM);
+
+ if (c->pad_len) {
+ memcpy(&c->pad[c->pad_len], buf, BLOCKSIZE - c->pad_len);
+ av_aes_crypt(c->aes_encrypt, out_buf, c->pad, 1, c->encrypt_iv, 0);
+ blocks--;
+ }
+
+ av_aes_crypt(c->aes_encrypt, &out_buf[c->pad_len ? BLOCKSIZE : 0],
+ &buf[c->pad_len ? BLOCKSIZE - c->pad_len: 0],
+ blocks, c->encrypt_iv, 0);
+
+ ret = ffurl_write(c->hd, out_buf, out_size);
+ av_free(out_buf);
+ if (ret < 0)
+ return ret;
+
+ memcpy(c->pad, &buf[size - pad_len], pad_len);
+ } else
+ memcpy(&c->pad[c->pad_len], buf, size);
+
+ c->pad_len = pad_len;
+
+ return size;
+}
+
static int crypto_close(URLContext *h)
{
CryptoContext *c = h->priv_data;
+ uint8_t out_buf[BLOCKSIZE];
+ int ret, pad;
+
+ if (c->aes_encrypt) {
+ pad = BLOCKSIZE - c->pad_len;
+ memset(&c->pad[c->pad_len], pad, pad);
+ av_aes_crypt(c->aes_encrypt, out_buf, c->pad, 1, c->encrypt_iv, 0);
+ if ((ret = ffurl_write(c->hd, out_buf, BLOCKSIZE)) < 0)
+ return ret;
+ }
+
if (c->hd)
ffurl_close(c->hd);
- av_freep(&c->aes);
+ av_freep(&c->aes_decrypt);
+ av_freep(&c->aes_encrypt);
return 0;
}
URLProtocol ff_crypto_protocol = {
.name = "crypto",
- .url_open = crypto_open,
+ .url_open2 = crypto_open2,
.url_read = crypto_read,
+ .url_write = crypto_write,
.url_close = crypto_close,
.priv_data_size = sizeof(CryptoContext),
.priv_data_class = &crypto_class,
diff --git a/libavformat/cutils.c b/libavformat/cutils.c
index f5339a9c5d..d86ba05441 100644
--- a/libavformat/cutils.c
+++ b/libavformat/cutils.c
@@ -2,20 +2,20 @@
* 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
*/
@@ -23,27 +23,6 @@
#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/dashenc.c b/libavformat/dashenc.c
index d715654ca7..8b6be7133e 100644
--- a/libavformat/dashenc.c
+++ b/libavformat/dashenc.c
@@ -2,20 +2,20 @@
* MPEG-DASH ISO BMFF segmenter
* Copyright (c) 2014 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
*/
@@ -525,7 +525,7 @@ static int write_manifest(AVFormatContext *s, int final)
avio_printf(out, "</MPD>\n");
avio_flush(out);
avio_close(out);
- return ff_rename(temp_filename, s->filename);
+ return ff_rename(temp_filename, s->filename, s);
}
static int dash_write_header(AVFormatContext *s)
@@ -574,7 +574,9 @@ static int dash_write_header(AVFormatContext *s)
AVDictionary *opts = NULL;
char filename[1024];
- os->bit_rate = s->streams[i]->codec->bit_rate;
+ os->bit_rate = s->streams[i]->codec->bit_rate ?
+ s->streams[i]->codec->bit_rate :
+ s->streams[i]->codec->rc_max_rate;
if (os->bit_rate) {
snprintf(os->bandwidth_str, sizeof(os->bandwidth_str),
" bandwidth=\"%d\"", os->bit_rate);
@@ -793,7 +795,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream)
} else {
ffurl_close(os->out);
os->out = NULL;
- ret = ff_rename(temp_path, full_path);
+ ret = ff_rename(temp_path, full_path, s);
if (ret < 0)
break;
}
@@ -885,7 +887,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
else
os->max_pts = FFMAX(os->max_pts, pkt->pts + pkt->duration);
os->packets_written++;
- return ff_write_chained(os->ctx, 0, pkt, s);
+ return ff_write_chained(os->ctx, 0, pkt, s, 0);
}
static int dash_write_trailer(AVFormatContext *s)
diff --git a/libavformat/data_uri.c b/libavformat/data_uri.c
new file mode 100644
index 0000000000..1598b999ae
--- /dev/null
+++ b/libavformat/data_uri.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2012 Nicolas George
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include "libavutil/avstring.h"
+#include "libavutil/base64.h"
+#include "url.h"
+
+typedef struct {
+ const uint8_t *data;
+ void *tofree;
+ size_t size;
+ size_t pos;
+} DataContext;
+
+static av_cold int data_open(URLContext *h, const char *uri, int flags)
+{
+ DataContext *dc = h->priv_data;
+ const char *data, *opt, *next;
+ char *ddata;
+ int ret, base64 = 0;
+ size_t in_size;
+
+ /* data:content/type[;base64],payload */
+
+ av_strstart(uri, "data:", &uri);
+ data = strchr(uri, ',');
+ if (!data) {
+ av_log(h, AV_LOG_ERROR, "No ',' delimiter in URI\n");
+ return AVERROR(EINVAL);
+ }
+ opt = uri;
+ while (opt < data) {
+ next = av_x_if_null(memchr(opt, ';', data - opt), data);
+ if (opt == uri) {
+ if (!memchr(opt, '/', next - opt)) { /* basic validity check */
+ av_log(h, AV_LOG_ERROR, "Invalid content-type '%.*s'\n",
+ (int)(next - opt), opt);
+ return AVERROR(EINVAL);
+ }
+ av_log(h, AV_LOG_VERBOSE, "Content-type: %.*s\n",
+ (int)(next - opt), opt);
+ } else {
+ if (!av_strncasecmp(opt, "base64", next - opt)) {
+ base64 = 1;
+ } else {
+ av_log(h, AV_LOG_VERBOSE, "Ignoring option '%.*s'\n",
+ (int)(next - opt), opt);
+ }
+ }
+ opt = next + 1;
+ }
+
+ data++;
+ in_size = strlen(data);
+ if (base64) {
+ size_t out_size = 3 * (in_size / 4) + 1;
+
+ if (out_size > INT_MAX || !(ddata = av_malloc(out_size)))
+ return AVERROR(ENOMEM);
+ if ((ret = av_base64_decode(ddata, data, out_size)) < 0) {
+ av_free(ddata);
+ av_log(h, AV_LOG_ERROR, "Invalid base64 in URI\n");
+ return ret;
+ }
+ dc->data = dc->tofree = ddata;
+ dc->size = ret;
+ } else {
+ dc->data = data;
+ dc->size = in_size;
+ }
+ return 0;
+}
+
+static av_cold int data_close(URLContext *h)
+{
+ DataContext *dc = h->priv_data;
+
+ av_freep(&dc->tofree);
+ return 0;
+}
+
+static int data_read(URLContext *h, unsigned char *buf, int size)
+{
+ DataContext *dc = h->priv_data;
+
+ if (dc->pos >= dc->size)
+ return AVERROR_EOF;
+ size = FFMIN(size, dc->size - dc->pos);
+ memcpy(buf, dc->data + dc->pos, size);
+ dc->pos += size;
+ return size;
+}
+
+URLProtocol ff_data_protocol = {
+ .name = "data",
+ .url_open = data_open,
+ .url_close = data_close,
+ .url_read = data_read,
+ .priv_data_size = sizeof(DataContext),
+};
diff --git a/libavformat/dauddec.c b/libavformat/dauddec.c
index 1855e84aae..f6e7491985 100644
--- a/libavformat/dauddec.c
+++ b/libavformat/dauddec.c
@@ -2,20 +2,20 @@
* D-Cinema audio demuxer
* Copyright (c) 2005 Reimar Döffinger
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -41,7 +41,7 @@ static int daud_header(AVFormatContext *s) {
static int daud_packet(AVFormatContext *s, AVPacket *pkt) {
AVIOContext *pb = s->pb;
int ret, size;
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR(EIO);
size = avio_rb16(pb);
avio_rb16(pb); // unknown
@@ -55,5 +55,5 @@ AVInputFormat ff_daud_demuxer = {
.long_name = NULL_IF_CONFIG_SMALL("D-Cinema audio"),
.read_header = daud_header,
.read_packet = daud_packet,
- .extensions = "302",
+ .extensions = "302,daud",
};
diff --git a/libavformat/daudenc.c b/libavformat/daudenc.c
index 416e895395..99b18d35aa 100644
--- a/libavformat/daudenc.c
+++ b/libavformat/daudenc.c
@@ -2,20 +2,20 @@
* D-Cinema audio muxer
* Copyright (c) 2005 Reimar Döffinger
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/dfa.c b/libavformat/dfa.c
index c097d72cf8..450bc2125c 100644
--- a/libavformat/dfa.c
+++ b/libavformat/dfa.c
@@ -2,20 +2,20 @@
* Chronomaster DFA Format Demuxer
* Copyright (c) 2011 Konstantin Shishkov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -30,6 +30,9 @@ static int dfa_probe(AVProbeData *p)
if (p->buf_size < 4 || AV_RL32(p->buf) != MKTAG('D', 'F', 'I', 'A'))
return 0;
+ if (AV_RL32(p->buf + 16) != 0x80)
+ return AVPROBE_SCORE_MAX / 4;
+
return AVPROBE_SCORE_MAX;
}
@@ -38,13 +41,15 @@ static int dfa_read_header(AVFormatContext *s)
AVIOContext *pb = s->pb;
AVStream *st;
int frames;
+ int version;
uint32_t mspf;
if (avio_rl32(pb) != MKTAG('D', 'F', 'I', 'A')) {
av_log(s, AV_LOG_ERROR, "Invalid magic for DFA\n");
return AVERROR_INVALIDDATA;
}
- avio_skip(pb, 2); // unused
+
+ version = avio_rl16(pb);
frames = avio_rl16(pb);
st = avformat_new_stream(s, NULL);
@@ -64,6 +69,12 @@ static int dfa_read_header(AVFormatContext *s)
avio_skip(pb, 128 - 16); // padding
st->duration = frames;
+ if (ff_alloc_extradata(st->codec, 2))
+ return AVERROR(ENOMEM);
+ AV_WL16(st->codec->extradata, version);
+ if (version == 0x100)
+ st->sample_aspect_ratio = (AVRational){2, 1};
+
return 0;
}
@@ -73,12 +84,12 @@ static int dfa_read_packet(AVFormatContext *s, AVPacket *pkt)
uint32_t frame_size;
int ret, first = 1;
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR_EOF;
if (av_get_packet(pb, pkt, 12) != 12)
return AVERROR(EIO);
- while (!pb->eof_reached) {
+ while (!avio_feof(pb)) {
if (!first) {
ret = av_append_packet(pb, pkt, 12);
if (ret < 0) {
diff --git a/libavformat/diracdec.c b/libavformat/diracdec.c
index f275212d4a..e061ba5e8e 100644
--- a/libavformat/diracdec.c
+++ b/libavformat/diracdec.c
@@ -2,20 +2,20 @@
* RAW Dirac demuxer
* Copyright (c) 2007 Marco Gerards <marco@gnu.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -25,10 +25,19 @@
static int dirac_probe(AVProbeData *p)
{
- if (AV_RL32(p->buf) == MKTAG('B', 'B', 'C', 'D'))
- return AVPROBE_SCORE_MAX;
- else
+ unsigned size;
+ if (AV_RL32(p->buf) != MKTAG('B', 'B', 'C', 'D'))
return 0;
+
+ size = AV_RB32(p->buf+5);
+ if (size < 13)
+ return 0;
+ if (size + 13LL > p->buf_size)
+ return AVPROBE_SCORE_MAX / 4;
+ if (AV_RL32(p->buf + size) != MKTAG('B', 'B', 'C', 'D'))
+ return 0;
+
+ return AVPROBE_SCORE_MAX;
}
FF_DEF_RAWVIDEO_DEMUXER(dirac, "raw Dirac", dirac_probe, NULL, AV_CODEC_ID_DIRAC)
diff --git a/libavformat/dnxhddec.c b/libavformat/dnxhddec.c
index 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/dsfdec.c b/libavformat/dsfdec.c
new file mode 100644
index 0000000000..ae198b2e93
--- /dev/null
+++ b/libavformat/dsfdec.c
@@ -0,0 +1,159 @@
+/*
+ * DSD Stream File (DSF) demuxer
+ * Copyright (c) 2014 Peter Ross
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "internal.h"
+#include "id3v2.h"
+
+typedef struct {
+ uint64_t data_end;
+} DSFContext;
+
+static int dsf_probe(AVProbeData *p)
+{
+ if (p->buf_size < 12 || memcmp(p->buf, "DSD ", 4) || AV_RL64(p->buf + 4) != 28)
+ return 0;
+ return AVPROBE_SCORE_MAX;
+}
+
+static const uint64_t dsf_channel_layout[] = {
+ 0,
+ AV_CH_LAYOUT_MONO,
+ AV_CH_LAYOUT_STEREO,
+ AV_CH_LAYOUT_SURROUND,
+ AV_CH_LAYOUT_QUAD,
+ AV_CH_LAYOUT_4POINT0,
+ AV_CH_LAYOUT_5POINT0_BACK,
+ AV_CH_LAYOUT_5POINT1_BACK,
+};
+
+static void read_id3(AVFormatContext *s, uint64_t id3pos)
+{
+ ID3v2ExtraMeta *id3v2_extra_meta = NULL;
+ if (avio_seek(s->pb, id3pos, SEEK_SET) < 0)
+ return;
+
+ ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, 0);
+ if (id3v2_extra_meta)
+ ff_id3v2_parse_apic(s, &id3v2_extra_meta);
+ ff_id3v2_free_extra_meta(&id3v2_extra_meta);
+}
+
+static int dsf_read_header(AVFormatContext *s)
+{
+ DSFContext *dsf = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVStream *st;
+ uint64_t id3pos;
+ unsigned int channel_type;
+
+ avio_skip(pb, 4);
+ if (avio_rl64(pb) != 28)
+ return AVERROR_INVALIDDATA;
+
+ /* create primary stream before any id3 coverart streams */
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avio_skip(pb, 8);
+ id3pos = avio_rl64(pb);
+ if (pb->seekable) {
+ read_id3(s, id3pos);
+ avio_seek(pb, 28, SEEK_SET);
+ }
+
+ /* fmt chunk */
+
+ if (avio_rl32(pb) != MKTAG('f', 'm', 't', ' ') || avio_rl64(pb) != 52)
+ return AVERROR_INVALIDDATA;
+
+ if (avio_rl32(pb) != 1) {
+ avpriv_request_sample(s, "unknown format version");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (avio_rl32(pb)) {
+ avpriv_request_sample(s, "unknown format id");
+ return AVERROR_INVALIDDATA;
+ }
+
+ channel_type = avio_rl32(pb);
+ if (channel_type < FF_ARRAY_ELEMS(dsf_channel_layout))
+ st->codec->channel_layout = dsf_channel_layout[channel_type];
+ if (!st->codec->channel_layout)
+ avpriv_request_sample(s, "channel type %i", channel_type);
+
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->channels = avio_rl32(pb);
+ st->codec->sample_rate = avio_rl32(pb) / 8;
+
+ switch(avio_rl32(pb)) {
+ case 1: st->codec->codec_id = AV_CODEC_ID_DSD_LSBF_PLANAR; break;
+ case 8: st->codec->codec_id = AV_CODEC_ID_DSD_MSBF_PLANAR; break;
+ default:
+ avpriv_request_sample(s, "unknown most significant bit");
+ return AVERROR_INVALIDDATA;
+ }
+
+ avio_skip(pb, 8);
+ st->codec->block_align = avio_rl32(pb);
+ if (st->codec->block_align > INT_MAX / st->codec->channels) {
+ avpriv_request_sample(s, "block_align overflow");
+ return AVERROR_INVALIDDATA;
+ }
+ st->codec->block_align *= st->codec->channels;
+ avio_skip(pb, 4);
+
+ /* data chunk */
+
+ dsf->data_end = avio_tell(pb);
+ if (avio_rl32(pb) != MKTAG('d', 'a', 't', 'a'))
+ return AVERROR_INVALIDDATA;
+ dsf->data_end += avio_rl64(pb);
+
+ return 0;
+}
+
+static int dsf_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ DSFContext *dsf = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVStream *st = s->streams[0];
+ int64_t pos = avio_tell(pb);
+
+ if (pos >= dsf->data_end)
+ return AVERROR_EOF;
+
+ pkt->stream_index = 0;
+ return av_get_packet(pb, pkt, FFMIN(dsf->data_end - pos, st->codec->block_align));
+}
+
+AVInputFormat ff_dsf_demuxer = {
+ .name = "dsf",
+ .long_name = NULL_IF_CONFIG_SMALL("DSD Stream File (DSF)"),
+ .priv_data_size = sizeof(DSFContext),
+ .read_probe = dsf_probe,
+ .read_header = dsf_read_header,
+ .read_packet = dsf_read_packet,
+ .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK,
+};
diff --git a/libavformat/dsicin.c b/libavformat/dsicin.c
index 6a7c8b948a..4b5a934ca1 100644
--- a/libavformat/dsicin.c
+++ b/libavformat/dsicin.c
@@ -2,20 +2,20 @@
* Delphine Software International CIN File Demuxer
* Copyright (c) 2006 Gregory Montoir (cyx@users.sourceforge.net)
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -28,6 +28,7 @@
#include "libavutil/intreadwrite.h"
#include "avformat.h"
#include "internal.h"
+#include "avio_internal.h"
typedef struct CinFileHeader {
@@ -149,7 +150,7 @@ static int cin_read_frame_header(CinDemuxContext *cin, AVIOContext *pb) {
hdr->video_frame_size = avio_rl32(pb);
hdr->audio_frame_size = avio_rl32(pb);
- if (pb->eof_reached || pb->error)
+ if (avio_feof(pb) || pb->error)
return AVERROR(EIO);
if (avio_rl32(pb) != 0xAA55AA55)
@@ -183,6 +184,8 @@ static int cin_read_packet(AVFormatContext *s, AVPacket *pkt)
/* palette and video packet */
pkt_size = (palette_type + 3) * hdr->pal_colors_count + hdr->video_frame_size;
+ pkt_size = ffio_limit(pb, pkt_size);
+
ret = av_new_packet(pkt, 4 + pkt_size);
if (ret < 0)
return ret;
diff --git a/libavformat/dtsdec.c b/libavformat/dtsdec.c
index 9010711599..d054f43537 100644
--- a/libavformat/dtsdec.c
+++ b/libavformat/dtsdec.c
@@ -2,24 +2,26 @@
* RAW DTS demuxer
* Copyright (c) 2008 Benjamin Larsson
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libavcodec/bytestream.h"
+#include "libavcodec/get_bits.h"
+#include "libavcodec/dca.h"
#include "avformat.h"
#include "rawdec.h"
@@ -32,34 +34,86 @@ static int dts_probe(AVProbeData *p)
{
const uint8_t *buf, *bufp;
uint32_t state = -1;
- int markers[3] = {0};
- int sum, max;
+ int markers[4*16] = {0};
+ int sum, max, i;
+ int64_t diff = 0;
+ uint8_t hdr[12 + FF_INPUT_BUFFER_PADDING_SIZE] = { 0 };
- buf = p->buf;
+ buf = p->buf + FFMIN(4096, p->buf_size);
for(; buf < (p->buf+p->buf_size)-2; buf+=2) {
+ int marker, sample_blocks, sample_rate, sr_code, framesize;
+ int lfe;
+ GetBitContext gb;
+
bufp = buf;
state = (state << 16) | bytestream_get_be16(&bufp);
+ if (buf - p->buf >= 4)
+ diff += FFABS(((int16_t)AV_RL16(buf)) - (int16_t)AV_RL16(buf-4));
+
/* regular bitstream */
- if (state == DCA_MARKER_RAW_BE || state == DCA_MARKER_RAW_LE)
- markers[0]++;
+ if (state == DCA_MARKER_RAW_BE)
+ marker = 0;
+ else if (state == DCA_MARKER_RAW_LE)
+ marker = 1;
/* 14 bits big-endian bitstream */
- if (state == DCA_MARKER_14B_BE)
- if ((bytestream_get_be16(&bufp) & 0xFFF0) == 0x07F0)
- markers[1]++;
+ else if (state == DCA_MARKER_14B_BE &&
+ (bytestream_get_be16(&bufp) & 0xFFF0) == 0x07F0)
+ marker = 2;
/* 14 bits little-endian bitstream */
- if (state == DCA_MARKER_14B_LE)
- if ((bytestream_get_be16(&bufp) & 0xF0FF) == 0xF007)
- markers[2]++;
+ else if (state == DCA_MARKER_14B_LE &&
+ (bytestream_get_be16(&bufp) & 0xF0FF) == 0xF007)
+ marker = 3;
+ else
+ continue;
+
+ if (avpriv_dca_convert_bitstream(buf-2, 12, hdr, 12) < 0)
+ continue;
+
+ init_get_bits(&gb, hdr, 96);
+ skip_bits_long(&gb, 39);
+
+ sample_blocks = get_bits(&gb, 7) + 1;
+ if (sample_blocks < 8)
+ continue;
+
+ framesize = get_bits(&gb, 14) + 1;
+ if (framesize < 95)
+ continue;
+
+ skip_bits(&gb, 6);
+ sr_code = get_bits(&gb, 4);
+ sample_rate = avpriv_dca_sample_rates[sr_code];
+ if (sample_rate == 0)
+ continue;
+
+ get_bits(&gb, 5);
+ if (get_bits(&gb, 1))
+ continue;
+
+ skip_bits_long(&gb, 9);
+ lfe = get_bits(&gb, 2);
+ if (lfe > 2)
+ continue;
+
+ marker += 4* sr_code;
+
+ markers[marker] ++;
}
- sum = markers[0] + markers[1] + markers[2];
- max = markers[1] > markers[0];
- max = markers[2] > markers[max] ? 2 : max;
+
+ sum = max = 0;
+ for (i=0; i<FF_ARRAY_ELEMS(markers); i++) {
+ sum += markers[i];
+ if (markers[max] < markers[i])
+ max = i;
+ }
+
if (markers[max] > 3 && p->buf_size / markers[max] < 32*1024 &&
- markers[max] * 4 > sum * 3)
+ markers[max] * 4 > sum * 3 &&
+ diff / p->buf_size > 200)
return AVPROBE_SCORE_EXTENSION + 1;
return 0;
diff --git a/libavformat/dtshddec.c b/libavformat/dtshddec.c
new file mode 100644
index 0000000000..0fd0304703
--- /dev/null
+++ b/libavformat/dtshddec.c
@@ -0,0 +1,139 @@
+/*
+ * Raw DTS-HD demuxer
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/dict.h"
+#include "avformat.h"
+
+#define AUPR_HDR 0x415550522D484452
+#define AUPRINFO 0x41555052494E464F
+#define BITSHVTB 0x4249545348565442
+#define BLACKOUT 0x424C41434B4F5554
+#define BRANCHPT 0x4252414E43485054
+#define BUILDVER 0x4255494C44564552
+#define CORESSMD 0x434F524553534D44
+#define DTSHDHDR 0x4454534844484452
+#define EXTSS_MD 0x45585453535f4d44
+#define FILEINFO 0x46494C45494E464F
+#define NAVI_TBL 0x4E4156492D54424C
+#define STRMDATA 0x5354524D44415441
+#define TIMECODE 0x54494D45434F4445
+
+typedef struct DTSHDDemuxContext {
+ uint64_t data_end;
+} DTSHDDemuxContext;
+
+static int dtshd_probe(AVProbeData *p)
+{
+ if (AV_RB64(p->buf) == DTSHDHDR)
+ return AVPROBE_SCORE_MAX;
+ return 0;
+}
+
+static int dtshd_read_header(AVFormatContext *s)
+{
+ DTSHDDemuxContext *dtshd = s->priv_data;
+ AVIOContext *pb = s->pb;
+ uint64_t chunk_type, chunk_size;
+ AVStream *st;
+ int ret;
+ char *value;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->codec_id = AV_CODEC_ID_DTS;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
+
+ while (!avio_feof(pb)) {
+ chunk_type = avio_rb64(pb);
+ chunk_size = avio_rb64(pb);
+
+ if (chunk_size < 4) {
+ av_log(s, AV_LOG_ERROR, "chunk size too small\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if (chunk_size > ((uint64_t)1 << 61)) {
+ av_log(s, AV_LOG_ERROR, "chunk size too big\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ switch (chunk_type) {
+ case STRMDATA:
+ dtshd->data_end = chunk_size + avio_tell(pb);
+ if (dtshd->data_end <= chunk_size)
+ return AVERROR_INVALIDDATA;
+ return 0;
+ break;
+ case FILEINFO:
+ if (chunk_size > INT_MAX)
+ goto skip;
+ value = av_malloc(chunk_size);
+ if (!value)
+ goto skip;
+ avio_read(pb, value, chunk_size);
+ value[chunk_size - 1] = 0;
+ av_dict_set(&s->metadata, "fileinfo", value,
+ AV_DICT_DONT_STRDUP_VAL);
+ break;
+ default:
+skip:
+ ret = avio_skip(pb, chunk_size);
+ if (ret < 0)
+ return ret;
+ };
+ }
+
+ return AVERROR_EOF;
+}
+
+static int raw_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ DTSHDDemuxContext *dtshd = s->priv_data;
+ int64_t size, left;
+ int ret;
+
+ left = dtshd->data_end - avio_tell(s->pb);
+ size = FFMIN(left, 1024);
+ if (size <= 0)
+ return AVERROR_EOF;
+
+ ret = av_get_packet(s->pb, pkt, size);
+ if (ret < 0)
+ return ret;
+
+ pkt->stream_index = 0;
+
+ return ret;
+}
+
+AVInputFormat ff_dtshd_demuxer = {
+ .name = "dtshd",
+ .long_name = NULL_IF_CONFIG_SMALL("raw DTS-HD"),
+ .priv_data_size = sizeof(DTSHDDemuxContext),
+ .read_probe = dtshd_probe,
+ .read_header = dtshd_read_header,
+ .read_packet = raw_read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+ .extensions = "dtshd",
+ .raw_codec_id = AV_CODEC_ID_DTS,
+};
diff --git a/libavformat/dump.c b/libavformat/dump.c
index 99188f0a51..56b37ff7d8 100644
--- a/libavformat/dump.c
+++ b/libavformat/dump.c
@@ -1,20 +1,21 @@
/*
- * Various pretty-printing functions for use within Libav
+ * Various pretty-printing functions for use within FFmpeg
+ * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -26,6 +27,8 @@
#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
#include "libavutil/mathematics.h"
+#include "libavutil/opt.h"
+#include "libavutil/avstring.h"
#include "libavutil/replaygain.h"
#include "libavutil/stereo3d.h"
@@ -76,7 +79,7 @@ void av_hex_dump_log(void *avcl, int level, const uint8_t *buf, int size)
hex_dump_internal(avcl, NULL, level, buf, size);
}
-static void pkt_dump_internal(void *avcl, FILE *f, int level, AVPacket *pkt,
+static void pkt_dump_internal(void *avcl, FILE *f, int level, const AVPacket *pkt,
int dump_payload, AVRational time_base)
{
HEXDUMP_PRINT("stream #%d:\n", pkt->stream_index);
@@ -100,13 +103,13 @@ static void pkt_dump_internal(void *avcl, FILE *f, int level, AVPacket *pkt,
av_hex_dump(f, pkt->data, pkt->size);
}
-void av_pkt_dump2(FILE *f, AVPacket *pkt, int dump_payload, AVStream *st)
+void av_pkt_dump2(FILE *f, const AVPacket *pkt, int dump_payload, const AVStream *st)
{
pkt_dump_internal(NULL, f, 0, pkt, dump_payload, st->time_base);
}
-void av_pkt_dump_log2(void *avcl, int level, AVPacket *pkt, int dump_payload,
- AVStream *st)
+void av_pkt_dump_log2(void *avcl, int level, const AVPacket *pkt, int dump_payload,
+ const AVStream *st)
{
pkt_dump_internal(avcl, NULL, level, pkt, dump_payload, st->time_base);
}
@@ -130,9 +133,22 @@ static void dump_metadata(void *ctx, AVDictionary *m, const char *indent)
av_log(ctx, AV_LOG_INFO, "%sMetadata:\n", indent);
while ((tag = av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX)))
- if (strcmp("language", tag->key))
+ if (strcmp("language", tag->key)) {
+ const char *p = tag->value;
av_log(ctx, AV_LOG_INFO,
- "%s %-16s: %s\n", indent, tag->key, tag->value);
+ "%s %-16s: ", indent, tag->key);
+ while (*p) {
+ char tmp[256];
+ size_t len = strcspn(p, "\x8\xa\xb\xc\xd");
+ av_strlcpy(tmp, p, FFMIN(sizeof(tmp), len+1));
+ av_log(ctx, AV_LOG_INFO, "%s", tmp);
+ p += len;
+ if (*p == 0xd) av_log(ctx, AV_LOG_INFO, " ");
+ if (*p == 0xa) av_log(ctx, AV_LOG_INFO, "\n%s %-16s: ", indent, "");
+ if (*p) p++;
+ }
+ av_log(ctx, AV_LOG_INFO, "\n");
+ }
}
}
@@ -330,8 +346,15 @@ static void dump_stream_format(AVFormatContext *ic, int i,
int flags = (is_output ? ic->oformat->flags : ic->iformat->flags);
AVStream *st = ic->streams[i];
AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0);
+ char *separator = ic->dump_separator;
+ char **codec_separator = av_opt_ptr(st->codec->av_class, st->codec, "dump_separator");
+ int use_format_separator = !*codec_separator;
+ if (use_format_separator)
+ *codec_separator = av_strdup(separator);
avcodec_string(buf, sizeof(buf), st->codec, is_output);
+ if (use_format_separator)
+ av_freep(codec_separator);
av_log(NULL, AV_LOG_INFO, " Stream #%d:%d", index, i);
/* the pid is an important information, so we display it */
@@ -351,20 +374,24 @@ static void dump_stream_format(AVFormatContext *ic, int i,
st->codec->width * st->sample_aspect_ratio.num,
st->codec->height * st->sample_aspect_ratio.den,
1024 * 1024);
- av_log(NULL, AV_LOG_INFO, ", PAR %d:%d DAR %d:%d",
+ av_log(NULL, AV_LOG_INFO, ", SAR %d:%d DAR %d:%d",
st->sample_aspect_ratio.num, st->sample_aspect_ratio.den,
display_aspect_ratio.num, display_aspect_ratio.den);
}
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
int fps = st->avg_frame_rate.den && st->avg_frame_rate.num;
+ int tbr = st->r_frame_rate.den && st->r_frame_rate.num;
int tbn = st->time_base.den && st->time_base.num;
int tbc = st->codec->time_base.den && st->codec->time_base.num;
- if (fps || tbn || tbc)
- av_log(NULL, AV_LOG_INFO, "\n ");
+ if (fps || tbr || tbn || tbc)
+ av_log(NULL, AV_LOG_INFO, "%s", separator);
+
if (fps)
- print_fps(av_q2d(st->avg_frame_rate), tbn || tbc ? "fps, " : "fps");
+ print_fps(av_q2d(st->avg_frame_rate), tbr || tbn || tbc ? "fps, " : "fps");
+ if (tbr)
+ print_fps(av_q2d(st->r_frame_rate), tbn || tbc ? "tbr, " : "tbr");
if (tbn)
print_fps(1 / av_q2d(st->time_base), tbc ? "tbn, " : "tbn");
if (tbc)
@@ -417,8 +444,9 @@ void av_dump_format(AVFormatContext *ic, int index,
av_log(NULL, AV_LOG_INFO, " Duration: ");
if (ic->duration != AV_NOPTS_VALUE) {
int hours, mins, secs, us;
- secs = ic->duration / AV_TIME_BASE;
- us = ic->duration % AV_TIME_BASE;
+ int64_t duration = ic->duration + 5000;
+ secs = duration / AV_TIME_BASE;
+ us = duration % AV_TIME_BASE;
mins = secs / 60;
secs %= 60;
hours = mins / 60;
diff --git a/libavformat/dv.c b/libavformat/dv.c
index 1ba8698d20..485a5f8742 100644
--- a/libavformat/dv.c
+++ b/libavformat/dv.c
@@ -12,20 +12,20 @@
* Copyright (c) 2006 Daniel Maas <dmaas@maasdigital.com>
* Funded by BBC Research & Development
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <time.h>
@@ -36,7 +36,9 @@
#include "libavutil/channel_layout.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
+#include "libavutil/timecode.h"
#include "dv.h"
+#include "libavutil/avassert.h"
struct DVDemuxContext {
const AVDVProfile* sys; /* Current DV profile. E.g.: 525/60, 625/50 */
@@ -70,27 +72,33 @@ static inline uint16_t dv_audio_12to16(uint16_t sample)
return result;
}
-/*
- * This is the dumbest implementation of all -- it simply looks at
- * a fixed offset and if pack isn't there -- fails. We might want
- * to have a fallback mechanism for complete search of missing packs.
- */
-static const uint8_t *dv_extract_pack(uint8_t *frame, enum dv_pack_type t)
+static const uint8_t *dv_extract_pack(const uint8_t *frame, enum dv_pack_type t)
{
int offs;
+ int c;
- switch (t) {
- case dv_audio_source:
- offs = (80 * 6 + 80 * 16 * 3 + 3);
- break;
- case dv_audio_control:
- offs = (80 * 6 + 80 * 16 * 4 + 3);
- break;
- case dv_video_control:
- offs = (80 * 5 + 48 + 5);
- break;
- default:
- return NULL;
+ for (c = 0; c < 10; c++) {
+ switch (t) {
+ case dv_audio_source:
+ if (c&1) offs = (80 * 6 + 80 * 16 * 0 + 3 + c*12000);
+ else offs = (80 * 6 + 80 * 16 * 3 + 3 + c*12000);
+ break;
+ case dv_audio_control:
+ if (c&1) offs = (80 * 6 + 80 * 16 * 1 + 3 + c*12000);
+ else offs = (80 * 6 + 80 * 16 * 4 + 3 + c*12000);
+ break;
+ case dv_video_control:
+ if (c&1) offs = (80 * 3 + 8 + c*12000);
+ else offs = (80 * 5 + 48 + 5 + c*12000);
+ break;
+ case dv_timecode:
+ offs = (80*1 + 3 + 3);
+ break;
+ default:
+ return NULL;
+ }
+ if (frame[offs] == t)
+ break;
}
return frame[offs] == t ? &frame[offs] : NULL;
@@ -108,7 +116,7 @@ static const int dv_audio_frequency[3] = {
* 3. Audio is always returned as 16bit linear samples: 12bit nonlinear samples
* are converted into 16bit linear ones.
*/
-static int dv_extract_audio(uint8_t *frame, uint8_t **ppcm,
+static int dv_extract_audio(const uint8_t *frame, uint8_t **ppcm,
const AVDVProfile *sys)
{
int size, chan, i, j, d, of, smpls, freq, quant, half_ch;
@@ -137,9 +145,14 @@ static int dv_extract_audio(uint8_t *frame, uint8_t **ppcm,
* channels 0,1 and odd 2,3. */
ipcm = (sys->height == 720 && !(frame[1] & 0x0C)) ? 2 : 0;
+ if (ipcm + sys->n_difchan > (quant == 1 ? 2 : 4)) {
+ av_log(NULL, AV_LOG_ERROR, "too many dv pcm frames\n");
+ return AVERROR_INVALIDDATA;
+ }
+
/* for each DIF channel */
for (chan = 0; chan < sys->n_difchan; chan++) {
- /* next stereo channel (50Mbps and 100Mbps only) */
+ av_assert0(ipcm<4);
pcm = ppcm[ipcm++];
if (!pcm)
break;
@@ -149,6 +162,7 @@ static int dv_extract_audio(uint8_t *frame, uint8_t **ppcm,
frame += 6 * 80; /* skip DIF segment header */
if (quant == 1 && i == half_ch) {
/* next stereo channel (12bit mode only) */
+ av_assert0(ipcm<4);
pcm = ppcm[ipcm++];
if (!pcm)
break;
@@ -205,7 +219,7 @@ static int dv_extract_audio(uint8_t *frame, uint8_t **ppcm,
return size;
}
-static int dv_extract_audio_info(DVDemuxContext *c, uint8_t *frame)
+static int dv_extract_audio_info(DVDemuxContext *c, const uint8_t *frame)
{
const uint8_t *as_pack;
int freq, stype, smpls, quant, i, ach;
@@ -265,7 +279,7 @@ static int dv_extract_audio_info(DVDemuxContext *c, uint8_t *frame)
return (c->sys->audio_min_samples[freq] + smpls) * 4; /* 2ch, 2bytes */
}
-static int dv_extract_video_info(DVDemuxContext *c, uint8_t *frame)
+static int dv_extract_video_info(DVDemuxContext *c, const uint8_t *frame)
{
const uint8_t *vsc_pack;
AVCodecContext *avctx;
@@ -278,11 +292,6 @@ static int dv_extract_video_info(DVDemuxContext *c, uint8_t *frame)
avpriv_set_pts_info(c->vst, 64, c->sys->time_base.num,
c->sys->time_base.den);
c->vst->avg_frame_rate = av_inv_q(c->vst->time_base);
- if (!avctx->width) {
- avctx->width = c->sys->width;
- avctx->height = c->sys->height;
- }
- avctx->pix_fmt = c->sys->pix_fmt;
/* finding out SAR is a little bit messy */
vsc_pack = dv_extract_pack(frame, dv_video_control);
@@ -298,6 +307,22 @@ static int dv_extract_video_info(DVDemuxContext *c, uint8_t *frame)
return size;
}
+static int dv_extract_timecode(DVDemuxContext* c, const uint8_t* frame, char *tc)
+{
+ const uint8_t *tc_pack;
+
+ // For PAL systems, drop frame bit is replaced by an arbitrary
+ // bit so its value should not be considered. Drop frame timecode
+ // is only relevant for NTSC systems.
+ int prevent_df = c->sys->ltc_divisor == 25 || c->sys->ltc_divisor == 50;
+
+ tc_pack = dv_extract_pack(frame, dv_timecode);
+ if (!tc_pack)
+ return 0;
+ av_timecode_make_smpte_tc_string(tc, AV_RB32(tc_pack + 1), prevent_df);
+ return 1;
+}
+
/* The following 3 functions constitute our interface to the world */
DVDemuxContext *avpriv_dv_init_demux(AVFormatContext *s)
@@ -341,7 +366,7 @@ int avpriv_dv_get_packet(DVDemuxContext *c, AVPacket *pkt)
}
int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt,
- uint8_t *buf, int buf_size)
+ uint8_t *buf, int buf_size, int64_t pos)
{
int size, i;
uint8_t *ppcm[5] = { 0 };
@@ -356,6 +381,7 @@ int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt,
/* FIXME: in case of no audio/bad audio we have to do something */
size = dv_extract_audio_info(c, buf);
for (i = 0; i < c->ach; i++) {
+ c->audio_pkt[i].pos = pos;
c->audio_pkt[i].size = size;
c->audio_pkt[i].pts = c->abytes * 30000 * 8 /
c->ast[i]->codec->bit_rate;
@@ -381,6 +407,7 @@ int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt,
size = dv_extract_video_info(c, buf);
av_init_packet(pkt);
pkt->data = buf;
+ pkt->pos = pos;
pkt->size = size;
pkt->flags |= AV_PKT_FLAG_KEY;
pkt->stream_index = c->vst->index;
@@ -395,8 +422,8 @@ static int64_t dv_frame_offset(AVFormatContext *s, DVDemuxContext *c,
int64_t timestamp, int flags)
{
// FIXME: sys may be wrong if last dv_read_packet() failed (buffer is junk)
- const AVDVProfile *sys = av_dv_codec_profile(c->vst->codec->width, c->vst->codec->height,
- c->vst->codec->pix_fmt);
+ const AVDVProfile *sys = av_dv_codec_profile2(c->vst->codec->width, c->vst->codec->height,
+ c->vst->codec->pix_fmt, c->vst->codec->time_base);
int64_t offset;
int64_t size = avio_size(s->pb) - s->internal->data_offset;
int64_t max_offset = ((size - 1) / sys->frame_size) * sys->frame_size;
@@ -414,10 +441,13 @@ static int64_t dv_frame_offset(AVFormatContext *s, DVDemuxContext *c,
void ff_dv_offset_reset(DVDemuxContext *c, int64_t frame_offset)
{
c->frames = frame_offset;
- if (c->ach)
+ if (c->ach) {
+ if (c->sys) {
c->abytes = av_rescale_q(c->frames, c->sys->time_base,
(AVRational) { 8, c->ast[0]->codec->bit_rate });
-
+ } else
+ av_log(c->fctx, AV_LOG_ERROR, "cannot adjust audio bytes\n");
+ }
c->audio_pkt[0].size = c->audio_pkt[1].size = 0;
c->audio_pkt[2].size = c->audio_pkt[3].size = 0;
}
@@ -431,6 +461,41 @@ 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;
+ if (!partial_frame)
+ return AVERROR(ENOMEM);
+
+ ret = avio_read(s->pb, partial_frame, partial_frame_size);
+ if (ret < 0)
+ goto finish;
+
+ if (ret < partial_frame_size) {
+ ret = -1;
+ goto finish;
+ }
+
+ ret = dv_extract_timecode(c->dv_demux, partial_frame, timecode);
+ if (ret)
+ av_dict_set(&s->metadata, "timecode", timecode, 0);
+ else
+ av_log(s, AV_LOG_ERROR, "Detected timecode is invalid\n");
+
+finish:
+ av_free(partial_frame);
+ avio_seek(s->pb, pos, SEEK_SET);
+ return ret;
+}
+
static int dv_read_header(AVFormatContext *s)
{
unsigned state, marker_pos = 0;
@@ -442,7 +507,7 @@ static int dv_read_header(AVFormatContext *s)
state = avio_rb32(s->pb);
while ((state & 0xffffff7f) != 0x1f07003f) {
- if (s->pb->eof_reached) {
+ if (avio_feof(s->pb)) {
av_log(s, AV_LOG_ERROR, "Cannot find DV header.\n");
return -1;
}
@@ -457,7 +522,7 @@ static int dv_read_header(AVFormatContext *s)
}
AV_WB32(c->buf, state);
- if (avio_read(s->pb, c->buf + 4, DV_PROFILE_BYTES - 4) <= 0 ||
+ if (avio_read(s->pb, c->buf + 4, DV_PROFILE_BYTES - 4) != DV_PROFILE_BYTES - 4 ||
avio_seek(s->pb, -DV_PROFILE_BYTES, SEEK_CUR) < 0)
return AVERROR(EIO);
@@ -474,6 +539,9 @@ static int dv_read_header(AVFormatContext *s)
(AVRational) { 8, 1 },
c->dv_demux->sys->time_base);
+ if (s->pb->seekable)
+ dv_read_timecode(s);
+
return 0;
}
@@ -485,13 +553,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;
@@ -514,37 +583,43 @@ static int dv_read_seek(AVFormatContext *s, int stream_index,
static int dv_read_close(AVFormatContext *s)
{
RawDVContext *c = s->priv_data;
- av_free(c->dv_demux);
+ av_freep(&c->dv_demux);
return 0;
}
static int dv_probe(AVProbeData *p)
{
- unsigned state, marker_pos = 0;
+ unsigned marker_pos = 0;
int i;
int matches = 0;
+ int firstmatch = 0;
int secondary_matches = 0;
if (p->buf_size < 5)
return 0;
- state = AV_RB32(p->buf);
- for (i = 4; i < p->buf_size; i++) {
- if ((state & 0xffffff7f) == 0x1f07003f)
- matches++;
- // any section header, also with seq/chan num != 0,
- // should appear around every 12000 bytes, at least 10 per frame
- if ((state & 0xff07ff7f) == 0x1f07003f)
- secondary_matches++;
- if (state == 0x003f0700 || state == 0xff3f0700)
- marker_pos = i;
- if (state == 0xff3f0701 && i - marker_pos == 80)
- matches++;
- state = (state << 8) | p->buf[i];
+ for (i = 0; i < p->buf_size-4; i++) {
+ unsigned state = AV_RB32(p->buf+i);
+ if ((state & 0x0007f840) == 0x00070000) {
+ // any section header, also with seq/chan num != 0,
+ // should appear around every 12000 bytes, at least 10 per frame
+ if ((state & 0xff07ff7f) == 0x1f07003f) {
+ secondary_matches++;
+ if ((state & 0xffffff7f) == 0x1f07003f) {
+ matches++;
+ if (!i)
+ firstmatch = 1;
+ }
+ }
+ if (state == 0x003f0700 || state == 0xff3f0700)
+ marker_pos = i;
+ if (state == 0xff3f0701 && i - marker_pos == 80)
+ matches++;
+ }
}
if (matches && p->buf_size / matches < 1024 * 1024) {
- if (matches > 4 ||
+ if (matches > 4 || firstmatch ||
(secondary_matches >= 10 &&
p->buf_size / secondary_matches < 24000))
// not max to avoid dv in mov to match
diff --git a/libavformat/dv.h b/libavformat/dv.h
index 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/dvbsub.c b/libavformat/dvbsub.c
new file mode 100644
index 0000000000..d52bc861eb
--- /dev/null
+++ b/libavformat/dvbsub.c
@@ -0,0 +1,68 @@
+/*
+ * RAW dvbsub demuxer
+ * Copyright (c) 2015 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 "avformat.h"
+#include "rawdec.h"
+
+
+static int dvbsub_probe(AVProbeData *p)
+{
+ int i, j, k;
+ const uint8_t *end = p->buf + p->buf_size;
+ int type, len;
+ int max_score = 0;
+
+ for(i=0; i<p->buf_size; i++){
+ if (p->buf[i] == 0x0f) {
+ const uint8_t *ptr = p->buf + i;
+ uint8_t histogram[6] = {0};
+ int min = 255;
+ for(j=0; ptr + 6 < end; j++) {
+ if (*ptr != 0x0f)
+ break;
+ type = ptr[1];
+ //page_id = AV_RB16(ptr + 2);
+ len = AV_RB16(ptr + 4);
+ if (type == 0x80) {
+ ;
+ } else if (type >= 0x10 && type <= 0x14) {
+ histogram[type - 0x10] ++;
+ } else
+ break;
+ ptr += 6 + len;
+ }
+ for (k=0; k < 4; k++) {
+ min = FFMIN(min, histogram[k]);
+ }
+ if (min && j > max_score)
+ max_score = j;
+ }
+ }
+
+ if (max_score > 5)
+ return AVPROBE_SCORE_EXTENSION;
+
+ return 0;
+}
+
+FF_DEF_RAWSUB_DEMUXER(dvbsub, "raw dvbsub", dvbsub_probe, NULL, AV_CODEC_ID_DVB_SUBTITLE, AVFMT_GENERIC_INDEX)
diff --git a/libavformat/dvenc.c b/libavformat/dvenc.c
index a33973f192..e99ac3ce7e 100644
--- a/libavformat/dvenc.c
+++ b/libavformat/dvenc.c
@@ -11,20 +11,20 @@
* 50 Mbps (DVCPRO50) support
* Copyright (c) 2006 Daniel Maas <dmaas@maasdigital.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <time.h>
@@ -37,10 +37,14 @@
#include "dv.h"
#include "libavutil/fifo.h"
#include "libavutil/mathematics.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+#include "libavutil/timecode.h"
#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio
struct DVMuxContext {
+ AVClass *av_class;
const AVDVProfile* sys; /* current DV profile, e.g.: 525/60, 625/50 */
int n_ast; /* number of stereo audio streams (up to 2) */
AVStream *ast[2]; /* stereo audio streams */
@@ -50,6 +54,7 @@ struct DVMuxContext {
int has_audio; /* frame under construction has audio */
int has_video; /* frame under construction has video */
uint8_t frame_buf[DV_MAX_FRAME_SIZE]; /* frame under construction */
+ AVTimecode tc; /* timecode context */
};
static const int dv_aaux_packs_dist[12][9] = {
@@ -67,8 +72,16 @@ static const int dv_aaux_packs_dist[12][9] = {
{ 0x50, 0x51, 0x52, 0x53, 0xff, 0xff, 0xff, 0xff, 0xff },
};
-static int dv_audio_frame_size(const AVDVProfile* sys, int frame)
+static int dv_audio_frame_size(const AVDVProfile* sys, int frame, int sample_rate)
{
+ if ((sys->time_base.den == 25 || sys->time_base.den == 50) && sys->time_base.num == 1) {
+ if (sample_rate == 32000) return 1280;
+ else if (sample_rate == 44100) return 1764;
+ else return 1920;
+ }
+
+ av_assert0(sample_rate == 48000);
+
return sys->audio_samples_dist[frame % (sizeof(sys->audio_samples_dist) /
sizeof(sys->audio_samples_dist[0]))];
}
@@ -77,41 +90,29 @@ static int dv_write_pack(enum dv_pack_type pack_id, DVMuxContext *c, uint8_t* bu
{
struct tm tc;
time_t ct;
- int ltc_frame;
+ uint32_t timecode;
va_list ap;
+ int audio_type = 0;
+ int channel;
buf[0] = (uint8_t)pack_id;
switch (pack_id) {
case dv_timecode:
- ct = (time_t)av_rescale_rnd(c->frames, c->sys->time_base.num,
- c->sys->time_base.den, AV_ROUND_DOWN);
- ff_brktimegm(ct, &tc);
- /*
- * LTC drop-frame frame counter drops two frames (0 and 1) every
- * minute, unless it is exactly divisible by 10
- */
- ltc_frame = (c->frames + 2 * ct / 60 - 2 * ct / 600) % c->sys->ltc_divisor;
- buf[1] = (0 << 7) | /* color frame: 0 - unsync; 1 - sync mode */
- (1 << 6) | /* drop frame timecode: 0 - nondrop; 1 - drop */
- ((ltc_frame / 10) << 4) | /* tens of frames */
- (ltc_frame % 10); /* units of frames */
- buf[2] = (1 << 7) | /* biphase mark polarity correction: 0 - even; 1 - odd */
- ((tc.tm_sec / 10) << 4) | /* tens of seconds */
- (tc.tm_sec % 10); /* units of seconds */
- buf[3] = (1 << 7) | /* binary group flag BGF0 */
- ((tc.tm_min / 10) << 4) | /* tens of minutes */
- (tc.tm_min % 10); /* units of minutes */
- buf[4] = (1 << 7) | /* binary group flag BGF2 */
- (1 << 6) | /* binary group flag BGF1 */
- ((tc.tm_hour / 10) << 4) | /* tens of hours */
- (tc.tm_hour % 10); /* units of hours */
+ timecode = av_timecode_get_smpte_from_framenum(&c->tc, c->frames);
+ timecode |= 1<<23 | 1<<15 | 1<<7 | 1<<6; // biphase and binary group flags
+ AV_WB32(buf + 1, timecode);
break;
case dv_audio_source: /* AAUX source pack */
va_start(ap, buf);
+ channel = va_arg(ap, int);
+ if (c->ast[channel]->codec->sample_rate == 44100) {
+ audio_type = 1;
+ } else if (c->ast[channel]->codec->sample_rate == 32000)
+ audio_type = 2;
buf[1] = (1 << 7) | /* locked mode -- SMPTE only supports locked mode */
(1 << 6) | /* reserved -- always 1 */
- (dv_audio_frame_size(c->sys, c->frames) -
- c->sys->audio_min_samples[0]);
+ (dv_audio_frame_size(c->sys, c->frames, c->ast[channel]->codec->sample_rate) -
+ c->sys->audio_min_samples[audio_type]);
/* # of samples */
buf[2] = (0 << 7) | /* multi-stereo */
(0 << 5) | /* #of audio channels per block: 0 -- 1 channel */
@@ -123,8 +124,9 @@ static int dv_write_pack(enum dv_pack_type pack_id, DVMuxContext *c, uint8_t* bu
(c->sys->n_difchan & 2); /* definition: 0 -- 25Mbps, 2 -- 50Mbps */
buf[4] = (1 << 7) | /* emphasis: 1 -- off */
(0 << 6) | /* emphasis time constant: 0 -- reserved */
- (0 << 3) | /* frequency: 0 -- 48kHz, 1 -- 44,1kHz, 2 -- 32kHz */
+ (audio_type << 3) | /* frequency: 0 -- 48kHz, 1 -- 44,1kHz, 2 -- 32kHz */
0; /* quantization: 0 -- 16bit linear, 1 -- 12bit nonlinear */
+
va_end(ap);
break;
case dv_audio_control:
@@ -184,12 +186,12 @@ static int dv_write_pack(enum dv_pack_type pack_id, DVMuxContext *c, uint8_t* bu
static void dv_inject_audio(DVMuxContext *c, int channel, uint8_t* frame_ptr)
{
int i, j, d, of, size;
- size = 4 * dv_audio_frame_size(c->sys, c->frames);
+ size = 4 * dv_audio_frame_size(c->sys, c->frames, c->ast[channel]->codec->sample_rate);
frame_ptr += channel * c->sys->difseg_size * 150 * 80;
for (i = 0; i < c->sys->difseg_size; i++) {
frame_ptr += 6 * 80; /* skip DIF segment header */
for (j = 0; j < 9; j++) {
- dv_write_pack(dv_aaux_packs_dist[i][j], c, &frame_ptr[3], i >= c->sys->difseg_size/2);
+ dv_write_pack(dv_aaux_packs_dist[i][j], c, &frame_ptr[3], channel, i >= c->sys->difseg_size/2);
for (d = 8; d < 80; d+=2) {
of = c->sys->audio_shuffle[i][j] + (d - 8)/2 * c->sys->audio_stride;
if (of*2 >= size)
@@ -242,7 +244,6 @@ static int dv_assemble_frame(DVMuxContext *c, AVStream* st,
int i, reqasize;
*frame = &c->frame_buf[0];
- reqasize = 4 * dv_audio_frame_size(c->sys, c->frames);
switch (st->codec->codec_type) {
case AVMEDIA_TYPE_VIDEO:
@@ -261,6 +262,8 @@ static int dv_assemble_frame(DVMuxContext *c, AVStream* st,
av_log(st->codec, AV_LOG_ERROR, "Can't process DV frame #%d. Insufficient video data or severe sync problem.\n", c->frames);
av_fifo_generic_write(c->audio_data[i], data, data_size, NULL);
+ reqasize = 4 * dv_audio_frame_size(c->sys, c->frames, st->codec->sample_rate);
+
/* Let us see if we've got enough audio for one DV frame. */
c->has_audio |= ((reqasize <= av_fifo_size(c->audio_data[i])) << i);
@@ -275,6 +278,7 @@ static int dv_assemble_frame(DVMuxContext *c, AVStream* st,
c->has_audio = 0;
for (i=0; i < c->n_ast; i++) {
dv_inject_audio(c, i, *frame);
+ reqasize = 4 * dv_audio_frame_size(c->sys, c->frames, c->ast[i]->codec->sample_rate);
av_fifo_drain(c->audio_data[i], reqasize);
c->has_audio |= ((reqasize <= av_fifo_size(c->audio_data[i])) << i);
}
@@ -323,15 +327,28 @@ static DVMuxContext* dv_init_mux(AVFormatContext* s)
if (!vst || vst->codec->codec_id != AV_CODEC_ID_DVVIDEO)
goto bail_out;
for (i=0; i<c->n_ast; i++) {
- if (c->ast[i] && (c->ast[i]->codec->codec_id != AV_CODEC_ID_PCM_S16LE ||
- c->ast[i]->codec->sample_rate != 48000 ||
- c->ast[i]->codec->channels != 2))
- goto bail_out;
+ if (c->ast[i]) {
+ if(c->ast[i]->codec->codec_id != AV_CODEC_ID_PCM_S16LE ||
+ c->ast[i]->codec->channels != 2)
+ goto bail_out;
+ if (c->ast[i]->codec->sample_rate != 48000 &&
+ c->ast[i]->codec->sample_rate != 44100 &&
+ c->ast[i]->codec->sample_rate != 32000 )
+ goto bail_out;
+ }
}
- c->sys = av_dv_codec_profile(vst->codec->width, vst->codec->height, vst->codec->pix_fmt);
+ c->sys = av_dv_codec_profile2(vst->codec->width, vst->codec->height,
+ vst->codec->pix_fmt, vst->codec->time_base);
if (!c->sys)
goto bail_out;
+ if ((c->sys->time_base.den != 25 && c->sys->time_base.den != 50) || c->sys->time_base.num != 1) {
+ if (c->ast[0] && c->ast[0]->codec->sample_rate != 48000)
+ goto bail_out;
+ if (c->ast[1] && c->ast[1]->codec->sample_rate != 48000)
+ goto bail_out;
+ }
+
if ((c->n_ast > 1) && (c->sys->n_difchan < 2)) {
/* only 1 stereo pair is allowed in 25Mbps mode */
goto bail_out;
@@ -345,10 +362,10 @@ static DVMuxContext* dv_init_mux(AVFormatContext* s)
c->start_time = ff_iso8601_to_unix_time(t->value);
for (i=0; i < c->n_ast; i++) {
- if (c->ast[i] && !(c->audio_data[i]=av_fifo_alloc(100*MAX_AUDIO_FRAME_SIZE))) {
+ if (c->ast[i] && !(c->audio_data[i]=av_fifo_alloc_array(100, MAX_AUDIO_FRAME_SIZE))) {
while (i > 0) {
i--;
- av_fifo_free(c->audio_data[i]);
+ av_fifo_freep(&c->audio_data[i]);
}
goto bail_out;
}
@@ -364,19 +381,35 @@ static void dv_delete_mux(DVMuxContext *c)
{
int i;
for (i=0; i < c->n_ast; i++)
- av_fifo_free(c->audio_data[i]);
+ av_fifo_freep(&c->audio_data[i]);
}
static int dv_write_header(AVFormatContext *s)
{
+ AVRational rate;
+ DVMuxContext *dvc = s->priv_data;
+ AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
+
if (!dv_init_mux(s)) {
av_log(s, AV_LOG_ERROR, "Can't initialize DV format!\n"
"Make sure that you supply exactly two streams:\n"
- " video: 25fps or 29.97fps, audio: 2ch/48kHz/PCM\n"
+ " video: 25fps or 29.97fps, audio: 2ch/48|44|32kHz/PCM\n"
" (50Mbps allows an optional second audio stream)\n");
return -1;
}
- return 0;
+ rate.num = dvc->sys->ltc_divisor;
+ rate.den = 1;
+ if (!tcr) { // no global timecode, look into the streams
+ int i;
+ for (i = 0; i < s->nb_streams; i++) {
+ tcr = av_dict_get(s->streams[i]->metadata, "timecode", NULL, 0);
+ if (tcr)
+ break;
+ }
+ }
+ if (tcr && av_timecode_init_from_string(&dvc->tc, rate, tcr->value, s) >= 0)
+ return 0;
+ return av_timecode_init(&dvc->tc, rate, 0, 0, s);
}
static int dv_write_packet(struct AVFormatContext *s, AVPacket *pkt)
diff --git a/libavformat/dxa.c b/libavformat/dxa.c
index 4a4d7c2cb0..44033563f8 100644
--- a/libavformat/dxa.c
+++ b/libavformat/dxa.c
@@ -2,20 +2,20 @@
* DXA demuxer
* Copyright (c) 2007 Konstantin Shishkov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -67,12 +67,12 @@ static int dxa_read_header(AVFormatContext *s)
tag = avio_rl32(pb);
if (tag != MKTAG('D', 'E', 'X', 'A'))
- return -1;
+ return AVERROR_INVALIDDATA;
flags = avio_r8(pb);
c->frames = avio_rb16(pb);
if(!c->frames){
av_log(s, AV_LOG_ERROR, "File contains no frames ???\n");
- return -1;
+ return AVERROR_INVALIDDATA;
}
fps = avio_rb32(pb);
@@ -92,7 +92,7 @@ static int dxa_read_header(AVFormatContext *s)
st = avformat_new_stream(s, NULL);
if (!st)
- return -1;
+ return AVERROR(ENOMEM);
// Parse WAV data header
if(avio_rl32(pb) == MKTAG('W', 'A', 'V', 'E')){
@@ -105,14 +105,14 @@ static int dxa_read_header(AVFormatContext *s)
ast = avformat_new_stream(s, NULL);
if (!ast)
- return -1;
- ret = ff_get_wav_header(pb, ast->codec, fsize);
+ return AVERROR(ENOMEM);
+ ret = ff_get_wav_header(pb, ast->codec, fsize, 0);
if (ret < 0)
return ret;
if (ast->codec->sample_rate > 0)
avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate);
// find 'data' chunk
- while(avio_tell(pb) < c->vidpos && !pb->eof_reached){
+ while(avio_tell(pb) < c->vidpos && !avio_feof(pb)){
tag = avio_rl32(pb);
fsize = avio_rl32(pb);
if(tag == MKTAG('d', 'a', 't', 'a')) break;
@@ -170,8 +170,11 @@ static int dxa_read_packet(AVFormatContext *s, AVPacket *pkt)
return 0;
}
avio_seek(s->pb, c->vidpos, SEEK_SET);
- while(!s->pb->eof_reached && c->frames){
- avio_read(s->pb, buf, 4);
+ while(!avio_feof(s->pb) && c->frames){
+ if ((ret = avio_read(s->pb, buf, 4)) != 4) {
+ av_log(s, AV_LOG_ERROR, "failed reading chunk type\n");
+ return ret < 0 ? ret : AVERROR_INVALIDDATA;
+ }
switch(AV_RL32(buf)){
case MKTAG('N', 'U', 'L', 'L'):
if(av_new_packet(pkt, 4 + pal_size) < 0)
@@ -189,12 +192,15 @@ static int dxa_read_packet(AVFormatContext *s, AVPacket *pkt)
avio_read(s->pb, pal + 4, 768);
break;
case MKTAG('F', 'R', 'A', 'M'):
- avio_read(s->pb, buf + 4, DXA_EXTRA_SIZE - 4);
+ if ((ret = avio_read(s->pb, buf + 4, DXA_EXTRA_SIZE - 4)) != DXA_EXTRA_SIZE - 4) {
+ av_log(s, AV_LOG_ERROR, "failed reading dxa_extra\n");
+ return ret < 0 ? ret : AVERROR_INVALIDDATA;
+ }
size = AV_RB32(buf + 5);
if(size > 0xFFFFFF){
av_log(s, AV_LOG_ERROR, "Frame size is too big: %"PRIu32"\n",
size);
- return -1;
+ return AVERROR_INVALIDDATA;
}
if(av_new_packet(pkt, size + DXA_EXTRA_SIZE + pal_size) < 0)
return AVERROR(ENOMEM);
@@ -212,10 +218,10 @@ static int dxa_read_packet(AVFormatContext *s, AVPacket *pkt)
return 0;
default:
av_log(s, AV_LOG_ERROR, "Unknown tag %c%c%c%c\n", buf[0], buf[1], buf[2], buf[3]);
- return -1;
+ return AVERROR_INVALIDDATA;
}
}
- return AVERROR(EIO);
+ return AVERROR_EOF;
}
AVInputFormat ff_dxa_demuxer = {
diff --git a/libavformat/eacdata.c b/libavformat/eacdata.c
index c81dec847d..165ff1af99 100644
--- a/libavformat/eacdata.c
+++ b/libavformat/eacdata.c
@@ -2,20 +2,20 @@
* Electronic Arts .cdata file Demuxer
* Copyright (c) 2007 Peter Ross
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -40,7 +40,7 @@ static int cdata_probe(AVProbeData *p)
{
const uint8_t *b = p->buf;
- if (b[0] == 0x04 && (b[1] == 0x00 || b[1] == 0x04 || b[1] == 0x0C))
+ if (b[0] == 0x04 && (b[1] == 0x00 || b[1] == 0x04 || b[1] == 0x0C || b[1] == 0x14))
return AVPROBE_SCORE_MAX/8;
return 0;
}
@@ -51,19 +51,21 @@ static int cdata_read_header(AVFormatContext *s)
AVIOContext *pb = s->pb;
unsigned int sample_rate, header;
AVStream *st;
+ int64_t channel_layout = 0;
header = avio_rb16(pb);
switch (header) {
case 0x0400: cdata->channels = 1; break;
case 0x0404: cdata->channels = 2; break;
- case 0x040C: cdata->channels = 4; break;
+ case 0x040C: cdata->channels = 4; channel_layout = AV_CH_LAYOUT_QUAD; break;
+ case 0x0414: cdata->channels = 6; channel_layout = AV_CH_LAYOUT_5POINT1_BACK; break;
default:
av_log(s, AV_LOG_INFO, "unknown header 0x%04x\n", header);
return -1;
};
sample_rate = avio_rb16(pb);
- avio_skip(pb, 12);
+ avio_skip(pb, (avio_r8(pb) & 0x20) ? 15 : 11);
st = avformat_new_stream(s, NULL);
if (!st)
@@ -72,6 +74,7 @@ static int cdata_read_header(AVFormatContext *s)
st->codec->codec_tag = 0; /* no fourcc */
st->codec->codec_id = AV_CODEC_ID_ADPCM_EA_XAS;
st->codec->channels = cdata->channels;
+ st->codec->channel_layout = channel_layout;
st->codec->sample_rate = sample_rate;
avpriv_set_pts_info(st, 64, 1, sample_rate);
diff --git a/libavformat/electronicarts.c b/libavformat/electronicarts.c
index 7c1fabee0f..d6a396b1ef 100644
--- a/libavformat/electronicarts.c
+++ b/libavformat/electronicarts.c
@@ -1,21 +1,21 @@
/* Electronic Arts Multimedia File Demuxer
- * Copyright (c) 2004 The ffmpeg Project
+ * Copyright (c) 2004 The FFmpeg Project
* Copyright (c) 2006-2008 Peter Ross
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -68,6 +68,7 @@ typedef struct EaDemuxContext {
enum AVCodecID video_codec;
AVRational time_base;
int width, height;
+ int nb_frames;
int video_stream_index;
enum AVCodecID audio_codec;
@@ -108,7 +109,7 @@ static int process_audio_header_elements(AVFormatContext *s)
ea->sample_rate = -1;
ea->num_channels = 1;
- while (!pb->eof_reached && in_header) {
+ while (!avio_feof(pb) && in_header) {
int in_subheader;
uint8_t byte;
byte = avio_r8(pb);
@@ -117,7 +118,7 @@ static int process_audio_header_elements(AVFormatContext *s)
case 0xFD:
av_log(s, AV_LOG_DEBUG, "entered audio subheader\n");
in_subheader = 1;
- while (!pb->eof_reached && in_subheader) {
+ while (!avio_feof(pb) && in_subheader) {
uint8_t subbyte;
subbyte = avio_r8(pb);
@@ -211,8 +212,7 @@ static int process_audio_header_elements(AVFormatContext *s)
case -1:
break;
default:
- av_log(s, AV_LOG_ERROR,
- "unsupported stream type; revision=%i\n", revision);
+ avpriv_request_sample(s, "stream type; revision=%i", revision);
return 0;
}
switch (revision2) {
@@ -220,7 +220,14 @@ static int process_audio_header_elements(AVFormatContext *s)
ea->audio_codec = AV_CODEC_ID_PCM_S16LE_PLANAR;
break;
case 10:
- ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R2;
+ switch (revision) {
+ case -1:
+ case 2: ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R1; break;
+ case 3: ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R2; break;
+ default:
+ avpriv_request_sample(s, "stream type; revision=%i, revision2=%i", revision, revision2);
+ return 0;
+ }
break;
case 16:
ea->audio_codec = AV_CODEC_ID_MP3;
@@ -229,15 +236,14 @@ static int process_audio_header_elements(AVFormatContext *s)
break;
default:
ea->audio_codec = AV_CODEC_ID_NONE;
- av_log(s, AV_LOG_ERROR,
- "unsupported stream type; revision2=%i\n", revision2);
+ avpriv_request_sample(s, "stream type; revision2=%i", revision2);
return 0;
}
break;
default:
- av_log(s, AV_LOG_ERROR,
- "unsupported stream type; compression_type=%i\n",
- compression_type);
+ avpriv_request_sample(s,
+ "stream type; compression_type=%i",
+ compression_type);
return 0;
}
@@ -278,9 +284,9 @@ static void process_audio_header_eacs(AVFormatContext *s)
ea->audio_codec = AV_CODEC_ID_ADPCM_IMA_EA_EACS;
break;
default:
- av_log(s, AV_LOG_ERROR,
- "unsupported stream type; audio compression_type=%i\n",
- compression_type);
+ avpriv_request_sample(s,
+ "stream type; audio compression_type=%i",
+ compression_type);
}
}
@@ -306,15 +312,23 @@ static void process_video_header_mdec(AVFormatContext *s)
ea->video_codec = AV_CODEC_ID_MDEC;
}
-static void process_video_header_vp6(AVFormatContext *s)
+static int process_video_header_vp6(AVFormatContext *s)
{
EaDemuxContext *ea = s->priv_data;
AVIOContext *pb = s->pb;
- avio_skip(pb, 16);
+ avio_skip(pb, 8);
+ ea->nb_frames = avio_rl32(pb);
+ avio_skip(pb, 4);
ea->time_base.den = avio_rl32(pb);
ea->time_base.num = avio_rl32(pb);
+ if (ea->time_base.den <= 0 || ea->time_base.num <= 0) {
+ av_log(s, AV_LOG_ERROR, "Timebase is invalid\n");
+ return AVERROR_INVALIDDATA;
+ }
ea->video_codec = AV_CODEC_ID_VP6;
+
+ return 1;
}
static void process_video_header_cmv(AVFormatContext *s)
@@ -339,20 +353,25 @@ static int process_ea_header(AVFormatContext *s)
int i;
for (i = 0; i < 5 && (!ea->audio_codec || !ea->video_codec); i++) {
- unsigned int startpos = avio_tell(pb);
+ uint64_t startpos = avio_tell(pb);
int err = 0;
blockid = avio_rl32(pb);
size = avio_rl32(pb);
if (i == 0)
- ea->big_endian = size > 0x000FFFFF;
+ ea->big_endian = size > av_bswap32(size);
if (ea->big_endian)
size = av_bswap32(size);
+ if (size < 8) {
+ av_log(s, AV_LOG_ERROR, "chunk size too small\n");
+ return AVERROR_INVALIDDATA;
+ }
+
switch (blockid) {
case ISNh_TAG:
if (avio_rl32(pb) != EACS_TAG) {
- av_log(s, AV_LOG_ERROR, "unknown 1SNh headerid\n");
+ avpriv_request_sample(s, "unknown 1SNh headerid");
return 0;
}
process_audio_header_eacs(s);
@@ -364,7 +383,7 @@ static int process_ea_header(AVFormatContext *s)
if (blockid == GSTR_TAG) {
avio_skip(pb, 4);
} else if ((blockid & 0xFFFF) != PT00_TAG) {
- av_log(s, AV_LOG_ERROR, "unknown SCHl headerid\n");
+ avpriv_request_sample(s, "unknown SCHl headerid");
return 0;
}
err = process_audio_header_elements(s);
@@ -380,7 +399,6 @@ static int process_ea_header(AVFormatContext *s)
case kVGT_TAG:
ea->video_codec = AV_CODEC_ID_TGV;
- ea->time_base = (AVRational) { 1, 15 };
break;
case mTCD_TAG:
@@ -409,7 +427,7 @@ static int process_ea_header(AVFormatContext *s)
break;
case MVhd_TAG:
- process_video_header_vp6(s);
+ err = process_video_header_vp6(s);
break;
}
@@ -428,6 +446,8 @@ static int process_ea_header(AVFormatContext *s)
static int ea_probe(AVProbeData *p)
{
+ unsigned big_endian, size;
+
switch (AV_RL32(&p->buf[0])) {
case ISNh_TAG:
case SCHl_TAG:
@@ -442,7 +462,11 @@ static int ea_probe(AVProbeData *p)
default:
return 0;
}
- if (AV_RL32(&p->buf[4]) > 0xfffff && AV_RB32(&p->buf[4]) > 0xfffff)
+ size = AV_RL32(&p->buf[4]);
+ big_endian = size > 0x000FFFFF;
+ if (big_endian)
+ size = av_bswap32(size);
+ if (size > 0xfffff || size < 8)
return 0;
return AVPROBE_SCORE_MAX;
@@ -453,7 +477,7 @@ static int ea_read_header(AVFormatContext *s)
EaDemuxContext *ea = s->priv_data;
AVStream *st;
- if (!process_ea_header(s))
+ if (process_ea_header(s)<=0)
return AVERROR(EIO);
if (ea->video_codec) {
@@ -464,12 +488,17 @@ static int ea_read_header(AVFormatContext *s)
ea->video_stream_index = st->index;
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
st->codec->codec_id = ea->video_codec;
+ // parsing is necessary to make FFmpeg generate correct timestamps
+ if (st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO)
+ st->need_parsing = AVSTREAM_PARSE_HEADERS;
st->codec->codec_tag = 0; /* no fourcc */
st->codec->width = ea->width;
st->codec->height = ea->height;
- avpriv_set_pts_info(st, 33, ea->time_base.num, ea->time_base.den);
- st->avg_frame_rate = (AVRational) { ea->time_base.den,
- ea->time_base.num };
+ st->duration = st->nb_frames = ea->nb_frames;
+ if (ea->time_base.num)
+ avpriv_set_pts_info(st, 64, ea->time_base.num, ea->time_base.den);
+ st->r_frame_rate =
+ st->avg_frame_rate = av_inv_q(ea->time_base);
}
if (ea->audio_codec) {
@@ -519,11 +548,12 @@ static int ea_read_packet(AVFormatContext *s, AVPacket *pkt)
{
EaDemuxContext *ea = s->priv_data;
AVIOContext *pb = s->pb;
+ int partial_packet = 0;
unsigned int chunk_type, chunk_size;
int ret = 0, packet_read = 0, key = 0;
int av_uninit(num_samples);
- while (!packet_read) {
+ while (!packet_read || partial_packet) {
chunk_type = avio_rl32(pb);
chunk_size = ea->big_endian ? avio_rb32(pb) : avio_rl32(pb);
if (chunk_size < 8)
@@ -551,6 +581,13 @@ static int ea_read_packet(AVFormatContext *s, AVPacket *pkt)
avio_skip(pb, 8);
chunk_size -= 12;
}
+
+ if (partial_packet) {
+ avpriv_request_sample(s, "video header followed by audio packet");
+ av_free_packet(pkt);
+ partial_packet = 0;
+ }
+
if (!chunk_size)
continue;
@@ -630,9 +667,15 @@ get_video_packet:
if (!chunk_size)
continue;
- ret = av_get_packet(pb, pkt, chunk_size);
- if (ret < 0)
- return ret;
+ if (partial_packet) {
+ ret = av_append_packet(pb, pkt, chunk_size);
+ } else
+ ret = av_get_packet(pb, pkt, chunk_size);
+ if (ret < 0) {
+ packet_read = 1;
+ break;
+ }
+ partial_packet = chunk_type == MVIh_TAG;
pkt->stream_index = ea->video_stream_index;
pkt->flags |= key;
packet_read = 1;
@@ -644,6 +687,8 @@ get_video_packet:
}
}
+ if (ret < 0 && partial_packet)
+ av_free_packet(pkt);
return ret;
}
diff --git a/libavformat/epafdec.c b/libavformat/epafdec.c
new file mode 100644
index 0000000000..c737892325
--- /dev/null
+++ b/libavformat/epafdec.c
@@ -0,0 +1,104 @@
+/*
+ * Ensoniq Paris Audio File demuxer
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "internal.h"
+#include "pcm.h"
+
+static int epaf_probe(AVProbeData *p)
+{
+ if (((AV_RL32(p->buf) == MKTAG('f','a','p',' ') &&
+ AV_RL32(p->buf + 8) == 1) ||
+ (AV_RL32(p->buf) == MKTAG(' ','p','a','f') &&
+ AV_RN32(p->buf + 8) == 0)) &&
+ !AV_RN32(p->buf + 4) && AV_RN32(p->buf + 12) &&
+ AV_RN32(p->buf + 20))
+ return AVPROBE_SCORE_MAX / 4 * 3;
+ return 0;
+}
+
+static int epaf_read_header(AVFormatContext *s)
+{
+ int le, sample_rate, codec, channels;
+ AVStream *st;
+
+ avio_skip(s->pb, 4);
+ if (avio_rl32(s->pb))
+ return AVERROR_INVALIDDATA;
+
+ le = avio_rl32(s->pb);
+ if (le && le != 1)
+ return AVERROR_INVALIDDATA;
+
+ if (le) {
+ sample_rate = avio_rl32(s->pb);
+ codec = avio_rl32(s->pb);
+ channels = avio_rl32(s->pb);
+ } else {
+ sample_rate = avio_rb32(s->pb);
+ codec = avio_rb32(s->pb);
+ channels = avio_rb32(s->pb);
+ }
+
+ if (!channels || !sample_rate)
+ return AVERROR_INVALIDDATA;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->channels = channels;
+ st->codec->sample_rate = sample_rate;
+ switch (codec) {
+ case 0:
+ st->codec->codec_id = le ? AV_CODEC_ID_PCM_S16LE : AV_CODEC_ID_PCM_S16BE;
+ break;
+ case 2:
+ st->codec->codec_id = AV_CODEC_ID_PCM_S8;
+ break;
+ case 1:
+ avpriv_request_sample(s, "24-bit Paris PCM format");
+ default:
+ return AVERROR_INVALIDDATA;
+ }
+
+ st->codec->bits_per_coded_sample = av_get_bits_per_sample(st->codec->codec_id);
+ st->codec->block_align = st->codec->bits_per_coded_sample * st->codec->channels / 8;
+
+ avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+
+ if (avio_skip(s->pb, 2024) < 0)
+ return AVERROR_INVALIDDATA;
+ return 0;
+}
+
+AVInputFormat ff_epaf_demuxer = {
+ .name = "epaf",
+ .long_name = NULL_IF_CONFIG_SMALL("Ensoniq Paris Audio File"),
+ .read_probe = epaf_probe,
+ .read_header = epaf_read_header,
+ .read_packet = ff_pcm_read_packet,
+ .read_seek = ff_pcm_read_seek,
+ .extensions = "paf,fap",
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/ffm.h b/libavformat/ffm.h
new file mode 100644
index 0000000000..b392b8d8d8
--- /dev/null
+++ b/libavformat/ffm.h
@@ -0,0 +1,60 @@
+/*
+ * FFM (ffserver live feed) common header
+ * Copyright (c) 2001 Fabrice Bellard
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_FFM_H
+#define AVFORMAT_FFM_H
+
+#include <stdint.h>
+#include "avformat.h"
+#include "avio.h"
+
+/* The FFM file is made of blocks of fixed size */
+#define FFM_HEADER_SIZE 14
+#define FFM_PACKET_SIZE 4096
+#define PACKET_ID 0x666d
+
+/* each packet contains frames (which can span several packets */
+#define FRAME_HEADER_SIZE 16
+#define FLAG_KEY_FRAME 0x01
+#define FLAG_DTS 0x02
+
+enum {
+ READ_HEADER,
+ READ_DATA,
+};
+
+typedef struct FFMContext {
+ /* only reading mode */
+ int64_t write_index, file_size;
+ int read_state;
+ uint8_t header[FRAME_HEADER_SIZE+4];
+
+ /* read and write */
+ int first_packet; /* true if first packet, needed to set the discontinuity tag */
+ int packet_size;
+ int frame_offset;
+ int64_t dts;
+ uint8_t *packet_ptr, *packet_end;
+ uint8_t packet[FFM_PACKET_SIZE];
+ int64_t start_time;
+} FFMContext;
+
+#endif /* AVFORMAT_FFM_H */
diff --git a/libavformat/ffmdec.c b/libavformat/ffmdec.c
new file mode 100644
index 0000000000..987f419c96
--- /dev/null
+++ b/libavformat/ffmdec.c
@@ -0,0 +1,723 @@
+/*
+ * FFM (ffserver live feed) demuxer
+ * Copyright (c) 2001 Fabrice Bellard
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/intfloat.h"
+#include "libavutil/opt.h"
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "avformat.h"
+#include "internal.h"
+#include "ffm.h"
+#include "avio_internal.h"
+
+static int ffm_is_avail_data(AVFormatContext *s, int size)
+{
+ FFMContext *ffm = s->priv_data;
+ int64_t pos, avail_size;
+ int len;
+
+ len = ffm->packet_end - ffm->packet_ptr;
+ if (size <= len)
+ return 1;
+ pos = avio_tell(s->pb);
+ if (!ffm->write_index) {
+ if (pos == ffm->file_size)
+ return AVERROR_EOF;
+ avail_size = ffm->file_size - pos;
+ } else {
+ if (pos == ffm->write_index) {
+ /* exactly at the end of stream */
+ return AVERROR(EAGAIN);
+ } else if (pos < ffm->write_index) {
+ avail_size = ffm->write_index - pos;
+ } else {
+ avail_size = (ffm->file_size - pos) + (ffm->write_index - FFM_PACKET_SIZE);
+ }
+ }
+ avail_size = (avail_size / ffm->packet_size) * (ffm->packet_size - FFM_HEADER_SIZE) + len;
+ if (size <= avail_size)
+ return 1;
+ else
+ return AVERROR(EAGAIN);
+}
+
+static int ffm_resync(AVFormatContext *s, int state)
+{
+ av_log(s, AV_LOG_ERROR, "resyncing\n");
+ while (state != PACKET_ID) {
+ if (avio_feof(s->pb)) {
+ av_log(s, AV_LOG_ERROR, "cannot find FFM syncword\n");
+ return -1;
+ }
+ state = (state << 8) | avio_r8(s->pb);
+ }
+ return 0;
+}
+
+/* first is true if we read the frame header */
+static int ffm_read_data(AVFormatContext *s,
+ uint8_t *buf, int size, int header)
+{
+ FFMContext *ffm = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int len, fill_size, size1, frame_offset, id;
+
+ size1 = size;
+ while (size > 0) {
+ redo:
+ len = ffm->packet_end - ffm->packet_ptr;
+ if (len < 0)
+ return -1;
+ if (len > size)
+ len = size;
+ if (len == 0) {
+ if (avio_tell(pb) == ffm->file_size)
+ avio_seek(pb, ffm->packet_size, SEEK_SET);
+ retry_read:
+ if (pb->buffer_size != ffm->packet_size) {
+ int64_t tell = avio_tell(pb);
+ ffio_set_buf_size(pb, ffm->packet_size);
+ avio_seek(pb, tell, SEEK_SET);
+ }
+ id = avio_rb16(pb); /* PACKET_ID */
+ if (id != PACKET_ID)
+ if (ffm_resync(s, id) < 0)
+ return -1;
+ fill_size = avio_rb16(pb);
+ ffm->dts = avio_rb64(pb);
+ frame_offset = avio_rb16(pb);
+ avio_read(pb, ffm->packet, ffm->packet_size - FFM_HEADER_SIZE);
+ ffm->packet_end = ffm->packet + (ffm->packet_size - FFM_HEADER_SIZE - fill_size);
+ if (ffm->packet_end < ffm->packet || frame_offset < 0)
+ return -1;
+ /* if first packet or resynchronization packet, we must
+ handle it specifically */
+ if (ffm->first_packet || (frame_offset & 0x8000)) {
+ if (!frame_offset) {
+ /* This packet has no frame headers in it */
+ if (avio_tell(pb) >= ffm->packet_size * 3LL) {
+ avio_seek(pb, -ffm->packet_size * 2LL, SEEK_CUR);
+ goto retry_read;
+ }
+ /* This is bad, we cannot find a valid frame header */
+ return 0;
+ }
+ ffm->first_packet = 0;
+ if ((frame_offset & 0x7fff) < FFM_HEADER_SIZE)
+ return -1;
+ ffm->packet_ptr = ffm->packet + (frame_offset & 0x7fff) - FFM_HEADER_SIZE;
+ if (!header)
+ break;
+ } else {
+ ffm->packet_ptr = ffm->packet;
+ }
+ goto redo;
+ }
+ memcpy(buf, ffm->packet_ptr, len);
+ buf += len;
+ ffm->packet_ptr += len;
+ size -= len;
+ header = 0;
+ }
+ return size1 - size;
+}
+
+/* ensure that acutal seeking happens between FFM_PACKET_SIZE
+ and file_size - FFM_PACKET_SIZE */
+static int64_t ffm_seek1(AVFormatContext *s, int64_t pos1)
+{
+ FFMContext *ffm = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int64_t pos;
+
+ pos = FFMIN(pos1, ffm->file_size - FFM_PACKET_SIZE);
+ pos = FFMAX(pos, FFM_PACKET_SIZE);
+ av_dlog(s, "seek to %"PRIx64" -> %"PRIx64"\n", pos1, pos);
+ return avio_seek(pb, pos, SEEK_SET);
+}
+
+static int64_t get_dts(AVFormatContext *s, int64_t pos)
+{
+ AVIOContext *pb = s->pb;
+ int64_t dts;
+
+ ffm_seek1(s, pos);
+ avio_skip(pb, 4);
+ dts = avio_rb64(pb);
+ av_dlog(s, "dts=%0.6f\n", dts / 1000000.0);
+ return dts;
+}
+
+static void adjust_write_index(AVFormatContext *s)
+{
+ FFMContext *ffm = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int64_t pts;
+ //int64_t orig_write_index = ffm->write_index;
+ int64_t pos_min, pos_max;
+ int64_t pts_start;
+ int64_t ptr = avio_tell(pb);
+
+
+ pos_min = 0;
+ pos_max = ffm->file_size - 2 * FFM_PACKET_SIZE;
+
+ pts_start = get_dts(s, pos_min);
+
+ pts = get_dts(s, pos_max);
+
+ if (pts - 100000 > pts_start)
+ goto end;
+
+ ffm->write_index = FFM_PACKET_SIZE;
+
+ pts_start = get_dts(s, pos_min);
+
+ pts = get_dts(s, pos_max);
+
+ if (pts - 100000 <= pts_start) {
+ while (1) {
+ int64_t newpos;
+ int64_t newpts;
+
+ newpos = ((pos_max + pos_min) / (2 * FFM_PACKET_SIZE)) * FFM_PACKET_SIZE;
+
+ if (newpos == pos_min)
+ break;
+
+ newpts = get_dts(s, newpos);
+
+ if (newpts - 100000 <= pts) {
+ pos_max = newpos;
+ pts = newpts;
+ } else {
+ pos_min = newpos;
+ }
+ }
+ ffm->write_index += pos_max;
+ }
+
+ end:
+ avio_seek(pb, ptr, SEEK_SET);
+}
+
+
+static int ffm_close(AVFormatContext *s)
+{
+ int i;
+
+ for (i = 0; i < s->nb_streams; i++)
+ av_freep(&s->streams[i]->codec->rc_eq);
+
+ return 0;
+}
+
+static int ffm_append_recommended_configuration(AVStream *st, char **conf)
+{
+ int ret;
+ size_t newsize;
+ av_assert0(conf && st);
+ if (!*conf)
+ return 0;
+ if (!st->recommended_encoder_configuration) {
+ st->recommended_encoder_configuration = *conf;
+ *conf = 0;
+ return 0;
+ }
+ newsize = strlen(*conf) + strlen(st->recommended_encoder_configuration) + 2;
+ if ((ret = av_reallocp(&st->recommended_encoder_configuration, newsize)) < 0)
+ return ret;
+ av_strlcat(st->recommended_encoder_configuration, ",", newsize);
+ av_strlcat(st->recommended_encoder_configuration, *conf, newsize);
+ av_freep(conf);
+ return 0;
+}
+
+static int ffm2_read_header(AVFormatContext *s)
+{
+ FFMContext *ffm = s->priv_data;
+ AVStream *st;
+ AVIOContext *pb = s->pb;
+ AVCodecContext *codec;
+ int ret;
+ int f_main = 0, f_cprv, f_stvi, f_stau;
+ AVCodec *enc;
+ char *buffer;
+
+ ffm->packet_size = avio_rb32(pb);
+ if (ffm->packet_size != FFM_PACKET_SIZE) {
+ av_log(s, AV_LOG_ERROR, "Invalid packet size %d, expected size was %d\n",
+ ffm->packet_size, FFM_PACKET_SIZE);
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ ffm->write_index = avio_rb64(pb);
+ /* get also filesize */
+ if (pb->seekable) {
+ ffm->file_size = avio_size(pb);
+ if (ffm->write_index && 0)
+ adjust_write_index(s);
+ } else {
+ ffm->file_size = (UINT64_C(1) << 63) - 1;
+ }
+
+ while(!avio_feof(pb)) {
+ unsigned id = avio_rb32(pb);
+ unsigned size = avio_rb32(pb);
+ int64_t next = avio_tell(pb) + size;
+ char rc_eq_buf[128];
+
+ if(!id)
+ break;
+
+ switch(id) {
+ case MKBETAG('M', 'A', 'I', 'N'):
+ if (f_main++) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ avio_rb32(pb); /* nb_streams */
+ avio_rb32(pb); /* total bitrate */
+ break;
+ case MKBETAG('C', 'O', 'M', 'M'):
+ f_cprv = f_stvi = f_stau = 0;
+ st = avformat_new_stream(s, NULL);
+ if (!st) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ avpriv_set_pts_info(st, 64, 1, 1000000);
+
+ codec = st->codec;
+ /* generic info */
+ codec->codec_id = avio_rb32(pb);
+ codec->codec_type = avio_r8(pb);
+ codec->bit_rate = avio_rb32(pb);
+ codec->flags = avio_rb32(pb);
+ codec->flags2 = avio_rb32(pb);
+ codec->debug = avio_rb32(pb);
+ if (codec->flags & CODEC_FLAG_GLOBAL_HEADER) {
+ if (ff_get_extradata(codec, pb, avio_rb32(pb)) < 0)
+ return AVERROR(ENOMEM);
+ }
+ break;
+ case MKBETAG('S', 'T', 'V', 'I'):
+ if (f_stvi++) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ codec->time_base.num = avio_rb32(pb);
+ codec->time_base.den = avio_rb32(pb);
+ codec->width = avio_rb16(pb);
+ codec->height = avio_rb16(pb);
+ codec->gop_size = avio_rb16(pb);
+ codec->pix_fmt = avio_rb32(pb);
+ codec->qmin = avio_r8(pb);
+ codec->qmax = avio_r8(pb);
+ codec->max_qdiff = avio_r8(pb);
+ codec->qcompress = avio_rb16(pb) / 10000.0;
+ codec->qblur = avio_rb16(pb) / 10000.0;
+ codec->bit_rate_tolerance = avio_rb32(pb);
+ avio_get_str(pb, INT_MAX, rc_eq_buf, sizeof(rc_eq_buf));
+ codec->rc_eq = av_strdup(rc_eq_buf);
+ codec->rc_max_rate = avio_rb32(pb);
+ codec->rc_min_rate = avio_rb32(pb);
+ codec->rc_buffer_size = avio_rb32(pb);
+ codec->i_quant_factor = av_int2double(avio_rb64(pb));
+ codec->b_quant_factor = av_int2double(avio_rb64(pb));
+ codec->i_quant_offset = av_int2double(avio_rb64(pb));
+ codec->b_quant_offset = av_int2double(avio_rb64(pb));
+ codec->dct_algo = avio_rb32(pb);
+ codec->strict_std_compliance = avio_rb32(pb);
+ codec->max_b_frames = avio_rb32(pb);
+ codec->mpeg_quant = avio_rb32(pb);
+ codec->intra_dc_precision = avio_rb32(pb);
+ codec->me_method = avio_rb32(pb);
+ codec->mb_decision = avio_rb32(pb);
+ codec->nsse_weight = avio_rb32(pb);
+ codec->frame_skip_cmp = avio_rb32(pb);
+ codec->rc_buffer_aggressivity = av_int2double(avio_rb64(pb));
+ codec->codec_tag = avio_rb32(pb);
+ codec->thread_count = avio_r8(pb);
+ codec->coder_type = avio_rb32(pb);
+ codec->me_cmp = avio_rb32(pb);
+ codec->me_subpel_quality = avio_rb32(pb);
+ codec->me_range = avio_rb32(pb);
+ codec->keyint_min = avio_rb32(pb);
+ codec->scenechange_threshold = avio_rb32(pb);
+ codec->b_frame_strategy = avio_rb32(pb);
+ codec->qcompress = av_int2double(avio_rb64(pb));
+ codec->qblur = av_int2double(avio_rb64(pb));
+ codec->max_qdiff = avio_rb32(pb);
+ codec->refs = avio_rb32(pb);
+ break;
+ case MKBETAG('S', 'T', 'A', 'U'):
+ if (f_stau++) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ codec->sample_rate = avio_rb32(pb);
+ codec->channels = avio_rl16(pb);
+ codec->frame_size = avio_rl16(pb);
+ break;
+ case MKBETAG('C', 'P', 'R', 'V'):
+ if (f_cprv++) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ enc = avcodec_find_encoder(codec->codec_id);
+ if (enc && enc->priv_data_size && enc->priv_class) {
+ buffer = av_malloc(size + 1);
+ if (!buffer) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ avio_get_str(pb, size, buffer, size + 1);
+ if ((ret = ffm_append_recommended_configuration(st, &buffer)) < 0)
+ goto fail;
+ }
+ break;
+ case MKBETAG('S', '2', 'V', 'I'):
+ if (f_stvi++) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ buffer = av_malloc(size);
+ if (!buffer) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ avio_get_str(pb, INT_MAX, buffer, size);
+ av_set_options_string(codec, buffer, "=", ",");
+ if ((ret = ffm_append_recommended_configuration(st, &buffer)) < 0)
+ goto fail;
+ break;
+ case MKBETAG('S', '2', 'A', 'U'):
+ if (f_stau++) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ buffer = av_malloc(size);
+ if (!buffer) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ avio_get_str(pb, INT_MAX, buffer, size);
+ av_set_options_string(codec, buffer, "=", ",");
+ ffm_append_recommended_configuration(st, &buffer);
+ break;
+ }
+ avio_seek(pb, next, SEEK_SET);
+ }
+
+ /* get until end of block reached */
+ while ((avio_tell(pb) % ffm->packet_size) != 0)
+ avio_r8(pb);
+
+ /* init packet demux */
+ ffm->packet_ptr = ffm->packet;
+ ffm->packet_end = ffm->packet;
+ ffm->frame_offset = 0;
+ ffm->dts = 0;
+ ffm->read_state = READ_HEADER;
+ ffm->first_packet = 1;
+ return 0;
+ fail:
+ ffm_close(s);
+ return ret;
+}
+
+static int ffm_read_header(AVFormatContext *s)
+{
+ FFMContext *ffm = s->priv_data;
+ AVStream *st;
+ AVIOContext *pb = s->pb;
+ AVCodecContext *codec;
+ int i, nb_streams;
+ uint32_t tag;
+
+ /* header */
+ tag = avio_rl32(pb);
+ if (tag == MKTAG('F', 'F', 'M', '2'))
+ return ffm2_read_header(s);
+ if (tag != MKTAG('F', 'F', 'M', '1'))
+ goto fail;
+ ffm->packet_size = avio_rb32(pb);
+ if (ffm->packet_size != FFM_PACKET_SIZE)
+ goto fail;
+ ffm->write_index = avio_rb64(pb);
+ /* get also filesize */
+ if (pb->seekable) {
+ ffm->file_size = avio_size(pb);
+ if (ffm->write_index && 0)
+ adjust_write_index(s);
+ } else {
+ ffm->file_size = (UINT64_C(1) << 63) - 1;
+ }
+
+ nb_streams = avio_rb32(pb);
+ avio_rb32(pb); /* total bitrate */
+ /* read each stream */
+ for(i=0;i<nb_streams;i++) {
+ char rc_eq_buf[128];
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ goto fail;
+
+ avpriv_set_pts_info(st, 64, 1, 1000000);
+
+ codec = st->codec;
+ /* generic info */
+ codec->codec_id = avio_rb32(pb);
+ codec->codec_type = avio_r8(pb); /* codec_type */
+ codec->bit_rate = avio_rb32(pb);
+ codec->flags = avio_rb32(pb);
+ codec->flags2 = avio_rb32(pb);
+ codec->debug = avio_rb32(pb);
+ /* specific info */
+ switch(codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ codec->time_base.num = avio_rb32(pb);
+ codec->time_base.den = avio_rb32(pb);
+ codec->width = avio_rb16(pb);
+ codec->height = avio_rb16(pb);
+ codec->gop_size = avio_rb16(pb);
+ codec->pix_fmt = avio_rb32(pb);
+ codec->qmin = avio_r8(pb);
+ codec->qmax = avio_r8(pb);
+ codec->max_qdiff = avio_r8(pb);
+ codec->qcompress = avio_rb16(pb) / 10000.0;
+ codec->qblur = avio_rb16(pb) / 10000.0;
+ codec->bit_rate_tolerance = avio_rb32(pb);
+ avio_get_str(pb, INT_MAX, rc_eq_buf, sizeof(rc_eq_buf));
+ codec->rc_eq = av_strdup(rc_eq_buf);
+ codec->rc_max_rate = avio_rb32(pb);
+ codec->rc_min_rate = avio_rb32(pb);
+ codec->rc_buffer_size = avio_rb32(pb);
+ codec->i_quant_factor = av_int2double(avio_rb64(pb));
+ codec->b_quant_factor = av_int2double(avio_rb64(pb));
+ codec->i_quant_offset = av_int2double(avio_rb64(pb));
+ codec->b_quant_offset = av_int2double(avio_rb64(pb));
+ codec->dct_algo = avio_rb32(pb);
+ codec->strict_std_compliance = avio_rb32(pb);
+ codec->max_b_frames = avio_rb32(pb);
+ codec->mpeg_quant = avio_rb32(pb);
+ codec->intra_dc_precision = avio_rb32(pb);
+ codec->me_method = avio_rb32(pb);
+ codec->mb_decision = avio_rb32(pb);
+ codec->nsse_weight = avio_rb32(pb);
+ codec->frame_skip_cmp = avio_rb32(pb);
+ codec->rc_buffer_aggressivity = av_int2double(avio_rb64(pb));
+ codec->codec_tag = avio_rb32(pb);
+ codec->thread_count = avio_r8(pb);
+ codec->coder_type = avio_rb32(pb);
+ codec->me_cmp = avio_rb32(pb);
+ codec->me_subpel_quality = avio_rb32(pb);
+ codec->me_range = avio_rb32(pb);
+ codec->keyint_min = avio_rb32(pb);
+ codec->scenechange_threshold = avio_rb32(pb);
+ codec->b_frame_strategy = avio_rb32(pb);
+ codec->qcompress = av_int2double(avio_rb64(pb));
+ codec->qblur = av_int2double(avio_rb64(pb));
+ codec->max_qdiff = avio_rb32(pb);
+ codec->refs = avio_rb32(pb);
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ codec->sample_rate = avio_rb32(pb);
+ codec->channels = avio_rl16(pb);
+ codec->frame_size = avio_rl16(pb);
+ break;
+ default:
+ goto fail;
+ }
+ if (codec->flags & CODEC_FLAG_GLOBAL_HEADER) {
+ if (ff_get_extradata(codec, pb, avio_rb32(pb)) < 0)
+ return AVERROR(ENOMEM);
+ }
+ }
+
+ /* get until end of block reached */
+ while ((avio_tell(pb) % ffm->packet_size) != 0)
+ avio_r8(pb);
+
+ /* init packet demux */
+ ffm->packet_ptr = ffm->packet;
+ ffm->packet_end = ffm->packet;
+ ffm->frame_offset = 0;
+ ffm->dts = 0;
+ ffm->read_state = READ_HEADER;
+ ffm->first_packet = 1;
+ return 0;
+ fail:
+ ffm_close(s);
+ return -1;
+}
+
+/* return < 0 if eof */
+static int ffm_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ int size;
+ FFMContext *ffm = s->priv_data;
+ int duration, ret;
+
+ switch(ffm->read_state) {
+ case READ_HEADER:
+ if ((ret = ffm_is_avail_data(s, FRAME_HEADER_SIZE+4)) < 0)
+ return ret;
+
+ av_dlog(s, "pos=%08"PRIx64" spos=%"PRIx64", write_index=%"PRIx64" size=%"PRIx64"\n",
+ avio_tell(s->pb), s->pb->pos, ffm->write_index, ffm->file_size);
+ if (ffm_read_data(s, ffm->header, FRAME_HEADER_SIZE, 1) !=
+ FRAME_HEADER_SIZE)
+ return -1;
+ if (ffm->header[1] & FLAG_DTS)
+ if (ffm_read_data(s, ffm->header+16, 4, 1) != 4)
+ return -1;
+ ffm->read_state = READ_DATA;
+ /* fall through */
+ case READ_DATA:
+ size = AV_RB24(ffm->header + 2);
+ if ((ret = ffm_is_avail_data(s, size)) < 0)
+ return ret;
+
+ duration = AV_RB24(ffm->header + 5);
+
+ if (av_new_packet(pkt, size) < 0) {
+ return AVERROR(ENOMEM);
+ }
+ pkt->stream_index = ffm->header[0];
+ if ((unsigned)pkt->stream_index >= s->nb_streams) {
+ av_log(s, AV_LOG_ERROR, "invalid stream index %d\n", pkt->stream_index);
+ av_free_packet(pkt);
+ ffm->read_state = READ_HEADER;
+ return -1;
+ }
+ pkt->pos = avio_tell(s->pb);
+ if (ffm->header[1] & FLAG_KEY_FRAME)
+ pkt->flags |= AV_PKT_FLAG_KEY;
+
+ ffm->read_state = READ_HEADER;
+ if (ffm_read_data(s, pkt->data, size, 0) != size) {
+ /* bad case: desynchronized packet. we cancel all the packet loading */
+ av_free_packet(pkt);
+ return -1;
+ }
+ pkt->pts = AV_RB64(ffm->header+8);
+ if (ffm->header[1] & FLAG_DTS)
+ pkt->dts = pkt->pts - AV_RB32(ffm->header+16);
+ else
+ pkt->dts = pkt->pts;
+ pkt->duration = duration;
+ break;
+ }
+ return 0;
+}
+
+/* seek to a given time in the file. The file read pointer is
+ positioned at or before pts. XXX: the following code is quite
+ approximative */
+static int ffm_seek(AVFormatContext *s, int stream_index, int64_t wanted_pts, int flags)
+{
+ FFMContext *ffm = s->priv_data;
+ int64_t pos_min, pos_max, pos;
+ int64_t pts_min, pts_max, pts;
+ double pos1;
+
+ av_dlog(s, "wanted_pts=%0.6f\n", wanted_pts / 1000000.0);
+ /* find the position using linear interpolation (better than
+ dichotomy in typical cases) */
+ if (ffm->write_index && ffm->write_index < ffm->file_size) {
+ if (get_dts(s, FFM_PACKET_SIZE) < wanted_pts) {
+ pos_min = FFM_PACKET_SIZE;
+ pos_max = ffm->write_index - FFM_PACKET_SIZE;
+ } else {
+ pos_min = ffm->write_index;
+ pos_max = ffm->file_size - FFM_PACKET_SIZE;
+ }
+ } else {
+ pos_min = FFM_PACKET_SIZE;
+ pos_max = ffm->file_size - FFM_PACKET_SIZE;
+ }
+ while (pos_min <= pos_max) {
+ pts_min = get_dts(s, pos_min);
+ pts_max = get_dts(s, pos_max);
+ if (pts_min > wanted_pts || pts_max <= wanted_pts) {
+ pos = pts_min > wanted_pts ? pos_min : pos_max;
+ goto found;
+ }
+ /* linear interpolation */
+ pos1 = (double)(pos_max - pos_min) * (double)(wanted_pts - pts_min) /
+ (double)(pts_max - pts_min);
+ pos = (((int64_t)pos1) / FFM_PACKET_SIZE) * FFM_PACKET_SIZE;
+ if (pos <= pos_min)
+ pos = pos_min;
+ else if (pos >= pos_max)
+ pos = pos_max;
+ pts = get_dts(s, pos);
+ /* check if we are lucky */
+ if (pts == wanted_pts) {
+ goto found;
+ } else if (pts > wanted_pts) {
+ pos_max = pos - FFM_PACKET_SIZE;
+ } else {
+ pos_min = pos + FFM_PACKET_SIZE;
+ }
+ }
+ pos = (flags & AVSEEK_FLAG_BACKWARD) ? pos_min : pos_max;
+
+ found:
+ if (ffm_seek1(s, pos) < 0)
+ return -1;
+
+ /* reset read state */
+ ffm->read_state = READ_HEADER;
+ ffm->packet_ptr = ffm->packet;
+ ffm->packet_end = ffm->packet;
+ ffm->first_packet = 1;
+
+ return 0;
+}
+
+static int ffm_probe(AVProbeData *p)
+{
+ if (
+ p->buf[0] == 'F' && p->buf[1] == 'F' && p->buf[2] == 'M' &&
+ (p->buf[3] == '1' || p->buf[3] == '2'))
+ return AVPROBE_SCORE_MAX + 1;
+ return 0;
+}
+
+AVInputFormat ff_ffm_demuxer = {
+ .name = "ffm",
+ .long_name = NULL_IF_CONFIG_SMALL("FFM (FFserver live feed)"),
+ .priv_data_size = sizeof(FFMContext),
+ .read_probe = ffm_probe,
+ .read_header = ffm_read_header,
+ .read_packet = ffm_read_packet,
+ .read_close = ffm_close,
+ .read_seek = ffm_seek,
+};
diff --git a/libavformat/ffmenc.c b/libavformat/ffmenc.c
new file mode 100644
index 0000000000..e6d1a31dd3
--- /dev/null
+++ b/libavformat/ffmenc.c
@@ -0,0 +1,372 @@
+/*
+ * FFM (ffserver live feed) muxer
+ * Copyright (c) 2001 Fabrice Bellard
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/intfloat.h"
+#include "libavutil/avassert.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "internal.h"
+#include "ffm.h"
+
+static void flush_packet(AVFormatContext *s)
+{
+ FFMContext *ffm = s->priv_data;
+ int fill_size, h;
+ AVIOContext *pb = s->pb;
+
+ fill_size = ffm->packet_end - ffm->packet_ptr;
+ memset(ffm->packet_ptr, 0, fill_size);
+
+ av_assert1(avio_tell(pb) % ffm->packet_size == 0);
+
+ /* put header */
+ avio_wb16(pb, PACKET_ID);
+ avio_wb16(pb, fill_size);
+ avio_wb64(pb, ffm->dts);
+ h = ffm->frame_offset;
+ if (ffm->first_packet)
+ h |= 0x8000;
+ avio_wb16(pb, h);
+ avio_write(pb, ffm->packet, ffm->packet_end - ffm->packet);
+ avio_flush(pb);
+
+ /* prepare next packet */
+ ffm->frame_offset = 0; /* no key frame */
+ ffm->packet_ptr = ffm->packet;
+ ffm->first_packet = 0;
+}
+
+/* 'first' is true if first data of a frame */
+static void ffm_write_data(AVFormatContext *s,
+ const uint8_t *buf, int size,
+ int64_t dts, int header)
+{
+ FFMContext *ffm = s->priv_data;
+ int len;
+
+ if (header && ffm->frame_offset == 0) {
+ ffm->frame_offset = ffm->packet_ptr - ffm->packet + FFM_HEADER_SIZE;
+ ffm->dts = dts;
+ }
+
+ /* write as many packets as needed */
+ while (size > 0) {
+ len = ffm->packet_end - ffm->packet_ptr;
+ if (len > size)
+ len = size;
+ memcpy(ffm->packet_ptr, buf, len);
+
+ ffm->packet_ptr += len;
+ buf += len;
+ size -= len;
+ if (ffm->packet_ptr >= ffm->packet_end)
+ flush_packet(s);
+ }
+}
+
+static void write_header_chunk(AVIOContext *pb, AVIOContext *dpb, unsigned id)
+{
+ uint8_t *dyn_buf;
+ int dyn_size= avio_close_dyn_buf(dpb, &dyn_buf);
+ avio_wb32(pb, id);
+ avio_wb32(pb, dyn_size);
+ avio_write(pb, dyn_buf, dyn_size);
+ av_free(dyn_buf);
+}
+
+static int ffm_write_header_codec_private_ctx(AVIOContext *pb, AVCodecContext *ctx, int type)
+{
+ AVIOContext *tmp;
+ char *buf = NULL;
+ int ret;
+ const AVCodec *enc = ctx->codec ? ctx->codec : avcodec_find_encoder(ctx->codec_id);
+
+ if (!enc)
+ return AVERROR(EINVAL);
+ if (ctx->priv_data && enc->priv_class && enc->priv_data_size) {
+ if ((ret = av_opt_serialize(ctx->priv_data, AV_OPT_FLAG_ENCODING_PARAM | type,
+ AV_OPT_SERIALIZE_SKIP_DEFAULTS, &buf, '=', ',')) < 0)
+ return ret;
+ if (buf && strlen(buf)) {
+ if (avio_open_dyn_buf(&tmp) < 0) {
+ av_free(buf);
+ return AVERROR(ENOMEM);
+ }
+ avio_put_str(tmp, buf);
+ write_header_chunk(pb, tmp, MKBETAG('C', 'P', 'R', 'V'));
+ }
+ av_free(buf);
+ }
+ return 0;
+}
+
+static int ffm_write_header_codec_ctx(AVIOContext *pb, AVCodecContext *ctx, unsigned tag, int type)
+{
+ AVIOContext *tmp;
+ char *buf = NULL;
+ uint8_t *p = NULL;
+ int ret, need_coma = 0;
+
+#define SKIP_DEFAULTS AV_OPT_SERIALIZE_SKIP_DEFAULTS
+#define OPT_FLAGS_EXACT AV_OPT_SERIALIZE_OPT_FLAGS_EXACT
+#define ENC AV_OPT_FLAG_ENCODING_PARAM
+
+ if (avio_open_dyn_buf(&tmp) < 0)
+ return AVERROR(ENOMEM);
+ if ((ret = av_opt_serialize(ctx, ENC | type, SKIP_DEFAULTS, &buf, '=', ',')) < 0)
+ goto fail;
+ if (buf && strlen(buf)) {
+ avio_write(tmp, buf, strlen(buf));
+ av_free(buf);
+ need_coma = 1;
+ }
+ if ((ret = av_opt_serialize(ctx, 0, SKIP_DEFAULTS | OPT_FLAGS_EXACT, &buf, '=', ',')) < 0)
+ goto fail;
+ if (buf && strlen(buf)) {
+ if (need_coma)
+ avio_w8(tmp, ',');
+ avio_write(tmp, buf, strlen(buf));
+ av_free(buf);
+ }
+ avio_w8(tmp, 0);
+ write_header_chunk(pb, tmp, tag);
+ return 0;
+ fail:
+ av_free(buf);
+ avio_close_dyn_buf(tmp, &p);
+ av_free(p);
+ return ret;
+
+#undef SKIP_DEFAULTS
+#undef OPT_FLAGS_EXACT
+#undef ENC
+}
+
+static int ffm_write_recommended_config(AVIOContext *pb, AVCodecContext *ctx, unsigned tag,
+ const char *configuration)
+{
+ int ret;
+ const AVCodec *enc = ctx->codec ? ctx->codec : avcodec_find_encoder(ctx->codec_id);
+ AVIOContext *tmp;
+ AVDictionaryEntry *t = NULL;
+ AVDictionary *all = NULL, *comm = NULL, *prv = NULL;
+ char *buf = NULL;
+
+ if (!enc || !enc->priv_class || !enc->priv_data_size) {
+ /* codec is not known/has no private options, so save everything as common options */
+ if (avio_open_dyn_buf(&tmp) < 0)
+ return AVERROR(ENOMEM);
+ avio_put_str(tmp, configuration);
+ write_header_chunk(pb, tmp, tag);
+ return 0;
+ }
+
+ if ((ret = av_dict_parse_string(&all, configuration, "=", ",", 0)) < 0)
+ return ret;
+
+ while ((t = av_dict_get(all, "", t, AV_DICT_IGNORE_SUFFIX))) {
+ if (av_opt_find((void *)&enc->priv_class, t->key, NULL, 0, AV_OPT_SEARCH_FAKE_OBJ)) {
+ if ((ret = av_dict_set(&prv, t->key, t->value, 0)) < 0)
+ goto fail;
+ } else if ((ret = av_dict_set(&comm, t->key, t->value, 0)) < 0)
+ goto fail;
+ }
+
+ if (comm) {
+ if ((ret = av_dict_get_string(comm, &buf, '=', ',')) < 0 ||
+ (ret = avio_open_dyn_buf(&tmp)) < 0)
+ goto fail;
+ avio_put_str(tmp, buf);
+ av_freep(&buf);
+ write_header_chunk(pb, tmp, tag);
+ }
+ if (prv) {
+ if ((ret = av_dict_get_string(prv, &buf, '=', ',')) < 0 ||
+ (ret = avio_open_dyn_buf(&tmp)) < 0)
+ goto fail;
+ avio_put_str(tmp, buf);
+ write_header_chunk(pb, tmp, MKBETAG('C', 'P', 'R', 'V'));
+ }
+
+ fail:
+ av_free(buf);
+ av_dict_free(&all);
+ av_dict_free(&comm);
+ av_dict_free(&prv);
+ return ret;
+}
+
+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, ret;
+
+ if (t = av_dict_get(s->metadata, "creation_time", NULL, 0)) {
+ ret = av_parse_time(&ffm->start_time, t->value, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ ffm->packet_size = FFM_PACKET_SIZE;
+
+ /* header */
+ avio_wl32(pb, MKTAG('F', 'F', 'M', '2'));
+ avio_wb32(pb, ffm->packet_size);
+ avio_wb64(pb, 0); /* current write position */
+
+ if(avio_open_dyn_buf(&pb) < 0)
+ return AVERROR(ENOMEM);
+
+ avio_wb32(pb, s->nb_streams);
+ bit_rate = 0;
+ for(i=0;i<s->nb_streams;i++) {
+ st = s->streams[i];
+ bit_rate += st->codec->bit_rate;
+ }
+ avio_wb32(pb, bit_rate);
+
+ write_header_chunk(s->pb, pb, MKBETAG('M', 'A', 'I', 'N'));
+
+ /* list of streams */
+ for(i=0;i<s->nb_streams;i++) {
+ st = s->streams[i];
+ avpriv_set_pts_info(st, 64, 1, 1000000);
+ if(avio_open_dyn_buf(&pb) < 0)
+ return AVERROR(ENOMEM);
+
+ codec = st->codec;
+ /* generic info */
+ avio_wb32(pb, codec->codec_id);
+ avio_w8(pb, codec->codec_type);
+ avio_wb32(pb, codec->bit_rate);
+ avio_wb32(pb, codec->flags);
+ avio_wb32(pb, codec->flags2);
+ avio_wb32(pb, codec->debug);
+ if (codec->flags & CODEC_FLAG_GLOBAL_HEADER) {
+ avio_wb32(pb, codec->extradata_size);
+ avio_write(pb, codec->extradata, codec->extradata_size);
+ }
+ write_header_chunk(s->pb, pb, MKBETAG('C', 'O', 'M', 'M'));
+ /* specific info */
+ switch(codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ if (st->recommended_encoder_configuration) {
+ av_log(NULL, AV_LOG_DEBUG, "writing recommended configuration: %s\n",
+ st->recommended_encoder_configuration);
+ if ((ret = ffm_write_recommended_config(s->pb, codec, MKBETAG('S', '2', 'V', 'I'),
+ st->recommended_encoder_configuration)) < 0)
+ return ret;
+ } else if ((ret = ffm_write_header_codec_ctx(s->pb, codec, MKBETAG('S', '2', 'V', 'I'), AV_OPT_FLAG_VIDEO_PARAM)) < 0 ||
+ (ret = ffm_write_header_codec_private_ctx(s->pb, codec, AV_OPT_FLAG_VIDEO_PARAM)) < 0)
+ return ret;
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ if (st->recommended_encoder_configuration) {
+ av_log(NULL, AV_LOG_DEBUG, "writing recommended configuration: %s\n",
+ st->recommended_encoder_configuration);
+ if ((ret = ffm_write_recommended_config(s->pb, codec, MKBETAG('S', '2', 'A', 'U'),
+ st->recommended_encoder_configuration)) < 0)
+ return ret;
+ } else if ((ret = ffm_write_header_codec_ctx(s->pb, codec, MKBETAG('S', '2', 'A', 'U'), AV_OPT_FLAG_AUDIO_PARAM)) < 0 ||
+ (ret = ffm_write_header_codec_private_ctx(s->pb, codec, AV_OPT_FLAG_AUDIO_PARAM)) < 0)
+ return ret;
+ break;
+ default:
+ return -1;
+ }
+ }
+ pb = s->pb;
+
+ avio_wb64(pb, 0); // end of header
+
+ /* flush until end of block reached */
+ while ((avio_tell(pb) % ffm->packet_size) != 0)
+ avio_w8(pb, 0);
+
+ avio_flush(pb);
+
+ /* init packet mux */
+ ffm->packet_ptr = ffm->packet;
+ ffm->packet_end = ffm->packet + ffm->packet_size - FFM_HEADER_SIZE;
+ av_assert0(ffm->packet_end >= ffm->packet);
+ ffm->frame_offset = 0;
+ ffm->dts = 0;
+ ffm->first_packet = 1;
+
+ return 0;
+}
+
+static int ffm_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ FFMContext *ffm = s->priv_data;
+ int64_t dts;
+ uint8_t header[FRAME_HEADER_SIZE+4];
+ int header_size = FRAME_HEADER_SIZE;
+
+ dts = ffm->start_time + pkt->dts;
+ /* packet size & key_frame */
+ header[0] = pkt->stream_index;
+ header[1] = 0;
+ if (pkt->flags & AV_PKT_FLAG_KEY)
+ header[1] |= FLAG_KEY_FRAME;
+ AV_WB24(header+2, pkt->size);
+ AV_WB24(header+5, pkt->duration);
+ AV_WB64(header+8, ffm->start_time + pkt->pts);
+ if (pkt->pts != pkt->dts) {
+ header[1] |= FLAG_DTS;
+ AV_WB32(header+16, pkt->pts - pkt->dts);
+ header_size += 4;
+ }
+ ffm_write_data(s, header, header_size, dts, 1);
+ ffm_write_data(s, pkt->data, pkt->size, dts, 0);
+
+ return 0;
+}
+
+static int ffm_write_trailer(AVFormatContext *s)
+{
+ FFMContext *ffm = s->priv_data;
+
+ /* flush packets */
+ if (ffm->packet_ptr > ffm->packet)
+ flush_packet(s);
+
+ return 0;
+}
+
+AVOutputFormat ff_ffm_muxer = {
+ .name = "ffm",
+ .long_name = NULL_IF_CONFIG_SMALL("FFM (FFserver live feed)"),
+ .extensions = "ffm",
+ .priv_data_size = sizeof(FFMContext),
+ .audio_codec = AV_CODEC_ID_MP2,
+ .video_codec = AV_CODEC_ID_MPEG1VIDEO,
+ .write_header = ffm_write_header,
+ .write_packet = ffm_write_packet,
+ .write_trailer = ffm_write_trailer,
+ .flags = AVFMT_TS_NEGATIVE,
+};
diff --git a/libavformat/ffmeta.h b/libavformat/ffmeta.h
index 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..e226406932 100644
--- a/libavformat/ffmetadec.c
+++ b/libavformat/ffmetadec.c
@@ -2,20 +2,20 @@
* Metadata demuxer
* Copyright (c) 2010 Anton Khirnov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -50,7 +50,7 @@ static void get_line(AVIOContext *s, uint8_t *buf, int size)
buf[i++] = c;
}
buf[i] = 0;
- } while (!s->eof_reached && (buf[0] == ';' || buf[0] == '#' || buf[0] == 0));
+ } while (!avio_feof(s) && (buf[0] == ';' || buf[0] == '#' || buf[0] == 0));
}
static AVChapter *read_chapter(AVFormatContext *s)
@@ -78,10 +78,11 @@ static AVChapter *read_chapter(AVFormatContext *s)
return avpriv_new_chapter(s, s->nb_chapters, tb, start, end, NULL);
}
-static uint8_t *unescape(uint8_t *buf, int size)
+static uint8_t *unescape(const uint8_t *buf, int size)
{
uint8_t *ret = av_malloc(size + 1);
- uint8_t *p1 = ret, *p2 = buf;
+ uint8_t *p1 = ret;
+ const uint8_t *p2 = buf;
if (!ret)
return NULL;
@@ -95,9 +96,10 @@ static uint8_t *unescape(uint8_t *buf, int size)
return ret;
}
-static int read_tag(uint8_t *line, AVDictionary **m)
+static int read_tag(const uint8_t *line, AVDictionary **m)
{
- uint8_t *key, *value, *p = line;
+ uint8_t *key, *value;
+ const uint8_t *p = line;
/* find first not escaped '=' */
while (1) {
@@ -128,14 +130,14 @@ static int read_header(AVFormatContext *s)
AVDictionary **m = &s->metadata;
uint8_t line[1024];
- while(!s->pb->eof_reached) {
+ while(!avio_feof(s->pb)) {
get_line(s->pb, line, sizeof(line));
if (!memcmp(line, ID_STREAM, strlen(ID_STREAM))) {
AVStream *st = avformat_new_stream(s, NULL);
if (!st)
- return -1;
+ return AVERROR(ENOMEM);
st->codec->codec_type = AVMEDIA_TYPE_DATA;
st->codec->codec_id = AV_CODEC_ID_FFMETADATA;
@@ -145,7 +147,7 @@ static int read_header(AVFormatContext *s)
AVChapter *ch = read_chapter(s);
if (!ch)
- return -1;
+ return AVERROR(ENOMEM);
m = &ch->metadata;
} else
diff --git a/libavformat/ffmetaenc.c b/libavformat/ffmetaenc.c
index 19fe6c960f..a9adbb1d19 100644
--- a/libavformat/ffmetaenc.c
+++ b/libavformat/ffmetaenc.c
@@ -2,20 +2,20 @@
* Metadata muxer
* Copyright (c) 2010 Anton Khirnov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/file.c b/libavformat/file.c
index 2837e9f150..6511328de7 100644
--- a/libavformat/file.c
+++ b/libavformat/file.c
@@ -2,20 +2,20 @@
* buffered file I/O
* Copyright (c) 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -35,6 +35,14 @@
#include "os_support.h"
#include "url.h"
+/* Some systems may not have S_ISFIFO */
+#ifndef S_ISFIFO
+# ifdef S_IFIFO
+# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+# else
+# define S_ISFIFO(m) 0
+# endif
+#endif
/* standard file protocol */
@@ -42,10 +50,17 @@ typedef struct FileContext {
const AVClass *class;
int fd;
int trunc;
+ int blocksize;
} FileContext;
static const AVOption file_options[] = {
- { "truncate", "Truncate existing files on write", offsetof(FileContext, trunc), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
+ { "truncate", "truncate existing files on write", offsetof(FileContext, trunc), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
+ { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { NULL }
+};
+
+static const AVOption pipe_options[] = {
+ { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
{ NULL }
};
@@ -56,16 +71,29 @@ static const AVClass file_class = {
.version = LIBAVUTIL_VERSION_INT,
};
+static const AVClass pipe_class = {
+ .class_name = "pipe",
+ .item_name = av_default_item_name,
+ .option = pipe_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
static int file_read(URLContext *h, unsigned char *buf, int size)
{
FileContext *c = h->priv_data;
- return read(c->fd, buf, size);
+ int r;
+ size = FFMIN(size, c->blocksize);
+ r = read(c->fd, buf, size);
+ return (-1 == r)?AVERROR(errno):r;
}
static int file_write(URLContext *h, const unsigned char *buf, int size)
{
FileContext *c = h->priv_data;
- return write(c->fd, buf, size);
+ int r;
+ size = FFMIN(size, c->blocksize);
+ r = write(c->fd, buf, size);
+ return (-1 == r)?AVERROR(errno):r;
}
static int file_get_handle(URLContext *h)
@@ -76,14 +104,30 @@ static int file_get_handle(URLContext *h)
static int file_check(URLContext *h, int mask)
{
+ int ret = 0;
+ const char *filename = h->filename;
+ av_strstart(filename, "file:", &filename);
+
+ {
+#if HAVE_ACCESS && defined(R_OK)
+ if (access(filename, F_OK) < 0)
+ return AVERROR(errno);
+ if (mask&AVIO_FLAG_READ)
+ if (access(filename, R_OK) >= 0)
+ ret |= AVIO_FLAG_READ;
+ if (mask&AVIO_FLAG_WRITE)
+ if (access(filename, W_OK) >= 0)
+ ret |= AVIO_FLAG_WRITE;
+#else
struct stat st;
- int ret = stat(h->filename, &st);
+ ret = stat(filename, &st);
if (ret < 0)
return AVERROR(errno);
ret |= st.st_mode&S_IRUSR ? mask&AVIO_FLAG_READ : 0;
ret |= st.st_mode&S_IWUSR ? mask&AVIO_FLAG_WRITE : 0;
-
+#endif
+ }
return ret;
}
@@ -94,6 +138,7 @@ static int file_open(URLContext *h, const char *filename, int flags)
FileContext *c = h->priv_data;
int access;
int fd;
+ struct stat st;
av_strstart(filename, "file:", &filename);
@@ -115,6 +160,9 @@ static int file_open(URLContext *h, const char *filename, int flags)
if (fd == -1)
return AVERROR(errno);
c->fd = fd;
+
+ h->is_streamed = !fstat(fd, &st) && S_ISFIFO(st.st_mode);
+
return 0;
}
@@ -126,9 +174,8 @@ static int64_t file_seek(URLContext *h, int64_t pos, int whence)
if (whence == AVSEEK_SIZE) {
struct stat st;
-
ret = fstat(c->fd, &st);
- return ret < 0 ? AVERROR(errno) : st.st_size;
+ return ret < 0 ? AVERROR(errno) : (S_ISFIFO(st.st_mode) ? 0 : st.st_size);
}
ret = lseek(c->fd, pos, whence);
@@ -190,6 +237,7 @@ URLProtocol ff_pipe_protocol = {
.url_get_file_handle = file_get_handle,
.url_check = file_check,
.priv_data_size = sizeof(FileContext),
+ .priv_data_class = &pipe_class,
};
#endif /* CONFIG_PIPE_PROTOCOL */
diff --git a/libavformat/filmstripdec.c b/libavformat/filmstripdec.c
index c82656e72f..cdbb93b11a 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;
}
@@ -67,6 +67,12 @@ static int read_header(AVFormatContext *s)
st->codec->width = avio_rb16(pb);
st->codec->height = avio_rb16(pb);
film->leading = avio_rb16(pb);
+
+ if (st->codec->width * 4LL * st->codec->height >= INT_MAX) {
+ av_log(s, AV_LOG_ERROR, "dimensions too large\n");
+ return AVERROR_PATCHWELCOME;
+ }
+
avpriv_set_pts_info(st, 64, 1, avio_rb16(pb));
avio_seek(pb, 0, SEEK_SET);
@@ -80,9 +86,9 @@ static int read_packet(AVFormatContext *s,
FilmstripDemuxContext *film = s->priv_data;
AVStream *st = s->streams[0];
- if (s->pb->eof_reached)
+ if (avio_feof(s->pb))
return AVERROR(EIO);
- pkt->dts = avio_tell(s->pb) / (st->codec->width * (st->codec->height + film->leading) * 4);
+ pkt->dts = avio_tell(s->pb) / (st->codec->width * (int64_t)(st->codec->height + film->leading) * 4);
pkt->size = av_get_packet(s->pb, pkt, st->codec->width * st->codec->height * 4);
avio_skip(s->pb, st->codec->width * (int64_t) film->leading * 4);
if (pkt->size < 0)
diff --git a/libavformat/filmstripenc.c b/libavformat/filmstripenc.c
index 0794b18c50..9e2c71082f 100644
--- a/libavformat/filmstripenc.c
+++ b/libavformat/filmstripenc.c
@@ -2,20 +2,20 @@
* Adobe Filmstrip muxer
* Copyright (c) 2010 Peter Ross
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -65,7 +65,7 @@ static int write_trailer(AVFormatContext *s)
avio_wb16(pb, st->codec->height);
avio_wb16(pb, 0); // leading
// TODO: should be avg_frame_rate
- avio_wb16(pb, 1/av_q2d(st->time_base));
+ avio_wb16(pb, st->time_base.den / st->time_base.num);
for (i = 0; i < 16; i++)
avio_w8(pb, 0x00); // reserved
diff --git a/libavformat/flac_picture.c b/libavformat/flac_picture.c
index a6b55379f8..669fd2e782 100644
--- a/libavformat/flac_picture.c
+++ b/libavformat/flac_picture.c
@@ -1,23 +1,25 @@
/*
* Raw FLAC picture parser
+ * Copyright (c) 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/avassert.h"
#include "avformat.h"
#include "flac_picture.h"
#include "id3v2.h"
@@ -32,7 +34,8 @@ int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size)
AVIOContext *pb = NULL;
AVStream *st;
int width, height, ret = 0;
- unsigned int type, len;
+ int len;
+ unsigned int type;
pb = avio_alloc_context(buf, buf_size, 0, NULL, NULL, NULL, NULL);
if (!pb)
@@ -43,15 +46,14 @@ int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size)
if (type >= FF_ARRAY_ELEMS(ff_id3v2_picture_types)) {
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;
}
/* picture mimetype */
len = avio_rb32(pb);
- if (!len || len >= 64 ||
+ if (len <= 0 || len >= 64 ||
avio_read(pb, mimetype, FFMIN(len, sizeof(mimetype) - 1)) != len) {
av_log(s, AV_LOG_ERROR, "Could not read mimetype from an attached "
"picture.\n");
@@ -59,6 +61,7 @@ int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size)
ret = AVERROR_INVALIDDATA;
goto fail;
}
+ av_assert0(len < sizeof(mimetype));
mimetype[len] = 0;
while (mime->id != AV_CODEC_ID_NONE) {
@@ -80,8 +83,7 @@ int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size)
len = avio_rb32(pb);
if (len > 0) {
if (!(desc = av_malloc(len + 1))) {
- ret = AVERROR(ENOMEM);
- goto fail;
+ RETURN_ERROR(AVERROR(ENOMEM));
}
if (avio_read(pb, desc, len) != len) {
@@ -100,16 +102,16 @@ int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size)
/* picture data */
len = avio_rb32(pb);
- if (!len) {
+ if (len <= 0) {
av_log(s, AV_LOG_ERROR, "Invalid attached picture size: %d.\n", len);
if (s->error_recognition & AV_EF_EXPLODE)
ret = AVERROR_INVALIDDATA;
goto fail;
}
- if (!(data = av_buffer_alloc(len))) {
- ret = AVERROR(ENOMEM);
- goto fail;
+ if (!(data = av_buffer_alloc(len + FF_INPUT_BUFFER_PADDING_SIZE))) {
+ RETURN_ERROR(AVERROR(ENOMEM));
}
+ memset(data->data + len, 0, FF_INPUT_BUFFER_PADDING_SIZE);
if (avio_read(pb, data->data, len) != len) {
av_log(s, AV_LOG_ERROR, "Error reading attached picture data.\n");
if (s->error_recognition & AV_EF_EXPLODE)
@@ -119,8 +121,7 @@ int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size)
st = avformat_new_stream(s, NULL);
if (!st) {
- ret = AVERROR(ENOMEM);
- goto fail;
+ RETURN_ERROR(AVERROR(ENOMEM));
}
av_init_packet(&st->attached_pic);
diff --git a/libavformat/flac_picture.h b/libavformat/flac_picture.h
index c7005829bc..4374b6f4f6 100644
--- a/libavformat/flac_picture.h
+++ b/libavformat/flac_picture.h
@@ -1,20 +1,21 @@
/*
* Raw FLAC picture parser
+ * Copyright (c) 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -23,6 +24,8 @@
#include "avformat.h"
+#define RETURN_ERROR(code) do { ret = (code); goto fail; } while (0)
+
int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size);
#endif /* AVFORMAT_FLAC_PICTURE_H */
diff --git a/libavformat/flacdec.c b/libavformat/flacdec.c
index 8abdd9d5b4..1a8dc19af3 100644
--- a/libavformat/flacdec.c
+++ b/libavformat/flacdec.c
@@ -2,20 +2,20 @@
* Raw FLAC demuxer
* Copyright (c) 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -38,7 +38,7 @@ static int flac_read_header(AVFormatContext *s)
return AVERROR(ENOMEM);
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = AV_CODEC_ID_FLAC;
- st->need_parsing = AVSTREAM_PARSE_FULL;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
/* the parameters will be extracted from the compressed bitstream */
/* if fLaC marker is not found, assume there is no header */
@@ -48,7 +48,7 @@ static int flac_read_header(AVFormatContext *s)
}
/* process metadata blocks */
- while (!s->pb->eof_reached && !metadata_last) {
+ while (!avio_feof(s->pb) && !metadata_last) {
avio_read(s->pb, header, 4);
flac_parse_block_header(header, &metadata_last, &metadata_type,
&metadata_size);
@@ -63,8 +63,7 @@ static int flac_read_header(AVFormatContext *s)
return AVERROR(ENOMEM);
}
if (avio_read(s->pb, buffer, metadata_size) != metadata_size) {
- av_freep(&buffer);
- return AVERROR(EIO);
+ RETURN_ERROR(AVERROR(EIO));
}
break;
/* skip metadata block for unsupported types */
@@ -80,12 +79,10 @@ static int flac_read_header(AVFormatContext *s)
/* 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;
@@ -109,24 +106,25 @@ static int flac_read_header(AVFormatContext *s)
const uint8_t *offset;
int i, chapters, track, ti;
if (metadata_size < 431)
- return AVERROR_INVALIDDATA;
+ RETURN_ERROR(AVERROR_INVALIDDATA);
offset = buffer + 395;
chapters = bytestream_get_byte(&offset) - 1;
if (chapters <= 0)
- return AVERROR_INVALIDDATA;
+ RETURN_ERROR(AVERROR_INVALIDDATA);
for (i = 0; i < chapters; i++) {
if (offset + 36 - buffer > metadata_size)
- return AVERROR_INVALIDDATA;
+ RETURN_ERROR(AVERROR_INVALIDDATA);
start = bytestream_get_be64(&offset);
track = bytestream_get_byte(&offset);
bytestream_get_buffer(&offset, isrc, 12);
isrc[12] = 0;
offset += 14;
ti = bytestream_get_byte(&offset);
- if (ti <= 0) return AVERROR_INVALIDDATA;
+ if (ti <= 0) RETURN_ERROR(AVERROR_INVALIDDATA);
offset += ti * 12;
avpriv_new_chapter(s, track, st->time_base, start, AV_NOPTS_VALUE, isrc);
}
+ av_freep(&buffer);
} else if (metadata_type == FLAC_METADATA_TYPE_PICTURE) {
ret = ff_flac_parse_picture(s, buffer, metadata_size);
av_freep(&buffer);
@@ -137,8 +135,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) {
@@ -173,6 +170,10 @@ static int flac_read_header(AVFormatContext *s)
return ret;
return 0;
+
+fail:
+ av_free(buffer);
+ return ret;
}
static int flac_probe(AVProbeData *p)
@@ -182,12 +183,61 @@ static int flac_probe(AVProbeData *p)
return AVPROBE_SCORE_EXTENSION;
}
+static av_unused int64_t flac_read_timestamp(AVFormatContext *s, int stream_index,
+ int64_t *ppos, int64_t pos_limit)
+{
+ AVPacket pkt, out_pkt;
+ AVStream *st = s->streams[stream_index];
+ AVCodecParserContext *parser;
+ int ret;
+ int64_t pts = AV_NOPTS_VALUE;
+
+ if (avio_seek(s->pb, *ppos, SEEK_SET) < 0)
+ return AV_NOPTS_VALUE;
+
+ av_init_packet(&pkt);
+ parser = av_parser_init(st->codec->codec_id);
+ if (!parser){
+ return AV_NOPTS_VALUE;
+ }
+ parser->flags |= PARSER_FLAG_USE_CODEC_TS;
+
+ for (;;){
+ ret = ff_raw_read_partial_packet(s, &pkt);
+ if (ret < 0){
+ if (ret == AVERROR(EAGAIN))
+ continue;
+ else
+ break;
+ }
+ av_init_packet(&out_pkt);
+ ret = av_parser_parse2(parser, st->codec,
+ &out_pkt.data, &out_pkt.size, pkt.data, pkt.size,
+ pkt.pts, pkt.dts, *ppos);
+
+ av_free_packet(&pkt);
+ if (out_pkt.size){
+ int size = out_pkt.size;
+ if (parser->pts != AV_NOPTS_VALUE){
+ // seeking may not have started from beginning of a frame
+ // calculate frame start position from next frame backwards
+ *ppos = parser->next_frame_offset - size;
+ pts = parser->pts;
+ break;
+ }
+ }
+ }
+ av_parser_close(parser);
+ return pts;
+}
+
AVInputFormat ff_flac_demuxer = {
.name = "flac",
.long_name = NULL_IF_CONFIG_SMALL("raw FLAC"),
.read_probe = flac_probe,
.read_header = flac_read_header,
.read_packet = ff_raw_read_partial_packet,
+ .read_timestamp = flac_read_timestamp,
.flags = AVFMT_GENERIC_INDEX,
.extensions = "flac",
.raw_codec_id = AV_CODEC_ID_FLAC,
diff --git a/libavformat/flacenc.c b/libavformat/flacenc.c
index a686826543..0eea94288f 100644
--- a/libavformat/flacenc.c
+++ b/libavformat/flacenc.c
@@ -2,20 +2,20 @@
* raw FLAC muxer
* Copyright (c) 2006-2009 Justin Ruggles
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -23,6 +23,7 @@
#include "libavutil/opt.h"
#include "libavcodec/flac.h"
#include "avformat.h"
+#include "avio_internal.h"
#include "flacenc.h"
#include "vorbiscomment.h"
#include "libavcodec/bytestream.h"
@@ -41,17 +42,14 @@ static int flac_write_block_padding(AVIOContext *pb, unsigned int n_padding_byte
{
avio_w8(pb, last_block ? 0x81 : 0x01);
avio_wb24(pb, n_padding_bytes);
- while (n_padding_bytes > 0) {
- avio_w8(pb, 0);
- n_padding_bytes--;
- }
+ ffio_fill(pb, 0, n_padding_bytes);
return 0;
}
static int flac_write_block_comment(AVIOContext *pb, AVDictionary **m,
int last_block, int bitexact)
{
- const char *vendor = bitexact ? "Libav" : LIBAVFORMAT_IDENT;
+ const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT;
unsigned int len;
uint8_t *p, *p0;
@@ -77,12 +75,28 @@ static int flac_write_block_comment(AVIOContext *pb, AVDictionary **m,
static int flac_write_header(struct AVFormatContext *s)
{
int ret;
+ int padding = s->metadata_header_padding;
AVCodecContext *codec = s->streams[0]->codec;
FlacMuxerContext *c = s->priv_data;
if (!c->write_header)
return 0;
+ if (s->nb_streams > 1) {
+ av_log(s, AV_LOG_ERROR, "only one stream is supported\n");
+ return AVERROR(EINVAL);
+ }
+ if (codec->codec_id != AV_CODEC_ID_FLAC) {
+ av_log(s, AV_LOG_ERROR, "unsupported codec\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (padding < 0)
+ padding = 8192;
+ /* The FLAC specification states that 24 bits are used to represent the
+ * size of a metadata block so we must clip this value to 2^24-1. */
+ padding = av_clip(padding, 0, 16777215);
+
ret = ff_flac_write_header(s->pb, codec->extradata,
codec->extradata_size, 0);
if (ret)
@@ -105,7 +119,7 @@ static int flac_write_header(struct AVFormatContext *s)
}
}
- ret = flac_write_block_comment(s->pb, &s->metadata, 0,
+ ret = flac_write_block_comment(s->pb, &s->metadata, !padding,
s->flags & AVFMT_FLAG_BITEXACT);
if (ret)
return ret;
@@ -113,8 +127,9 @@ static int flac_write_header(struct AVFormatContext *s)
/* The command line flac encoder defaults to placing a seekpoint
* every 10s. So one might add padding to allow that later
* but there seems to be no simple way to get the duration here.
- * So let's try the flac default of 8192 bytes */
- flac_write_block_padding(s->pb, 8192, 1);
+ * So just add the amount requested by the user. */
+ if (padding)
+ flac_write_block_padding(s->pb, padding, 1);
return ret;
}
diff --git a/libavformat/flacenc.h b/libavformat/flacenc.h
index 54dd8333bc..d5d53a5dcb 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 4f9bb206e5..61833cc8b9 100644
--- a/libavformat/flacenc_header.c
+++ b/libavformat/flacenc_header.c
@@ -2,20 +2,20 @@
* raw FLAC muxer
* Copyright (C) 2009 Justin Ruggles
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/flic.c b/libavformat/flic.c
index 2eb6f057b4..bef70c188a 100644
--- a/libavformat/flic.c
+++ b/libavformat/flic.c
@@ -1,21 +1,21 @@
/*
* FLI/FLC Animation File Demuxer
- * Copyright (c) 2003 The ffmpeg Project
+ * Copyright (c) 2003 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -80,7 +80,7 @@ static int flic_probe(AVProbeData *p)
return 0;
- return AVPROBE_SCORE_MAX;
+ return AVPROBE_SCORE_MAX - 1;
}
static int flic_read_header(AVFormatContext *s)
@@ -117,7 +117,7 @@ static int flic_read_header(AVFormatContext *s)
if (!st->codec->width || !st->codec->height) {
/* Ugly hack needed for the following sample: */
- /* http://samples.libav.org/fli-flc/fli-bugs/specular.flc */
+ /* http://samples.mplayerhq.hu/fli-flc/fli-bugs/specular.flc */
av_log(s, AV_LOG_WARNING,
"File with no specified width/height. Trying 640x480.\n");
st->codec->width = 640;
@@ -125,8 +125,8 @@ static int flic_read_header(AVFormatContext *s)
}
/* send over the whole 128-byte FLIC header */
- st->codec->extradata_size = FLIC_HEADER_SIZE;
- st->codec->extradata = av_malloc(FLIC_HEADER_SIZE);
+ if (ff_alloc_extradata(st->codec, FLIC_HEADER_SIZE))
+ return AVERROR(ENOMEM);
memcpy(st->codec->extradata, header, FLIC_HEADER_SIZE);
/* peek at the preamble to detect TFTD videos - they seem to always start with an audio chunk */
@@ -158,7 +158,6 @@ static int flic_read_header(AVFormatContext *s)
ast->codec->codec_tag = 0;
ast->codec->sample_rate = FLIC_TFTD_SAMPLE_RATE;
ast->codec->channels = 1;
- ast->codec->sample_fmt = AV_SAMPLE_FMT_U8;
ast->codec->bit_rate = st->codec->sample_rate * 8;
ast->codec->bits_per_coded_sample = 8;
ast->codec->channel_layout = AV_CH_LAYOUT_MONO;
@@ -176,9 +175,9 @@ static int flic_read_header(AVFormatContext *s)
avio_seek(pb, 12, SEEK_SET);
/* send over abbreviated FLIC header chunk */
- av_free(st->codec->extradata);
- st->codec->extradata_size = 12;
- st->codec->extradata = av_malloc(12);
+ av_freep(&st->codec->extradata);
+ if (ff_alloc_extradata(st->codec, 12))
+ return AVERROR(ENOMEM);
memcpy(st->codec->extradata, header, 12);
} else if (magic_number == FLIC_FILE_MAGIC_1) {
@@ -187,7 +186,7 @@ static int flic_read_header(AVFormatContext *s)
(magic_number == FLIC_FILE_MAGIC_3)) {
avpriv_set_pts_info(st, 64, speed, 1000);
} else {
- av_log(s, AV_LOG_INFO, "Invalid or unsupported magic chunk in file\n");
+ av_log(s, AV_LOG_ERROR, "Invalid or unsupported magic chunk in file\n");
return AVERROR_INVALIDDATA;
}
diff --git a/libavformat/flv.h b/libavformat/flv.h
index fe0fc90d83..db9468f469 100644
--- a/libavformat/flv.h
+++ b/libavformat/flv.h
@@ -1,22 +1,22 @@
/*
* FLV common header
*
- * Copyright (c) 2006 The Libav Project
+ * Copyright (c) 2006 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -46,6 +46,11 @@
#define AMF_END_OF_OBJECT 0x09
+#define KEYFRAMES_TAG "keyframes"
+#define KEYFRAMES_TIMESTAMP_TAG "times"
+#define KEYFRAMES_BYTEOFFSET_TAG "filepositions"
+
+
enum {
FLV_HEADER_FLAG_HASVIDEO = 1,
FLV_HEADER_FLAG_HASAUDIO = 4,
@@ -58,6 +63,13 @@ enum {
};
enum {
+ FLV_STREAM_TYPE_VIDEO,
+ FLV_STREAM_TYPE_AUDIO,
+ FLV_STREAM_TYPE_DATA,
+ FLV_STREAM_TYPE_NB,
+};
+
+enum {
FLV_MONO = 0,
FLV_STEREO = 1,
};
@@ -95,12 +107,16 @@ enum {
FLV_CODECID_VP6A = 5,
FLV_CODECID_SCREEN2 = 6,
FLV_CODECID_H264 = 7,
+ FLV_CODECID_REALH263= 8,
+ FLV_CODECID_MPEG4 = 9,
};
enum {
- FLV_FRAME_KEY = 1 << FLV_VIDEO_FRAMETYPE_OFFSET,
- FLV_FRAME_INTER = 2 << FLV_VIDEO_FRAMETYPE_OFFSET,
- FLV_FRAME_DISP_INTER = 3 << FLV_VIDEO_FRAMETYPE_OFFSET,
+ FLV_FRAME_KEY = 1 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< key frame (for AVC, a seekable frame)
+ FLV_FRAME_INTER = 2 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< inter frame (for AVC, a non-seekable frame)
+ FLV_FRAME_DISP_INTER = 3 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< disposable inter frame (H.263 only)
+ FLV_FRAME_GENERATED_KEY = 4 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< generated key frame (reserved for server use only)
+ FLV_FRAME_VIDEO_INFO_CMD = 5 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< video info/command frame
};
typedef enum {
diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c
index ff8637202b..1701f76e80 100644
--- a/libavformat/flvdec.c
+++ b/libavformat/flvdec.c
@@ -1,26 +1,26 @@
/*
* FLV demuxer
- * Copyright (c) 2003 The Libav Project
+ * Copyright (c) 2003 The FFmpeg Project
*
* This demuxer will generate a 1 byte extradata for VP6F content.
* It is composed of:
* - upper 4bits: difference between encoded width and visible width
* - lower 4bits: difference between encoded height and visible height
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -37,18 +37,14 @@
#include "avio_internal.h"
#include "flv.h"
-#define KEYFRAMES_TAG "keyframes"
-#define KEYFRAMES_TIMESTAMP_TAG "times"
-#define KEYFRAMES_BYTEOFFSET_TAG "filepositions"
-
#define VALIDATE_INDEX_TS_THRESH 2500
typedef struct FLVContext {
const AVClass *class; ///< Class for private options.
int trust_metadata; ///< configure streams according onMetaData
int wrong_dts; ///< wrong dts due to negative cts
- uint8_t *new_extradata[2];
- int new_extradata_size[2];
+ uint8_t *new_extradata[FLV_STREAM_TYPE_NB];
+ int new_extradata_size[FLV_STREAM_TYPE_NB];
int last_sample_rate;
int last_channels;
struct {
@@ -57,29 +53,49 @@ typedef struct FLVContext {
} validate_index[2];
int validate_next;
int validate_count;
+ int searched_for_end;
} FLVContext;
-static int flv_probe(AVProbeData *p)
+static int probe(AVProbeData *p, int live)
{
- const uint8_t *d;
+ const uint8_t *d = p->buf;
+ unsigned offset = AV_RB32(d + 5);
- d = p->buf;
if (d[0] == 'F' &&
d[1] == 'L' &&
d[2] == 'V' &&
d[3] < 5 && d[5] == 0 &&
- AV_RB32(d + 5) > 8) {
- return AVPROBE_SCORE_MAX;
+ offset + 100 < p->buf_size &&
+ offset > 8) {
+ int is_live = !memcmp(d + offset + 40, "NGINX RTMP", 10);
+
+ if (live == is_live)
+ return AVPROBE_SCORE_MAX;
}
return 0;
}
+static int flv_probe(AVProbeData *p)
+{
+ return probe(p, 0);
+}
+
+static int live_flv_probe(AVProbeData *p)
+{
+ return probe(p, 1);
+}
+
static AVStream *create_stream(AVFormatContext *s, int codec_type)
{
AVStream *st = avformat_new_stream(s, NULL);
if (!st)
return NULL;
st->codec->codec_type = codec_type;
+ if (s->nb_streams>=3 ||( s->nb_streams==2
+ && s->streams[0]->codec->codec_type != AVMEDIA_TYPE_DATA
+ && s->streams[1]->codec->codec_type != AVMEDIA_TYPE_DATA))
+ s->ctx_flags &= ~AVFMTCTX_NOHEADER;
+
avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
return st;
}
@@ -189,7 +205,7 @@ static void flv_set_audio_codec(AVFormatContext *s, AVStream *astream,
acodec->codec_id = AV_CODEC_ID_PCM_ALAW;
break;
default:
- av_log(s, AV_LOG_INFO, "Unsupported audio codec (%x)\n",
+ avpriv_request_sample(s, "Audio codec (%x)",
flv_codecid >> FLV_AUDIO_CODECID_OFFSET);
acodec->codec_tag = flv_codecid >> FLV_AUDIO_CODECID_OFFSET;
}
@@ -228,6 +244,9 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream,
case FLV_CODECID_H263:
vcodec->codec_id = AV_CODEC_ID_FLV1;
break;
+ case FLV_CODECID_REALH263:
+ vcodec->codec_id = AV_CODEC_ID_H263;
+ break; // Really mean it this time
case FLV_CODECID_SCREEN:
vcodec->codec_id = AV_CODEC_ID_FLASHSV;
break;
@@ -241,9 +260,7 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream,
vcodec->codec_id = AV_CODEC_ID_VP6A;
if (read) {
if (vcodec->extradata_size != 1) {
- vcodec->extradata = av_malloc(1);
- if (vcodec->extradata)
- vcodec->extradata_size = 1;
+ ff_alloc_extradata(vcodec, 1);
}
if (vcodec->extradata)
vcodec->extradata[0] = avio_r8(s->pb);
@@ -253,9 +270,13 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream,
return 1; // 1 byte body size adjustment for flv_read_packet()
case FLV_CODECID_H264:
vcodec->codec_id = AV_CODEC_ID_H264;
+ vstream->need_parsing = AVSTREAM_PARSE_HEADERS;
return 3; // not 4, reading packet type will consume one byte
+ case FLV_CODECID_MPEG4:
+ vcodec->codec_id = AV_CODEC_ID_MPEG4;
+ return 3;
default:
- av_log(s, AV_LOG_INFO, "Unsupported video codec (%x)\n", flv_codecid);
+ avpriv_request_sample(s, "Video codec (%x)", flv_codecid);
vcodec->codec_tag = flv_codecid;
}
@@ -281,56 +302,55 @@ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc,
AVStream *vstream, int64_t max_pos)
{
FLVContext *flv = s->priv_data;
- unsigned int arraylen = 0, timeslen = 0, fileposlen = 0, i;
- double num_val;
+ unsigned int timeslen = 0, fileposlen = 0, i;
char str_val[256];
int64_t *times = NULL;
int64_t *filepositions = NULL;
int ret = AVERROR(ENOSYS);
int64_t initial_pos = avio_tell(ioc);
+ if (vstream->nb_index_entries>0) {
+ av_log(s, AV_LOG_WARNING, "Skipping duplicate index\n");
+ return 0;
+ }
+
if (s->flags & AVFMT_FLAG_IGNIDX)
return 0;
while (avio_tell(ioc) < max_pos - 2 &&
amf_get_string(ioc, str_val, sizeof(str_val)) > 0) {
- int64_t *current_array;
+ int64_t **current_array;
+ unsigned int arraylen;
// Expect array object in context
if (avio_r8(ioc) != AMF_DATA_TYPE_ARRAY)
break;
arraylen = avio_rb32(ioc);
- if (arraylen >> 28)
+ if (arraylen>>28)
break;
- /* Expect only 'times' or 'filepositions' sub-arrays in other
- * case refuse to use such metadata for indexing. */
- if (!strcmp(KEYFRAMES_TIMESTAMP_TAG, str_val) && !times) {
- if (!(times = av_mallocz(sizeof(*times) * arraylen))) {
- ret = AVERROR(ENOMEM);
- goto finish;
- }
+ if (!strcmp(KEYFRAMES_TIMESTAMP_TAG , str_val) && !times) {
+ current_array = &times;
timeslen = arraylen;
- current_array = times;
} else if (!strcmp(KEYFRAMES_BYTEOFFSET_TAG, str_val) &&
!filepositions) {
- if (!(filepositions = av_mallocz(sizeof(*filepositions) * arraylen))) {
- ret = AVERROR(ENOMEM);
- goto finish;
- }
+ current_array = &filepositions;
fileposlen = arraylen;
- current_array = filepositions;
} else
// unexpected metatag inside keyframes, will not use such
// metadata for indexing
break;
+ if (!(*current_array = av_mallocz(sizeof(**current_array) * arraylen))) {
+ ret = AVERROR(ENOMEM);
+ goto finish;
+ }
+
for (i = 0; i < arraylen && avio_tell(ioc) < max_pos - 1; i++) {
if (avio_r8(ioc) != AMF_DATA_TYPE_NUMBER)
- goto finish;
- num_val = av_int2double(avio_rb64(ioc));
- current_array[i] = num_val;
+ goto invalid;
+ current_array[0][i] = av_int2double(avio_rb64(ioc));
}
if (times && filepositions) {
// All done, exiting at a position allowing amf_parse_object
@@ -340,7 +360,7 @@ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc,
}
}
- if (!ret && timeslen == fileposlen) {
+ if (timeslen == fileposlen && fileposlen>1 && max_pos <= filepositions[0]) {
for (i = 0; i < fileposlen; i++) {
av_add_index_entry(vstream, filepositions[i], times[i] * 1000,
0, 0, AVINDEX_KEYFRAME);
@@ -350,16 +370,15 @@ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc,
flv->validate_count = i + 1;
}
}
- } else
+ } else {
+invalid:
av_log(s, AV_LOG_WARNING, "Invalid keyframes object, skipping.\n");
+ }
finish:
av_freep(&times);
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;
}
@@ -371,7 +390,7 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
FLVContext *flv = s->priv_data;
AVIOContext *ioc;
AMFDataType amf_type;
- char str_val[256];
+ char str_val[1024];
double num_val;
num_val = 0;
@@ -386,23 +405,28 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
num_val = avio_r8(ioc);
break;
case AMF_DATA_TYPE_STRING:
- if (amf_get_string(ioc, str_val, sizeof(str_val)) < 0)
+ if (amf_get_string(ioc, str_val, sizeof(str_val)) < 0) {
+ av_log(s, AV_LOG_ERROR, "AMF_DATA_TYPE_STRING parsing failed\n");
return -1;
+ }
break;
case AMF_DATA_TYPE_OBJECT:
if ((vstream || astream) && key &&
+ ioc->seekable &&
!strcmp(KEYFRAMES_TAG, key) && depth == 1)
if (parse_keyframes_index(s, ioc, vstream ? vstream : astream,
max_pos) < 0)
- return -1;
+ av_log(s, AV_LOG_ERROR, "Keyframe index parsing failed\n");
while (avio_tell(ioc) < max_pos - 2 &&
amf_get_string(ioc, str_val, sizeof(str_val)) > 0)
if (amf_parse_object(s, astream, vstream, str_val, max_pos,
depth + 1) < 0)
return -1; // if we couldn't skip, bomb out.
- if (avio_r8(ioc) != AMF_END_OF_OBJECT)
+ if (avio_r8(ioc) != AMF_END_OF_OBJECT) {
+ av_log(s, AV_LOG_ERROR, "Missing AMF_END_OF_OBJECT in AMF_DATA_TYPE_OBJECT\n");
return -1;
+ }
break;
case AMF_DATA_TYPE_NULL:
case AMF_DATA_TYPE_UNDEFINED:
@@ -417,8 +441,10 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
if (amf_parse_object(s, astream, vstream, str_val, max_pos,
depth + 1) < 0)
return -1;
- if (avio_r8(ioc) != AMF_END_OF_OBJECT)
+ if (avio_r8(ioc) != AMF_END_OF_OBJECT) {
+ av_log(s, AV_LOG_ERROR, "Missing AMF_END_OF_OBJECT in AMF_DATA_TYPE_MIXEDARRAY\n");
return -1;
+ }
break;
case AMF_DATA_TYPE_ARRAY:
{
@@ -435,15 +461,16 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
avio_skip(ioc, 8 + 2); // timestamp (double) and UTC offset (int16)
break;
default: // unsupported type, we couldn't skip
+ av_log(s, AV_LOG_ERROR, "unsupported amf type %d\n", amf_type);
return -1;
}
if (key) {
+ acodec = astream ? astream->codec : NULL;
+ vcodec = vstream ? vstream->codec : NULL;
+
// stream info doesn't live any deeper than the first object
if (depth == 1) {
- acodec = astream ? astream->codec : NULL;
- vcodec = vstream ? vstream->codec : NULL;
-
if (amf_type == AMF_DATA_TYPE_NUMBER ||
amf_type == AMF_DATA_TYPE_BOOL) {
if (!strcmp(key, "duration"))
@@ -483,6 +510,11 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
}
}
+ if (amf_type == AMF_DATA_TYPE_OBJECT && s->nb_streams == 1 &&
+ ((!acodec && !strcmp(key, "audiocodecid")) ||
+ (!vcodec && !strcmp(key, "videocodecid"))))
+ s->ctx_flags &= ~AVFMTCTX_NOHEADER; //If there is either audio/video missing, codecid will be an empty object
+
if (!strcmp(key, "duration") ||
!strcmp(key, "filesize") ||
!strcmp(key, "width") ||
@@ -513,10 +545,14 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
return 0;
}
+#define TYPE_ONTEXTDATA 1
+#define TYPE_UNKNOWN 2
+
static int flv_read_metabody(AVFormatContext *s, int64_t next_pos)
{
AMFDataType type;
AVStream *stream, *astream, *vstream;
+ AVStream av_unused *dstream;
AVIOContext *ioc;
int i;
// only needs to hold the string "onMetaData".
@@ -525,28 +561,31 @@ static int flv_read_metabody(AVFormatContext *s, int64_t next_pos)
astream = NULL;
vstream = NULL;
+ dstream = NULL;
ioc = s->pb;
// first object needs to be "onMetaData" string
type = avio_r8(ioc);
if (type != AMF_DATA_TYPE_STRING ||
amf_get_string(ioc, buffer, sizeof(buffer)) < 0)
- return -1;
+ return TYPE_UNKNOWN;
if (!strcmp(buffer, "onTextData"))
- return 1;
+ return TYPE_ONTEXTDATA;
if (strcmp(buffer, "onMetaData") && strcmp(buffer, "onCuePoint"))
- return -1;
+ return TYPE_UNKNOWN;
// 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)
+ if (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO)
vstream = stream;
+ else if (stream->codec->codec_type == AVMEDIA_TYPE_AUDIO)
+ astream = stream;
+ else if (stream->codec->codec_type == AVMEDIA_TYPE_DATA)
+ dstream = stream;
}
// parse the second object (we want a mixed array)
@@ -571,6 +610,8 @@ static int flv_read_header(AVFormatContext *s)
if (flags & FLV_HEADER_FLAG_HASAUDIO)
if (!create_stream(s, AVMEDIA_TYPE_AUDIO))
return AVERROR(ENOMEM);
+ // Flag doesn't indicate whether or not there is script-data present. Must
+ // create that stream if it's encountered.
offset = avio_rb32(s->pb);
avio_seek(s->pb, offset, SEEK_SET);
@@ -583,20 +624,18 @@ static int flv_read_header(AVFormatContext *s)
static int flv_read_close(AVFormatContext *s)
{
+ int i;
FLVContext *flv = s->priv_data;
- av_freep(&flv->new_extradata[0]);
- av_freep(&flv->new_extradata[1]);
+ for (i=0; i<FLV_STREAM_TYPE_NB; i++)
+ av_freep(&flv->new_extradata[i]);
return 0;
}
static int flv_get_extradata(AVFormatContext *s, AVStream *st, int size)
{
- av_free(st->codec->extradata);
- st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
+ av_freep(&st->codec->extradata);
+ if (ff_get_extradata(st->codec, s->pb, size) < 0)
return AVERROR(ENOMEM);
- st->codec->extradata_size = size;
- avio_read(s->pb, st->codec->extradata, st->codec->extradata_size);
return 0;
}
@@ -737,21 +776,23 @@ skip:
static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
{
FLVContext *flv = s->priv_data;
- int ret, i, type, size, flags, is_audio;
- int64_t next, pos;
+ int ret, i, type, size, flags;
+ int stream_type=-1;
+ int64_t next, pos, meta_pos;
int64_t dts, pts = AV_NOPTS_VALUE;
- int sample_rate = 0, channels = 0;
+ int av_uninit(channels);
+ int av_uninit(sample_rate);
AVStream *st = NULL;
/* pkt size is repeated at end. skip it */
for (;; avio_skip(s->pb, 4)) {
pos = avio_tell(s->pb);
- type = avio_r8(s->pb);
+ type = (avio_r8(s->pb) & 0x1F);
size = avio_rb24(s->pb);
dts = avio_rb24(s->pb);
dts |= avio_r8(s->pb) << 24;
- av_dlog(s, "type:%d, size:%d, dts:%"PRId64"\n", type, size, dts);
- if (s->pb->eof_reached)
+ av_dlog(s, "type:%d, size:%d, dts:%"PRId64" pos:%"PRId64"\n", type, size, dts, avio_tell(s->pb));
+ if (avio_feof(s->pb))
return AVERROR_EOF;
avio_skip(s->pb, 3); /* stream id, always 0 */
flags = 0;
@@ -778,24 +819,28 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
next = size + avio_tell(s->pb);
if (type == FLV_TAG_TYPE_AUDIO) {
- is_audio = 1;
+ stream_type = FLV_STREAM_TYPE_AUDIO;
flags = avio_r8(s->pb);
size--;
} else if (type == FLV_TAG_TYPE_VIDEO) {
- is_audio = 0;
+ stream_type = FLV_STREAM_TYPE_VIDEO;
flags = avio_r8(s->pb);
size--;
- if ((flags & 0xf0) == 0x50) /* video info / command frame */
+ if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD)
goto skip;
+ } else if (type == FLV_TAG_TYPE_META) {
+ stream_type=FLV_STREAM_TYPE_DATA;
+ if (size > 13 + 1 + 4 && dts == 0) { // Header-type metadata stuff
+ meta_pos = avio_tell(s->pb);
+ if (flv_read_metabody(s, next) <= 0) {
+ goto skip;
+ }
+ avio_seek(s->pb, meta_pos, SEEK_SET);
+ }
} else {
- if (type == FLV_TAG_TYPE_META && size > 13 + 1 + 4)
- if (flv_read_metabody(s, next) > 0) {
- return flv_data_packet(s, pkt, dts, next);
- } else /* skip packet */
- av_log(s, AV_LOG_DEBUG,
- "Skipping flv packet: type %d, size %d, flags %d.\n",
- type, size, flags);
-
+ av_log(s, AV_LOG_DEBUG,
+ "Skipping flv packet: type %d, size %d, flags %d.\n",
+ type, size, flags);
skip:
avio_seek(s->pb, next, SEEK_SET);
continue;
@@ -808,32 +853,37 @@ skip:
/* now find stream */
for (i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
- if (is_audio && st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
- if (flv_same_audio_codec(st->codec, flags))
+ if (stream_type == FLV_STREAM_TYPE_AUDIO) {
+ if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
+ (s->audio_codec_id || flv_same_audio_codec(st->codec, flags)))
break;
- } else if (!is_audio &&
- st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
- if (flv_same_video_codec(st->codec, flags))
+ } else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
+ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
+ (s->video_codec_id || flv_same_video_codec(st->codec, flags)))
+ break;
+ } else if (stream_type == FLV_STREAM_TYPE_DATA) {
+ if (st->codec->codec_type == AVMEDIA_TYPE_DATA)
break;
}
}
if (i == s->nb_streams) {
- st = create_stream(s, is_audio ? AVMEDIA_TYPE_AUDIO
- : AVMEDIA_TYPE_VIDEO);
+ static const enum AVMediaType stream_types[] = {AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_DATA};
+ av_log(s, AV_LOG_WARNING, "Stream discovered after head already parsed\n");
+ st = create_stream(s, stream_types[stream_type]);
if (!st)
return AVERROR(ENOMEM);
+
}
- av_dlog(s, "%d %X %d \n", is_audio, flags, st->discard);
+ av_dlog(s, "%d %X %d \n", stream_type, flags, st->discard);
if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||
- is_audio)
+ stream_type == FLV_STREAM_TYPE_AUDIO)
av_add_index_entry(st, pos, dts, size, 0, AVINDEX_KEYFRAME);
- if ((st->discard >= AVDISCARD_NONKEY &&
- !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || is_audio)) ||
- (st->discard >= AVDISCARD_BIDIR &&
- ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && !is_audio)) ||
- st->discard >= AVDISCARD_ALL) {
+ if ( (st->discard >= AVDISCARD_NONKEY && !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || (stream_type == FLV_STREAM_TYPE_AUDIO)))
+ ||(st->discard >= AVDISCARD_BIDIR && ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && (stream_type == FLV_STREAM_TYPE_VIDEO)))
+ || st->discard >= AVDISCARD_ALL
+ ) {
avio_seek(s->pb, next, SEEK_SET);
continue;
}
@@ -842,12 +892,13 @@ skip:
// if not streamed and no duration from metadata then seek to end to find
// the duration from the timestamps
- if (s->pb->seekable && (!s->duration || s->duration == AV_NOPTS_VALUE)) {
+ if (s->pb->seekable && (!s->duration || s->duration == AV_NOPTS_VALUE) && !flv->searched_for_end) {
int size;
const int64_t pos = avio_tell(s->pb);
// Read the last 4 bytes of the file, this should be the size of the
// previous FLV tag. Use the timestamp of its payload as duration.
- 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);
// Seek to the start of the last FLV tag at position (fsize - 4 - size)
@@ -856,12 +907,19 @@ skip:
if (size == avio_rb24(s->pb) + 11) {
uint32_t ts = avio_rb24(s->pb);
ts |= avio_r8(s->pb) << 24;
- s->duration = ts * (int64_t)AV_TIME_BASE / 1000;
+ if (ts)
+ s->duration = ts * (int64_t)AV_TIME_BASE / 1000;
+ else if (fsize >= 8 && fsize - 8 >= size) {
+ fsize -= size+4;
+ goto retry_duration;
+ }
}
+
avio_seek(s->pb, pos, SEEK_SET);
+ flv->searched_for_end = 1;
}
- if (is_audio) {
+ if (stream_type == FLV_STREAM_TYPE_AUDIO) {
int bits_per_coded_sample;
channels = (flags & FLV_AUDIO_CHANNEL_MASK) == FLV_STEREO ? 2 : 1;
sample_rate = 44100 << ((flags & FLV_AUDIO_SAMPLERATE_MASK) >>
@@ -884,49 +942,59 @@ skip:
flv->last_channels =
channels = st->codec->channels;
} else {
- AVCodecContext ctx;
+ AVCodecContext ctx = {0};
ctx.sample_rate = sample_rate;
ctx.bits_per_coded_sample = bits_per_coded_sample;
flv_set_audio_codec(s, st, &ctx, flags & FLV_AUDIO_CODECID_MASK);
sample_rate = ctx.sample_rate;
}
- } else {
+ } else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
size -= flv_set_video_codec(s, st, flags & FLV_VIDEO_CODECID_MASK, 1);
}
if (st->codec->codec_id == AV_CODEC_ID_AAC ||
- st->codec->codec_id == AV_CODEC_ID_H264) {
+ st->codec->codec_id == AV_CODEC_ID_H264 ||
+ st->codec->codec_id == AV_CODEC_ID_MPEG4) {
int type = avio_r8(s->pb);
size--;
- if (st->codec->codec_id == AV_CODEC_ID_H264) {
+ if (st->codec->codec_id == AV_CODEC_ID_H264 || st->codec->codec_id == AV_CODEC_ID_MPEG4) {
// sign extension
int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000;
pts = dts + cts;
- if (cts < 0 && !flv->wrong_dts) { // dts might be wrong
+ if (cts < 0) { // dts might be wrong
+ if (!flv->wrong_dts)
+ av_log(s, AV_LOG_WARNING,
+ "Negative cts, previous timestamps might be wrong.\n");
flv->wrong_dts = 1;
+ } else if (FFABS(dts - pts) > 1000*60*15) {
av_log(s, AV_LOG_WARNING,
- "Negative cts, previous timestamps might be wrong.\n");
+ "invalid timestamps %"PRId64" %"PRId64"\n", dts, pts);
+ dts = pts = AV_NOPTS_VALUE;
}
}
- if (type == 0) {
+ if (type == 0 && (!st->codec->extradata || st->codec->codec_id == AV_CODEC_ID_AAC ||
+ st->codec->codec_id == AV_CODEC_ID_H264)) {
+ AVDictionaryEntry *t;
+
if (st->codec->extradata) {
- if ((ret = flv_queue_extradata(flv, s->pb, is_audio, size)) < 0)
+ if ((ret = flv_queue_extradata(flv, s->pb, stream_type, size)) < 0)
return ret;
ret = AVERROR(EAGAIN);
goto leave;
}
if ((ret = flv_get_extradata(s, st, size)) < 0)
return ret;
- if (st->codec->codec_id == AV_CODEC_ID_AAC) {
- MPEG4AudioConfig cfg;
- /* Workaround for buggy Omnia A/XE encoder */
- AVDictionaryEntry *t = av_dict_get(s->metadata, "Encoder", NULL, 0);
- if (t && !strcmp(t->value, "Omnia A/XE"))
- st->codec->extradata_size = 2;
+ /* Workaround for buggy Omnia A/XE encoder */
+ t = av_dict_get(s->metadata, "Encoder", NULL, 0);
+ if (st->codec->codec_id == AV_CODEC_ID_AAC && t && !strcmp(t->value, "Omnia A/XE"))
+ st->codec->extradata_size = 2;
- avpriv_mpeg4audio_get_config(&cfg, st->codec->extradata,
- st->codec->extradata_size * 8, 1);
+ if (st->codec->codec_id == AV_CODEC_ID_AAC && 0) {
+ MPEG4AudioConfig cfg;
+
+ if (avpriv_mpeg4audio_get_config(&cfg, st->codec->extradata,
+ st->codec->extradata_size * 8, 1) >= 0) {
st->codec->channels = cfg.channels;
st->codec->channel_layout = 0;
if (cfg.ext_sample_rate)
@@ -935,6 +1003,7 @@ skip:
st->codec->sample_rate = cfg.sample_rate;
av_dlog(s, "mp4a config channels %d sample rate %d\n",
st->codec->channels, st->codec->sample_rate);
+ }
}
ret = AVERROR(EAGAIN);
@@ -950,31 +1019,31 @@ skip:
ret = av_get_packet(s->pb, pkt, size);
if (ret < 0)
- return AVERROR(EIO);
- /* note: we need to modify the packet size here to handle the last
- * packet */
- pkt->size = ret;
+ return ret;
pkt->dts = dts;
pkt->pts = pts == AV_NOPTS_VALUE ? dts : pts;
pkt->stream_index = st->index;
- if (flv->new_extradata[is_audio]) {
+ if (flv->new_extradata[stream_type]) {
uint8_t *side = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
- flv->new_extradata_size[is_audio]);
+ flv->new_extradata_size[stream_type]);
if (side) {
- memcpy(side, flv->new_extradata[is_audio],
- flv->new_extradata_size[is_audio]);
- av_freep(&flv->new_extradata[is_audio]);
- flv->new_extradata_size[is_audio] = 0;
+ memcpy(side, flv->new_extradata[stream_type],
+ flv->new_extradata_size[stream_type]);
+ av_freep(&flv->new_extradata[stream_type]);
+ flv->new_extradata_size[stream_type] = 0;
}
}
- if (is_audio && (sample_rate != flv->last_sample_rate ||
+ if (stream_type == FLV_STREAM_TYPE_AUDIO &&
+ (sample_rate != flv->last_sample_rate ||
channels != flv->last_channels)) {
flv->last_sample_rate = sample_rate;
flv->last_channels = channels;
ff_add_param_change(pkt, channels, 0, sample_rate, 0, 0);
}
- if (is_audio || ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY))
+ if ( stream_type == FLV_STREAM_TYPE_AUDIO ||
+ ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY) ||
+ stream_type == FLV_STREAM_TYPE_DATA)
pkt->flags |= AV_PKT_FLAG_KEY;
leave:
@@ -997,7 +1066,7 @@ static const AVOption options[] = {
{ NULL }
};
-static const AVClass class = {
+static const AVClass flv_class = {
.class_name = "flvdec",
.item_name = av_default_item_name,
.option = options,
@@ -1014,5 +1083,26 @@ AVInputFormat ff_flv_demuxer = {
.read_seek = flv_read_seek,
.read_close = flv_read_close,
.extensions = "flv",
- .priv_class = &class,
+ .priv_class = &flv_class,
+};
+
+static const AVClass live_flv_class = {
+ .class_name = "live_flvdec",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_live_flv_demuxer = {
+ .name = "live_flv",
+ .long_name = NULL_IF_CONFIG_SMALL("live RTMP FLV (Flash Video)"),
+ .priv_data_size = sizeof(FLVContext),
+ .read_probe = live_flv_probe,
+ .read_header = flv_read_header,
+ .read_packet = flv_read_packet,
+ .read_seek = flv_read_seek,
+ .read_close = flv_read_close,
+ .extensions = "flv",
+ .priv_class = &live_flv_class,
+ .flags = AVFMT_TS_DISCONT
};
diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
index 202df86db7..7d3a0ca99f 100644
--- a/libavformat/flvenc.c
+++ b/libavformat/flvenc.c
@@ -1,40 +1,43 @@
/*
* FLV muxer
- * Copyright (c) 2003 The Libav Project
+ * Copyright (c) 2003 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
#include "libavutil/intfloat.h"
+#include "libavutil/avassert.h"
#include "avc.h"
#include "avformat.h"
#include "flv.h"
#include "internal.h"
#include "metadata.h"
-#undef NDEBUG
-#include <assert.h>
static const AVCodecTag flv_video_codec_ids[] = {
{ AV_CODEC_ID_FLV1, FLV_CODECID_H263 },
+ { AV_CODEC_ID_H263, FLV_CODECID_REALH263 },
+ { AV_CODEC_ID_MPEG4, FLV_CODECID_MPEG4 },
{ AV_CODEC_ID_FLASHSV, FLV_CODECID_SCREEN },
{ AV_CODEC_ID_FLASHSV2, FLV_CODECID_SCREEN2 },
{ AV_CODEC_ID_VP6F, FLV_CODECID_VP6 },
+ { AV_CODEC_ID_VP6, FLV_CODECID_VP6 },
{ AV_CODEC_ID_VP6A, FLV_CODECID_VP6A },
{ AV_CODEC_ID_H264, FLV_CODECID_H264 },
{ AV_CODEC_ID_NONE, 0 }
@@ -82,12 +85,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 {
@@ -110,9 +113,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);
}
}
@@ -153,8 +156,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;
@@ -272,6 +276,30 @@ static void write_metadata(AVFormatContext *s, unsigned int ts)
}
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")
+ ||!strcmp(tag->key, "datasize")
+ ||!strcmp(tag->key, "lasttimestamp")
+ ||!strcmp(tag->key, "totalframes")
+ ||!strcmp(tag->key, "hasAudio")
+ ||!strcmp(tag->key, "hasVideo")
+ ||!strcmp(tag->key, "hasCuePoints")
+ ||!strcmp(tag->key, "hasMetadata")
+ ||!strcmp(tag->key, "hasKeyframes")
+ ){
+ 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);
@@ -320,8 +348,24 @@ static int flv_write_header(AVFormatContext *s)
}
flv->video_enc = enc;
if (enc->codec_tag == 0) {
- av_log(s, AV_LOG_ERROR, "video codec not compatible with flv\n");
- return -1;
+ av_log(s, AV_LOG_ERROR, "Video codec '%s' for stream %d is not compatible with FLV\n",
+ avcodec_get_name(enc->codec_id), i);
+ return AVERROR(EINVAL);
+ }
+ if (enc->codec_id == AV_CODEC_ID_MPEG4 ||
+ enc->codec_id == AV_CODEC_ID_H263) {
+ int error = s->strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL;
+ av_log(s, error ? AV_LOG_ERROR : AV_LOG_WARNING,
+ "Codec %s is not supported in the official FLV specification,\n", avcodec_get_name(enc->codec_id));
+
+ if (error) {
+ av_log(s, AV_LOG_ERROR,
+ "use vstrict=-1 / -strict -1 to use it anyway.\n");
+ return AVERROR(EINVAL);
+ }
+ } else if (enc->codec_id == AV_CODEC_ID_VP6) {
+ av_log(s, AV_LOG_WARNING,
+ "Muxing VP6 in flv will produce flipped video on playback.\n");
}
break;
case AVMEDIA_TYPE_AUDIO:
@@ -333,17 +377,30 @@ static int flv_write_header(AVFormatContext *s)
flv->audio_enc = enc;
if (get_audio_flags(s, enc) < 0)
return AVERROR_INVALIDDATA;
+ if (enc->codec_id == AV_CODEC_ID_PCM_S16BE)
+ av_log(s, AV_LOG_WARNING,
+ "16-bit big-endian audio in flv is valid but most likely unplayable (hardware dependent); use s16le\n");
break;
case AVMEDIA_TYPE_DATA:
+ if (enc->codec_id != AV_CODEC_ID_TEXT && enc->codec_id != AV_CODEC_ID_NONE) {
+ 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;
+ }
+ flv->data_enc = enc;
+ break;
+ case AVMEDIA_TYPE_SUBTITLE:
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, "Subtitle codec '%s' for stream %d is not compatible with FLV\n",
+ avcodec_get_name(enc->codec_id), i);
return AVERROR_INVALIDDATA;
}
flv->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 */
@@ -377,7 +434,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);
@@ -420,7 +477,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);
}
@@ -449,12 +506,12 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
unsigned ts;
int size = pkt->size;
uint8_t *data = NULL;
- int flags = 0, flags_size;
+ int flags = -1, flags_size, ret;
if (enc->codec_id == AV_CODEC_ID_VP6F || enc->codec_id == AV_CODEC_ID_VP6A ||
- enc->codec_id == AV_CODEC_ID_AAC)
+ enc->codec_id == AV_CODEC_ID_VP6 || enc->codec_id == AV_CODEC_ID_AAC)
flags_size = 2;
- else if (enc->codec_id == AV_CODEC_ID_H264)
+ else if (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4)
flags_size = 5;
else
flags_size = 1;
@@ -482,9 +539,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;
@@ -492,10 +549,11 @@ 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;
+ case AVMEDIA_TYPE_SUBTITLE:
case AVMEDIA_TYPE_DATA:
avio_w8(pb, FLV_TAG_TYPE_META);
break;
@@ -503,11 +561,21 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
return AVERROR(EINVAL);
}
- if (enc->codec_id == AV_CODEC_ID_H264)
- /* check if extradata looks like MP4 */
+ if (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) {
+ /* check if extradata looks like mp4 formated */
if (enc->extradata_size > 0 && *(uint8_t*)enc->extradata != 1)
- if (ff_avc_parse_nal_units_buf(pkt->data, &data, &size) < 0)
- return -1;
+ if ((ret = ff_avc_parse_nal_units_buf(pkt->data, &data, &size)) < 0)
+ return ret;
+ } else if (enc->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 &&
+ (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) {
+ if (!s->streams[pkt->stream_index]->nb_frames) {
+ av_log(s, AV_LOG_ERROR, "Malformed AAC bitstream detected: "
+ "use the audio bitstream filter 'aac_adtstoasc' to fix it "
+ "('-bsf:a aac_adtstoasc' option with ffmpeg)\n");
+ return AVERROR_INVALIDDATA;
+ }
+ av_log(s, AV_LOG_WARNING, "aac bitstream error\n");
+ }
/* check Speex packet duration */
if (enc->codec_id == AV_CODEC_ID_SPEEX && ts - sc->last_ts > 160)
@@ -518,26 +586,39 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
if (sc->last_ts < ts)
sc->last_ts = ts;
+ if (size + flags_size >= 1<<24) {
+ av_log(s, AV_LOG_ERROR, "Too large packet with size %u >= %u\n",
+ size + flags_size, 1<<24);
+ return AVERROR(EINVAL);
+ }
+
avio_wb24(pb, size + flags_size);
- avio_wb24(pb, ts);
+ avio_wb24(pb, ts & 0xFFFFFF);
avio_w8(pb, (ts >> 24) & 0x7F); // timestamps are 32 bits _signed_
avio_wb24(pb, flv->reserved);
- if (enc->codec_type == AVMEDIA_TYPE_DATA) {
+ if (enc->codec_type == AVMEDIA_TYPE_DATA ||
+ enc->codec_type == AVMEDIA_TYPE_SUBTITLE ) {
int data_size;
int64_t metadata_size_pos = avio_tell(pb);
- avio_w8(pb, AMF_DATA_TYPE_STRING);
- put_amf_string(pb, "onTextData");
- avio_w8(pb, AMF_DATA_TYPE_MIXEDARRAY);
- avio_wb32(pb, 2);
- put_amf_string(pb, "type");
- avio_w8(pb, AMF_DATA_TYPE_STRING);
- put_amf_string(pb, "Text");
- put_amf_string(pb, "text");
- avio_w8(pb, AMF_DATA_TYPE_STRING);
- put_amf_string(pb, pkt->data);
- put_amf_string(pb, "");
- avio_w8(pb, AMF_END_OF_OBJECT);
+ if (enc->codec_id == AV_CODEC_ID_TEXT) {
+ // legacy FFmpeg magic?
+ avio_w8(pb, AMF_DATA_TYPE_STRING);
+ put_amf_string(pb, "onTextData");
+ avio_w8(pb, AMF_DATA_TYPE_MIXEDARRAY);
+ avio_wb32(pb, 2);
+ put_amf_string(pb, "type");
+ avio_w8(pb, AMF_DATA_TYPE_STRING);
+ put_amf_string(pb, "Text");
+ put_amf_string(pb, "text");
+ avio_w8(pb, AMF_DATA_TYPE_STRING);
+ put_amf_string(pb, pkt->data);
+ put_amf_string(pb, "");
+ avio_w8(pb, AMF_END_OF_OBJECT);
+ } else {
+ // just pass the metadata through
+ avio_write(pb, data ? data : pkt->data, size);
+ }
/* write total size of tag */
data_size = avio_tell(pb) - metadata_size_pos;
avio_seek(pb, metadata_size_pos - 10, SEEK_SET);
@@ -545,7 +626,10 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
avio_seek(pb, data_size + 10 - 3, SEEK_CUR);
avio_wb32(pb, data_size + 11);
} else {
+ av_assert1(flags>=0);
avio_w8(pb,flags);
+ if (enc->codec_id == AV_CODEC_ID_VP6)
+ avio_w8(pb,0);
if (enc->codec_id == AV_CODEC_ID_VP6F || enc->codec_id == AV_CODEC_ID_VP6A) {
if (enc->extradata_size)
avio_w8(pb, enc->extradata[0]);
@@ -554,7 +638,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
(FFALIGN(enc->height, 16) - enc->height));
} else if (enc->codec_id == AV_CODEC_ID_AAC)
avio_w8(pb, 1); // AAC raw
- else if (enc->codec_id == AV_CODEC_ID_H264) {
+ else if (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) {
avio_w8(pb, 1); // AVC NALU
avio_wb24(pb, pkt->pts - pkt->dts);
}
diff --git a/libavformat/format.c b/libavformat/format.c
index 24b7205d44..fa94b7d3b5 100644
--- a/libavformat/format.c
+++ b/libavformat/format.c
@@ -2,24 +2,26 @@
* Format register and lookup
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/atomic.h"
#include "libavutil/avstring.h"
+#include "libavutil/bprint.h"
#include "libavutil/opt.h"
#include "avio_internal.h"
@@ -27,6 +29,7 @@
#include "id3v2.h"
#include "internal.h"
+
/**
* @file
* Format register and lookup
@@ -36,6 +39,9 @@ static AVInputFormat *first_iformat = NULL;
/** head of registered output format linked list */
static AVOutputFormat *first_oformat = NULL;
+static AVInputFormat **last_iformat = &first_iformat;
+static AVOutputFormat **last_oformat = &first_oformat;
+
AVInputFormat *av_iformat_next(const AVInputFormat *f)
{
if (f)
@@ -54,50 +60,34 @@ AVOutputFormat *av_oformat_next(const AVOutputFormat *f)
void av_register_input_format(AVInputFormat *format)
{
- AVInputFormat **p = &first_iformat;
-
- while (*p)
- p = &(*p)->next;
+ AVInputFormat **p = last_iformat;
- *p = format;
format->next = NULL;
+ while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))
+ p = &(*p)->next;
+ last_iformat = &format->next;
}
void av_register_output_format(AVOutputFormat *format)
{
- AVOutputFormat **p = &first_oformat;
-
- while (*p)
- p = &(*p)->next;
+ AVOutputFormat **p = last_oformat;
- *p = format;
format->next = NULL;
+ while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))
+ p = &(*p)->next;
+ last_oformat = &format->next;
}
int av_match_ext(const char *filename, const char *extensions)
{
- const char *ext, *p;
- char ext1[32], *q;
+ const char *ext;
if (!filename)
return 0;
ext = strrchr(filename, '.');
- if (ext) {
- ext++;
- p = extensions;
- for (;;) {
- q = ext1;
- while (*p != '\0' && *p != ',' && q - ext1 < sizeof(ext1) - 1)
- *q++ = *p++;
- *q = '\0';
- if (!av_strcasecmp(ext1, ext))
- return 1;
- if (*p == '\0')
- break;
- p++;
- }
- }
+ if (ext)
+ return av_match_name(ext + 1, extensions);
return 0;
}
@@ -120,7 +110,7 @@ AVOutputFormat *av_guess_format(const char *short_name, const char *filename,
score_max = 0;
while ((fmt = av_oformat_next(fmt))) {
score = 0;
- if (fmt->name && short_name && !av_strcasecmp(fmt->name, short_name))
+ if (fmt->name && short_name && av_match_name(short_name, fmt->name))
score += 100;
if (fmt->mime_type && mime_type && !strcmp(fmt->mime_type, mime_type))
score += 10;
@@ -140,6 +130,12 @@ enum AVCodecID av_guess_codec(AVOutputFormat *fmt, const char *short_name,
const char *filename, const char *mime_type,
enum AVMediaType type)
{
+ if (av_match_name("segment", fmt->name) || av_match_name("ssegment", fmt->name)) {
+ AVOutputFormat *fmt2 = av_guess_format(NULL, filename, NULL);
+ if (fmt2)
+ fmt = fmt2;
+ }
+
if (type == AVMEDIA_TYPE_VIDEO) {
enum AVCodecID codec_id = AV_CODEC_ID_NONE;
@@ -155,6 +151,8 @@ enum AVCodecID av_guess_codec(AVOutputFormat *fmt, const char *short_name,
return fmt->audio_codec;
else if (type == AVMEDIA_TYPE_SUBTITLE)
return fmt->subtitle_codec;
+ else if (type == AVMEDIA_TYPE_DATA)
+ return fmt->data_codec;
else
return AV_CODEC_ID_NONE;
}
@@ -168,108 +166,121 @@ AVInputFormat *av_find_input_format(const char *short_name)
return NULL;
}
-AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened,
- int *score_max)
+AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened,
+ int *score_ret)
{
AVProbeData lpd = *pd;
AVInputFormat *fmt1 = NULL, *fmt;
- int score, id3 = 0;
+ int score, nodat = 0, score_max = 0;
+ const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE];
+
+ if (!lpd.buf)
+ lpd.buf = zerobuffer;
if (lpd.buf_size > 10 && ff_id3v2_match(lpd.buf, ID3v2_DEFAULT_MAGIC)) {
int id3len = ff_id3v2_tag_len(lpd.buf);
if (lpd.buf_size > id3len + 16) {
lpd.buf += id3len;
lpd.buf_size -= id3len;
- }
- id3 = 1;
+ } else if (id3len >= PROBE_BUF_MAX) {
+ nodat = 2;
+ } else
+ nodat = 1;
}
fmt = NULL;
while ((fmt1 = av_iformat_next(fmt1))) {
- if (!is_opened == !(fmt1->flags & AVFMT_NOFILE))
+ if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2"))
continue;
score = 0;
if (fmt1->read_probe) {
score = fmt1->read_probe(&lpd);
+ if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {
+ if (nodat == 0) score = FFMAX(score, 1);
+ else if (nodat == 1) score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);
+ else score = FFMAX(score, AVPROBE_SCORE_EXTENSION);
+ }
} else if (fmt1->extensions) {
if (av_match_ext(lpd.filename, fmt1->extensions))
score = AVPROBE_SCORE_EXTENSION;
}
if (av_match_name(lpd.mime_type, fmt1->mime_type))
score = FFMAX(score, AVPROBE_SCORE_MIME);
- if (score > *score_max) {
- *score_max = score;
- fmt = fmt1;
- } else if (score == *score_max)
+ if (score > score_max) {
+ score_max = score;
+ fmt = fmt1;
+ } else if (score == score_max)
fmt = NULL;
}
-
- // A hack for files with huge id3v2 tags -- try to guess by file extension.
- if (!fmt && is_opened && *score_max < AVPROBE_SCORE_EXTENSION / 2) {
- while ((fmt = av_iformat_next(fmt)))
- if (fmt->extensions &&
- av_match_ext(lpd.filename, fmt->extensions)) {
- *score_max = AVPROBE_SCORE_EXTENSION / 2;
- break;
- }
- }
-
- if (!fmt && id3 && *score_max < AVPROBE_SCORE_EXTENSION / 2 - 1) {
- while ((fmt = av_iformat_next(fmt)))
- if (fmt->extensions && av_match_ext("mp3", fmt->extensions)) {
- *score_max = AVPROBE_SCORE_EXTENSION / 2 - 1;
- break;
- }
- }
+ if (nodat == 1)
+ score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max);
+ *score_ret = score_max;
return fmt;
}
+AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max)
+{
+ int score_ret;
+ AVInputFormat *fmt = av_probe_input_format3(pd, is_opened, &score_ret);
+ if (score_ret > *score_max) {
+ *score_max = score_ret;
+ return fmt;
+ } else
+ return NULL;
+}
+
AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened)
{
int score = 0;
return av_probe_input_format2(pd, is_opened, &score);
}
-/* size of probe buffer, for guessing file type from file contents */
-#define PROBE_BUF_MIN 2048
-#define PROBE_BUF_MAX (1 << 20)
-
-int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt,
+int av_probe_input_buffer2(AVIOContext *pb, AVInputFormat **fmt,
const char *filename, void *logctx,
unsigned int offset, unsigned int max_probe_size)
{
AVProbeData pd = { filename ? filename : "" };
uint8_t *buf = NULL;
- int ret = 0, probe_size;
- uint8_t *mime_type_opt = NULL;
+ int ret = 0, probe_size, buf_offset = 0;
+ int score = 0;
+ int ret2;
if (!max_probe_size)
max_probe_size = PROBE_BUF_MAX;
- else if (max_probe_size > PROBE_BUF_MAX)
- max_probe_size = PROBE_BUF_MAX;
- else if (max_probe_size < PROBE_BUF_MIN)
+ else if (max_probe_size < PROBE_BUF_MIN) {
+ av_log(logctx, AV_LOG_ERROR,
+ "Specified probe size value %u cannot be < %u\n", max_probe_size, PROBE_BUF_MIN);
return AVERROR(EINVAL);
+ }
if (offset >= max_probe_size)
return AVERROR(EINVAL);
- avio_skip(pb, offset);
- max_probe_size -= offset;
+
if (pb->av_class) {
+ uint8_t *mime_type_opt = NULL;
av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type_opt);
pd.mime_type = (const char *)mime_type_opt;
- mime_type_opt = NULL;
}
+#if 0
+ if (!*fmt && pb->av_class && av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type) >= 0 && mime_type) {
+ if (!av_strcasecmp(mime_type, "audio/aacp")) {
+ *fmt = av_find_input_format("aac");
+ }
+ av_freep(&mime_type);
+ }
+#endif
+
for (probe_size = PROBE_BUF_MIN; probe_size <= max_probe_size && !*fmt;
probe_size = FFMIN(probe_size << 1,
FFMAX(max_probe_size, probe_size + 1))) {
- int score = probe_size < max_probe_size ? AVPROBE_SCORE_MAX / 4 : 0;
+ score = probe_size < max_probe_size ? AVPROBE_SCORE_RETRY : 0;
/* Read probe data. */
if ((ret = av_reallocp(&buf, probe_size + AVPROBE_PADDING_SIZE)) < 0)
goto fail;
- if ((ret = avio_read(pb, buf + pd.buf_size,
- probe_size - pd.buf_size)) < 0) {
+ if ((ret = avio_read(pb, buf + buf_offset,
+ probe_size - buf_offset)) < 0) {
/* Fail if error was not end of file, otherwise, lower score. */
if (ret != AVERROR_EOF)
goto fail;
@@ -277,8 +288,11 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt,
score = 0;
ret = 0; /* error was end of file, nothing read */
}
- pd.buf_size += ret;
- pd.buf = buf;
+ buf_offset += ret;
+ if (buf_offset < offset)
+ continue;
+ pd.buf_size = buf_offset - offset;
+ pd.buf = &buf[offset];
memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE);
@@ -286,13 +300,19 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt,
*fmt = av_probe_input_format2(&pd, 1, &score);
if (*fmt) {
/* This can only be true in the last iteration. */
- if (score <= AVPROBE_SCORE_MAX / 4) {
+ if (score <= AVPROBE_SCORE_RETRY) {
av_log(logctx, AV_LOG_WARNING,
- "Format detected only with low score of %d, "
- "misdetection possible!\n", score);
+ "Format %s detected only with low score of %d, "
+ "misdetection possible!\n", (*fmt)->name, score);
} else
av_log(logctx, AV_LOG_DEBUG,
- "Probed with size=%d and score=%d\n", probe_size, score);
+ "Format %s probed with size=%d and score=%d\n",
+ (*fmt)->name, probe_size, score);
+#if 0
+ FILE *f = fopen("probestat.tmp", "ab");
+ fprintf(f, "probe_size:%d format:%s score:%d filename:%s\n", probe_size, (*fmt)->name, score, filename);
+ fclose(f);
+#endif
}
}
@@ -301,10 +321,18 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt,
fail:
/* Rewind. Reuse probe buffer to avoid seeking. */
- if (ret < 0 ||
- (ret = ffio_rewind_with_probe_data(pb, buf, pd.buf_size)) < 0) {
- av_free(buf);
- }
+ ret2 = ffio_rewind_with_probe_data(pb, &buf, buf_offset);
+ if (ret >= 0)
+ ret = ret2;
+
av_freep(&pd.mime_type);
- return ret;
+ return ret < 0 ? ret : score;
+}
+
+int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt,
+ const char *filename, void *logctx,
+ unsigned int offset, unsigned int max_probe_size)
+{
+ int ret = av_probe_input_buffer2(pb, fmt, filename, logctx, offset, max_probe_size);
+ return ret < 0 ? ret : 0;
}
diff --git a/libavformat/framecrcenc.c b/libavformat/framecrcenc.c
index 4d5483a70e..805b5428f9 100644
--- a/libavformat/framecrcenc.c
+++ b/libavformat/framecrcenc.c
@@ -2,36 +2,76 @@
* frame CRC encoder (for codec/format testing)
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <inttypes.h>
#include "libavutil/adler32.h"
+#include "libavutil/avstring.h"
#include "avformat.h"
#include "internal.h"
+static int framecrc_write_header(struct AVFormatContext *s)
+{
+ int i;
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ AVCodecContext *avctx = st->codec;
+ if (avctx->extradata) {
+ uint32_t crc = av_adler32_update(0, avctx->extradata, avctx->extradata_size);
+ avio_printf(s->pb, "#extradata %d: %8d, 0x%08"PRIx32"\n",
+ i, avctx->extradata_size, crc);
+ }
+ }
+
+ return ff_framehash_write_header(s);
+}
+
static int framecrc_write_packet(struct AVFormatContext *s, AVPacket *pkt)
{
uint32_t crc = av_adler32_update(0, pkt->data, pkt->size);
char buf[256];
- snprintf(buf, sizeof(buf), "%d, %10"PRId64", %10"PRId64", %8d, %8d, 0x%08"PRIx32"\n",
+ snprintf(buf, sizeof(buf), "%d, %10"PRId64", %10"PRId64", %8d, %8d, 0x%08"PRIx32,
pkt->stream_index, pkt->dts, pkt->pts, pkt->duration, pkt->size, crc);
+ if (pkt->flags != AV_PKT_FLAG_KEY)
+ av_strlcatf(buf, sizeof(buf), ", F=0x%0X", pkt->flags);
+ if (pkt->side_data_elems) {
+ int i, j;
+ av_strlcatf(buf, sizeof(buf), ", S=%d", pkt->side_data_elems);
+
+ for (i=0; i<pkt->side_data_elems; i++) {
+ uint32_t side_data_crc = 0;
+ if (HAVE_BIGENDIAN && AV_PKT_DATA_PALETTE == pkt->side_data[i].type) {
+ for (j=0; j<pkt->side_data[i].size; j++) {
+ side_data_crc = av_adler32_update(side_data_crc,
+ pkt->side_data[i].data + (j^3),
+ 1);
+ }
+ } else {
+ side_data_crc = av_adler32_update(0,
+ pkt->side_data[i].data,
+ pkt->side_data[i].size);
+ }
+ av_strlcatf(buf, sizeof(buf), ", %8d, 0x%08x", pkt->side_data[i].size, side_data_crc);
+ }
+ }
+ av_strlcatf(buf, sizeof(buf), "\n");
avio_write(s->pb, buf, strlen(buf));
return 0;
}
@@ -39,10 +79,9 @@ static int framecrc_write_packet(struct AVFormatContext *s, AVPacket *pkt)
AVOutputFormat ff_framecrc_muxer = {
.name = "framecrc",
.long_name = NULL_IF_CONFIG_SMALL("framecrc testing"),
- .extensions = "",
.audio_codec = AV_CODEC_ID_PCM_S16LE,
.video_codec = AV_CODEC_ID_RAWVIDEO,
- .write_header = ff_framehash_write_header,
+ .write_header = framecrc_write_header,
.write_packet = framecrc_write_packet,
.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT |
AVFMT_TS_NEGATIVE,
diff --git a/libavformat/framehash.c b/libavformat/framehash.c
index 6a6da98ae4..a8357b04ae 100644
--- a/libavformat/framehash.c
+++ b/libavformat/framehash.c
@@ -1,20 +1,20 @@
/*
* Common functions for the frame{crc,md5} muxers
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -23,6 +23,9 @@
int ff_framehash_write_header(AVFormatContext *s)
{
int i;
+
+ if (s->nb_streams && !(s->flags & AVFMT_FLAG_BITEXACT))
+ avio_printf(s->pb, "#software: %s\n", LIBAVFORMAT_IDENT);
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
avio_printf(s->pb, "#tb %d: %d/%d\n", i, st->time_base.num, st->time_base.den);
diff --git a/libavformat/frmdec.c b/libavformat/frmdec.c
new file mode 100644
index 0000000000..a6f19afcd4
--- /dev/null
+++ b/libavformat/frmdec.c
@@ -0,0 +1,110 @@
+/*
+ * Megalux Frame demuxer
+ * Copyright (c) 2010 Peter Ross <pross@xvid.org>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Megalux Frame demuxer
+ */
+
+#include "libavcodec/raw.h"
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+
+static const PixelFormatTag frm_pix_fmt_tags[] = {
+ { AV_PIX_FMT_RGB555, 1 },
+ { AV_PIX_FMT_RGB0, 2 },
+ { AV_PIX_FMT_RGB24, 3 },
+ { AV_PIX_FMT_BGR0, 4 },
+ { AV_PIX_FMT_BGRA, 5 },
+ { AV_PIX_FMT_NONE, 0 },
+};
+
+typedef struct {
+ int count;
+} FrmContext;
+
+static int frm_read_probe(AVProbeData *p)
+{
+ if (p->buf_size > 8 &&
+ p->buf[0] == 'F' && p->buf[1] == 'R' && p->buf[2] == 'M' &&
+ AV_RL16(&p->buf[4]) && AV_RL16(&p->buf[6]))
+ return AVPROBE_SCORE_MAX / 4;
+ return 0;
+}
+
+static int frm_read_header(AVFormatContext *avctx)
+{
+ AVIOContext *pb = avctx->pb;
+ AVStream *st = avformat_new_stream(avctx, 0);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codec->codec_id = AV_CODEC_ID_RAWVIDEO;
+ avio_skip(pb, 3);
+
+ st->codec->pix_fmt = avpriv_find_pix_fmt(frm_pix_fmt_tags, avio_r8(pb));
+ if (!st->codec->pix_fmt)
+ return AVERROR_INVALIDDATA;
+
+ st->codec->codec_tag = 0;
+ st->codec->width = avio_rl16(pb);
+ st->codec->height = avio_rl16(pb);
+ return 0;
+}
+
+static int frm_read_packet(AVFormatContext *avctx, AVPacket *pkt)
+{
+ FrmContext *s = avctx->priv_data;
+ AVCodecContext *stc = avctx->streams[0]->codec;
+ int packet_size, ret;
+
+ if (s->count)
+ return AVERROR_EOF;
+
+ packet_size = avpicture_get_size(stc->pix_fmt, stc->width, stc->height);
+ if (packet_size < 0)
+ return AVERROR_INVALIDDATA;
+
+ ret = av_get_packet(avctx->pb, pkt, packet_size);
+ if (ret < 0)
+ return ret;
+
+ if (stc->pix_fmt == AV_PIX_FMT_BGRA) {
+ int i;
+ for (i = 3; i + 1 <= pkt->size; i += 4)
+ pkt->data[i] = 0xFF - pkt->data[i];
+ }
+
+ pkt->stream_index = 0;
+ s->count++;
+
+ return 0;
+}
+
+AVInputFormat ff_frm_demuxer = {
+ .name = "frm",
+ .priv_data_size = sizeof(FrmContext),
+ .long_name = NULL_IF_CONFIG_SMALL("Megalux Frame"),
+ .read_probe = frm_read_probe,
+ .read_header = frm_read_header,
+ .read_packet = frm_read_packet,
+};
diff --git a/libavformat/ftp.c b/libavformat/ftp.c
new file mode 100644
index 0000000000..7faf4a5d66
--- /dev/null
+++ b/libavformat/ftp.c
@@ -0,0 +1,801 @@
+/*
+ * Copyright (c) 2013 Lukasz Marek <lukasz.m.luki@gmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/avstring.h"
+#include "avformat.h"
+#include "internal.h"
+#include "url.h"
+#include "libavutil/opt.h"
+#include "libavutil/bprint.h"
+
+#define CONTROL_BUFFER_SIZE 1024
+#define CREDENTIALS_BUFFER_SIZE 128
+
+typedef enum {
+ UNKNOWN,
+ READY,
+ DOWNLOADING,
+ UPLOADING,
+ DISCONNECTED
+} FTPState;
+
+typedef struct {
+ const AVClass *class;
+ URLContext *conn_control; /**< Control connection */
+ URLContext *conn_data; /**< Data connection, NULL when not connected */
+ uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */
+ uint8_t *control_buf_ptr, *control_buf_end;
+ int server_data_port; /**< Data connection port opened by server, -1 on error. */
+ int server_control_port; /**< Control connection port, default is 21 */
+ char hostname[512]; /**< Server address. */
+ char credencials[CREDENTIALS_BUFFER_SIZE]; /**< Authentication data */
+ char path[MAX_URL_SIZE]; /**< Path to resource on server. */
+ int64_t filesize; /**< Size of file on server, -1 on error. */
+ int64_t position; /**< Current position, calculated. */
+ int rw_timeout; /**< Network timeout. */
+ const char *anonymous_password; /**< Password to be used for anonymous user. An email should be used. */
+ int write_seekable; /**< Control seekability, 0 = disable, 1 = enable. */
+ FTPState state; /**< State of data connection */
+} FTPContext;
+
+#define OFFSET(x) offsetof(FTPContext, x)
+#define D AV_OPT_FLAG_DECODING_PARAM
+#define E AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
+ {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
+ {"ftp-anonymous-password", "password for anonymous login. E-mail address should be used.", OFFSET(anonymous_password), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
+ {NULL}
+};
+
+static const AVClass ftp_context_class = {
+ .class_name = "ftp",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+static int ftp_getc(FTPContext *s)
+{
+ int len;
+ if (s->control_buf_ptr >= s->control_buf_end) {
+ len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
+ if (len < 0) {
+ return len;
+ } else if (!len) {
+ return -1;
+ } else {
+ s->control_buf_ptr = s->control_buffer;
+ s->control_buf_end = s->control_buffer + len;
+ }
+ }
+ return *s->control_buf_ptr++;
+}
+
+static int ftp_get_line(FTPContext *s, char *line, int line_size)
+{
+ int ch;
+ char *q = line;
+
+ for (;;) {
+ ch = ftp_getc(s);
+ if (ch < 0) {
+ return ch;
+ }
+ if (ch == '\n') {
+ /* process line */
+ if (q > line && q[-1] == '\r')
+ q--;
+ *q = '\0';
+ return 0;
+ } else {
+ if ((q - line) < line_size - 1)
+ *q++ = ch;
+ }
+ }
+}
+
+/*
+ * This routine returns ftp server response code.
+ * Server may send more than one response for a certain command.
+ * First expected code is returned.
+ */
+static int ftp_status(FTPContext *s, char **line, const int response_codes[])
+{
+ int err, i, dash = 0, result = 0, code_found = 0, linesize;
+ char buf[CONTROL_BUFFER_SIZE];
+ AVBPrint line_buffer;
+
+ if (line)
+ av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC);
+
+ while (!code_found || dash) {
+ if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
+ if (line)
+ av_bprint_finalize(&line_buffer, NULL);
+ return err;
+ }
+
+ av_log(s, AV_LOG_DEBUG, "%s\n", buf);
+
+ linesize = strlen(buf);
+ err = 0;
+ if (linesize >= 3) {
+ for (i = 0; i < 3; ++i) {
+ if (buf[i] < '0' || buf[i] > '9') {
+ err = 0;
+ break;
+ }
+ err *= 10;
+ err += buf[i] - '0';
+ }
+ }
+
+ if (!code_found) {
+ if (err >= 500) {
+ code_found = 1;
+ result = err;
+ } else
+ for (i = 0; response_codes[i]; ++i) {
+ if (err == response_codes[i]) {
+ code_found = 1;
+ result = err;
+ break;
+ }
+ }
+ }
+ if (code_found) {
+ if (line)
+ av_bprintf(&line_buffer, "%s\r\n", buf);
+ if (linesize >= 4) {
+ if (!dash && buf[3] == '-')
+ dash = err;
+ else if (err == dash && buf[3] == ' ')
+ dash = 0;
+ }
+ }
+ }
+
+ if (line)
+ av_bprint_finalize(&line_buffer, line);
+ return result;
+}
+
+static int ftp_send_command(FTPContext *s, const char *command,
+ const int response_codes[], char **response)
+{
+ int err;
+
+ if (response)
+ *response = NULL;
+
+ if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
+ return err;
+ if (!err)
+ return -1;
+
+ /* return status */
+ if (response_codes) {
+ return ftp_status(s, response, response_codes);
+ }
+ return 0;
+}
+
+static void ftp_close_data_connection(FTPContext *s)
+{
+ ffurl_closep(&s->conn_data);
+ s->position = 0;
+ s->state = DISCONNECTED;
+}
+
+static void ftp_close_both_connections(FTPContext *s)
+{
+ ffurl_closep(&s->conn_control);
+ ftp_close_data_connection(s);
+}
+
+static int ftp_auth(FTPContext *s)
+{
+ const char *user = NULL, *pass = NULL;
+ char *end = NULL, buf[CONTROL_BUFFER_SIZE], credencials[CREDENTIALS_BUFFER_SIZE];
+ int err;
+ static const int user_codes[] = {331, 230, 0};
+ static const int pass_codes[] = {230, 0};
+
+ /* Authentication may be repeated, original string has to be saved */
+ av_strlcpy(credencials, s->credencials, sizeof(credencials));
+
+ user = av_strtok(credencials, ":", &end);
+ pass = av_strtok(end, ":", &end);
+
+ if (!user) {
+ user = "anonymous";
+ pass = s->anonymous_password ? s->anonymous_password : "nopassword";
+ }
+
+ snprintf(buf, sizeof(buf), "USER %s\r\n", user);
+ err = ftp_send_command(s, buf, user_codes, NULL);
+ if (err == 331) {
+ if (pass) {
+ snprintf(buf, sizeof(buf), "PASS %s\r\n", pass);
+ err = ftp_send_command(s, buf, pass_codes, NULL);
+ } else
+ return AVERROR(EACCES);
+ }
+ if (err != 230)
+ return AVERROR(EACCES);
+
+ return 0;
+}
+
+static int ftp_passive_mode_epsv(FTPContext *s)
+{
+ char *res = NULL, *start = NULL, *end = NULL;
+ int i;
+ static const char d = '|';
+ static const char *command = "EPSV\r\n";
+ static const int epsv_codes[] = {229, 0};
+
+ if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
+ goto fail;
+
+ for (i = 0; res[i]; ++i) {
+ if (res[i] == '(') {
+ start = res + i + 1;
+ } else if (res[i] == ')') {
+ end = res + i;
+ break;
+ }
+ }
+ if (!start || !end)
+ goto fail;
+
+ *end = '\0';
+ if (strlen(start) < 5)
+ goto fail;
+ if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
+ goto fail;
+ start += 3;
+ end[-1] = '\0';
+
+ s->server_data_port = atoi(start);
+ av_dlog(s, "Server data port: %d\n", s->server_data_port);
+
+ av_free(res);
+ return 0;
+
+ fail:
+ av_free(res);
+ s->server_data_port = -1;
+ return AVERROR(ENOSYS);
+}
+
+static int ftp_passive_mode(FTPContext *s)
+{
+ char *res = NULL, *start = NULL, *end = NULL;
+ int i;
+ static const char *command = "PASV\r\n";
+ static const int pasv_codes[] = {227, 0};
+
+ if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
+ goto fail;
+
+ for (i = 0; res[i]; ++i) {
+ if (res[i] == '(') {
+ start = res + i + 1;
+ } else if (res[i] == ')') {
+ end = res + i;
+ break;
+ }
+ }
+ if (!start || !end)
+ goto fail;
+
+ *end = '\0';
+ /* skip ip */
+ if (!av_strtok(start, ",", &end)) goto fail;
+ if (!av_strtok(end, ",", &end)) goto fail;
+ if (!av_strtok(end, ",", &end)) goto fail;
+ if (!av_strtok(end, ",", &end)) goto fail;
+
+ /* parse port number */
+ start = av_strtok(end, ",", &end);
+ if (!start) goto fail;
+ s->server_data_port = atoi(start) * 256;
+ start = av_strtok(end, ",", &end);
+ if (!start) goto fail;
+ s->server_data_port += atoi(start);
+ av_dlog(s, "Server data port: %d\n", s->server_data_port);
+
+ av_free(res);
+ return 0;
+
+ fail:
+ av_free(res);
+ s->server_data_port = -1;
+ return AVERROR(EIO);
+}
+
+static int ftp_current_dir(FTPContext *s)
+{
+ char *res = NULL, *start = NULL, *end = NULL;
+ int i;
+ static const char *command = "PWD\r\n";
+ static const int pwd_codes[] = {257, 0};
+
+ if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
+ goto fail;
+
+ for (i = 0; res[i]; ++i) {
+ if (res[i] == '"') {
+ if (!start) {
+ start = res + i + 1;
+ continue;
+ }
+ end = res + i;
+ break;
+ }
+ }
+
+ if (!end)
+ goto fail;
+
+ if (end > res && end[-1] == '/') {
+ end[-1] = '\0';
+ } else
+ *end = '\0';
+ av_strlcpy(s->path, start, sizeof(s->path));
+
+ av_free(res);
+ return 0;
+
+ fail:
+ av_free(res);
+ return AVERROR(EIO);
+}
+
+static int ftp_file_size(FTPContext *s)
+{
+ char command[CONTROL_BUFFER_SIZE];
+ char *res = NULL;
+ static const int size_codes[] = {213, 0};
+
+ snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
+ if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
+ s->filesize = strtoll(&res[4], NULL, 10);
+ } else {
+ s->filesize = -1;
+ av_free(res);
+ return AVERROR(EIO);
+ }
+
+ av_free(res);
+ return 0;
+}
+
+static int ftp_retrieve(FTPContext *s)
+{
+ char command[CONTROL_BUFFER_SIZE];
+ static const int retr_codes[] = {150, 0};
+
+ snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
+ if (ftp_send_command(s, command, retr_codes, NULL) != 150)
+ return AVERROR(EIO);
+
+ s->state = DOWNLOADING;
+
+ return 0;
+}
+
+static int ftp_store(FTPContext *s)
+{
+ char command[CONTROL_BUFFER_SIZE];
+ static const int stor_codes[] = {150, 0};
+
+ snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
+ if (ftp_send_command(s, command, stor_codes, NULL) != 150)
+ return AVERROR(EIO);
+
+ s->state = UPLOADING;
+
+ return 0;
+}
+
+static int ftp_type(FTPContext *s)
+{
+ static const char *command = "TYPE I\r\n";
+ static const int type_codes[] = {200, 0};
+
+ if (ftp_send_command(s, command, type_codes, NULL) != 200)
+ return AVERROR(EIO);
+
+ return 0;
+}
+
+static int ftp_restart(FTPContext *s, int64_t pos)
+{
+ char command[CONTROL_BUFFER_SIZE];
+ static const int rest_codes[] = {350, 0};
+
+ snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
+ if (ftp_send_command(s, command, rest_codes, NULL) != 350)
+ return AVERROR(EIO);
+
+ return 0;
+}
+
+static int ftp_features(FTPContext *s)
+{
+ static const char *feat_command = "FEAT\r\n";
+ static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
+ static const int feat_codes[] = {211, 0};
+ static const int opts_codes[] = {200, 451};
+ char *feat = NULL;
+
+ if (ftp_send_command(s, feat_command, feat_codes, &feat) == 211) {
+ if (av_stristr(feat, "UTF8"))
+ ftp_send_command(s, enable_utf8_command, opts_codes, NULL);
+ }
+ av_freep(&feat);
+
+ return 0;
+}
+
+static int ftp_connect_control_connection(URLContext *h)
+{
+ char buf[CONTROL_BUFFER_SIZE], *response = NULL;
+ int err;
+ AVDictionary *opts = NULL;
+ FTPContext *s = h->priv_data;
+ static const int connect_codes[] = {220, 0};
+
+ if (!s->conn_control) {
+ ff_url_join(buf, sizeof(buf), "tcp", NULL,
+ s->hostname, s->server_control_port, NULL);
+ if (s->rw_timeout != -1) {
+ av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
+ } /* if option is not given, don't pass it and let tcp use its own default */
+ err = ffurl_open(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, &opts);
+ av_dict_free(&opts);
+ if (err < 0) {
+ av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
+ return err;
+ }
+
+ /* check if server is ready */
+ if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
+ av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
+ return AVERROR(EACCES);
+ }
+
+ if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
+ av_log(h, AV_LOG_WARNING, "Pure-FTPd server is used as an output protocol. It is known issue this implementation may produce incorrect content and it cannot be fixed at this moment.");
+ }
+ av_free(response);
+
+ if ((err = ftp_auth(s)) < 0) {
+ av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
+ return err;
+ }
+
+ if ((err = ftp_type(s)) < 0) {
+ av_log(h, AV_LOG_ERROR, "Set content type failed\n");
+ return err;
+ }
+
+ ftp_features(s);
+ }
+ return 0;
+}
+
+static int ftp_connect_data_connection(URLContext *h)
+{
+ int err;
+ char buf[CONTROL_BUFFER_SIZE];
+ AVDictionary *opts = NULL;
+ FTPContext *s = h->priv_data;
+
+ if (!s->conn_data) {
+ /* Enter passive mode */
+ if (ftp_passive_mode_epsv(s) < 0) {
+ /* Use PASV as fallback */
+ if ((err = ftp_passive_mode(s)) < 0)
+ return err;
+ }
+ /* Open data connection */
+ ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
+ if (s->rw_timeout != -1) {
+ av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
+ } /* if option is not given, don't pass it and let tcp use its own default */
+ err = ffurl_open(&s->conn_data, buf, h->flags,
+ &h->interrupt_callback, &opts);
+ av_dict_free(&opts);
+ if (err < 0)
+ return err;
+
+ if (s->position)
+ if ((err = ftp_restart(s, s->position)) < 0)
+ return err;
+ }
+ s->state = READY;
+ return 0;
+}
+
+static int ftp_abort(URLContext *h)
+{
+ static const char *command = "ABOR\r\n";
+ int err;
+ static const int abor_codes[] = {225, 226, 0};
+ FTPContext *s = h->priv_data;
+
+ /* According to RCF 959:
+ "ABOR command tells the server to abort the previous FTP
+ service command and any associated transfer of data."
+
+ There are FTP server implementations that don't response
+ to any commands during data transfer in passive mode (including ABOR).
+
+ This implementation closes data connection by force.
+ */
+
+ if (ftp_send_command(s, command, NULL, NULL) < 0) {
+ ftp_close_both_connections(s);
+ if ((err = ftp_connect_control_connection(h)) < 0) {
+ av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
+ return err;
+ }
+ } else {
+ ftp_close_data_connection(s);
+ if (ftp_status(s, NULL, abor_codes) < 225) {
+ /* wu-ftpd also closes control connection after data connection closing */
+ ffurl_closep(&s->conn_control);
+ if ((err = ftp_connect_control_connection(h)) < 0) {
+ av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int ftp_open(URLContext *h, const char *url, int flags)
+{
+ char proto[10], path[MAX_URL_SIZE];
+ int err;
+ FTPContext *s = h->priv_data;
+
+ av_dlog(h, "ftp protocol open\n");
+
+ s->state = DISCONNECTED;
+ s->filesize = -1;
+ s->position = 0;
+
+ av_url_split(proto, sizeof(proto),
+ s->credencials, sizeof(s->credencials),
+ s->hostname, sizeof(s->hostname),
+ &s->server_control_port,
+ path, sizeof(path),
+ url);
+
+ if (s->server_control_port < 0 || s->server_control_port > 65535)
+ s->server_control_port = 21;
+
+ if ((err = ftp_connect_control_connection(h)) < 0)
+ goto fail;
+
+ if ((err = ftp_current_dir(s)) < 0)
+ goto fail;
+ av_strlcat(s->path, path, sizeof(s->path));
+
+ if (ftp_restart(s, 0) < 0) {
+ h->is_streamed = 1;
+ } else {
+ if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
+ h->is_streamed = 1;
+ if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
+ h->is_streamed = 1;
+ }
+
+ return 0;
+
+ fail:
+ av_log(h, AV_LOG_ERROR, "FTP open failed\n");
+ ffurl_closep(&s->conn_control);
+ ffurl_closep(&s->conn_data);
+ return err;
+}
+
+static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
+{
+ FTPContext *s = h->priv_data;
+ int err;
+ int64_t new_pos, fake_pos;
+
+ av_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
+
+ switch(whence) {
+ case AVSEEK_SIZE:
+ return s->filesize;
+ case SEEK_SET:
+ new_pos = pos;
+ break;
+ case SEEK_CUR:
+ new_pos = s->position + pos;
+ break;
+ case SEEK_END:
+ if (s->filesize < 0)
+ return AVERROR(EIO);
+ new_pos = s->filesize + pos;
+ break;
+ default:
+ return AVERROR(EINVAL);
+ }
+
+ if (h->is_streamed)
+ return AVERROR(EIO);
+
+ if (new_pos < 0) {
+ av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
+ return AVERROR(EINVAL);
+ }
+
+ fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
+ if (fake_pos != s->position) {
+ if ((err = ftp_abort(h)) < 0)
+ return err;
+ s->position = fake_pos;
+ }
+ return new_pos;
+}
+
+static int ftp_read(URLContext *h, unsigned char *buf, int size)
+{
+ FTPContext *s = h->priv_data;
+ int read, err, retry_done = 0;
+
+ av_dlog(h, "ftp protocol read %d bytes\n", size);
+ retry:
+ if (s->state == DISCONNECTED) {
+ /* optimization */
+ if (s->position >= s->filesize)
+ return 0;
+ if ((err = ftp_connect_data_connection(h)) < 0)
+ return err;
+ }
+ if (s->state == READY) {
+ if (s->position >= s->filesize)
+ return 0;
+ if ((err = ftp_retrieve(s)) < 0)
+ return err;
+ }
+ if (s->conn_data && s->state == DOWNLOADING) {
+ read = ffurl_read(s->conn_data, buf, size);
+ if (read >= 0) {
+ s->position += read;
+ if (s->position >= s->filesize) {
+ /* server will terminate, but keep current position to avoid madness */
+ /* save position to restart from it */
+ int64_t pos = s->position;
+ if (ftp_abort(h) < 0) {
+ s->position = pos;
+ return AVERROR(EIO);
+ }
+ s->position = pos;
+ }
+ }
+ if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
+ /* Server closed connection. Probably due to inactivity */
+ int64_t pos = s->position;
+ av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
+ if ((err = ftp_abort(h)) < 0)
+ return err;
+ if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
+ av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
+ return err;
+ }
+ if (!retry_done) {
+ retry_done = 1;
+ goto retry;
+ }
+ }
+ return read;
+ }
+
+ av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
+ return AVERROR(EIO);
+}
+
+static int ftp_write(URLContext *h, const unsigned char *buf, int size)
+{
+ int err;
+ FTPContext *s = h->priv_data;
+ int written;
+
+ av_dlog(h, "ftp protocol write %d bytes\n", size);
+
+ if (s->state == DISCONNECTED) {
+ if ((err = ftp_connect_data_connection(h)) < 0)
+ return err;
+ }
+ if (s->state == READY) {
+ if ((err = ftp_store(s)) < 0)
+ return err;
+ }
+ if (s->conn_data && s->state == UPLOADING) {
+ written = ffurl_write(s->conn_data, buf, size);
+ if (written > 0) {
+ s->position += written;
+ s->filesize = FFMAX(s->filesize, s->position);
+ }
+ return written;
+ }
+
+ av_log(h, AV_LOG_ERROR, "FTP write failed\n");
+ return AVERROR(EIO);
+}
+
+static int ftp_close(URLContext *h)
+{
+ av_dlog(h, "ftp protocol close\n");
+
+ ftp_close_both_connections(h->priv_data);
+
+ return 0;
+}
+
+static int ftp_get_file_handle(URLContext *h)
+{
+ FTPContext *s = h->priv_data;
+
+ av_dlog(h, "ftp protocol get_file_handle\n");
+
+ if (s->conn_data)
+ return ffurl_get_file_handle(s->conn_data);
+
+ return AVERROR(EIO);
+}
+
+static int ftp_shutdown(URLContext *h, int flags)
+{
+ FTPContext *s = h->priv_data;
+
+ av_dlog(h, "ftp protocol shutdown\n");
+
+ if (s->conn_data)
+ return ffurl_shutdown(s->conn_data, flags);
+
+ return AVERROR(EIO);
+}
+
+URLProtocol ff_ftp_protocol = {
+ .name = "ftp",
+ .url_open = ftp_open,
+ .url_read = ftp_read,
+ .url_write = ftp_write,
+ .url_seek = ftp_seek,
+ .url_close = ftp_close,
+ .url_get_file_handle = ftp_get_file_handle,
+ .url_shutdown = ftp_shutdown,
+ .priv_data_size = sizeof(FTPContext),
+ .priv_data_class = &ftp_context_class,
+ .flags = URL_PROTOCOL_FLAG_NETWORK,
+};
diff --git a/libavformat/g722.c b/libavformat/g722.c
index 8052939c79..1a87c7d28c 100644
--- a/libavformat/g722.c
+++ b/libavformat/g722.c
@@ -2,23 +2,24 @@
* g722 raw demuxer
* Copyright (c) 2010 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/avassert.h"
#include "avformat.h"
#include "internal.h"
#include "rawdec.h"
@@ -39,7 +40,7 @@ static int g722_read_header(AVFormatContext *s)
st->codec->bits_per_coded_sample =
av_get_bits_per_sample(st->codec->codec_id);
- assert(st->codec->bits_per_coded_sample > 0);
+ av_assert0(st->codec->bits_per_coded_sample > 0);
avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
return 0;
diff --git a/libavformat/g723_1.c b/libavformat/g723_1.c
index b67c07c404..4f3ce8f0ae 100644
--- a/libavformat/g723_1.c
+++ b/libavformat/g723_1.c
@@ -2,20 +2,20 @@
* G.723.1 demuxer
* Copyright (c) 2010 Mohamed Naufal Basheer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -81,6 +81,6 @@ AVInputFormat ff_g723_1_demuxer = {
.long_name = NULL_IF_CONFIG_SMALL("G.723.1"),
.read_header = g723_1_init,
.read_packet = g723_1_read_packet,
- .extensions = "tco",
+ .extensions = "tco,rco,g723_1",
.flags = AVFMT_GENERIC_INDEX
};
diff --git a/libavformat/g729dec.c b/libavformat/g729dec.c
new file mode 100644
index 0000000000..794558ef10
--- /dev/null
+++ b/libavformat/g729dec.c
@@ -0,0 +1,103 @@
+/*
+ * G.729 raw format demuxer
+ * Copyright (c) 2011 Vladimir Voroshilov
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "libavutil/log.h"
+#include "libavutil/opt.h"
+
+typedef struct G729DemuxerContext {
+ AVClass *class;
+ int bit_rate;
+} G729DemuxerContext;
+
+static int g729_read_header(AVFormatContext *s)
+{
+ AVStream* st;
+ G729DemuxerContext *s1 = s->priv_data;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->codec_id = AV_CODEC_ID_G729;
+ st->codec->sample_rate = 8000;
+ st->codec->channels = 1;
+
+ if (s1 && s1->bit_rate) {
+ s->bit_rate = s1->bit_rate;
+ }
+
+ if (s->bit_rate == 0) {
+ av_log(s, AV_LOG_DEBUG, "No bitrate specified. Assuming 8000 b/s\n");
+ s->bit_rate = 8000;
+ }
+
+ if (s->bit_rate == 6400) {
+ st->codec->block_align = 8;
+ } else if (s->bit_rate == 8000) {
+ st->codec->block_align = 10;
+ } else {
+ av_log(s, AV_LOG_ERROR, "Only 8000 b/s and 6400 b/s bitrates are supported. Provided: %d b/s\n", s->bit_rate);
+ return AVERROR_INVALIDDATA;
+ }
+
+ avpriv_set_pts_info(st, st->codec->block_align << 3, 1, st->codec->sample_rate);
+ return 0;
+}
+static int g729_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ int ret;
+
+ ret = av_get_packet(s->pb, pkt, s->streams[0]->codec->block_align);
+
+ pkt->stream_index = 0;
+ if (ret < 0)
+ return ret;
+
+ pkt->dts = pkt->pts = pkt->pos / s->streams[0]->codec->block_align;
+
+ return ret;
+}
+
+static const AVOption g729_options[] = {
+ { "bit_rate", "", offsetof(G729DemuxerContext, bit_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+static const AVClass g729_demuxer_class = {
+ .class_name = "g729 demuxer",
+ .item_name = av_default_item_name,
+ .option = g729_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_g729_demuxer = {
+ .name = "g729",
+ .long_name = NULL_IF_CONFIG_SMALL("G.729 raw format demuxer"),
+ .priv_data_size = sizeof(G729DemuxerContext),
+ .read_header = g729_read_header,
+ .read_packet = g729_read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+ .extensions = "g729",
+ .priv_class = &g729_demuxer_class,
+};
diff --git a/libavformat/gif.c b/libavformat/gif.c
index ddec778d82..e5d558df07 100644
--- a/libavformat/gif.c
+++ b/libavformat/gif.c
@@ -4,347 +4,220 @@
*
* first version by Francois Revol <revol@free.fr>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-/*
- * Features and limitations:
- * - currently no compression is performed,
- * in fact the size of the data is 9/8 the size of the image in 8bpp
- * - uses only a global standard palette
- * - tested with IE 5.0, Opera for BeOS, NetPositive (BeOS), and Mozilla (BeOS).
- *
- * Reference documents:
- * http://www.goice.co.jp/member/mo/formats/gif.html
- * http://astronomy.swin.edu.au/pbourke/dataformats/gif/
- * http://www.dcs.ed.ac.uk/home/mxr/gfx/2d/GIF89a.txt
- *
- * this url claims to have an LZW algorithm not covered by Unisys patent:
- * http://www.msg.net/utility/whirlgif/gifencod.html
- * could help reduce the size of the files _a lot_...
- * some sites mentions an RLE type compression also.
- */
-
#include "avformat.h"
+#include "internal.h"
+#include "libavutil/avassert.h"
+#include "libavutil/imgutils.h"
#include "libavutil/log.h"
#include "libavutil/opt.h"
-/* The GIF format uses reversed order for bitstreams... */
-/* at least they don't use PDP_ENDIAN :) */
-#define BITSTREAM_WRITER_LE
-
-#include "libavcodec/put_bits.h"
-
-/* bitstream minipacket size */
-#define GIF_CHUNKS 100
-
-/* slows down the decoding (and some browsers don't like it) */
-/* update on the 'some browsers don't like it issue from above:
- * this was probably due to missing 'Data Sub-block Terminator'
- * (byte 19) in the app_header */
-#define GIF_ADD_APP_HEADER // required to enable looping of animated gif
-
-typedef struct rgb_triplet {
- unsigned char r;
- unsigned char g;
- unsigned char b;
-} rgb_triplet;
-
-/* we use the standard 216 color palette */
-
-/* this script was used to create the palette:
- * for r in 00 33 66 99 cc ff; do
- * for g in 00 33 66 99 cc ff; do
- * echo -n " "
- * for b in 00 33 66 99 cc ff; do
- * echo -n "{ 0x$r, 0x$g, 0x$b }, "
- * done
- * echo ""
- * done
- * done
- */
-
-static const rgb_triplet gif_clut[216] = {
- { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x33 }, { 0x00, 0x00, 0x66 }, { 0x00, 0x00, 0x99 }, { 0x00, 0x00, 0xcc }, { 0x00, 0x00, 0xff },
- { 0x00, 0x33, 0x00 }, { 0x00, 0x33, 0x33 }, { 0x00, 0x33, 0x66 }, { 0x00, 0x33, 0x99 }, { 0x00, 0x33, 0xcc }, { 0x00, 0x33, 0xff },
- { 0x00, 0x66, 0x00 }, { 0x00, 0x66, 0x33 }, { 0x00, 0x66, 0x66 }, { 0x00, 0x66, 0x99 }, { 0x00, 0x66, 0xcc }, { 0x00, 0x66, 0xff },
- { 0x00, 0x99, 0x00 }, { 0x00, 0x99, 0x33 }, { 0x00, 0x99, 0x66 }, { 0x00, 0x99, 0x99 }, { 0x00, 0x99, 0xcc }, { 0x00, 0x99, 0xff },
- { 0x00, 0xcc, 0x00 }, { 0x00, 0xcc, 0x33 }, { 0x00, 0xcc, 0x66 }, { 0x00, 0xcc, 0x99 }, { 0x00, 0xcc, 0xcc }, { 0x00, 0xcc, 0xff },
- { 0x00, 0xff, 0x00 }, { 0x00, 0xff, 0x33 }, { 0x00, 0xff, 0x66 }, { 0x00, 0xff, 0x99 }, { 0x00, 0xff, 0xcc }, { 0x00, 0xff, 0xff },
- { 0x33, 0x00, 0x00 }, { 0x33, 0x00, 0x33 }, { 0x33, 0x00, 0x66 }, { 0x33, 0x00, 0x99 }, { 0x33, 0x00, 0xcc }, { 0x33, 0x00, 0xff },
- { 0x33, 0x33, 0x00 }, { 0x33, 0x33, 0x33 }, { 0x33, 0x33, 0x66 }, { 0x33, 0x33, 0x99 }, { 0x33, 0x33, 0xcc }, { 0x33, 0x33, 0xff },
- { 0x33, 0x66, 0x00 }, { 0x33, 0x66, 0x33 }, { 0x33, 0x66, 0x66 }, { 0x33, 0x66, 0x99 }, { 0x33, 0x66, 0xcc }, { 0x33, 0x66, 0xff },
- { 0x33, 0x99, 0x00 }, { 0x33, 0x99, 0x33 }, { 0x33, 0x99, 0x66 }, { 0x33, 0x99, 0x99 }, { 0x33, 0x99, 0xcc }, { 0x33, 0x99, 0xff },
- { 0x33, 0xcc, 0x00 }, { 0x33, 0xcc, 0x33 }, { 0x33, 0xcc, 0x66 }, { 0x33, 0xcc, 0x99 }, { 0x33, 0xcc, 0xcc }, { 0x33, 0xcc, 0xff },
- { 0x33, 0xff, 0x00 }, { 0x33, 0xff, 0x33 }, { 0x33, 0xff, 0x66 }, { 0x33, 0xff, 0x99 }, { 0x33, 0xff, 0xcc }, { 0x33, 0xff, 0xff },
- { 0x66, 0x00, 0x00 }, { 0x66, 0x00, 0x33 }, { 0x66, 0x00, 0x66 }, { 0x66, 0x00, 0x99 }, { 0x66, 0x00, 0xcc }, { 0x66, 0x00, 0xff },
- { 0x66, 0x33, 0x00 }, { 0x66, 0x33, 0x33 }, { 0x66, 0x33, 0x66 }, { 0x66, 0x33, 0x99 }, { 0x66, 0x33, 0xcc }, { 0x66, 0x33, 0xff },
- { 0x66, 0x66, 0x00 }, { 0x66, 0x66, 0x33 }, { 0x66, 0x66, 0x66 }, { 0x66, 0x66, 0x99 }, { 0x66, 0x66, 0xcc }, { 0x66, 0x66, 0xff },
- { 0x66, 0x99, 0x00 }, { 0x66, 0x99, 0x33 }, { 0x66, 0x99, 0x66 }, { 0x66, 0x99, 0x99 }, { 0x66, 0x99, 0xcc }, { 0x66, 0x99, 0xff },
- { 0x66, 0xcc, 0x00 }, { 0x66, 0xcc, 0x33 }, { 0x66, 0xcc, 0x66 }, { 0x66, 0xcc, 0x99 }, { 0x66, 0xcc, 0xcc }, { 0x66, 0xcc, 0xff },
- { 0x66, 0xff, 0x00 }, { 0x66, 0xff, 0x33 }, { 0x66, 0xff, 0x66 }, { 0x66, 0xff, 0x99 }, { 0x66, 0xff, 0xcc }, { 0x66, 0xff, 0xff },
- { 0x99, 0x00, 0x00 }, { 0x99, 0x00, 0x33 }, { 0x99, 0x00, 0x66 }, { 0x99, 0x00, 0x99 }, { 0x99, 0x00, 0xcc }, { 0x99, 0x00, 0xff },
- { 0x99, 0x33, 0x00 }, { 0x99, 0x33, 0x33 }, { 0x99, 0x33, 0x66 }, { 0x99, 0x33, 0x99 }, { 0x99, 0x33, 0xcc }, { 0x99, 0x33, 0xff },
- { 0x99, 0x66, 0x00 }, { 0x99, 0x66, 0x33 }, { 0x99, 0x66, 0x66 }, { 0x99, 0x66, 0x99 }, { 0x99, 0x66, 0xcc }, { 0x99, 0x66, 0xff },
- { 0x99, 0x99, 0x00 }, { 0x99, 0x99, 0x33 }, { 0x99, 0x99, 0x66 }, { 0x99, 0x99, 0x99 }, { 0x99, 0x99, 0xcc }, { 0x99, 0x99, 0xff },
- { 0x99, 0xcc, 0x00 }, { 0x99, 0xcc, 0x33 }, { 0x99, 0xcc, 0x66 }, { 0x99, 0xcc, 0x99 }, { 0x99, 0xcc, 0xcc }, { 0x99, 0xcc, 0xff },
- { 0x99, 0xff, 0x00 }, { 0x99, 0xff, 0x33 }, { 0x99, 0xff, 0x66 }, { 0x99, 0xff, 0x99 }, { 0x99, 0xff, 0xcc }, { 0x99, 0xff, 0xff },
- { 0xcc, 0x00, 0x00 }, { 0xcc, 0x00, 0x33 }, { 0xcc, 0x00, 0x66 }, { 0xcc, 0x00, 0x99 }, { 0xcc, 0x00, 0xcc }, { 0xcc, 0x00, 0xff },
- { 0xcc, 0x33, 0x00 }, { 0xcc, 0x33, 0x33 }, { 0xcc, 0x33, 0x66 }, { 0xcc, 0x33, 0x99 }, { 0xcc, 0x33, 0xcc }, { 0xcc, 0x33, 0xff },
- { 0xcc, 0x66, 0x00 }, { 0xcc, 0x66, 0x33 }, { 0xcc, 0x66, 0x66 }, { 0xcc, 0x66, 0x99 }, { 0xcc, 0x66, 0xcc }, { 0xcc, 0x66, 0xff },
- { 0xcc, 0x99, 0x00 }, { 0xcc, 0x99, 0x33 }, { 0xcc, 0x99, 0x66 }, { 0xcc, 0x99, 0x99 }, { 0xcc, 0x99, 0xcc }, { 0xcc, 0x99, 0xff },
- { 0xcc, 0xcc, 0x00 }, { 0xcc, 0xcc, 0x33 }, { 0xcc, 0xcc, 0x66 }, { 0xcc, 0xcc, 0x99 }, { 0xcc, 0xcc, 0xcc }, { 0xcc, 0xcc, 0xff },
- { 0xcc, 0xff, 0x00 }, { 0xcc, 0xff, 0x33 }, { 0xcc, 0xff, 0x66 }, { 0xcc, 0xff, 0x99 }, { 0xcc, 0xff, 0xcc }, { 0xcc, 0xff, 0xff },
- { 0xff, 0x00, 0x00 }, { 0xff, 0x00, 0x33 }, { 0xff, 0x00, 0x66 }, { 0xff, 0x00, 0x99 }, { 0xff, 0x00, 0xcc }, { 0xff, 0x00, 0xff },
- { 0xff, 0x33, 0x00 }, { 0xff, 0x33, 0x33 }, { 0xff, 0x33, 0x66 }, { 0xff, 0x33, 0x99 }, { 0xff, 0x33, 0xcc }, { 0xff, 0x33, 0xff },
- { 0xff, 0x66, 0x00 }, { 0xff, 0x66, 0x33 }, { 0xff, 0x66, 0x66 }, { 0xff, 0x66, 0x99 }, { 0xff, 0x66, 0xcc }, { 0xff, 0x66, 0xff },
- { 0xff, 0x99, 0x00 }, { 0xff, 0x99, 0x33 }, { 0xff, 0x99, 0x66 }, { 0xff, 0x99, 0x99 }, { 0xff, 0x99, 0xcc }, { 0xff, 0x99, 0xff },
- { 0xff, 0xcc, 0x00 }, { 0xff, 0xcc, 0x33 }, { 0xff, 0xcc, 0x66 }, { 0xff, 0xcc, 0x99 }, { 0xff, 0xcc, 0xcc }, { 0xff, 0xcc, 0xff },
- { 0xff, 0xff, 0x00 }, { 0xff, 0xff, 0x33 }, { 0xff, 0xff, 0x66 }, { 0xff, 0xff, 0x99 }, { 0xff, 0xff, 0xcc }, { 0xff, 0xff, 0xff },
-};
+/* XXX: random value that shouldn't be taken into effect if there is no
+ * transparent color in the palette (the transparency bit will be set to 0) */
+#define DEFAULT_TRANSPARENCY_INDEX 0x1f
-/* GIF header */
-static int gif_image_write_header(AVIOContext *pb, int width, int height,
- int loop_count, uint32_t *palette)
+static int get_palette_transparency_index(const uint32_t *palette)
{
- int i;
- unsigned int v;
+ int transparent_color_index = -1;
+ unsigned i, smallest_alpha = 0xff;
- avio_write(pb, "GIF", 3);
- avio_write(pb, "89a", 3);
- avio_wl16(pb, width);
- avio_wl16(pb, height);
-
- avio_w8(pb, 0xf7); /* flags: global clut, 256 entries */
- avio_w8(pb, 0x1f); /* background color index */
- avio_w8(pb, 0); /* aspect ratio */
-
- /* the global palette */
- if (!palette) {
- avio_write(pb, (const unsigned char *)gif_clut, 216 * 3);
- for (i = 0; i < ((256 - 216) * 3); i++)
- avio_w8(pb, 0);
- } else {
- for (i = 0; i < 256; i++) {
- v = palette[i];
- avio_w8(pb, (v >> 16) & 0xff);
- avio_w8(pb, (v >> 8) & 0xff);
- avio_w8(pb, (v) & 0xff);
- }
- }
+ if (!palette)
+ return -1;
- /* update: this is the 'NETSCAPE EXTENSION' that allows for looped animated
- * GIF, see http://members.aol.com/royalef/gifabout.htm#net-extension
- *
- * byte 1 : 33 (hex 0x21) GIF Extension code
- * byte 2 : 255 (hex 0xFF) Application Extension Label
- * byte 3 : 11 (hex (0x0B) Length of Application Block
- * (eleven bytes of data to follow)
- * bytes 4 to 11 : "NETSCAPE"
- * bytes 12 to 14 : "2.0"
- * byte 15 : 3 (hex 0x03) Length of Data Sub-Block
- * (three bytes of data to follow)
- * byte 16 : 1 (hex 0x01)
- * bytes 17 to 18 : 0 to 65535, an unsigned integer in
- * lo-hi byte format. This indicate the
- * number of iterations the loop should
- * be executed.
- * bytes 19 : 0 (hex 0x00) a Data Sub-block Terminator
- */
-
- /* application extension header */
-#ifdef GIF_ADD_APP_HEADER
- if (loop_count >= 0 && loop_count <= 65535) {
- avio_w8(pb, 0x21);
- avio_w8(pb, 0xff);
- avio_w8(pb, 0x0b);
- // bytes 4 to 14
- avio_write(pb, "NETSCAPE2.0", sizeof("NETSCAPE2.0") - 1);
- avio_w8(pb, 0x03); // byte 15
- avio_w8(pb, 0x01); // byte 16
- avio_wl16(pb, (uint16_t)loop_count);
- avio_w8(pb, 0x00); // byte 19
+ for (i = 0; i < AVPALETTE_COUNT; i++) {
+ const uint32_t v = palette[i];
+ if (v >> 24 < smallest_alpha) {
+ smallest_alpha = v >> 24;
+ transparent_color_index = i;
+ }
}
-#endif
- return 0;
+ return smallest_alpha < 128 ? transparent_color_index : -1;
}
-/* this is maybe slow, but allows for extensions */
-static inline unsigned char gif_clut_index(uint8_t r, uint8_t g, uint8_t b)
-{
- return (((r) / 47) % 6) * 6 * 6 + (((g) / 47) % 6) * 6 + (((b) / 47) % 6);
-}
-
-static int gif_image_write_image(AVIOContext *pb,
- int x1, int y1, int width, int height,
- const uint8_t *buf, int linesize, int pix_fmt)
+static int gif_image_write_header(AVIOContext *pb, const AVCodecContext *avctx,
+ int loop_count, uint32_t *palette)
{
- PutBitContext p;
- uint8_t buffer[200]; /* 100 * 9 / 8 = 113 */
- int i, left, w, v;
- const uint8_t *ptr;
- /* image block */
-
- avio_w8(pb, 0x2c);
- avio_wl16(pb, x1);
- avio_wl16(pb, y1);
- avio_wl16(pb, width);
- avio_wl16(pb, height);
- avio_w8(pb, 0x00); /* flags */
- /* no local clut */
+ int i;
+ int64_t aspect = 0;
+ const AVRational sar = avctx->sample_aspect_ratio;
- avio_w8(pb, 0x08);
+ if (sar.num > 0 && sar.den > 0) {
+ aspect = sar.num * 64LL / sar.den - 15;
+ if (aspect < 0 || aspect > 255)
+ aspect = 0;
+ }
- left = width * height;
+ avio_write(pb, "GIF", 3);
+ avio_write(pb, "89a", 3);
+ avio_wl16(pb, avctx->width);
+ avio_wl16(pb, avctx->height);
- init_put_bits(&p, buffer, 130);
+ if (palette) {
+ const int bcid = get_palette_transparency_index(palette);
-/*
- * the thing here is the bitstream is written as little packets, with a size
- * byte before but it's still the same bitstream between packets (no flush !)
- */
- ptr = buf;
- w = width;
- while (left > 0) {
- put_bits(&p, 9, 0x0100); /* clear code */
-
- for (i = (left < GIF_CHUNKS) ? left : GIF_CHUNKS; i; i--) {
- if (pix_fmt == AV_PIX_FMT_RGB24) {
- v = gif_clut_index(ptr[0], ptr[1], ptr[2]);
- ptr += 3;
- } else {
- v = *ptr++;
- }
- put_bits(&p, 9, v);
- if (--w == 0) {
- w = width;
- buf += linesize;
- ptr = buf;
- }
+ avio_w8(pb, 0xf7); /* flags: global clut, 256 entries */
+ avio_w8(pb, bcid < 0 ? DEFAULT_TRANSPARENCY_INDEX : bcid); /* background color index */
+ avio_w8(pb, aspect);
+ for (i = 0; i < 256; i++) {
+ const uint32_t v = palette[i] & 0xffffff;
+ avio_wb24(pb, v);
}
+ } else {
+ avio_w8(pb, 0); /* flags */
+ avio_w8(pb, 0); /* background color index */
+ avio_w8(pb, aspect);
+ }
- if (left <= GIF_CHUNKS) {
- put_bits(&p, 9, 0x101); /* end of stream */
- flush_put_bits(&p);
- }
- if (put_bits_ptr(&p) - p.buf > 0) {
- avio_w8(pb, put_bits_ptr(&p) - p.buf); /* byte count of the packet */
- avio_write(pb, p.buf, put_bits_ptr(&p) - p.buf); /* the actual buffer */
- p.buf_ptr = p.buf; /* dequeue the bytes off the bitstream */
- }
- left -= GIF_CHUNKS;
+
+ if (loop_count >= 0 ) {
+ /* "NETSCAPE EXTENSION" for looped animation GIF */
+ avio_w8(pb, 0x21); /* GIF Extension code */
+ avio_w8(pb, 0xff); /* Application Extension Label */
+ avio_w8(pb, 0x0b); /* Length of Application Block */
+ avio_write(pb, "NETSCAPE2.0", sizeof("NETSCAPE2.0") - 1);
+ avio_w8(pb, 0x03); /* Length of Data Sub-Block */
+ avio_w8(pb, 0x01);
+ avio_wl16(pb, (uint16_t)loop_count);
+ avio_w8(pb, 0x00); /* Data Sub-block Terminator */
}
- avio_w8(pb, 0x00); /* end of image block */
+ avio_flush(pb);
return 0;
}
typedef struct GIFContext {
- AVClass *class; /** Class for private options. */
- int64_t time, file_time;
- uint8_t buffer[100]; /* data chunks */
+ AVClass *class;
int loop;
+ int last_delay;
+ AVPacket *prev_pkt;
+ int duration;
} GIFContext;
static int gif_write_header(AVFormatContext *s)
{
GIFContext *gif = s->priv_data;
- AVIOContext *pb = s->pb;
- AVCodecContext *enc, *video_enc;
- int i, width, height /*, rate*/;
+ AVCodecContext *video_enc;
+ uint32_t palette[AVPALETTE_COUNT];
-/* XXX: do we reject audio streams or just ignore them ?
- * if (s->nb_streams > 1)
- * return -1;
- */
- gif->time = 0;
- gif->file_time = 0;
-
- video_enc = NULL;
- for (i = 0; i < s->nb_streams; i++) {
- enc = s->streams[i]->codec;
- if (enc->codec_type != AVMEDIA_TYPE_AUDIO)
- video_enc = enc;
+ if (s->nb_streams != 1 ||
+ s->streams[0]->codec->codec_type != AVMEDIA_TYPE_VIDEO ||
+ s->streams[0]->codec->codec_id != AV_CODEC_ID_GIF) {
+ av_log(s, AV_LOG_ERROR,
+ "GIF muxer supports only a single video GIF stream.\n");
+ return AVERROR(EINVAL);
}
- if (!video_enc) {
- av_free(gif);
- return -1;
- } else {
- width = video_enc->width;
- height = video_enc->height;
-// rate = video_enc->time_base.den;
- }
+ video_enc = s->streams[0]->codec;
- if (video_enc->pix_fmt != AV_PIX_FMT_RGB24) {
- av_log(s, AV_LOG_ERROR,
- "ERROR: gif only handles the rgb24 pixel format. Use -pix_fmt rgb24.\n");
- return AVERROR(EIO);
+ avpriv_set_pts_info(s->streams[0], 64, 1, 100);
+ if (avpriv_set_systematic_pal2(palette, video_enc->pix_fmt) < 0) {
+ av_assert0(video_enc->pix_fmt == AV_PIX_FMT_PAL8);
+ /* delay header writing: we wait for the first palette to put it
+ * globally */
+ } else {
+ gif_image_write_header(s->pb, video_enc, gif->loop, palette);
}
- gif_image_write_header(pb, width, height, gif->loop, NULL);
-
- avio_flush(s->pb);
return 0;
}
-static int gif_write_video(AVFormatContext *s, AVCodecContext *enc,
- const uint8_t *buf, int size)
+static int flush_packet(AVFormatContext *s, AVPacket *new)
{
+ GIFContext *gif = s->priv_data;
+ int size, bcid;
AVIOContext *pb = s->pb;
- int jiffies;
+ const uint32_t *palette;
+ AVPacket *pkt = gif->prev_pkt;
+
+ if (!pkt)
+ return 0;
+
+ /* Mark one colour as transparent if the input palette contains at least
+ * one colour that is more than 50% transparent. */
+ palette = (uint32_t*)av_packet_get_side_data(pkt, AV_PKT_DATA_PALETTE, &size);
+ if (palette && size != AVPALETTE_SIZE) {
+ av_log(s, AV_LOG_ERROR, "Invalid palette extradata\n");
+ return AVERROR_INVALIDDATA;
+ }
+ bcid = get_palette_transparency_index(palette);
+
+ if (new && new->pts != AV_NOPTS_VALUE)
+ gif->duration = av_clip_uint16(new->pts - gif->prev_pkt->pts);
+ else if (!new && gif->last_delay >= 0)
+ gif->duration = gif->last_delay;
/* graphic control extension block */
avio_w8(pb, 0x21);
avio_w8(pb, 0xf9);
avio_w8(pb, 0x04); /* block size */
- avio_w8(pb, 0x04); /* flags */
-
- /* 1 jiffy is 1/70 s */
- /* the delay_time field indicates the number of jiffies - 1 */
- /* XXX: should use delay, in order to be more accurate */
- /* instead of using the same rounded value each time */
- /* XXX: don't even remember if I really use it for now */
- jiffies = (70 * enc->time_base.num / enc->time_base.den) - 1;
-
- avio_wl16(pb, jiffies);
-
- avio_w8(pb, 0x1f); /* transparent color index */
+ avio_w8(pb, 1<<2 | (bcid >= 0));
+ avio_wl16(pb, gif->duration);
+ avio_w8(pb, bcid < 0 ? DEFAULT_TRANSPARENCY_INDEX : bcid);
avio_w8(pb, 0x00);
- gif_image_write_image(pb, 0, 0, enc->width, enc->height,
- buf, enc->width * 3, AV_PIX_FMT_RGB24);
+ avio_write(pb, pkt->data, pkt->size);
+
+ av_free_packet(gif->prev_pkt);
+ if (new)
+ av_copy_packet(gif->prev_pkt, new);
return 0;
}
static int gif_write_packet(AVFormatContext *s, AVPacket *pkt)
{
- AVCodecContext *codec = s->streams[pkt->stream_index]->codec;
- if (codec->codec_type == AVMEDIA_TYPE_AUDIO)
- return 0; /* just ignore audio */
- else
- return gif_write_video(s, codec, pkt->data, pkt->size);
+ GIFContext *gif = s->priv_data;
+ const AVCodecContext *video_enc = s->streams[0]->codec;
+
+ if (!gif->prev_pkt) {
+ gif->prev_pkt = av_malloc(sizeof(*gif->prev_pkt));
+ if (!gif->prev_pkt)
+ return AVERROR(ENOMEM);
+
+ /* Write the first palette as global palette */
+ if (video_enc->pix_fmt == AV_PIX_FMT_PAL8) {
+ int size;
+ void *palette = av_packet_get_side_data(pkt, AV_PKT_DATA_PALETTE, &size);
+
+ if (!palette) {
+ av_log(s, AV_LOG_ERROR, "PAL8 packet is missing palette in extradata\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if (size != AVPALETTE_SIZE) {
+ av_log(s, AV_LOG_ERROR, "Invalid palette extradata\n");
+ return AVERROR_INVALIDDATA;
+ }
+ gif_image_write_header(s->pb, video_enc, gif->loop, palette);
+ }
+
+ return av_copy_packet(gif->prev_pkt, pkt);
+ }
+ return flush_packet(s, pkt);
}
static int gif_write_trailer(AVFormatContext *s)
{
+ GIFContext *gif = s->priv_data;
AVIOContext *pb = s->pb;
+ flush_packet(s, NULL);
+ av_freep(&gif->prev_pkt);
avio_w8(pb, 0x3b);
return 0;
@@ -353,8 +226,10 @@ static int gif_write_trailer(AVFormatContext *s)
#define OFFSET(x) offsetof(GIFContext, x)
#define ENC AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
- { "loop", "Number of times to loop the output.", OFFSET(loop),
- AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 65535, ENC },
+ { "loop", "Number of times to loop the output: -1 - no loop, 0 - infinite loop", OFFSET(loop),
+ AV_OPT_TYPE_INT, { .i64 = 0 }, -1, 65535, ENC },
+ { "final_delay", "Force delay (in centiseconds) after the last frame", OFFSET(last_delay),
+ AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 65535, ENC },
{ NULL },
};
@@ -372,9 +247,10 @@ AVOutputFormat ff_gif_muxer = {
.extensions = "gif",
.priv_data_size = sizeof(GIFContext),
.audio_codec = AV_CODEC_ID_NONE,
- .video_codec = AV_CODEC_ID_RAWVIDEO,
+ .video_codec = AV_CODEC_ID_GIF,
.write_header = gif_write_header,
.write_packet = gif_write_packet,
.write_trailer = gif_write_trailer,
.priv_class = &gif_muxer_class,
+ .flags = AVFMT_VARIABLE_FPS,
};
diff --git a/libavformat/gifdec.c b/libavformat/gifdec.c
new file mode 100644
index 0000000000..7db5a27408
--- /dev/null
+++ b/libavformat/gifdec.c
@@ -0,0 +1,334 @@
+/*
+ * GIF demuxer
+ * Copyright (c) 2012 Vitaliy E Sugrobov
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * GIF demuxer.
+ */
+
+#include "avformat.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "libavcodec/gif.h"
+
+typedef struct GIFDemuxContext {
+ const AVClass *class;
+ /**
+ * Time span in hundredths of second before
+ * the next frame should be drawn on screen.
+ */
+ int delay;
+ /**
+ * Minimum allowed delay between frames in hundredths of
+ * second. Values below this threshold considered to be
+ * invalid and set to value of default_delay.
+ */
+ int min_delay;
+ int default_delay;
+
+ /**
+ * loop options
+ */
+ int total_iter;
+ int iter_count;
+ int ignore_loop;
+} GIFDemuxContext;
+
+/**
+ * Major web browsers display gifs at ~10-15fps when rate
+ * is not explicitly set or have too low values. We assume default rate to be 10.
+ * Default delay = 100hundredths of second / 10fps = 10hos per frame.
+ */
+#define GIF_DEFAULT_DELAY 10
+/**
+ * By default delay values less than this threshold considered to be invalid.
+ */
+#define GIF_MIN_DELAY 2
+
+static int gif_probe(AVProbeData *p)
+{
+ /* check magick */
+ if (memcmp(p->buf, gif87a_sig, 6) && memcmp(p->buf, gif89a_sig, 6))
+ return 0;
+
+ /* width or height contains zero? */
+ if (!AV_RL16(&p->buf[6]) || !AV_RL16(&p->buf[8]))
+ return 0;
+
+ return AVPROBE_SCORE_MAX;
+}
+
+static int resync(AVIOContext *pb)
+{
+ int i;
+ for (i = 0; i < 6; i++) {
+ int b = avio_r8(pb);
+ if (b != gif87a_sig[i] && b != gif89a_sig[i])
+ i = -(b != 'G');
+ if (avio_feof(pb))
+ return AVERROR_EOF;
+ }
+ return 0;
+}
+
+static int gif_read_header(AVFormatContext *s)
+{
+ GIFDemuxContext *gdc = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVStream *st;
+ int width, height, ret;
+
+ if ((ret = resync(pb)) < 0)
+ return ret;
+
+ gdc->delay = gdc->default_delay;
+ width = avio_rl16(pb);
+ height = avio_rl16(pb);
+
+ if (width == 0 || height == 0)
+ return AVERROR_INVALIDDATA;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ /* GIF format operates with time in "hundredths of second",
+ * therefore timebase is 1/100 */
+ avpriv_set_pts_info(st, 64, 1, 100);
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codec->codec_id = AV_CODEC_ID_GIF;
+ st->codec->width = width;
+ st->codec->height = height;
+
+ /* jump to start because gif decoder needs header data too */
+ if (avio_seek(pb, 0, SEEK_SET) != 0)
+ return AVERROR(EIO);
+
+ return 0;
+}
+
+static int gif_skip_subblocks(AVIOContext *pb)
+{
+ int sb_size, ret = 0;
+
+ while (0x00 != (sb_size = avio_r8(pb))) {
+ if ((ret = avio_skip(pb, sb_size)) < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int gif_read_ext(AVFormatContext *s)
+{
+ GIFDemuxContext *gdc = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int sb_size, ext_label = avio_r8(pb);
+ int ret;
+
+ if (ext_label == GIF_GCE_EXT_LABEL) {
+ if ((sb_size = avio_r8(pb)) < 4) {
+ av_log(s, AV_LOG_FATAL, "Graphic Control Extension block's size less than 4.\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* skip packed fields */
+ if ((ret = avio_skip(pb, 1)) < 0)
+ return ret;
+
+ gdc->delay = avio_rl16(pb);
+
+ if (gdc->delay < gdc->min_delay)
+ gdc->delay = gdc->default_delay;
+
+ /* skip the rest of the Graphic Control Extension block */
+ if ((ret = avio_skip(pb, sb_size - 3)) < 0 )
+ return ret;
+ } else if (ext_label == GIF_APP_EXT_LABEL) {
+ uint8_t data[256];
+
+ sb_size = avio_r8(pb);
+ ret = avio_read(pb, data, sb_size);
+ if (ret < 0 || !sb_size)
+ return ret;
+
+ if (sb_size == strlen(NETSCAPE_EXT_STR)) {
+ sb_size = avio_r8(pb);
+ ret = avio_read(pb, data, sb_size);
+ if (ret < 0 || !sb_size)
+ return ret;
+
+ if (sb_size == 3 && data[0] == 1) {
+ gdc->total_iter = AV_RL16(data+1);
+
+ if (gdc->total_iter == 0)
+ gdc->total_iter = -1;
+ }
+ }
+ }
+
+ if ((ret = gif_skip_subblocks(pb)) < 0)
+ return ret;
+
+ return 0;
+}
+
+static int gif_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ GIFDemuxContext *gdc = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int packed_fields, block_label, ct_size,
+ keyframe, frame_parsed = 0, ret;
+ int64_t frame_start = avio_tell(pb), frame_end;
+ unsigned char buf[6];
+
+ if ((ret = avio_read(pb, buf, 6)) == 6) {
+ keyframe = memcmp(buf, gif87a_sig, 6) == 0 ||
+ memcmp(buf, gif89a_sig, 6) == 0;
+ } else if (ret < 0) {
+ return ret;
+ } else {
+ keyframe = 0;
+ }
+
+ if (keyframe) {
+parse_keyframe:
+ /* skip 2 bytes of width and 2 of height */
+ if ((ret = avio_skip(pb, 4)) < 0)
+ return ret;
+
+ packed_fields = avio_r8(pb);
+
+ /* skip 1 byte of Background Color Index and 1 byte of Pixel Aspect Ratio */
+ if ((ret = avio_skip(pb, 2)) < 0)
+ return ret;
+
+ /* global color table presence */
+ if (packed_fields & 0x80) {
+ ct_size = 3 * (1 << ((packed_fields & 0x07) + 1));
+
+ if ((ret = avio_skip(pb, ct_size)) < 0)
+ return ret;
+ }
+ } else {
+ avio_seek(pb, -ret, SEEK_CUR);
+ ret = AVERROR_EOF;
+ }
+
+ while (GIF_TRAILER != (block_label = avio_r8(pb)) && !avio_feof(pb)) {
+ if (block_label == GIF_EXTENSION_INTRODUCER) {
+ if ((ret = gif_read_ext (s)) < 0 )
+ goto resync;
+ } else if (block_label == GIF_IMAGE_SEPARATOR) {
+ /* skip to last byte of Image Descriptor header */
+ if ((ret = avio_skip(pb, 8)) < 0)
+ return ret;
+
+ packed_fields = avio_r8(pb);
+
+ /* local color table presence */
+ if (packed_fields & 0x80) {
+ ct_size = 3 * (1 << ((packed_fields & 0x07) + 1));
+
+ if ((ret = avio_skip(pb, ct_size)) < 0)
+ return ret;
+ }
+
+ /* read LZW Minimum Code Size */
+ if (avio_r8(pb) < 1) {
+ av_log(s, AV_LOG_ERROR, "lzw minimum code size must be >= 1\n");
+ goto resync;
+ }
+
+ if ((ret = gif_skip_subblocks(pb)) < 0)
+ goto resync;
+
+ frame_end = avio_tell(pb);
+
+ if (avio_seek(pb, frame_start, SEEK_SET) != frame_start)
+ return AVERROR(EIO);
+
+ ret = av_get_packet(pb, pkt, frame_end - frame_start);
+ if (ret < 0)
+ return ret;
+
+ if (keyframe)
+ pkt->flags |= AV_PKT_FLAG_KEY;
+
+ pkt->stream_index = 0;
+ pkt->duration = gdc->delay;
+
+ /* Graphic Control Extension's scope is single frame.
+ * Remove its influence. */
+ gdc->delay = gdc->default_delay;
+ frame_parsed = 1;
+
+ break;
+ } else {
+ av_log(s, AV_LOG_ERROR, "invalid block label\n");
+resync:
+ if (!keyframe)
+ avio_seek(pb, frame_start, SEEK_SET);
+ if ((ret = resync(pb)) < 0)
+ return ret;
+ frame_start = avio_tell(pb) - 6;
+ keyframe = 1;
+ goto parse_keyframe;
+ }
+ }
+
+ if ((ret >= 0 && !frame_parsed) || ret == AVERROR_EOF) {
+ /* This might happen when there is no image block
+ * between extension blocks and GIF_TRAILER or EOF */
+ if (!gdc->ignore_loop && (block_label == GIF_TRAILER || avio_feof(pb))
+ && (gdc->total_iter < 0 || ++gdc->iter_count < gdc->total_iter))
+ return avio_seek(pb, 0, SEEK_SET);
+ return AVERROR_EOF;
+ } else
+ return ret;
+}
+
+static const AVOption options[] = {
+ { "min_delay" , "minimum valid delay between frames (in hundredths of second)", offsetof(GIFDemuxContext, min_delay) , AV_OPT_TYPE_INT, {.i64 = GIF_MIN_DELAY} , 0, 100 * 60, AV_OPT_FLAG_DECODING_PARAM },
+ { "default_delay", "default delay between frames (in hundredths of second)" , offsetof(GIFDemuxContext, default_delay), AV_OPT_TYPE_INT, {.i64 = GIF_DEFAULT_DELAY}, 0, 100 * 60, AV_OPT_FLAG_DECODING_PARAM },
+ { "ignore_loop" , "ignore loop setting (netscape extension)" , offsetof(GIFDemuxContext, ignore_loop) , AV_OPT_TYPE_INT, {.i64 = 1} , 0, 1, AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+static const AVClass demuxer_class = {
+ .class_name = "GIF demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+ .category = AV_CLASS_CATEGORY_DEMUXER,
+};
+
+AVInputFormat ff_gif_demuxer = {
+ .name = "gif",
+ .long_name = NULL_IF_CONFIG_SMALL("CompuServe Graphics Interchange Format (GIF)"),
+ .priv_data_size = sizeof(GIFDemuxContext),
+ .read_probe = gif_probe,
+ .read_header = gif_read_header,
+ .read_packet = gif_read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+ .priv_class = &demuxer_class,
+};
diff --git a/libavformat/golomb_tab.c b/libavformat/golomb_tab.c
new file mode 100644
index 0000000000..063fae3647
--- /dev/null
+++ b/libavformat/golomb_tab.c
@@ -0,0 +1 @@
+#include "libavcodec/golomb.c"
diff --git a/libavformat/gopher.c b/libavformat/gopher.c
index 962fce798c..a5340d276c 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 e73aa0ddab..a9865dbee9 100644
--- a/libavformat/gsmdec.c
+++ b/libavformat/gsmdec.c
@@ -2,20 +2,20 @@
* RAW GSM demuxer
* Copyright (c) 2011 Justin Ruggles
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -48,7 +48,6 @@ static int gsm_read_packet(AVFormatContext *s, AVPacket *pkt)
av_free_packet(pkt);
return ret < 0 ? ret : AVERROR(EIO);
}
- pkt->size = ret;
pkt->duration = 1;
pkt->pts = pkt->pos / GSM_BLOCK_SIZE;
@@ -81,7 +80,7 @@ static const AVOption options[] = {
{ NULL },
};
-static const AVClass class = {
+static const AVClass gsm_class = {
.class_name = "gsm demuxer",
.item_name = av_default_item_name,
.option = options,
@@ -97,5 +96,5 @@ AVInputFormat ff_gsm_demuxer = {
.flags = AVFMT_GENERIC_INDEX,
.extensions = "gsm",
.raw_codec_id = AV_CODEC_ID_GSM,
- .priv_class = &class,
+ .priv_class = &gsm_class,
};
diff --git a/libavformat/gxf.c b/libavformat/gxf.c
index 189584035b..6c624f00a8 100644
--- a/libavformat/gxf.c
+++ b/libavformat/gxf.c
@@ -2,20 +2,20 @@
* GXF demuxer.
* Copyright (c) 2006 Reimar Doeffinger
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -33,9 +33,31 @@ struct gxf_stream_info {
int64_t last_field;
AVRational frames_per_second;
int32_t fields_per_frame;
+ int64_t track_aux_data;
};
/**
+ * @brief parse gxf timecode and add it to metadata
+ */
+static int add_timecode_metadata(AVDictionary **pm, const char *key, uint32_t timecode, int fields_per_frame)
+{
+ char tmp[128];
+ int field = timecode & 0xff;
+ int frame = fields_per_frame ? field / fields_per_frame : field;
+ int second = (timecode >> 8) & 0xff;
+ int minute = (timecode >> 16) & 0xff;
+ int hour = (timecode >> 24) & 0x1f;
+ int drop = (timecode >> 29) & 1;
+ // bit 30: color_frame, unused
+ // ignore invalid time code
+ if (timecode >> 31)
+ return 0;
+ snprintf(tmp, sizeof(tmp), "%02d:%02d:%02d%c%02d",
+ hour, minute, second, drop ? ';' : ':', frame);
+ return av_dict_set(pm, key, tmp, 0);
+}
+
+/**
* @brief parses a packet header, extracting type and length
* @param pb AVIOContext to read header from
* @param type detected packet type is stored here
@@ -96,12 +118,10 @@ static int get_sindex(AVFormatContext *s, int id, int format) {
st->codec->codec_id = AV_CODEC_ID_MJPEG;
break;
case 13:
- case 15:
- st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
- st->codec->codec_id = AV_CODEC_ID_DVVIDEO;
- break;
case 14:
+ case 15:
case 16:
+ case 25:
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
st->codec->codec_id = AV_CODEC_ID_DVVIDEO;
break;
@@ -145,6 +165,12 @@ static int get_sindex(AVFormatContext *s, int id, int format) {
st->codec->channel_layout = AV_CH_LAYOUT_STEREO;
st->codec->sample_rate = 48000;
break;
+ case 26: /* AVCi50 / AVCi100 (AVC Intra) */
+ case 29: /* AVCHD */
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codec->codec_id = AV_CODEC_ID_H264;
+ st->need_parsing = AVSTREAM_PARSE_HEADERS;
+ break;
// timecode tracks:
case 7:
case 8:
@@ -152,6 +178,10 @@ static int get_sindex(AVFormatContext *s, int id, int format) {
st->codec->codec_type = AVMEDIA_TYPE_DATA;
st->codec->codec_id = AV_CODEC_ID_NONE;
break;
+ case 30:
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codec->codec_id = AV_CODEC_ID_DNXHD;
+ break;
default:
st->codec->codec_type = AVMEDIA_TYPE_UNKNOWN;
st->codec->codec_id = AV_CODEC_ID_NONE;
@@ -228,6 +258,7 @@ static AVRational fps_umf2avr(uint32_t flags) {
static void gxf_track_tags(AVIOContext *pb, int *len, struct gxf_stream_info *si) {
si->frames_per_second = (AVRational){0, 0};
si->fields_per_frame = 0;
+ si->track_aux_data = 0x80000000;
while (*len >= 2) {
GXFTrackTag tag = avio_r8(pb);
int tlen = avio_r8(pb);
@@ -241,7 +272,9 @@ static void gxf_track_tags(AVIOContext *pb, int *len, struct gxf_stream_info *si
si->frames_per_second = fps_tag2avr(value);
else if (tag == TRACK_FPF && (value == 1 || value == 2))
si->fields_per_frame = value;
- } else
+ } else if (tlen == 8 && tag == TRACK_AUX)
+ si->track_aux_data = avio_rl64(pb);
+ else
avio_skip(pb, tlen);
}
}
@@ -251,15 +284,16 @@ static void gxf_track_tags(AVIOContext *pb, int *len, struct gxf_stream_info *si
*/
static void gxf_read_index(AVFormatContext *s, int pkt_len) {
AVIOContext *pb = s->pb;
- AVStream *st = s->streams[0];
+ AVStream *st;
uint32_t fields_per_map = avio_rl32(pb);
uint32_t map_cnt = avio_rl32(pb);
int i;
pkt_len -= 8;
- if (s->flags & AVFMT_FLAG_IGNIDX) {
+ if ((s->flags & AVFMT_FLAG_IGNIDX) || !s->streams) {
avio_skip(pb, pkt_len);
return;
}
+ st = s->streams[0];
if (map_cnt > 1000) {
av_log(s, AV_LOG_ERROR,
"too many index entries %"PRIu32" (%"PRIx32")\n",
@@ -321,8 +355,6 @@ static int gxf_header(AVFormatContext *s) {
track_id = avio_r8(pb);
track_len = avio_rb16(pb);
len -= track_len;
- gxf_track_tags(pb, &track_len, si);
- avio_skip(pb, track_len);
if (!(track_type & 0x80)) {
av_log(s, AV_LOG_ERROR, "invalid track type %x\n", track_type);
continue;
@@ -333,6 +365,16 @@ static int gxf_header(AVFormatContext *s) {
continue;
}
track_id &= 0x3f;
+ gxf_track_tags(pb, &track_len, si);
+ // check for timecode tracks
+ if (track_type == 7 || track_type == 8 || track_type == 24) {
+ add_timecode_metadata(&s->metadata, "timecode",
+ si->track_aux_data & 0xffffffff,
+ si->fields_per_frame);
+
+ }
+ avio_skip(pb, track_len);
+
idx = get_sindex(s, track_id, track_type);
if (idx < 0) continue;
st = s->streams[idx];
@@ -367,10 +409,21 @@ static int gxf_header(AVFormatContext *s) {
avio_skip(pb, 0x30); // payload description
fps = fps_umf2avr(avio_rl32(pb));
if (!main_timebase.num || !main_timebase.den) {
+ av_log(s, AV_LOG_WARNING, "No FPS track tag, using UMF fps tag."
+ " This might give wrong results.\n");
// this may not always be correct, but simply the best we can get
main_timebase.num = fps.den;
main_timebase.den = fps.num * 2;
}
+
+ if (len >= 0x18) {
+ len -= 0x18;
+ avio_skip(pb, 0x10);
+ add_timecode_metadata(&s->metadata, "timecode_at_mark_in",
+ avio_rl32(pb), si->fields_per_frame);
+ add_timecode_metadata(&s->metadata, "timecode_at_mark_out",
+ avio_rl32(pb), si->fields_per_frame);
+ }
} else
av_log(s, AV_LOG_INFO, "UMF packet too short\n");
} else
@@ -389,7 +442,7 @@ static int gxf_header(AVFormatContext *s) {
#define READ_ONE() \
{ \
- if (!max_interval-- || pb->eof_reached) \
+ if (!max_interval-- || avio_feof(pb)) \
goto out; \
tmp = tmp << 8 | avio_r8(pb); \
}
@@ -451,7 +504,7 @@ static int gxf_packet(AVFormatContext *s, AVPacket *pkt) {
int field_nr, field_info, skip = 0;
int stream_index;
if (!parse_packet_header(pb, &pkt_type, &pkt_len)) {
- if (!pb->eof_reached)
+ if (!avio_feof(pb))
av_log(s, AV_LOG_ERROR, "sync lost\n");
return -1;
}
@@ -503,7 +556,7 @@ static int gxf_packet(AVFormatContext *s, AVPacket *pkt) {
return ret;
}
- return AVERROR(EIO);
+ return AVERROR_EOF;
}
static int gxf_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) {
diff --git a/libavformat/gxf.h b/libavformat/gxf.h
index c1ac399458..dcdcdefc2d 100644
--- a/libavformat/gxf.h
+++ b/libavformat/gxf.h
@@ -2,20 +2,20 @@
* GXF demuxer
* copyright (c) 2006 Reimar Doeffinger
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/gxfenc.c b/libavformat/gxfenc.c
index fea1d5d818..12031f7cd9 100644
--- a/libavformat/gxfenc.c
+++ b/libavformat/gxfenc.c
@@ -2,25 +2,28 @@
* GXF muxer.
* Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier <baptiste dot coudurier at smartjog dot com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/avassert.h"
#include "libavutil/intfloat.h"
+#include "libavutil/opt.h"
#include "libavutil/mathematics.h"
+#include "libavutil/timecode.h"
#include "avformat.h"
#include "internal.h"
#include "gxf.h"
@@ -28,6 +31,18 @@
#define GXF_AUDIO_PACKET_SIZE 65536
+#define GXF_TIMECODE(c, d, h, m, s, f) \
+ ((c) << 30 | (d) << 29 | (h) << 24 | (m) << 16 | (s) << 8 | (f))
+
+typedef struct GXFTimecode{
+ int hh;
+ int mm;
+ int ss;
+ int ff;
+ int color;
+ int drop;
+} GXFTimecode;
+
typedef struct GXFStreamContext {
AudioInterleaveContext aic;
uint32_t track_type;
@@ -48,6 +63,7 @@ typedef struct GXFStreamContext {
} GXFStreamContext;
typedef struct GXFContext {
+ AVClass *av_class;
uint32_t nb_fields;
uint16_t audio_tracks;
uint16_t mpeg_tracks;
@@ -66,6 +82,7 @@ typedef struct GXFContext {
uint64_t *map_offsets; ///< offset of map packets
unsigned map_offsets_nb;
unsigned packet_count;
+ GXFTimecode tc;
} GXFContext;
static const struct {
@@ -188,33 +205,50 @@ static int gxf_write_mpeg_auxiliary(AVIOContext *pb, AVStream *st)
else
starting_line = 23; // default PAL
- size = snprintf(buffer, 1024, "Ver 1\nBr %.6f\nIpg 1\nPpi %d\nBpiop %d\n"
+ size = snprintf(buffer, sizeof(buffer), "Ver 1\nBr %.6f\nIpg 1\nPpi %d\nBpiop %d\n"
"Pix 0\nCf %d\nCg %d\nSl %d\nnl16 %d\nVi 1\nf1 1\n",
(float)st->codec->bit_rate, sc->p_per_gop, sc->b_per_i_or_p,
st->codec->pix_fmt == AV_PIX_FMT_YUV422P ? 2 : 1, sc->first_gop_closed == 1,
starting_line, (st->codec->height + 15) / 16);
+ av_assert0(size < sizeof(buffer));
avio_w8(pb, TRACK_MPG_AUX);
avio_w8(pb, size + 1);
avio_write(pb, (uint8_t *)buffer, size + 1);
return size + 3;
}
-static int gxf_write_timecode_auxiliary(AVIOContext *pb, GXFStreamContext *sc)
+static int gxf_write_dv_auxiliary(AVIOContext *pb, AVStream *st)
{
- avio_w8(pb, 0); /* fields */
- avio_w8(pb, 0); /* seconds */
- avio_w8(pb, 0); /* minutes */
- avio_w8(pb, 0); /* flags + hours */
+ int64_t track_aux_data = 0;
+
+ avio_w8(pb, TRACK_AUX);
+ avio_w8(pb, 8);
+ if (st->codec->pix_fmt == AV_PIX_FMT_YUV420P)
+ track_aux_data |= 0x01; /* marks stream as DVCAM instead of DVPRO */
+ track_aux_data |= 0x40000000; /* aux data is valid */
+ avio_wl64(pb, track_aux_data);
+ return 8;
+}
+
+static int gxf_write_timecode_auxiliary(AVIOContext *pb, GXFContext *gxf)
+{
+ uint32_t timecode = GXF_TIMECODE(gxf->tc.color, gxf->tc.drop,
+ gxf->tc.hh, gxf->tc.mm,
+ gxf->tc.ss, gxf->tc.ff);
+
+ avio_w8(pb, TRACK_AUX);
+ avio_w8(pb, 8);
+ avio_wl32(pb, timecode);
/* reserved */
- avio_wb32(pb, 0);
+ avio_wl32(pb, 0);
return 8;
}
static int gxf_write_track_description(AVFormatContext *s, GXFStreamContext *sc, int index)
{
+ GXFContext *gxf = s->priv_data;
AVIOContext *pb = s->pb;
int64_t pos;
- int mpeg = sc->track_type == 4 || sc->track_type == 9;
/* track description section */
avio_w8(pb, sc->media_type + 0x80);
@@ -230,13 +264,21 @@ static int gxf_write_track_description(AVFormatContext *s, GXFStreamContext *sc,
avio_wb16(pb, sc->media_info);
avio_w8(pb, 0);
- if (!mpeg) {
- /* auxiliary information */
- avio_w8(pb, TRACK_AUX);
- avio_w8(pb, 8);
- if (sc->track_type == 3)
- gxf_write_timecode_auxiliary(pb, sc);
- else
+ switch (sc->track_type) {
+ case 3: /* timecode */
+ gxf_write_timecode_auxiliary(pb, gxf);
+ break;
+ case 4: /* MPEG2 */
+ case 9: /* MPEG1 */
+ gxf_write_mpeg_auxiliary(pb, s->streams[index]);
+ break;
+ case 5: /* DV25 */
+ case 6: /* DV50 */
+ gxf_write_dv_auxiliary(pb, s->streams[index]);
+ break;
+ default:
+ avio_w8(pb, TRACK_AUX);
+ avio_w8(pb, 8);
avio_wl64(pb, 0);
}
@@ -245,9 +287,6 @@ static int gxf_write_track_description(AVFormatContext *s, GXFStreamContext *sc,
avio_w8(pb, 4);
avio_wb32(pb, 0);
- if (mpeg)
- gxf_write_mpeg_auxiliary(pb, s->streams[index]);
-
/* frame rate */
avio_w8(pb, TRACK_FPS);
avio_w8(pb, 4);
@@ -398,25 +437,36 @@ static int gxf_write_umf_material_description(AVFormatContext *s)
int timecode_base = gxf->time_base.den == 60000 ? 60 : 50;
int64_t timestamp = 0;
AVDictionaryEntry *t;
- uint32_t timecode;
+ uint64_t nb_fields;
+ uint32_t timecode_in; // timecode at mark in
+ uint32_t timecode_out; // timecode at mark out
if (t = av_dict_get(s->metadata, "creation_time", NULL, 0))
timestamp = ff_iso8601_to_unix_time(t->value);
- // XXX drop frame
- timecode =
- gxf->nb_fields / (timecode_base * 3600) % 24 << 24 | // hours
- gxf->nb_fields / (timecode_base * 60) % 60 << 16 | // minutes
- gxf->nb_fields / timecode_base % 60 << 8 | // seconds
- gxf->nb_fields % timecode_base; // fields
+ timecode_in = GXF_TIMECODE(gxf->tc.color, gxf->tc.drop,
+ gxf->tc.hh, gxf->tc.mm,
+ gxf->tc.ss, gxf->tc.ff);
+
+ nb_fields = gxf->nb_fields +
+ gxf->tc.hh * (timecode_base * 3600) +
+ gxf->tc.mm * (timecode_base * 60) +
+ gxf->tc.ss * timecode_base +
+ gxf->tc.ff;
+
+ timecode_out = GXF_TIMECODE(gxf->tc.color, gxf->tc.drop,
+ nb_fields / (timecode_base * 3600) % 24,
+ nb_fields / (timecode_base * 60) % 60,
+ nb_fields / timecode_base % 60,
+ nb_fields % timecode_base);
avio_wl32(pb, gxf->flags);
avio_wl32(pb, gxf->nb_fields); /* length of the longest track */
avio_wl32(pb, gxf->nb_fields); /* length of the shortest track */
avio_wl32(pb, 0); /* mark in */
avio_wl32(pb, gxf->nb_fields); /* mark out */
- avio_wl32(pb, 0); /* timecode mark in */
- avio_wl32(pb, timecode); /* timecode mark out */
+ avio_wl32(pb, timecode_in); /* timecode mark in */
+ avio_wl32(pb, timecode_out); /* timecode mark out */
avio_wl64(pb, timestamp); /* modification time */
avio_wl64(pb, timestamp); /* creation time */
avio_wl16(pb, 0); /* reserved */
@@ -491,9 +541,9 @@ static int gxf_write_umf_media_mpeg(AVIOContext *pb, AVStream *st)
return 32;
}
-static int gxf_write_umf_media_timecode(AVIOContext *pb, GXFStreamContext *sc)
+static int gxf_write_umf_media_timecode(AVIOContext *pb, int drop)
{
- avio_wl32(pb, 1); /* non drop frame */
+ avio_wl32(pb, drop); /* drop frame */
avio_wl32(pb, 0); /* reserved */
avio_wl32(pb, 0); /* reserved */
avio_wl32(pb, 0); /* reserved */
@@ -504,13 +554,20 @@ static int gxf_write_umf_media_timecode(AVIOContext *pb, GXFStreamContext *sc)
return 32;
}
-static int gxf_write_umf_media_dv(AVIOContext *pb, GXFStreamContext *sc)
+static int gxf_write_umf_media_dv(AVIOContext *pb, GXFStreamContext *sc, AVStream *st)
{
- int i;
-
- for (i = 0; i < 8; i++) {
- avio_wb32(pb, 0);
- }
+ int dv_umf_data = 0;
+
+ if (st->codec->pix_fmt == AV_PIX_FMT_YUV420P)
+ dv_umf_data |= 0x20; /* marks as DVCAM instead of DVPRO */
+ avio_wl32(pb, dv_umf_data);
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
return 32;
}
@@ -562,7 +619,7 @@ static int gxf_write_umf_media_description(AVFormatContext *s)
avio_wl32(pb, 0); /* reserved */
if (sc == &gxf->timecode_track)
- gxf_write_umf_media_timecode(pb, sc); /* 8 0bytes */
+ gxf_write_umf_media_timecode(pb, gxf->tc.drop);
else {
AVStream *st = s->streams[i];
switch (st->codec->codec_id) {
@@ -574,7 +631,7 @@ static int gxf_write_umf_media_description(AVFormatContext *s)
gxf_write_umf_media_audio(pb, sc);
break;
case AV_CODEC_ID_DVVIDEO:
- gxf_write_umf_media_dv(pb, sc);
+ gxf_write_umf_media_dv(pb, sc, st);
break;
}
}
@@ -625,6 +682,25 @@ static void gxf_init_timecode_track(GXFStreamContext *sc, GXFStreamContext *vsc)
sc->fields = vsc->fields;
}
+static int gxf_init_timecode(AVFormatContext *s, GXFTimecode *tc, const char *tcstr, int fields)
+{
+ char c;
+
+ if (sscanf(tcstr, "%d:%d:%d%c%d", &tc->hh, &tc->mm, &tc->ss, &c, &tc->ff) != 5) {
+ av_log(s, AV_LOG_ERROR, "unable to parse timecode, "
+ "syntax: hh:mm:ss[:;.]ff\n");
+ return -1;
+ }
+
+ tc->color = 0;
+ tc->drop = c != ':';
+
+ if (fields == 2)
+ tc->ff = tc->ff * 2;
+
+ return 0;
+}
+
static int gxf_write_header(AVFormatContext *s)
{
AVIOContext *pb = s->pb;
@@ -632,9 +708,11 @@ static int gxf_write_header(AVFormatContext *s)
GXFStreamContext *vsc = NULL;
uint8_t tracks[255] = {0};
int i, media_info = 0;
+ int ret;
+ AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
if (!pb->seekable) {
- av_log(s, AV_LOG_ERROR, "gxf muxer does not support streamed output, patch welcome");
+ av_log(s, AV_LOG_ERROR, "gxf muxer does not support streamed output, patch welcome\n");
return -1;
}
@@ -692,6 +770,8 @@ static int gxf_write_header(AVFormatContext *s)
"gxf muxer only accepts PAL or NTSC resolutions currently\n");
return -1;
}
+ if (!tcr)
+ tcr = av_dict_get(st->metadata, "timecode", NULL, 0);
avpriv_set_pts_info(st, 64, gxf->time_base.num, gxf->time_base.den);
if (gxf_find_lines_index(st) < 0)
sc->lines_index = -1;
@@ -743,10 +823,14 @@ static int gxf_write_header(AVFormatContext *s)
if (ff_audio_interleave_init(s, GXF_samples_per_frame, (AVRational){ 1, 48000 }) < 0)
return -1;
+ if (tcr && vsc)
+ gxf_init_timecode(s, &gxf->tc, tcr->value, vsc->fields);
+
gxf_init_timecode_track(&gxf->timecode_track, vsc);
gxf->flags |= 0x200000; // time code track is non-drop frame
- gxf_write_map_packet(s, 0);
+ if ((ret = gxf_write_map_packet(s, 0)) < 0)
+ return ret;
gxf_write_flt_packet(s);
gxf_write_umf_packet(s);
@@ -770,6 +854,7 @@ static int gxf_write_trailer(AVFormatContext *s)
AVIOContext *pb = s->pb;
int64_t end;
int i;
+ int ret;
ff_audio_interleave_close(s);
@@ -777,14 +862,16 @@ static int gxf_write_trailer(AVFormatContext *s)
end = avio_tell(pb);
avio_seek(pb, 0, SEEK_SET);
/* overwrite map, flt and umf packets with new values */
- gxf_write_map_packet(s, 1);
+ if ((ret = gxf_write_map_packet(s, 1)) < 0)
+ return ret;
gxf_write_flt_packet(s);
gxf_write_umf_packet(s);
avio_flush(pb);
/* update duration in all map packets */
for (i = 1; i < gxf->map_offsets_nb; i++) {
avio_seek(pb, gxf->map_offsets[i], SEEK_SET);
- gxf_write_map_packet(s, 1);
+ if ((ret = gxf_write_map_packet(s, 1)) < 0)
+ return ret;
avio_flush(pb);
}
@@ -862,7 +949,8 @@ static int gxf_write_packet(AVFormatContext *s, AVPacket *pkt)
AVStream *st = s->streams[pkt->stream_index];
int64_t pos = avio_tell(pb);
int padding = 0;
- int packet_start_offset = avio_tell(pb) / 1024;
+ unsigned packet_start_offset = avio_tell(pb) / 1024;
+ int ret;
gxf_write_packet_header(pb, PKT_MEDIA);
if (st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO && pkt->size % 4) /* MPEG-2 frames must be padded */
@@ -880,6 +968,7 @@ static int gxf_write_packet(AVFormatContext *s, AVPacket *pkt)
gxf->flt_entries_nb + 500,
sizeof(*gxf->flt_entries))) < 0) {
gxf->flt_entries_nb = 0;
+ gxf->nb_fields = 0;
av_log(s, AV_LOG_ERROR, "could not reallocate flt entries\n");
return err;
}
@@ -892,7 +981,8 @@ static int gxf_write_packet(AVFormatContext *s, AVPacket *pkt)
gxf->packet_count++;
if (gxf->packet_count == 100) {
- gxf_write_map_packet(s, 0);
+ if ((ret = gxf_write_map_packet(s, 0)) < 0)
+ return ret;
gxf->packet_count = 0;
}
diff --git a/libavformat/h261dec.c b/libavformat/h261dec.c
index 4a5805020f..a1d682128d 100644
--- a/libavformat/h261dec.c
+++ b/libavformat/h261dec.c
@@ -2,20 +2,20 @@
* RAW H.261 video demuxer
* Copyright (c) 2009 Michael Niedermayer <michaelni@gmx.at>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -25,33 +25,30 @@
static int h261_probe(AVProbeData *p)
{
- uint32_t code= -1;
int i;
int valid_psc=0;
int invalid_psc=0;
int next_gn=0;
int src_fmt=0;
- GetBitContext gb;
- init_get_bits(&gb, p->buf, p->buf_size*8);
+ for(i=0; i<p->buf_size; i++){
+ if ((AV_RB16(&p->buf[i]) - 1) < 0xFFU) {
+ int shift = av_log2_16bit(p->buf[i+1]);
+ uint32_t code = AV_RB64(&p->buf[FFMAX(i-1, 0)]) >> (24+shift);
+ if ((code & 0xffff0000) == 0x10000) {
+ int gn= (code>>12)&0xf;
+ if(!gn)
+ src_fmt= code&8;
+ if(gn != next_gn) invalid_psc++;
+ else valid_psc++;
- for(i=0; i<p->buf_size*8; i++){
- if ((code & 0x01ff0000) || !(code & 0xff00)) {
- code = (code<<8) + get_bits(&gb, 8);
- i += 7;
- } else
- code = (code<<1) + get_bits1(&gb);
- if ((code & 0xffff0000) == 0x10000) {
- int gn= (code>>12)&0xf;
- if(!gn)
- src_fmt= code&8;
- if(gn != next_gn) invalid_psc++;
- else valid_psc++;
-
- if(src_fmt){ // CIF
- next_gn= (gn+1 )%13;
- }else{ //QCIF
- next_gn= (gn+1+!!gn)% 7;
+ if(src_fmt){ // CIF
+ static const int lut[16]={1,2,3,4,5,6,7,8,9,10,11,12,0,16,16,16};
+ next_gn = lut[gn];
+ }else{ //QCIF
+ static const int lut[16]={1,3,16,5,16,0,16,16,16,16,16,16,16,16,16,16};
+ next_gn = lut[gn];
+ }
}
}
}
diff --git a/libavformat/h263dec.c b/libavformat/h263dec.c
index 4d826d8c2d..145fb85902 100644
--- a/libavformat/h263dec.c
+++ b/libavformat/h263dec.c
@@ -2,20 +2,20 @@
* RAW H.263 video demuxer
* Copyright (c) 2009 Michael Niedermayer <michaelni@gmx.at>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -31,24 +31,37 @@ static int h263_probe(AVProbeData *p)
int res_change=0;
int src_fmt, last_src_fmt=-1;
int last_gn=0;
+ int tr, last_tr = -1;
for(i=0; i<p->buf_size; i++){
code = (code<<8) + p->buf[i];
- if ((code & 0xfffffc0000) == 0x800000) {
- src_fmt= (code>>2)&3;
+ if ((code & 0xfffffc000000) == 0x80000000) {
+ tr = (code >> 18) & 0xFF;
+ src_fmt= (code>>10)&7;
if( src_fmt != last_src_fmt
&& last_src_fmt>0 && last_src_fmt<6
&& src_fmt<6)
res_change++;
- if((code&0x300)==0x200 && src_fmt){
+ if (tr == last_tr) {
+ invalid_psc++;
+ continue;
+ }
+
+ if (src_fmt != 7 && !(code&(1<<9)) && (code&(1<<5))) {
+ invalid_psc++;
+ continue;
+ }
+
+ if((code&0x30000)==0x20000 && src_fmt){
valid_psc++;
last_gn=0;
}else
invalid_psc++;
last_src_fmt= src_fmt;
- } else if((code & 0xffff800000) == 0x800000) {
- int gn= (code>>(23-5)) & 0x1F;
+ last_tr = tr;
+ } else if((code & 0xffff80000000) == 0x80000000) {
+ int gn= (code>>(31-5)) & 0x1F;
if(gn<last_gn){
invalid_psc++;
}else
diff --git a/libavformat/h264dec.c b/libavformat/h264dec.c
index 6fd45c1336..76073dda78 100644
--- a/libavformat/h264dec.c
+++ b/libavformat/h264dec.c
@@ -2,20 +2,20 @@
* RAW H.264 video demuxer
* Copyright (c) 2008 Michael Niedermayer <michaelni@gmx.at>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/hdsenc.c b/libavformat/hdsenc.c
index d96a3d5599..48810a9a34 100644
--- a/libavformat/hdsenc.c
+++ b/libavformat/hdsenc.c
@@ -2,20 +2,20 @@
* Live HDS fragmenter
* Copyright (c) 2013 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -139,21 +139,19 @@ static void hds_free(AVFormatContext *s)
return;
for (i = 0; i < s->nb_streams; i++) {
OutputStream *os = &c->streams[i];
- if (os->out)
- avio_close(os->out);
- os->out = NULL;
+ avio_closep(&os->out);
if (os->ctx && os->ctx_inited)
av_write_trailer(os->ctx);
- if (os->ctx && os->ctx->pb)
- av_free(os->ctx->pb);
+ if (os->ctx)
+ av_freep(&os->ctx->pb);
if (os->ctx)
avformat_free_context(os->ctx);
- av_free(os->metadata);
+ av_freep(&os->metadata);
for (j = 0; j < os->nb_extra_packets; j++)
- av_free(os->extra_packets[j]);
+ av_freep(&os->extra_packets[j]);
for (j = 0; j < os->nb_fragments; j++)
- av_free(os->fragments[j]);
- av_free(os->fragments);
+ av_freep(&os->fragments[j]);
+ av_freep(&os->fragments);
}
av_freep(&c->streams);
}
@@ -204,7 +202,7 @@ static int write_manifest(AVFormatContext *s, int final)
avio_printf(out, "</manifest>\n");
avio_flush(out);
avio_close(out);
- return ff_rename(temp_filename, filename);
+ return ff_rename(temp_filename, filename, s);
}
static void update_size(AVIOContext *out, int64_t pos)
@@ -285,7 +283,7 @@ static int write_abst(AVFormatContext *s, OutputStream *os, int final)
update_size(out, afrt_pos);
update_size(out, 0);
avio_close(out);
- return ff_rename(temp_filename, filename);
+ return ff_rename(temp_filename, filename, s);
}
static int init_file(AVFormatContext *s, OutputStream *os, int64_t start_ts)
@@ -311,8 +309,7 @@ static void close_file(OutputStream *os)
avio_seek(os->out, 0, SEEK_SET);
avio_wb32(os->out, pos);
avio_flush(os->out);
- avio_close(os->out);
- os->out = NULL;
+ avio_closep(&os->out);
}
static int hds_write_header(AVFormatContext *s)
@@ -323,6 +320,7 @@ static int hds_write_header(AVFormatContext *s)
if (mkdir(s->filename, 0777) == -1 && errno != EEXIST) {
ret = AVERROR(errno);
+ av_log(s, AV_LOG_ERROR , "Failed to create directory %s\n", s->filename);
goto fail;
}
@@ -332,7 +330,7 @@ static int hds_write_header(AVFormatContext *s)
goto fail;
}
- c->streams = av_mallocz(sizeof(*c->streams) * s->nb_streams);
+ c->streams = av_mallocz_array(s->nb_streams, sizeof(*c->streams));
if (!c->streams) {
ret = AVERROR(ENOMEM);
goto fail;
@@ -480,7 +478,7 @@ static int hds_flush(AVFormatContext *s, OutputStream *os, int final,
snprintf(target_filename, sizeof(target_filename),
"%s/stream%dSeg1-Frag%d", s->filename, index, os->fragment_index);
- ret = ff_rename(os->temp_filename, target_filename);
+ ret = ff_rename(os->temp_filename, target_filename, s);
if (ret < 0)
return ret;
add_fragment(os, target_filename, os->frag_start_ts, end_ts - os->frag_start_ts);
@@ -498,7 +496,7 @@ static int hds_flush(AVFormatContext *s, OutputStream *os, int final,
if (remove > 0) {
for (i = 0; i < remove; i++) {
unlink(os->fragments[i]->file);
- av_free(os->fragments[i]);
+ av_freep(&os->fragments[i]);
}
os->nb_fragments -= remove;
memmove(os->fragments, os->fragments + remove,
@@ -516,7 +514,7 @@ static int hds_write_packet(AVFormatContext *s, AVPacket *pkt)
HDSContext *c = s->priv_data;
AVStream *st = s->streams[pkt->stream_index];
OutputStream *os = &c->streams[s->streams[pkt->stream_index]->id];
- int64_t end_dts = os->fragment_index * (int64_t) c->min_frag_duration;
+ int64_t end_dts = os->fragment_index * (int64_t)c->min_frag_duration;
int ret;
if (st->first_dts == AV_NOPTS_VALUE)
@@ -538,7 +536,7 @@ static int hds_write_packet(AVFormatContext *s, AVPacket *pkt)
os->last_ts = pkt->dts;
os->packets_written++;
- return ff_write_chained(os->ctx, pkt->stream_index - os->first_stream, pkt, s);
+ return ff_write_chained(os->ctx, pkt->stream_index - os->first_stream, pkt, s, 0);
}
static int hds_write_trailer(AVFormatContext *s)
diff --git a/libavformat/hevc.c b/libavformat/hevc.c
index 37b35b4d2d..45b00c45bc 100644
--- a/libavformat/hevc.c
+++ b/libavformat/hevc.c
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2014 Tim Walker <tdskywalker@gmail.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -799,7 +799,7 @@ static void hvcc_init(HEVCDecoderConfigurationRecord *hvcc)
/*
* Initialize this field with an invalid value which can be used to detect
- * whether we didn't see any VUI (in wich case it should be reset to zero).
+ * whether we didn't see any VUI (in which case it should be reset to zero).
*/
hvcc->min_spatial_segmentation_idc = MAX_SPATIAL_SEGMENTATION + 1;
}
diff --git a/libavformat/hevc.h b/libavformat/hevc.h
index 03c43bd471..796eaf40b1 100644
--- a/libavformat/hevc.h
+++ b/libavformat/hevc.h
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2014 Tim Walker <tdskywalker@gmail.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -89,7 +89,7 @@ int ff_hevc_annexb2mp4_buf(const uint8_t *buf_in, uint8_t **buf_out,
* @param size size (in bytes) of the data buffer
* @param ps_array_completeness whether all parameter sets are in the hvcC (1)
* or there may be additional parameter sets in the bitstream (0)
- * @return 0 in case of success, a negative value corresponding to an AVERROR
+ * @return >=0 in case of success, a negative value corresponding to an AVERROR
* code in case of failure
*/
int ff_isom_write_hvcc(AVIOContext *pb, const uint8_t *data,
diff --git a/libavformat/hevcdec.c b/libavformat/hevcdec.c
index 65a3cdfb3c..e36a0513e3 100644
--- a/libavformat/hevcdec.c
+++ b/libavformat/hevcdec.c
@@ -2,20 +2,20 @@
* RAW HEVC video demuxer
* Copyright (c) 2013 Dirk Farin <dirk.farin@gmail.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/hls.c b/libavformat/hls.c
index 8b52a35508..f17b826f30 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -1,21 +1,22 @@
/*
* Apple HTTP Live Streaming demuxer
* Copyright (c) 2010 Martin Storsjo
+ * Copyright (c) 2013 Anssi Hannula
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -26,6 +27,7 @@
*/
#include "libavutil/avstring.h"
+#include "libavutil/avassert.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
@@ -35,9 +37,16 @@
#include "internal.h"
#include "avio_internal.h"
#include "url.h"
+#include "id3v2.h"
#define INITIAL_BUFFER_SIZE 32768
+#define MAX_FIELD_LEN 64
+#define MAX_CHARACTERISTICS_LEN 512
+
+#define MPEG_TIME_BASE 90000
+#define MPEG_TIME_BASE_Q (AVRational){1, MPEG_TIME_BASE}
+
/*
* An apple http stream consists of a playlist with media segment files,
* played sequentially. There may be several playlists with the same
@@ -57,19 +66,28 @@ enum KeyType {
struct segment {
int64_t duration;
- char url[MAX_URL_SIZE];
- char key[MAX_URL_SIZE];
+ int64_t url_offset;
+ int64_t size;
+ char *url;
+ char *key;
enum KeyType key_type;
uint8_t iv[16];
};
+struct rendition;
+
+enum PlaylistType {
+ PLS_TYPE_UNSPECIFIED,
+ PLS_TYPE_EVENT,
+ PLS_TYPE_VOD
+};
+
/*
- * Each variant has its own demuxer. If it currently is active,
+ * Each playlist has its own demuxer. If it currently is active,
* it has an open AVIOContext too, and potentially an AVPacket
* containing the next packet from this stream.
*/
-struct variant {
- int bandwidth;
+struct playlist {
char url[MAX_URL_SIZE];
AVIOContext pb;
uint8_t* read_buffer;
@@ -81,28 +99,86 @@ struct variant {
int stream_offset;
int finished;
+ enum PlaylistType type;
int64_t target_duration;
int start_seq_no;
int n_segments;
struct segment **segments;
int needed, cur_needed;
int cur_seq_no;
+ int64_t cur_seg_offset;
int64_t last_load_time;
char key_url[MAX_URL_SIZE];
uint8_t key[16];
+
+ /* ID3 timestamp handling (elementary audio streams have ID3 timestamps
+ * (and possibly other ID3 tags) in the beginning of each segment) */
+ int is_id3_timestamped; /* -1: not yet known */
+ int64_t id3_mpegts_timestamp; /* in mpegts tb */
+ int64_t id3_offset; /* in stream original tb */
+ uint8_t* id3_buf; /* temp buffer for id3 parsing */
+ unsigned int id3_buf_size;
+ AVDictionary *id3_initial; /* data from first id3 tag */
+ int id3_found; /* ID3 tag found at some point */
+ int id3_changed; /* ID3 tag data has changed at some point */
+ ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer is opened */
+
+ int64_t seek_timestamp;
+ int seek_flags;
+ int seek_stream_index; /* into subdemuxer stream array */
+
+ /* Renditions associated with this playlist, if any.
+ * Alternative rendition playlists have a single rendition associated
+ * with them, and variant main Media Playlists may have
+ * multiple (playlist-less) renditions associated with them. */
+ int n_renditions;
+ struct rendition **renditions;
+};
+
+/*
+ * Renditions are e.g. alternative subtitle or audio streams.
+ * The rendition may either be an external playlist or it may be
+ * contained in the main Media Playlist of the variant (in which case
+ * playlist is NULL).
+ */
+struct rendition {
+ enum AVMediaType type;
+ struct playlist *playlist;
+ char group_id[MAX_FIELD_LEN];
+ char language[MAX_FIELD_LEN];
+ char name[MAX_FIELD_LEN];
+ int disposition;
+};
+
+struct variant {
+ int bandwidth;
+
+ /* every variant contains at least the main Media Playlist in index 0 */
+ int n_playlists;
+ struct playlist **playlists;
+
+ char audio_group[MAX_FIELD_LEN];
+ char video_group[MAX_FIELD_LEN];
+ char subtitles_group[MAX_FIELD_LEN];
};
typedef struct HLSContext {
int n_variants;
struct variant **variants;
+ int n_playlists;
+ struct playlist **playlists;
+ int n_renditions;
+ struct rendition **renditions;
+
int cur_seq_no;
- int end_of_segment;
int first_packet;
int64_t first_timestamp;
- int64_t seek_timestamp;
- int seek_flags;
+ int64_t cur_timestamp;
AVIOInterruptCB *interrupt_callback;
+ char *user_agent; ///< holds HTTP user agent set as an AVOption to the HTTP protocol context
+ char *cookies; ///< holds HTTP cookie values set in either the initial response or as an AVOption to the HTTP protocol context
+ char *headers; ///< holds HTTP headers set as an AVOption to the HTTP protocol context
} HLSContext;
static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
@@ -113,13 +189,42 @@ static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
return len;
}
-static void free_segment_list(struct variant *var)
+static void free_segment_list(struct playlist *pls)
+{
+ int i;
+ for (i = 0; i < pls->n_segments; i++) {
+ av_freep(&pls->segments[i]->key);
+ av_freep(&pls->segments[i]->url);
+ av_freep(&pls->segments[i]);
+ }
+ av_freep(&pls->segments);
+ pls->n_segments = 0;
+}
+
+static void free_playlist_list(HLSContext *c)
{
int i;
- for (i = 0; i < var->n_segments; i++)
- av_free(var->segments[i]);
- av_freep(&var->segments);
- var->n_segments = 0;
+ for (i = 0; i < c->n_playlists; i++) {
+ struct playlist *pls = c->playlists[i];
+ free_segment_list(pls);
+ av_freep(&pls->renditions);
+ av_freep(&pls->id3_buf);
+ av_dict_free(&pls->id3_initial);
+ ff_id3v2_free_extra_meta(&pls->id3_deferred_extra);
+ av_free_packet(&pls->pkt);
+ av_freep(&pls->pb.buffer);
+ if (pls->input)
+ ffurl_close(pls->input);
+ if (pls->ctx) {
+ pls->ctx->pb = NULL;
+ avformat_close_input(&pls->ctx);
+ }
+ av_free(pls);
+ }
+ av_freep(&c->playlists);
+ av_freep(&c->cookies);
+ av_freep(&c->user_agent);
+ c->n_playlists = 0;
}
static void free_variant_list(HLSContext *c)
@@ -127,21 +232,22 @@ static void free_variant_list(HLSContext *c)
int i;
for (i = 0; i < c->n_variants; i++) {
struct variant *var = c->variants[i];
- free_segment_list(var);
- av_free_packet(&var->pkt);
- av_free(var->pb.buffer);
- if (var->input)
- ffurl_close(var->input);
- if (var->ctx) {
- var->ctx->pb = NULL;
- avformat_close_input(&var->ctx);
- }
+ av_freep(&var->playlists);
av_free(var);
}
av_freep(&c->variants);
c->n_variants = 0;
}
+static void free_rendition_list(HLSContext *c)
+{
+ int i;
+ for (i = 0; i < c->n_renditions; i++)
+ av_freep(&c->renditions[i]);
+ av_freep(&c->renditions);
+ c->n_renditions = 0;
+}
+
/*
* Used to reset a statically allocated AVPacket to a clean slate,
* containing no data.
@@ -152,29 +258,72 @@ static void reset_packet(AVPacket *pkt)
pkt->data = NULL;
}
-static struct variant *new_variant(HLSContext *c, int bandwidth,
- const char *url, const char *base)
+static struct playlist *new_playlist(HLSContext *c, const char *url,
+ const char *base)
{
- struct variant *var = av_mallocz(sizeof(struct variant));
- if (!var)
+ struct playlist *pls = av_mallocz(sizeof(struct playlist));
+ if (!pls)
return NULL;
- reset_packet(&var->pkt);
- var->bandwidth = bandwidth;
- ff_make_absolute_url(var->url, sizeof(var->url), base, url);
- dynarray_add(&c->variants, &c->n_variants, var);
- return var;
+ reset_packet(&pls->pkt);
+ ff_make_absolute_url(pls->url, sizeof(pls->url), base, url);
+ pls->seek_timestamp = AV_NOPTS_VALUE;
+
+ pls->is_id3_timestamped = -1;
+ pls->id3_mpegts_timestamp = AV_NOPTS_VALUE;
+
+ dynarray_add(&c->playlists, &c->n_playlists, pls);
+ return pls;
}
struct variant_info {
char bandwidth[20];
+ /* variant group ids: */
+ char audio[MAX_FIELD_LEN];
+ char video[MAX_FIELD_LEN];
+ char subtitles[MAX_FIELD_LEN];
};
+static struct variant *new_variant(HLSContext *c, struct variant_info *info,
+ const char *url, const char *base)
+{
+ struct variant *var;
+ struct playlist *pls;
+
+ pls = new_playlist(c, url, base);
+ if (!pls)
+ return NULL;
+
+ var = av_mallocz(sizeof(struct variant));
+ if (!var)
+ return NULL;
+
+ if (info) {
+ var->bandwidth = atoi(info->bandwidth);
+ strcpy(var->audio_group, info->audio);
+ strcpy(var->video_group, info->video);
+ strcpy(var->subtitles_group, info->subtitles);
+ }
+
+ dynarray_add(&c->variants, &c->n_variants, var);
+ dynarray_add(&var->playlists, &var->n_playlists, pls);
+ return var;
+}
+
static void handle_variant_args(struct variant_info *info, const char *key,
int key_len, char **dest, int *dest_len)
{
if (!strncmp(key, "BANDWIDTH=", key_len)) {
*dest = info->bandwidth;
*dest_len = sizeof(info->bandwidth);
+ } else if (!strncmp(key, "AUDIO=", key_len)) {
+ *dest = info->audio;
+ *dest_len = sizeof(info->audio);
+ } else if (!strncmp(key, "VIDEO=", key_len)) {
+ *dest = info->video;
+ *dest_len = sizeof(info->video);
+ } else if (!strncmp(key, "SUBTITLES=", key_len)) {
+ *dest = info->subtitles;
+ *dest_len = sizeof(info->subtitles);
}
}
@@ -199,24 +348,182 @@ static void handle_key_args(struct key_info *info, const char *key,
}
}
+struct rendition_info {
+ char type[16];
+ char uri[MAX_URL_SIZE];
+ char group_id[MAX_FIELD_LEN];
+ char language[MAX_FIELD_LEN];
+ char assoc_language[MAX_FIELD_LEN];
+ char name[MAX_FIELD_LEN];
+ char defaultr[4];
+ char forced[4];
+ char characteristics[MAX_CHARACTERISTICS_LEN];
+};
+
+static struct rendition *new_rendition(HLSContext *c, struct rendition_info *info,
+ const char *url_base)
+{
+ struct rendition *rend;
+ enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
+ char *characteristic;
+ char *chr_ptr;
+ char *saveptr;
+
+ if (!strcmp(info->type, "AUDIO"))
+ type = AVMEDIA_TYPE_AUDIO;
+ else if (!strcmp(info->type, "VIDEO"))
+ type = AVMEDIA_TYPE_VIDEO;
+ else if (!strcmp(info->type, "SUBTITLES"))
+ type = AVMEDIA_TYPE_SUBTITLE;
+ else if (!strcmp(info->type, "CLOSED-CAPTIONS"))
+ /* CLOSED-CAPTIONS is ignored since we do not support CEA-608 CC in
+ * AVC SEI RBSP anyway */
+ return NULL;
+
+ if (type == AVMEDIA_TYPE_UNKNOWN)
+ return NULL;
+
+ /* URI is mandatory for subtitles as per spec */
+ if (type == AVMEDIA_TYPE_SUBTITLE && !info->uri[0])
+ return NULL;
+
+ /* TODO: handle subtitles (each segment has to parsed separately) */
+ if (type == AVMEDIA_TYPE_SUBTITLE)
+ return NULL;
+
+ rend = av_mallocz(sizeof(struct rendition));
+ if (!rend)
+ return NULL;
+
+ dynarray_add(&c->renditions, &c->n_renditions, rend);
+
+ rend->type = type;
+ strcpy(rend->group_id, info->group_id);
+ strcpy(rend->language, info->language);
+ strcpy(rend->name, info->name);
+
+ /* add the playlist if this is an external rendition */
+ if (info->uri[0]) {
+ rend->playlist = new_playlist(c, info->uri, url_base);
+ if (rend->playlist)
+ dynarray_add(&rend->playlist->renditions,
+ &rend->playlist->n_renditions, rend);
+ }
+
+ if (info->assoc_language[0]) {
+ int langlen = strlen(rend->language);
+ if (langlen < sizeof(rend->language) - 3) {
+ rend->language[langlen] = ',';
+ strncpy(rend->language + langlen + 1, info->assoc_language,
+ sizeof(rend->language) - langlen - 2);
+ }
+ }
+
+ if (!strcmp(info->defaultr, "YES"))
+ rend->disposition |= AV_DISPOSITION_DEFAULT;
+ if (!strcmp(info->forced, "YES"))
+ rend->disposition |= AV_DISPOSITION_FORCED;
+
+ chr_ptr = info->characteristics;
+ while ((characteristic = av_strtok(chr_ptr, ",", &saveptr))) {
+ if (!strcmp(characteristic, "public.accessibility.describes-music-and-sound"))
+ rend->disposition |= AV_DISPOSITION_HEARING_IMPAIRED;
+ else if (!strcmp(characteristic, "public.accessibility.describes-video"))
+ rend->disposition |= AV_DISPOSITION_VISUAL_IMPAIRED;
+
+ chr_ptr = NULL;
+ }
+
+ return rend;
+}
+
+static void handle_rendition_args(struct rendition_info *info, const char *key,
+ int key_len, char **dest, int *dest_len)
+{
+ if (!strncmp(key, "TYPE=", key_len)) {
+ *dest = info->type;
+ *dest_len = sizeof(info->type);
+ } else if (!strncmp(key, "URI=", key_len)) {
+ *dest = info->uri;
+ *dest_len = sizeof(info->uri);
+ } else if (!strncmp(key, "GROUP-ID=", key_len)) {
+ *dest = info->group_id;
+ *dest_len = sizeof(info->group_id);
+ } else if (!strncmp(key, "LANGUAGE=", key_len)) {
+ *dest = info->language;
+ *dest_len = sizeof(info->language);
+ } else if (!strncmp(key, "ASSOC-LANGUAGE=", key_len)) {
+ *dest = info->assoc_language;
+ *dest_len = sizeof(info->assoc_language);
+ } else if (!strncmp(key, "NAME=", key_len)) {
+ *dest = info->name;
+ *dest_len = sizeof(info->name);
+ } else if (!strncmp(key, "DEFAULT=", key_len)) {
+ *dest = info->defaultr;
+ *dest_len = sizeof(info->defaultr);
+ } else if (!strncmp(key, "FORCED=", key_len)) {
+ *dest = info->forced;
+ *dest_len = sizeof(info->forced);
+ } else if (!strncmp(key, "CHARACTERISTICS=", key_len)) {
+ *dest = info->characteristics;
+ *dest_len = sizeof(info->characteristics);
+ }
+ /*
+ * ignored:
+ * - AUTOSELECT: client may autoselect based on e.g. system language
+ * - INSTREAM-ID: EIA-608 closed caption number ("CC1".."CC4")
+ */
+}
+
+/* used by parse_playlist to allocate a new variant+playlist when the
+ * playlist is detected to be a Media Playlist (not Master Playlist)
+ * and we have no parent Master Playlist (parsing of which would have
+ * allocated the variant and playlist already) */
+static int ensure_playlist(HLSContext *c, struct playlist **pls, const char *url)
+{
+ if (*pls)
+ return 0;
+ if (!new_variant(c, NULL, url, NULL))
+ return AVERROR(ENOMEM);
+ *pls = c->playlists[c->n_playlists - 1];
+ return 0;
+}
+
+/* pls = NULL => Master Playlist or parentless Media Playlist
+ * pls = !NULL => parented Media Playlist, playlist+variant allocated */
static int parse_playlist(HLSContext *c, const char *url,
- struct variant *var, AVIOContext *in)
+ struct playlist *pls, AVIOContext *in)
{
- int ret = 0, is_segment = 0, is_variant = 0, bandwidth = 0;
+ int ret = 0, is_segment = 0, is_variant = 0;
int64_t duration = 0;
enum KeyType key_type = KEY_NONE;
uint8_t iv[16] = "";
int has_iv = 0;
char key[MAX_URL_SIZE] = "";
- char line[1024];
+ char line[MAX_URL_SIZE];
const char *ptr;
int close_in = 0;
+ int64_t seg_offset = 0;
+ int64_t seg_size = -1;
uint8_t *new_url = NULL;
+ struct variant_info variant_info;
+ char tmp_str[MAX_URL_SIZE];
if (!in) {
+ AVDictionary *opts = NULL;
close_in = 1;
- if ((ret = avio_open2(&in, url, AVIO_FLAG_READ,
- c->interrupt_callback, NULL)) < 0)
+ /* Some HLS servers don't like being sent the range header */
+ av_dict_set(&opts, "seekable", "0", 0);
+
+ // broker prior HTTP options that should be consistent across requests
+ av_dict_set(&opts, "user-agent", c->user_agent, 0);
+ av_dict_set(&opts, "cookies", c->cookies, 0);
+ av_dict_set(&opts, "headers", c->headers, 0);
+
+ ret = avio_open2(&in, url, AVIO_FLAG_READ,
+ c->interrupt_callback, &opts);
+ av_dict_free(&opts);
+ if (ret < 0)
return ret;
}
@@ -229,18 +536,18 @@ static int parse_playlist(HLSContext *c, const char *url,
goto fail;
}
- if (var) {
- free_segment_list(var);
- var->finished = 0;
+ if (pls) {
+ free_segment_list(pls);
+ pls->finished = 0;
+ pls->type = PLS_TYPE_UNSPECIFIED;
}
- while (!in->eof_reached) {
+ while (!avio_feof(in)) {
read_chomp_line(in, line, sizeof(line));
if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) {
- struct variant_info info = {{0}};
is_variant = 1;
+ memset(&variant_info, 0, sizeof(variant_info));
ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args,
- &info);
- bandwidth = atoi(info.bandwidth);
+ &variant_info);
} else if (av_strstart(line, "#EXT-X-KEY:", &ptr)) {
struct key_info info = {{0}};
ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_key_args,
@@ -254,49 +561,58 @@ static int parse_playlist(HLSContext *c, const char *url,
has_iv = 1;
}
av_strlcpy(key, info.uri, sizeof(key));
+ } else if (av_strstart(line, "#EXT-X-MEDIA:", &ptr)) {
+ struct rendition_info info = {{0}};
+ ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_rendition_args,
+ &info);
+ new_rendition(c, &info, url);
} else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {
- if (!var) {
- var = new_variant(c, 0, url, NULL);
- if (!var) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
- }
- var->target_duration = atoi(ptr) * AV_TIME_BASE;
+ ret = ensure_playlist(c, &pls, url);
+ if (ret < 0)
+ goto fail;
+ pls->target_duration = atoi(ptr) * AV_TIME_BASE;
} else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
- if (!var) {
- var = new_variant(c, 0, url, NULL);
- if (!var) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
- }
- var->start_seq_no = atoi(ptr);
+ ret = ensure_playlist(c, &pls, url);
+ if (ret < 0)
+ goto fail;
+ pls->start_seq_no = atoi(ptr);
+ } else if (av_strstart(line, "#EXT-X-PLAYLIST-TYPE:", &ptr)) {
+ ret = ensure_playlist(c, &pls, url);
+ if (ret < 0)
+ goto fail;
+ if (!strcmp(ptr, "EVENT"))
+ pls->type = PLS_TYPE_EVENT;
+ else if (!strcmp(ptr, "VOD"))
+ pls->type = PLS_TYPE_VOD;
} else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {
- if (var)
- var->finished = 1;
+ if (pls)
+ pls->finished = 1;
} else if (av_strstart(line, "#EXTINF:", &ptr)) {
is_segment = 1;
duration = atof(ptr) * AV_TIME_BASE;
+ } else if (av_strstart(line, "#EXT-X-BYTERANGE:", &ptr)) {
+ seg_size = atoi(ptr);
+ ptr = strchr(ptr, '@');
+ if (ptr)
+ seg_offset = atoi(ptr+1);
} else if (av_strstart(line, "#", NULL)) {
continue;
} else if (line[0]) {
if (is_variant) {
- if (!new_variant(c, bandwidth, line, url)) {
+ if (!new_variant(c, &variant_info, line, url)) {
ret = AVERROR(ENOMEM);
goto fail;
}
is_variant = 0;
- bandwidth = 0;
}
if (is_segment) {
struct segment *seg;
- if (!var) {
- var = new_variant(c, 0, url, NULL);
- if (!var) {
+ if (!pls) {
+ if (!new_variant(c, 0, url, NULL)) {
ret = AVERROR(ENOMEM);
goto fail;
}
+ pls = c->playlists[c->n_playlists - 1];
}
seg = av_malloc(sizeof(struct segment));
if (!seg) {
@@ -308,19 +624,49 @@ static int parse_playlist(HLSContext *c, const char *url,
if (has_iv) {
memcpy(seg->iv, iv, sizeof(iv));
} else {
- int seq = var->start_seq_no + var->n_segments;
+ int seq = pls->start_seq_no + pls->n_segments;
memset(seg->iv, 0, sizeof(seg->iv));
AV_WB32(seg->iv + 12, seq);
}
- ff_make_absolute_url(seg->key, sizeof(seg->key), url, key);
- ff_make_absolute_url(seg->url, sizeof(seg->url), url, line);
- dynarray_add(&var->segments, &var->n_segments, seg);
+
+ if (key_type != KEY_NONE) {
+ ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, key);
+ seg->key = av_strdup(tmp_str);
+ if (!seg->key) {
+ av_free(seg);
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ } else {
+ seg->key = NULL;
+ }
+
+ ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, line);
+ seg->url = av_strdup(tmp_str);
+ if (!seg->url) {
+ av_free(seg->key);
+ av_free(seg);
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ dynarray_add(&pls->segments, &pls->n_segments, seg);
is_segment = 0;
+
+ seg->size = seg_size;
+ if (seg_size >= 0) {
+ seg->url_offset = seg_offset;
+ seg_offset += seg_size;
+ seg_size = -1;
+ } else {
+ seg->url_offset = 0;
+ seg_offset = 0;
+ }
}
}
}
- if (var)
- var->last_load_time = av_gettime_relative();
+ if (pls)
+ pls->last_load_time = av_gettime_relative();
fail:
av_free(new_url);
@@ -329,21 +675,269 @@ fail:
return ret;
}
-static int open_input(struct variant *var)
+enum ReadFromURLMode {
+ READ_NORMAL,
+ READ_COMPLETE,
+};
+
+/* read from URLContext, limiting read to current segment */
+static int read_from_url(struct playlist *pls, uint8_t *buf, int buf_size,
+ enum ReadFromURLMode mode)
+{
+ int ret;
+ struct segment *seg = pls->segments[pls->cur_seq_no - pls->start_seq_no];
+
+ /* limit read if the segment was only a part of a file */
+ if (seg->size >= 0)
+ buf_size = FFMIN(buf_size, seg->size - pls->cur_seg_offset);
+
+ if (mode == READ_COMPLETE)
+ ret = ffurl_read_complete(pls->input, buf, buf_size);
+ else
+ ret = ffurl_read(pls->input, buf, buf_size);
+
+ if (ret > 0)
+ pls->cur_seg_offset += ret;
+
+ return ret;
+}
+
+/* Parse the raw ID3 data and pass contents to caller */
+static void parse_id3(AVFormatContext *s, AVIOContext *pb,
+ AVDictionary **metadata, int64_t *dts,
+ ID3v2ExtraMetaAPIC **apic, ID3v2ExtraMeta **extra_meta)
+{
+ static const char id3_priv_owner_ts[] = "com.apple.streaming.transportStreamTimestamp";
+ ID3v2ExtraMeta *meta;
+
+ ff_id3v2_read_dict(pb, metadata, ID3v2_DEFAULT_MAGIC, extra_meta);
+ for (meta = *extra_meta; meta; meta = meta->next) {
+ if (!strcmp(meta->tag, "PRIV")) {
+ ID3v2ExtraMetaPRIV *priv = meta->data;
+ if (priv->datasize == 8 && !strcmp(priv->owner, id3_priv_owner_ts)) {
+ /* 33-bit MPEG timestamp */
+ int64_t ts = AV_RB64(priv->data);
+ av_log(s, AV_LOG_DEBUG, "HLS ID3 audio timestamp %"PRId64"\n", ts);
+ if ((ts & ~((1ULL << 33) - 1)) == 0)
+ *dts = ts;
+ else
+ av_log(s, AV_LOG_ERROR, "Invalid HLS ID3 audio timestamp %"PRId64"\n", ts);
+ }
+ } else if (!strcmp(meta->tag, "APIC") && apic)
+ *apic = meta->data;
+ }
+}
+
+/* Check if the ID3 metadata contents have changed */
+static int id3_has_changed_values(struct playlist *pls, AVDictionary *metadata,
+ ID3v2ExtraMetaAPIC *apic)
+{
+ AVDictionaryEntry *entry = NULL;
+ AVDictionaryEntry *oldentry;
+ /* check that no keys have changed values */
+ while ((entry = av_dict_get(metadata, "", entry, AV_DICT_IGNORE_SUFFIX))) {
+ oldentry = av_dict_get(pls->id3_initial, entry->key, NULL, AV_DICT_MATCH_CASE);
+ if (!oldentry || strcmp(oldentry->value, entry->value) != 0)
+ return 1;
+ }
+
+ /* check if apic appeared */
+ if (apic && (pls->ctx->nb_streams != 2 || !pls->ctx->streams[1]->attached_pic.data))
+ return 1;
+
+ if (apic) {
+ int size = pls->ctx->streams[1]->attached_pic.size;
+ if (size != apic->buf->size - FF_INPUT_BUFFER_PADDING_SIZE)
+ return 1;
+
+ if (memcmp(apic->buf->data, pls->ctx->streams[1]->attached_pic.data, size) != 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Parse ID3 data and handle the found data */
+static void handle_id3(AVIOContext *pb, struct playlist *pls)
+{
+ AVDictionary *metadata = NULL;
+ ID3v2ExtraMetaAPIC *apic = NULL;
+ ID3v2ExtraMeta *extra_meta = NULL;
+ int64_t timestamp = AV_NOPTS_VALUE;
+
+ parse_id3(pls->ctx, pb, &metadata, &timestamp, &apic, &extra_meta);
+
+ if (timestamp != AV_NOPTS_VALUE) {
+ pls->id3_mpegts_timestamp = timestamp;
+ pls->id3_offset = 0;
+ }
+
+ if (!pls->id3_found) {
+ /* initial ID3 tags */
+ av_assert0(!pls->id3_deferred_extra);
+ pls->id3_found = 1;
+
+ /* get picture attachment and set text metadata */
+ if (pls->ctx->nb_streams)
+ ff_id3v2_parse_apic(pls->ctx, &extra_meta);
+ else
+ /* demuxer not yet opened, defer picture attachment */
+ pls->id3_deferred_extra = extra_meta;
+
+ av_dict_copy(&pls->ctx->metadata, metadata, 0);
+ pls->id3_initial = metadata;
+
+ } else {
+ if (!pls->id3_changed && id3_has_changed_values(pls, metadata, apic)) {
+ avpriv_report_missing_feature(pls->ctx, "Changing ID3 metadata in HLS audio elementary stream");
+ pls->id3_changed = 1;
+ }
+ av_dict_free(&metadata);
+ }
+
+ if (!pls->id3_deferred_extra)
+ ff_id3v2_free_extra_meta(&extra_meta);
+}
+
+/* Intercept and handle ID3 tags between URLContext and AVIOContext */
+static void intercept_id3(struct playlist *pls, uint8_t *buf,
+ int buf_size, int *len)
+{
+ /* intercept id3 tags, we do not want to pass them to the raw
+ * demuxer on all segment switches */
+ int bytes;
+ int id3_buf_pos = 0;
+ int fill_buf = 0;
+
+ /* gather all the id3 tags */
+ while (1) {
+ /* see if we can retrieve enough data for ID3 header */
+ if (*len < ID3v2_HEADER_SIZE && buf_size >= ID3v2_HEADER_SIZE) {
+ bytes = read_from_url(pls, buf + *len, ID3v2_HEADER_SIZE - *len, READ_COMPLETE);
+ if (bytes > 0) {
+
+ if (bytes == ID3v2_HEADER_SIZE - *len)
+ /* no EOF yet, so fill the caller buffer again after
+ * we have stripped the ID3 tags */
+ fill_buf = 1;
+
+ *len += bytes;
+
+ } else if (*len <= 0) {
+ /* error/EOF */
+ *len = bytes;
+ fill_buf = 0;
+ }
+ }
+
+ if (*len < ID3v2_HEADER_SIZE)
+ break;
+
+ if (ff_id3v2_match(buf, ID3v2_DEFAULT_MAGIC)) {
+ struct segment *seg = pls->segments[pls->cur_seq_no - pls->start_seq_no];
+ int64_t maxsize = seg->size >= 0 ? seg->size : 1024*1024;
+ int taglen = ff_id3v2_tag_len(buf);
+ int tag_got_bytes = FFMIN(taglen, *len);
+ int remaining = taglen - tag_got_bytes;
+
+ if (taglen > maxsize) {
+ av_log(pls->ctx, AV_LOG_ERROR, "Too large HLS ID3 tag (%d > %"PRId64" bytes)\n",
+ taglen, maxsize);
+ break;
+ }
+
+ /*
+ * Copy the id3 tag to our temporary id3 buffer.
+ * We could read a small id3 tag directly without memcpy, but
+ * we would still need to copy the large tags, and handling
+ * both of those cases together with the possibility for multiple
+ * tags would make the handling a bit complex.
+ */
+ pls->id3_buf = av_fast_realloc(pls->id3_buf, &pls->id3_buf_size, id3_buf_pos + taglen);
+ if (!pls->id3_buf)
+ break;
+ memcpy(pls->id3_buf + id3_buf_pos, buf, tag_got_bytes);
+ id3_buf_pos += tag_got_bytes;
+
+ /* strip the intercepted bytes */
+ *len -= tag_got_bytes;
+ memmove(buf, buf + tag_got_bytes, *len);
+ av_log(pls->ctx, AV_LOG_DEBUG, "Stripped %d HLS ID3 bytes\n", tag_got_bytes);
+
+ if (remaining > 0) {
+ /* read the rest of the tag in */
+ if (read_from_url(pls, pls->id3_buf + id3_buf_pos, remaining, READ_COMPLETE) != remaining)
+ break;
+ id3_buf_pos += remaining;
+ av_log(pls->ctx, AV_LOG_DEBUG, "Stripped additional %d HLS ID3 bytes\n", remaining);
+ }
+
+ } else {
+ /* no more ID3 tags */
+ break;
+ }
+ }
+
+ /* re-fill buffer for the caller unless EOF */
+ if (*len >= 0 && (fill_buf || *len == 0)) {
+ bytes = read_from_url(pls, buf + *len, buf_size - *len, READ_NORMAL);
+
+ /* ignore error if we already had some data */
+ if (bytes >= 0)
+ *len += bytes;
+ else if (*len == 0)
+ *len = bytes;
+ }
+
+ if (pls->id3_buf) {
+ /* Now parse all the ID3 tags */
+ AVIOContext id3ioctx;
+ ffio_init_context(&id3ioctx, pls->id3_buf, id3_buf_pos, 0, NULL, NULL, NULL, NULL);
+ handle_id3(&id3ioctx, pls);
+ }
+
+ if (pls->is_id3_timestamped == -1)
+ pls->is_id3_timestamped = (pls->id3_mpegts_timestamp != AV_NOPTS_VALUE);
+}
+
+static int open_input(HLSContext *c, struct playlist *pls)
{
- struct segment *seg = var->segments[var->cur_seq_no - var->start_seq_no];
+ AVDictionary *opts = NULL;
+ AVDictionary *opts2 = NULL;
+ int ret;
+ struct segment *seg = pls->segments[pls->cur_seq_no - pls->start_seq_no];
+
+ // broker prior HTTP options that should be consistent across requests
+ av_dict_set(&opts, "user-agent", c->user_agent, 0);
+ av_dict_set(&opts, "cookies", c->cookies, 0);
+ av_dict_set(&opts, "headers", c->headers, 0);
+ av_dict_set(&opts, "seekable", "0", 0);
+
+ // Same opts for key request (ffurl_open mutilates the opts so it cannot be used twice)
+ av_dict_copy(&opts2, opts, 0);
+
+ if (seg->size >= 0) {
+ /* try to restrict the HTTP request to the part we want
+ * (if this is in fact a HTTP request) */
+ av_dict_set_int(&opts, "offset", seg->url_offset, 0);
+ av_dict_set_int(&opts, "end_offset", seg->url_offset + seg->size, 0);
+ }
+
+ av_log(pls->parent, AV_LOG_VERBOSE, "HLS request for url '%s', offset %"PRId64", playlist %d\n",
+ seg->url, seg->url_offset, pls->index);
+
if (seg->key_type == KEY_NONE) {
- return ffurl_open(&var->input, seg->url, AVIO_FLAG_READ,
- &var->parent->interrupt_callback, NULL);
+ ret = ffurl_open(&pls->input, seg->url, AVIO_FLAG_READ,
+ &pls->parent->interrupt_callback, &opts);
+
} else if (seg->key_type == KEY_AES_128) {
char iv[33], key[33], url[MAX_URL_SIZE];
- int ret;
- if (strcmp(seg->key, var->key_url)) {
+ if (strcmp(seg->key, pls->key_url)) {
URLContext *uc;
if (ffurl_open(&uc, seg->key, AVIO_FLAG_READ,
- &var->parent->interrupt_callback, NULL) == 0) {
- if (ffurl_read_complete(uc, var->key, sizeof(var->key))
- != sizeof(var->key)) {
+ &pls->parent->interrupt_callback, &opts2) == 0) {
+ if (ffurl_read_complete(uc, pls->key, sizeof(pls->key))
+ != sizeof(pls->key)) {
av_log(NULL, AV_LOG_ERROR, "Unable to read key file %s\n",
seg->key);
}
@@ -352,49 +946,101 @@ static int open_input(struct variant *var)
av_log(NULL, AV_LOG_ERROR, "Unable to open key file %s\n",
seg->key);
}
- av_strlcpy(var->key_url, seg->key, sizeof(var->key_url));
+ av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url));
}
ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0);
- ff_data_to_hex(key, var->key, sizeof(var->key), 0);
+ ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
iv[32] = key[32] = '\0';
if (strstr(seg->url, "://"))
snprintf(url, sizeof(url), "crypto+%s", seg->url);
else
snprintf(url, sizeof(url), "crypto:%s", seg->url);
- if ((ret = ffurl_alloc(&var->input, url, AVIO_FLAG_READ,
- &var->parent->interrupt_callback)) < 0)
- return ret;
- av_opt_set(var->input->priv_data, "key", key, 0);
- av_opt_set(var->input->priv_data, "iv", iv, 0);
- if ((ret = ffurl_connect(var->input, NULL)) < 0) {
- ffurl_close(var->input);
- var->input = NULL;
- return ret;
+ if ((ret = ffurl_alloc(&pls->input, url, AVIO_FLAG_READ,
+ &pls->parent->interrupt_callback)) < 0)
+ goto cleanup;
+ av_opt_set(pls->input->priv_data, "key", key, 0);
+ av_opt_set(pls->input->priv_data, "iv", iv, 0);
+
+ if ((ret = ffurl_connect(pls->input, &opts)) < 0) {
+ ffurl_close(pls->input);
+ pls->input = NULL;
+ goto cleanup;
}
- return 0;
+ ret = 0;
}
- return AVERROR(ENOSYS);
+ else
+ ret = AVERROR(ENOSYS);
+
+ /* Seek to the requested position. If this was a HTTP request, the offset
+ * should already be where want it to, but this allows e.g. local testing
+ * without a HTTP server. */
+ if (ret == 0 && seg->key_type == KEY_NONE) {
+ int seekret = ffurl_seek(pls->input, seg->url_offset, SEEK_SET);
+ if (seekret < 0) {
+ av_log(pls->parent, AV_LOG_ERROR, "Unable to seek to offset %"PRId64" of HLS segment '%s'\n", seg->url_offset, seg->url);
+ ret = seekret;
+ ffurl_close(pls->input);
+ pls->input = NULL;
+ }
+ }
+
+cleanup:
+ av_dict_free(&opts);
+ av_dict_free(&opts2);
+ pls->cur_seg_offset = 0;
+ return ret;
+}
+
+static int64_t default_reload_interval(struct playlist *pls)
+{
+ return pls->n_segments > 0 ?
+ pls->segments[pls->n_segments - 1]->duration :
+ pls->target_duration;
}
static int read_data(void *opaque, uint8_t *buf, int buf_size)
{
- struct variant *v = opaque;
+ struct playlist *v = opaque;
HLSContext *c = v->parent->priv_data;
int ret, i;
+ int just_opened = 0;
restart:
+ if (!v->needed)
+ return AVERROR_EOF;
+
if (!v->input) {
+ int64_t reload_interval;
+
+ /* Check that the playlist is still needed before opening a new
+ * segment. */
+ if (v->ctx && v->ctx->nb_streams &&
+ v->parent->nb_streams >= v->stream_offset + v->ctx->nb_streams) {
+ v->needed = 0;
+ for (i = v->stream_offset; i < v->stream_offset + v->ctx->nb_streams;
+ i++) {
+ if (v->parent->streams[i]->discard < AVDISCARD_ALL)
+ v->needed = 1;
+ }
+ }
+ if (!v->needed) {
+ av_log(v->parent, AV_LOG_INFO, "No longer receiving playlist %d\n",
+ v->index);
+ return AVERROR_EOF;
+ }
+
/* If this is a live stream and the reload interval has elapsed since
- * the last playlist reload, reload the variant playlists now. */
- int64_t reload_interval = v->n_segments > 0 ?
- v->segments[v->n_segments - 1]->duration :
- v->target_duration;
+ * the last playlist reload, reload the playlists now. */
+ reload_interval = default_reload_interval(v);
reload:
if (!v->finished &&
av_gettime_relative() - v->last_load_time >= reload_interval) {
- if ((ret = parse_playlist(c, v->url, v, NULL)) < 0)
+ if ((ret = parse_playlist(c, v->url, v, NULL)) < 0) {
+ av_log(v->parent, AV_LOG_WARNING, "Failed to reload playlist %d\n",
+ v->index);
return ret;
+ }
/* If we need to reload the playlist again below (if
* there's still no more segments), switch to a reload
* interval of half the target duration. */
@@ -418,44 +1064,205 @@ reload:
goto reload;
}
- ret = open_input(v);
- if (ret < 0)
+ ret = open_input(c, v);
+ if (ret < 0) {
+ av_log(v->parent, AV_LOG_WARNING, "Failed to open segment of playlist %d\n",
+ v->index);
return ret;
+ }
+ just_opened = 1;
}
- ret = ffurl_read(v->input, buf, buf_size);
- if (ret > 0)
+
+ ret = read_from_url(v, buf, buf_size, READ_NORMAL);
+ if (ret > 0) {
+ if (just_opened && v->is_id3_timestamped != 0) {
+ /* Intercept ID3 tags here, elementary audio streams are required
+ * to convey timestamps using them in the beginning of each segment. */
+ intercept_id3(v, buf, buf_size, &ret);
+ }
+
return ret;
+ }
ffurl_close(v->input);
v->input = NULL;
v->cur_seq_no++;
- c->end_of_segment = 1;
c->cur_seq_no = v->cur_seq_no;
- if (v->ctx && v->ctx->nb_streams &&
- v->parent->nb_streams >= v->stream_offset + v->ctx->nb_streams) {
- v->needed = 0;
- for (i = v->stream_offset; i < v->stream_offset + v->ctx->nb_streams;
- i++) {
- if (v->parent->streams[i]->discard < AVDISCARD_ALL)
- v->needed = 1;
+ goto restart;
+}
+
+static int playlist_in_multiple_variants(HLSContext *c, struct playlist *pls)
+{
+ int variant_count = 0;
+ int i, j;
+
+ for (i = 0; i < c->n_variants && variant_count < 2; i++) {
+ struct variant *v = c->variants[i];
+
+ for (j = 0; j < v->n_playlists; j++) {
+ if (v->playlists[j] == pls) {
+ variant_count++;
+ break;
+ }
}
}
- if (!v->needed) {
- av_log(v->parent, AV_LOG_INFO, "No longer receiving variant %d\n",
- v->index);
- return AVERROR_EOF;
+
+ return variant_count >= 2;
+}
+
+static void add_renditions_to_variant(HLSContext *c, struct variant *var,
+ enum AVMediaType type, const char *group_id)
+{
+ int i;
+
+ for (i = 0; i < c->n_renditions; i++) {
+ struct rendition *rend = c->renditions[i];
+
+ if (rend->type == type && !strcmp(rend->group_id, group_id)) {
+
+ if (rend->playlist)
+ /* rendition is an external playlist
+ * => add the playlist to the variant */
+ dynarray_add(&var->playlists, &var->n_playlists, rend->playlist);
+ else
+ /* rendition is part of the variant main Media Playlist
+ * => add the rendition to the main Media Playlist */
+ dynarray_add(&var->playlists[0]->renditions,
+ &var->playlists[0]->n_renditions,
+ rend);
+ }
}
- goto restart;
+}
+
+static void add_metadata_from_renditions(AVFormatContext *s, struct playlist *pls,
+ enum AVMediaType type)
+{
+ int rend_idx = 0;
+ int i;
+
+ for (i = 0; i < pls->ctx->nb_streams; i++) {
+ AVStream *st = s->streams[pls->stream_offset + i];
+
+ if (st->codec->codec_type != type)
+ continue;
+
+ for (; rend_idx < pls->n_renditions; rend_idx++) {
+ struct rendition *rend = pls->renditions[rend_idx];
+
+ if (rend->type != type)
+ continue;
+
+ if (rend->language[0])
+ av_dict_set(&st->metadata, "language", rend->language, 0);
+ if (rend->name[0])
+ av_dict_set(&st->metadata, "comment", rend->name, 0);
+
+ st->disposition |= rend->disposition;
+ }
+ if (rend_idx >=pls->n_renditions)
+ break;
+ }
+}
+
+/* if timestamp was in valid range: returns 1 and sets seq_no
+ * if not: returns 0 and sets seq_no to closest segment */
+static int find_timestamp_in_playlist(HLSContext *c, struct playlist *pls,
+ int64_t timestamp, int *seq_no)
+{
+ int i;
+ int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ?
+ 0 : c->first_timestamp;
+
+ if (timestamp < pos) {
+ *seq_no = pls->start_seq_no;
+ return 0;
+ }
+
+ for (i = 0; i < pls->n_segments; i++) {
+ int64_t diff = pos + pls->segments[i]->duration - timestamp;
+ if (diff > 0) {
+ *seq_no = pls->start_seq_no + i;
+ return 1;
+ }
+ pos += pls->segments[i]->duration;
+ }
+
+ *seq_no = pls->start_seq_no + pls->n_segments - 1;
+
+ return 0;
+}
+
+static int select_cur_seq_no(HLSContext *c, struct playlist *pls)
+{
+ int seq_no;
+
+ if (!pls->finished && !c->first_packet &&
+ av_gettime_relative() - pls->last_load_time >= default_reload_interval(pls))
+ /* reload the playlist since it was suspended */
+ parse_playlist(c, pls->url, pls, NULL);
+
+ /* If playback is already in progress (we are just selecting a new
+ * playlist) and this is a complete file, find the matching segment
+ * by counting durations. */
+ if (pls->finished && c->cur_timestamp != AV_NOPTS_VALUE) {
+ find_timestamp_in_playlist(c, pls, c->cur_timestamp, &seq_no);
+ return seq_no;
+ }
+
+ if (!pls->finished) {
+ if (!c->first_packet && /* we are doing a segment selection during playback */
+ c->cur_seq_no >= pls->start_seq_no &&
+ c->cur_seq_no < pls->start_seq_no + pls->n_segments)
+ /* While spec 3.4.3 says that we cannot assume anything about the
+ * content at the same sequence number on different playlists,
+ * in practice this seems to work and doing it otherwise would
+ * require us to download a segment to inspect its timestamps. */
+ return c->cur_seq_no;
+
+ /* If this is a live stream with more than 3 segments, start at the
+ * third last segment. */
+ if (pls->n_segments > 3)
+ return pls->start_seq_no + pls->n_segments - 3;
+ }
+
+ /* Otherwise just start on the first segment. */
+ return pls->start_seq_no;
}
static int hls_read_header(AVFormatContext *s)
{
+ URLContext *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb->opaque;
HLSContext *c = s->priv_data;
int ret = 0, i, j, stream_offset = 0;
c->interrupt_callback = &s->interrupt_callback;
+ c->first_packet = 1;
+ c->first_timestamp = AV_NOPTS_VALUE;
+ c->cur_timestamp = AV_NOPTS_VALUE;
+
+ // if the URL context is good, read important options we must broker later
+ if (u && u->prot->priv_data_class) {
+ // get the previous user agent & set back to null if string size is zero
+ av_freep(&c->user_agent);
+ av_opt_get(u->priv_data, "user-agent", 0, (uint8_t**)&(c->user_agent));
+ if (c->user_agent && !strlen(c->user_agent))
+ av_freep(&c->user_agent);
+
+ // get the previous cookies & set back to null if string size is zero
+ av_freep(&c->cookies);
+ av_opt_get(u->priv_data, "cookies", 0, (uint8_t**)&(c->cookies));
+ if (c->cookies && !strlen(c->cookies))
+ av_freep(&c->cookies);
+
+ // get the previous headers & set back to null if string size is zero
+ av_freep(&c->headers);
+ av_opt_get(u->priv_data, "headers", 0, (uint8_t**)&(c->headers));
+ if (c->headers && !strlen(c->headers))
+ av_freep(&c->headers);
+ }
+
if ((ret = parse_playlist(c, s->filename, NULL, s->pb)) < 0)
goto fail;
@@ -464,17 +1271,17 @@ static int hls_read_header(AVFormatContext *s)
ret = AVERROR_EOF;
goto fail;
}
- /* If the playlist only contained variants, parse each individual
- * variant playlist. */
- if (c->n_variants > 1 || c->variants[0]->n_segments == 0) {
- for (i = 0; i < c->n_variants; i++) {
- struct variant *v = c->variants[i];
- if ((ret = parse_playlist(c, v->url, v, NULL)) < 0)
+ /* If the playlist only contained playlists (Master Playlist),
+ * parse each individual playlist. */
+ if (c->n_playlists > 1 || c->playlists[0]->n_segments == 0) {
+ for (i = 0; i < c->n_playlists; i++) {
+ struct playlist *pls = c->playlists[i];
+ if ((ret = parse_playlist(c, pls->url, pls, NULL)) < 0)
goto fail;
}
}
- if (c->variants[0]->n_segments == 0) {
+ if (c->variants[0]->playlists[0]->n_segments == 0) {
av_log(NULL, AV_LOG_WARNING, "Empty playlist\n");
ret = AVERROR_EOF;
goto fail;
@@ -482,96 +1289,141 @@ static int hls_read_header(AVFormatContext *s)
/* If this isn't a live stream, calculate the total duration of the
* stream. */
- if (c->variants[0]->finished) {
+ if (c->variants[0]->playlists[0]->finished) {
int64_t duration = 0;
- for (i = 0; i < c->variants[0]->n_segments; i++)
- duration += c->variants[0]->segments[i]->duration;
+ for (i = 0; i < c->variants[0]->playlists[0]->n_segments; i++)
+ duration += c->variants[0]->playlists[0]->segments[i]->duration;
s->duration = duration;
}
- /* Open the demuxer for each variant */
+ /* Associate renditions with variants */
for (i = 0; i < c->n_variants; i++) {
- struct variant *v = c->variants[i];
+ struct variant *var = c->variants[i];
+
+ if (var->audio_group[0])
+ add_renditions_to_variant(c, var, AVMEDIA_TYPE_AUDIO, var->audio_group);
+ if (var->video_group[0])
+ add_renditions_to_variant(c, var, AVMEDIA_TYPE_VIDEO, var->video_group);
+ if (var->subtitles_group[0])
+ add_renditions_to_variant(c, var, AVMEDIA_TYPE_SUBTITLE, var->subtitles_group);
+ }
+
+ /* Open the demuxer for each playlist */
+ for (i = 0; i < c->n_playlists; i++) {
+ struct playlist *pls = c->playlists[i];
AVInputFormat *in_fmt = NULL;
- char bitrate_str[20];
- AVProgram *program;
- if (v->n_segments == 0)
+ if (pls->n_segments == 0)
continue;
- if (!(v->ctx = avformat_alloc_context())) {
+ if (!(pls->ctx = avformat_alloc_context())) {
ret = AVERROR(ENOMEM);
goto fail;
}
- v->index = i;
- v->needed = 1;
- v->parent = s;
-
- /* If this is a live stream with more than 3 segments, start at the
- * third last segment. */
- v->cur_seq_no = v->start_seq_no;
- if (!v->finished && v->n_segments > 3)
- v->cur_seq_no = v->start_seq_no + v->n_segments - 3;
+ pls->index = i;
+ pls->needed = 1;
+ pls->parent = s;
+ pls->cur_seq_no = select_cur_seq_no(c, pls);
- v->read_buffer = av_malloc(INITIAL_BUFFER_SIZE);
- ffio_init_context(&v->pb, v->read_buffer, INITIAL_BUFFER_SIZE, 0, v,
+ pls->read_buffer = av_malloc(INITIAL_BUFFER_SIZE);
+ ffio_init_context(&pls->pb, pls->read_buffer, INITIAL_BUFFER_SIZE, 0, pls,
read_data, NULL, NULL);
- v->pb.seekable = 0;
- ret = av_probe_input_buffer(&v->pb, &in_fmt, v->segments[0]->url,
+ pls->pb.seekable = 0;
+ ret = av_probe_input_buffer(&pls->pb, &in_fmt, pls->segments[0]->url,
NULL, 0, 0);
if (ret < 0) {
/* Free the ctx - it isn't initialized properly at this point,
* so avformat_close_input shouldn't be called. If
* avformat_open_input fails below, it frees and zeros the
* context, so it doesn't need any special treatment like this. */
- avformat_free_context(v->ctx);
- v->ctx = NULL;
+ av_log(s, AV_LOG_ERROR, "Error when loading first segment '%s'\n", pls->segments[0]->url);
+ avformat_free_context(pls->ctx);
+ pls->ctx = NULL;
goto fail;
}
- v->ctx->pb = &v->pb;
- v->stream_offset = stream_offset;
- ret = avformat_open_input(&v->ctx, v->segments[0]->url, in_fmt, NULL);
- if (ret < 0)
+ pls->ctx->pb = &pls->pb;
+ pls->stream_offset = stream_offset;
+
+ if ((ret = ff_copy_whitelists(pls->ctx, s)) < 0)
goto fail;
- v->ctx->ctx_flags &= ~AVFMTCTX_NOHEADER;
- ret = avformat_find_stream_info(v->ctx, NULL);
+ ret = avformat_open_input(&pls->ctx, pls->segments[0]->url, in_fmt, NULL);
if (ret < 0)
goto fail;
- snprintf(bitrate_str, sizeof(bitrate_str), "%d", v->bandwidth);
- program = av_new_program(s, i);
- if (!program)
+ if (pls->id3_deferred_extra && pls->ctx->nb_streams == 1) {
+ ff_id3v2_parse_apic(pls->ctx, &pls->id3_deferred_extra);
+ avformat_queue_attached_pictures(pls->ctx);
+ ff_id3v2_free_extra_meta(&pls->id3_deferred_extra);
+ pls->id3_deferred_extra = NULL;
+ }
+
+ pls->ctx->ctx_flags &= ~AVFMTCTX_NOHEADER;
+ ret = avformat_find_stream_info(pls->ctx, NULL);
+ if (ret < 0)
goto fail;
- av_dict_set(&program->metadata, "variant_bitrate", bitrate_str, 0);
- /* Create new AVStreams for each stream in this variant */
- for (j = 0; j < v->ctx->nb_streams; j++) {
+ if (pls->is_id3_timestamped == -1)
+ av_log(s, AV_LOG_WARNING, "No expected HTTP requests have been made\n");
+
+ /* Create new AVStreams for each stream in this playlist */
+ for (j = 0; j < pls->ctx->nb_streams; j++) {
AVStream *st = avformat_new_stream(s, NULL);
- AVStream *ist = v->ctx->streams[j];
+ AVStream *ist = pls->ctx->streams[j];
if (!st) {
ret = AVERROR(ENOMEM);
goto fail;
}
- ff_program_add_stream_index(s, i, stream_offset + j);
st->id = i;
- avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num, ist->time_base.den);
- avcodec_copy_context(st->codec, v->ctx->streams[j]->codec);
- if (v->bandwidth)
- av_dict_set(&st->metadata, "variant_bitrate", bitrate_str,
- 0);
+
+ avcodec_copy_context(st->codec, pls->ctx->streams[j]->codec);
+
+ if (pls->is_id3_timestamped) /* custom timestamps via id3 */
+ avpriv_set_pts_info(st, 33, 1, MPEG_TIME_BASE);
+ else
+ avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num, ist->time_base.den);
}
- stream_offset += v->ctx->nb_streams;
+
+ add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_AUDIO);
+ add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_VIDEO);
+ add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_SUBTITLE);
+
+ stream_offset += pls->ctx->nb_streams;
}
- c->first_packet = 1;
- c->first_timestamp = AV_NOPTS_VALUE;
- c->seek_timestamp = AV_NOPTS_VALUE;
+ /* Create a program for each variant */
+ for (i = 0; i < c->n_variants; i++) {
+ struct variant *v = c->variants[i];
+ AVProgram *program;
+
+ program = av_new_program(s, i);
+ if (!program)
+ goto fail;
+ av_dict_set_int(&program->metadata, "variant_bitrate", v->bandwidth, 0);
+
+ for (j = 0; j < v->n_playlists; j++) {
+ struct playlist *pls = v->playlists[j];
+ int is_shared = playlist_in_multiple_variants(c, pls);
+ int k;
+
+ for (k = 0; k < pls->ctx->nb_streams; k++) {
+ struct AVStream *st = s->streams[pls->stream_offset + k];
+
+ ff_program_add_stream_index(s, i, pls->stream_offset + k);
+
+ /* Set variant_bitrate for streams unique to this variant */
+ if (!is_shared && v->bandwidth)
+ av_dict_set_int(&st->metadata, "variant_bitrate", v->bandwidth, 0);
+ }
+ }
+ }
return 0;
fail:
+ free_playlist_list(c);
free_variant_list(c);
+ free_rendition_list(c);
return ret;
}
@@ -581,127 +1433,171 @@ static int recheck_discard_flags(AVFormatContext *s, int first)
int i, changed = 0;
/* Check if any new streams are needed */
- for (i = 0; i < c->n_variants; i++)
- c->variants[i]->cur_needed = 0;;
+ for (i = 0; i < c->n_playlists; i++)
+ c->playlists[i]->cur_needed = 0;
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
- struct variant *var = c->variants[s->streams[i]->id];
+ struct playlist *pls = c->playlists[s->streams[i]->id];
if (st->discard < AVDISCARD_ALL)
- var->cur_needed = 1;
+ pls->cur_needed = 1;
}
- for (i = 0; i < c->n_variants; i++) {
- struct variant *v = c->variants[i];
- if (v->cur_needed && !v->needed) {
- v->needed = 1;
+ for (i = 0; i < c->n_playlists; i++) {
+ struct playlist *pls = c->playlists[i];
+ if (pls->cur_needed && !pls->needed) {
+ pls->needed = 1;
changed = 1;
- v->cur_seq_no = c->cur_seq_no;
- v->pb.eof_reached = 0;
- av_log(s, AV_LOG_INFO, "Now receiving variant %d\n", i);
- } else if (first && !v->cur_needed && v->needed) {
- if (v->input)
- ffurl_close(v->input);
- v->input = NULL;
- v->needed = 0;
+ pls->cur_seq_no = select_cur_seq_no(c, pls);
+ pls->pb.eof_reached = 0;
+ if (c->cur_timestamp != AV_NOPTS_VALUE) {
+ /* catch up */
+ pls->seek_timestamp = c->cur_timestamp;
+ pls->seek_flags = AVSEEK_FLAG_ANY;
+ pls->seek_stream_index = -1;
+ }
+ av_log(s, AV_LOG_INFO, "Now receiving playlist %d, segment %d\n", i, pls->cur_seq_no);
+ } else if (first && !pls->cur_needed && pls->needed) {
+ if (pls->input)
+ ffurl_close(pls->input);
+ pls->input = NULL;
+ pls->needed = 0;
changed = 1;
- av_log(s, AV_LOG_INFO, "No longer receiving variant %d\n", i);
+ av_log(s, AV_LOG_INFO, "No longer receiving playlist %d\n", i);
}
}
return changed;
}
+static void fill_timing_for_id3_timestamped_stream(struct playlist *pls)
+{
+ if (pls->id3_offset >= 0) {
+ pls->pkt.dts = pls->id3_mpegts_timestamp +
+ av_rescale_q(pls->id3_offset,
+ pls->ctx->streams[pls->pkt.stream_index]->time_base,
+ MPEG_TIME_BASE_Q);
+ if (pls->pkt.duration)
+ pls->id3_offset += pls->pkt.duration;
+ else
+ pls->id3_offset = -1;
+ } else {
+ /* there have been packets with unknown duration
+ * since the last id3 tag, should not normally happen */
+ pls->pkt.dts = AV_NOPTS_VALUE;
+ }
+
+ if (pls->pkt.duration)
+ pls->pkt.duration = av_rescale_q(pls->pkt.duration,
+ pls->ctx->streams[pls->pkt.stream_index]->time_base,
+ MPEG_TIME_BASE_Q);
+
+ pls->pkt.pts = AV_NOPTS_VALUE;
+}
+
+static AVRational get_timebase(struct playlist *pls)
+{
+ if (pls->is_id3_timestamped)
+ return MPEG_TIME_BASE_Q;
+
+ return pls->ctx->streams[pls->pkt.stream_index]->time_base;
+}
+
+static int compare_ts_with_wrapdetect(int64_t ts_a, struct playlist *pls_a,
+ int64_t ts_b, struct playlist *pls_b)
+{
+ int64_t scaled_ts_a = av_rescale_q(ts_a, get_timebase(pls_a), MPEG_TIME_BASE_Q);
+ int64_t scaled_ts_b = av_rescale_q(ts_b, get_timebase(pls_b), MPEG_TIME_BASE_Q);
+
+ return av_compare_mod(scaled_ts_a, scaled_ts_b, 1LL << 33);
+}
+
static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
{
HLSContext *c = s->priv_data;
- int ret, i, minvariant = -1;
+ int ret, i, minplaylist = -1;
- if (c->first_packet) {
- recheck_discard_flags(s, 1);
- c->first_packet = 0;
- }
+ recheck_discard_flags(s, c->first_packet);
-start:
- c->end_of_segment = 0;
- for (i = 0; i < c->n_variants; i++) {
- struct variant *var = c->variants[i];
- /* Make sure we've got one buffered packet from each open variant
+ for (i = 0; i < c->n_playlists; i++) {
+ struct playlist *pls = c->playlists[i];
+ /* Make sure we've got one buffered packet from each open playlist
* stream */
- if (var->needed && !var->pkt.data) {
+ if (pls->needed && !pls->pkt.data) {
while (1) {
int64_t ts_diff;
- AVStream *st;
- ret = av_read_frame(var->ctx, &var->pkt);
+ AVRational tb;
+ ret = av_read_frame(pls->ctx, &pls->pkt);
if (ret < 0) {
- if (!var->pb.eof_reached)
+ if (!avio_feof(&pls->pb) && ret != AVERROR_EOF)
return ret;
- reset_packet(&var->pkt);
+ reset_packet(&pls->pkt);
break;
} else {
+ /* stream_index check prevents matching picture attachments etc. */
+ if (pls->is_id3_timestamped && pls->pkt.stream_index == 0) {
+ /* audio elementary streams are id3 timestamped */
+ fill_timing_for_id3_timestamped_stream(pls);
+ }
+
if (c->first_timestamp == AV_NOPTS_VALUE &&
- var->pkt.dts != AV_NOPTS_VALUE)
- c->first_timestamp = av_rescale_q(var->pkt.dts,
- var->ctx->streams[var->pkt.stream_index]->time_base,
- AV_TIME_BASE_Q);
+ pls->pkt.dts != AV_NOPTS_VALUE)
+ c->first_timestamp = av_rescale_q(pls->pkt.dts,
+ get_timebase(pls), AV_TIME_BASE_Q);
}
- if (c->seek_timestamp == AV_NOPTS_VALUE)
+ if (pls->seek_timestamp == AV_NOPTS_VALUE)
break;
- if (var->pkt.dts == AV_NOPTS_VALUE) {
- c->seek_timestamp = AV_NOPTS_VALUE;
- break;
- }
+ if (pls->seek_stream_index < 0 ||
+ pls->seek_stream_index == pls->pkt.stream_index) {
- st = var->ctx->streams[var->pkt.stream_index];
- ts_diff = av_rescale_rnd(var->pkt.dts, AV_TIME_BASE,
- st->time_base.den, AV_ROUND_DOWN) -
- c->seek_timestamp;
- if (ts_diff >= 0 && (c->seek_flags & AVSEEK_FLAG_ANY ||
- var->pkt.flags & AV_PKT_FLAG_KEY)) {
- c->seek_timestamp = AV_NOPTS_VALUE;
- break;
+ if (pls->pkt.dts == AV_NOPTS_VALUE) {
+ pls->seek_timestamp = AV_NOPTS_VALUE;
+ break;
+ }
+
+ tb = get_timebase(pls);
+ ts_diff = av_rescale_rnd(pls->pkt.dts, AV_TIME_BASE,
+ tb.den, AV_ROUND_DOWN) -
+ pls->seek_timestamp;
+ if (ts_diff >= 0 && (pls->seek_flags & AVSEEK_FLAG_ANY ||
+ pls->pkt.flags & AV_PKT_FLAG_KEY)) {
+ pls->seek_timestamp = AV_NOPTS_VALUE;
+ break;
+ }
}
- av_free_packet(&var->pkt);
- reset_packet(&var->pkt);
+ av_free_packet(&pls->pkt);
+ reset_packet(&pls->pkt);
}
}
- /* Check if this stream still is on an earlier segment number, or
- * has the packet with the lowest dts */
- if (var->pkt.data) {
- struct variant *minvar = minvariant < 0 ?
- NULL : c->variants[minvariant];
- if (minvariant < 0 || var->cur_seq_no < minvar->cur_seq_no) {
- minvariant = i;
- } else if (var->cur_seq_no == minvar->cur_seq_no) {
- int64_t dts = var->pkt.dts;
- int64_t mindts = minvar->pkt.dts;
- AVStream *st = var->ctx->streams[var->pkt.stream_index];
- AVStream *minst = minvar->ctx->streams[minvar->pkt.stream_index];
-
- if (dts == AV_NOPTS_VALUE) {
- minvariant = i;
- } else if (mindts != AV_NOPTS_VALUE) {
- if (st->start_time != AV_NOPTS_VALUE)
- dts -= st->start_time;
- if (minst->start_time != AV_NOPTS_VALUE)
- mindts -= minst->start_time;
-
- if (av_compare_ts(dts, st->time_base,
- mindts, minst->time_base) < 0)
- minvariant = i;
- }
+ /* Check if this stream has the packet with the lowest dts */
+ if (pls->pkt.data) {
+ struct playlist *minpls = minplaylist < 0 ?
+ NULL : c->playlists[minplaylist];
+ if (minplaylist < 0) {
+ minplaylist = i;
+ } else {
+ int64_t dts = pls->pkt.dts;
+ int64_t mindts = minpls->pkt.dts;
+
+ if (dts == AV_NOPTS_VALUE ||
+ (mindts != AV_NOPTS_VALUE && compare_ts_with_wrapdetect(dts, pls, mindts, minpls) < 0))
+ minplaylist = i;
}
}
}
- if (c->end_of_segment) {
- if (recheck_discard_flags(s, 0))
- goto start;
- }
+
/* If we got a packet, return it */
- if (minvariant >= 0) {
- *pkt = c->variants[minvariant]->pkt;
- pkt->stream_index += c->variants[minvariant]->stream_offset;
- reset_packet(&c->variants[minvariant]->pkt);
+ if (minplaylist >= 0) {
+ struct playlist *pls = c->playlists[minplaylist];
+ *pkt = pls->pkt;
+ pkt->stream_index += pls->stream_offset;
+ reset_packet(&c->playlists[minplaylist]->pkt);
+
+ if (pkt->dts != AV_NOPTS_VALUE)
+ c->cur_timestamp = av_rescale_q(pkt->dts,
+ pls->ctx->streams[pls->pkt.stream_index]->time_base,
+ AV_TIME_BASE_Q);
+
return 0;
}
return AVERROR_EOF;
@@ -711,7 +1607,9 @@ static int hls_close(AVFormatContext *s)
{
HLSContext *c = s->priv_data;
+ free_playlist_list(c);
free_variant_list(c);
+ free_rendition_list(c);
return 0;
}
@@ -719,58 +1617,80 @@ static int hls_read_seek(AVFormatContext *s, int stream_index,
int64_t timestamp, int flags)
{
HLSContext *c = s->priv_data;
- int i, j, ret;
+ struct playlist *seek_pls = NULL;
+ int i, seq_no;
+ int64_t first_timestamp, seek_timestamp, duration;
- if ((flags & AVSEEK_FLAG_BYTE) || !c->variants[0]->finished)
+ if ((flags & AVSEEK_FLAG_BYTE) ||
+ !(c->variants[0]->playlists[0]->finished || c->variants[0]->playlists[0]->type == PLS_TYPE_EVENT))
return AVERROR(ENOSYS);
- c->seek_flags = flags;
- c->seek_timestamp = stream_index < 0 ? timestamp :
- av_rescale_rnd(timestamp, AV_TIME_BASE,
- s->streams[stream_index]->time_base.den,
- flags & AVSEEK_FLAG_BACKWARD ?
- AV_ROUND_DOWN : AV_ROUND_UP);
- timestamp = av_rescale_rnd(timestamp, AV_TIME_BASE, stream_index >= 0 ?
- s->streams[stream_index]->time_base.den :
- AV_TIME_BASE, flags & AVSEEK_FLAG_BACKWARD ?
- AV_ROUND_DOWN : AV_ROUND_UP);
- if (s->duration < c->seek_timestamp) {
- c->seek_timestamp = AV_NOPTS_VALUE;
+ first_timestamp = c->first_timestamp == AV_NOPTS_VALUE ?
+ 0 : c->first_timestamp;
+
+ seek_timestamp = av_rescale_rnd(timestamp, AV_TIME_BASE,
+ s->streams[stream_index]->time_base.den,
+ flags & AVSEEK_FLAG_BACKWARD ?
+ AV_ROUND_DOWN : AV_ROUND_UP);
+
+ duration = s->duration == AV_NOPTS_VALUE ?
+ 0 : s->duration;
+
+ if (0 < duration && duration < seek_timestamp - first_timestamp)
return AVERROR(EIO);
+
+ /* find the playlist with the specified stream */
+ for (i = 0; i < c->n_playlists; i++) {
+ struct playlist *pls = c->playlists[i];
+ if (stream_index >= pls->stream_offset &&
+ stream_index - pls->stream_offset < pls->ctx->nb_streams) {
+ seek_pls = pls;
+ break;
+ }
}
+ /* check if the timestamp is valid for the playlist with the
+ * specified stream index */
+ if (!seek_pls || !find_timestamp_in_playlist(c, seek_pls, seek_timestamp, &seq_no))
+ return AVERROR(EIO);
- ret = AVERROR(EIO);
- for (i = 0; i < c->n_variants; i++) {
+ /* set segment now so we do not need to search again below */
+ seek_pls->cur_seq_no = seq_no;
+ seek_pls->seek_stream_index = stream_index - seek_pls->stream_offset;
+
+ for (i = 0; i < c->n_playlists; i++) {
/* Reset reading */
- struct variant *var = c->variants[i];
- int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ?
- 0 : c->first_timestamp;
- if (var->input) {
- ffurl_close(var->input);
- var->input = NULL;
+ struct playlist *pls = c->playlists[i];
+ if (pls->input) {
+ ffurl_close(pls->input);
+ pls->input = NULL;
}
- av_free_packet(&var->pkt);
- reset_packet(&var->pkt);
- var->pb.eof_reached = 0;
+ av_free_packet(&pls->pkt);
+ reset_packet(&pls->pkt);
+ pls->pb.eof_reached = 0;
/* Clear any buffered data */
- var->pb.buf_end = var->pb.buf_ptr = var->pb.buffer;
+ pls->pb.buf_end = pls->pb.buf_ptr = pls->pb.buffer;
/* Reset the pos, to let the mpegts demuxer know we've seeked. */
- var->pb.pos = 0;
-
- /* Locate the segment that contains the target timestamp */
- for (j = 0; j < var->n_segments; j++) {
- if (timestamp >= pos &&
- timestamp < pos + var->segments[j]->duration) {
- var->cur_seq_no = var->start_seq_no + j;
- ret = 0;
- break;
- }
- pos += var->segments[j]->duration;
+ pls->pb.pos = 0;
+ /* Flush the packet queue of the subdemuxer. */
+ ff_read_frame_flush(pls->ctx);
+
+ pls->seek_timestamp = seek_timestamp;
+ pls->seek_flags = flags;
+
+ if (pls != seek_pls) {
+ /* set closest segment seq_no for playlists not handled above */
+ find_timestamp_in_playlist(c, pls, seek_timestamp, &pls->cur_seq_no);
+ /* seek the playlist to the given position without taking
+ * keyframes into account since this playlist does not have the
+ * specified stream where we should look for the keyframes */
+ pls->seek_stream_index = -1;
+ pls->seek_flags |= AVSEEK_FLAG_ANY;
}
- if (ret)
- c->seek_timestamp = AV_NOPTS_VALUE;
}
- return ret;
+
+ c->cur_timestamp = seek_timestamp;
+
+ return 0;
}
static int hls_probe(AVProbeData *p)
diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
index ad53df5734..29bf30e192 100644
--- a/libavformat/hlsenc.c
+++ b/libavformat/hlsenc.c
@@ -2,26 +2,31 @@
* Apple HTTP Live Streaming segmenter
* Copyright (c) 2012, Luca Barbato
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "config.h"
#include <float.h>
#include <stdint.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "libavutil/avassert.h"
#include "libavutil/mathematics.h"
#include "libavutil/parseutils.h"
#include "libavutil/avstring.h"
@@ -30,12 +35,22 @@
#include "avformat.h"
#include "internal.h"
+#include "os_support.h"
-typedef struct ListEntry {
- char name[1024];
- int duration;
- struct ListEntry *next;
-} ListEntry;
+typedef struct HLSSegment {
+ char filename[1024];
+ double duration; /* in seconds */
+ int64_t pos;
+ int64_t size;
+
+ struct HLSSegment *next;
+} HLSSegment;
+
+typedef enum HLSFlags {
+ // Generate a single media file and use byte ranges in the playlist.
+ HLS_SINGLE_FILE = (1 << 0),
+ HLS_DELETE_SEGMENTS = (1 << 1),
+} HLSFlags;
typedef struct HLSContext {
const AVClass *class; // Class for private options.
@@ -43,36 +58,117 @@ typedef struct HLSContext {
int64_t sequence;
int64_t start_sequence;
AVOutputFormat *oformat;
+
AVFormatContext *avf;
+
float time; // Set by a private option.
- int size; // Set by a private option.
+ int max_nb_segments; // Set by a private option.
int wrap; // Set by a private option.
- int allowcache;
+ uint32_t flags; // enum HLSFlags
+ char *segment_filename;
+
+ int allowcache;
int64_t recording_time;
int has_video;
int64_t start_pts;
int64_t end_pts;
- int64_t duration; // last segment duration computed so far, in seconds
+ double duration; // last segment duration computed so far, in seconds
+ int64_t start_pos; // last segment starting position
+ int64_t size; // last segment size
int nb_entries;
- ListEntry *list;
- ListEntry *end_list;
+
+ HLSSegment *segments;
+ HLSSegment *last_segment;
+ HLSSegment *old_segments;
+
char *basename;
char *baseurl;
+ char *format_options_str;
+ AVDictionary *format_options;
+
AVIOContext *pb;
} HLSContext;
+static int hls_delete_old_segments(HLSContext *hls) {
+
+ HLSSegment *segment, *previous_segment = NULL;
+ float playlist_duration = 0.0f;
+ int ret = 0, path_size;
+ char *dirname = NULL, *p, *path;
+
+ segment = hls->segments;
+ while (segment) {
+ playlist_duration += segment->duration;
+ segment = segment->next;
+ }
+
+ segment = hls->old_segments;
+ while (segment) {
+ playlist_duration -= segment->duration;
+ previous_segment = segment;
+ segment = previous_segment->next;
+ if (playlist_duration <= -previous_segment->duration) {
+ previous_segment->next = NULL;
+ break;
+ }
+ }
+
+ if (segment) {
+ if (hls->segment_filename) {
+ dirname = av_strdup(hls->segment_filename);
+ } else {
+ dirname = av_strdup(hls->avf->filename);
+ }
+ if (!dirname) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ p = (char *)av_basename(dirname);
+ *p = '\0';
+ }
+
+ while (segment) {
+ av_log(hls, AV_LOG_DEBUG, "deleting old segment %s\n",
+ segment->filename);
+ path_size = strlen(dirname) + strlen(segment->filename) + 1;
+ path = av_malloc(path_size);
+ if (!path) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ av_strlcpy(path, dirname, path_size);
+ av_strlcat(path, segment->filename, path_size);
+ if (unlink(path) < 0) {
+ av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n",
+ path, strerror(errno));
+ }
+ av_free(path);
+ previous_segment = segment;
+ segment = previous_segment->next;
+ av_free(previous_segment);
+ }
+
+fail:
+ av_free(dirname);
+
+ return ret;
+}
+
static int hls_mux_init(AVFormatContext *s)
{
HLSContext *hls = s->priv_data;
AVFormatContext *oc;
- int i;
+ int i, ret;
- hls->avf = oc = avformat_alloc_context();
- if (!oc)
- return AVERROR(ENOMEM);
+ ret = avformat_alloc_output_context2(&hls->avf, hls->oformat, NULL, NULL);
+ if (ret < 0)
+ return ret;
+ oc = hls->avf;
oc->oformat = hls->oformat;
oc->interrupt_callback = s->interrupt_callback;
+ oc->max_delay = s->max_delay;
+ av_dict_copy(&oc->metadata, s->metadata, 0);
for (i = 0; i < s->nb_streams; i++) {
AVStream *st;
@@ -82,33 +178,46 @@ static int hls_mux_init(AVFormatContext *s)
st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
st->time_base = s->streams[i]->time_base;
}
+ hls->start_pos = 0;
return 0;
}
-static int append_entry(HLSContext *hls, uint64_t duration)
+/* Create a new segment and append it to the segment list */
+static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
+ int64_t size)
{
- ListEntry *en = av_malloc(sizeof(*en));
+ HLSSegment *en = av_malloc(sizeof(*en));
+ int ret;
if (!en)
return AVERROR(ENOMEM);
- av_strlcpy(en->name, av_basename(hls->avf->filename), sizeof(en->name));
+ av_strlcpy(en->filename, av_basename(hls->avf->filename), sizeof(en->filename));
en->duration = duration;
+ en->pos = pos;
+ en->size = size;
en->next = NULL;
- if (!hls->list)
- hls->list = en;
+ if (!hls->segments)
+ hls->segments = en;
else
- hls->end_list->next = en;
-
- hls->end_list = en;
-
- if (hls->nb_entries >= hls->size) {
- en = hls->list;
- hls->list = en->next;
- av_free(en);
+ hls->last_segment->next = en;
+
+ hls->last_segment = en;
+
+ if (hls->max_nb_segments && hls->nb_entries >= hls->max_nb_segments) {
+ en = hls->segments;
+ hls->segments = en->next;
+ if (en && hls->flags & HLS_DELETE_SEGMENTS &&
+ !(hls->flags & HLS_SINGLE_FILE || hls->wrap)) {
+ en->next = hls->old_segments;
+ hls->old_segments = en;
+ if ((ret = hls_delete_old_segments(hls)) < 0)
+ return ret;
+ } else
+ av_free(en);
} else
hls->nb_entries++;
@@ -117,9 +226,9 @@ static int append_entry(HLSContext *hls, uint64_t duration)
return 0;
}
-static void free_entries(HLSContext *hls)
+static void hls_free_segments(HLSSegment *p)
{
- ListEntry *p = hls->list, *en;
+ HLSSegment *en;
while(p) {
en = p;
@@ -131,22 +240,23 @@ static void free_entries(HLSContext *hls)
static int hls_window(AVFormatContext *s, int last)
{
HLSContext *hls = s->priv_data;
- ListEntry *en;
+ HLSSegment *en;
int target_duration = 0;
int ret = 0;
- int64_t sequence = FFMAX(hls->start_sequence, hls->sequence - hls->size);
+ int64_t sequence = FFMAX(hls->start_sequence, hls->sequence - hls->nb_entries);
+ int version = hls->flags & HLS_SINGLE_FILE ? 4 : 3;
if ((ret = avio_open2(&hls->pb, s->filename, AVIO_FLAG_WRITE,
&s->interrupt_callback, NULL)) < 0)
goto fail;
- for (en = hls->list; en; en = en->next) {
+ for (en = hls->segments; en; en = en->next) {
if (target_duration < en->duration)
- target_duration = en->duration;
+ target_duration = ceil(en->duration);
}
avio_printf(hls->pb, "#EXTM3U\n");
- avio_printf(hls->pb, "#EXT-X-VERSION:3\n");
+ avio_printf(hls->pb, "#EXT-X-VERSION:%d\n", version);
if (hls->allowcache == 0 || hls->allowcache == 1) {
avio_printf(hls->pb, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES");
}
@@ -156,11 +266,14 @@ static int hls_window(AVFormatContext *s, int last)
av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n",
sequence);
- for (en = hls->list; en; en = en->next) {
- avio_printf(hls->pb, "#EXTINF:%d,\n", en->duration);
+ for (en = hls->segments; en; en = en->next) {
+ avio_printf(hls->pb, "#EXTINF:%f,\n", en->duration);
+ if (hls->flags & HLS_SINGLE_FILE)
+ avio_printf(hls->pb, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
+ en->size, en->pos);
if (hls->baseurl)
avio_printf(hls->pb, "%s", hls->baseurl);
- avio_printf(hls->pb, "%s\n", en->name);
+ avio_printf(hls->pb, "%s\n", en->filename);
}
if (last)
@@ -177,9 +290,15 @@ static int hls_start(AVFormatContext *s)
AVFormatContext *oc = c->avf;
int err = 0;
- if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
- c->basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 0)
- return AVERROR(EINVAL);
+ if (c->flags & HLS_SINGLE_FILE)
+ av_strlcpy(oc->filename, c->basename,
+ sizeof(oc->filename));
+ else
+ if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
+ c->basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 0) {
+ av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->basename);
+ return AVERROR(EINVAL);
+ }
c->number++;
if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
@@ -198,12 +317,21 @@ static int hls_write_header(AVFormatContext *s)
int ret, i;
char *p;
const char *pattern = "%d.ts";
- int basename_size = strlen(s->filename) + strlen(pattern) + 1;
+ AVDictionary *options = NULL;
+ int basename_size;
hls->sequence = hls->start_sequence;
hls->recording_time = hls->time * AV_TIME_BASE;
hls->start_pts = AV_NOPTS_VALUE;
+ if (hls->format_options_str) {
+ ret = av_dict_parse_string(&hls->format_options, hls->format_options_str, "=", ":", 0);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "Could not parse format options list '%s'\n", hls->format_options_str);
+ goto fail;
+ }
+ }
+
for (i = 0; i < s->nb_streams; i++)
hls->has_video +=
s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO;
@@ -220,35 +348,55 @@ static int hls_write_header(AVFormatContext *s)
goto fail;
}
- hls->basename = av_malloc(basename_size);
-
- if (!hls->basename) {
- ret = AVERROR(ENOMEM);
- goto fail;
+ if (hls->segment_filename) {
+ hls->basename = av_strdup(hls->segment_filename);
+ if (!hls->basename) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ } else {
+ if (hls->flags & HLS_SINGLE_FILE)
+ pattern = ".ts";
+
+ basename_size = strlen(s->filename) + strlen(pattern) + 1;
+ hls->basename = av_malloc(basename_size);
+ if (!hls->basename) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ av_strlcpy(hls->basename, s->filename, basename_size);
+
+ p = strrchr(hls->basename, '.');
+ if (p)
+ *p = '\0';
+ av_strlcat(hls->basename, pattern, basename_size);
}
- strcpy(hls->basename, s->filename);
-
- p = strrchr(hls->basename, '.');
-
- if (p)
- *p = '\0';
-
- av_strlcat(hls->basename, pattern, basename_size);
-
if ((ret = hls_mux_init(s)) < 0)
goto fail;
if ((ret = hls_start(s)) < 0)
goto fail;
- if ((ret = avformat_write_header(hls->avf, NULL)) < 0)
- return ret;
-
-
+ av_dict_copy(&options, hls->format_options, 0);
+ ret = avformat_write_header(hls->avf, &options);
+ if (av_dict_count(options)) {
+ av_log(s, AV_LOG_ERROR, "Some of provided format options in '%s' are not recognized\n", hls->format_options_str);
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ av_assert0(s->nb_streams == hls->avf->nb_streams);
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *inner_st = hls->avf->streams[i];
+ AVStream *outer_st = s->streams[i];
+ avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
+ }
fail:
- if (ret) {
- av_free(hls->basename);
+
+ av_dict_free(&options);
+ if (ret < 0) {
+ av_freep(&hls->basename);
if (hls->avf)
avformat_free_context(hls->avf);
}
@@ -261,6 +409,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
AVFormatContext *oc = hls->avf;
AVStream *st = s->streams[pkt->stream_index];
int64_t end_pts = hls->recording_time * hls->number;
+ int is_ref_pkt = 1;
int ret, can_split = 1;
if (hls->start_pts == AV_NOPTS_VALUE) {
@@ -271,28 +420,41 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
if (hls->has_video) {
can_split = st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
pkt->flags & AV_PKT_FLAG_KEY;
+ is_ref_pkt = st->codec->codec_type == AVMEDIA_TYPE_VIDEO;
}
if (pkt->pts == AV_NOPTS_VALUE)
- can_split = 0;
- else
- hls->duration = av_rescale(pkt->pts - hls->end_pts,
- st->time_base.num, st->time_base.den);
+ is_ref_pkt = can_split = 0;
+
+ if (is_ref_pkt)
+ hls->duration = (double)(pkt->pts - hls->end_pts)
+ * st->time_base.num / st->time_base.den;
if (can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base,
end_pts, AV_TIME_BASE_Q) >= 0) {
- ret = append_entry(hls, hls->duration);
- if (ret)
+ int64_t new_start_pos;
+ av_write_frame(oc, NULL); /* Flush any buffered data */
+
+ new_start_pos = avio_tell(hls->avf->pb);
+ hls->size = new_start_pos - hls->start_pos;
+ ret = hls_append_segment(hls, hls->duration, hls->start_pos, hls->size);
+ hls->start_pos = new_start_pos;
+ if (ret < 0)
return ret;
hls->end_pts = pkt->pts;
hls->duration = 0;
- av_write_frame(oc, NULL); /* Flush any buffered data */
- avio_close(oc->pb);
+ if (hls->flags & HLS_SINGLE_FILE) {
+ if (hls->avf->oformat->priv_class && hls->avf->priv_data)
+ av_opt_set(hls->avf->priv_data, "mpegts_flags", "resend_headers", 0);
+ hls->number++;
+ } else {
+ avio_closep(&oc->pb);
- ret = hls_start(s);
+ ret = hls_start(s);
+ }
- if (ret)
+ if (ret < 0)
return ret;
oc = hls->avf;
@@ -301,7 +463,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
return ret;
}
- ret = ff_write_chained(oc, pkt->stream_index, pkt, s);
+ ret = ff_write_chained(oc, pkt->stream_index, pkt, s, 0);
return ret;
}
@@ -312,26 +474,37 @@ static int hls_write_trailer(struct AVFormatContext *s)
AVFormatContext *oc = hls->avf;
av_write_trailer(oc);
- avio_closep(&oc->pb);
+ if (oc->pb) {
+ hls->size = avio_tell(hls->avf->pb) - hls->start_pos;
+ avio_closep(&oc->pb);
+ hls_append_segment(hls, hls->duration, hls->start_pos, hls->size);
+ }
+ av_freep(&hls->basename);
avformat_free_context(oc);
- av_free(hls->basename);
- append_entry(hls, hls->duration);
+ hls->avf = NULL;
hls_window(s, 1);
- free_entries(hls);
- avio_close(hls->pb);
+ hls_free_segments(hls->segments);
+ hls_free_segments(hls->old_segments);
+ avio_closep(&hls->pb);
return 0;
}
#define OFFSET(x) offsetof(HLSContext, x)
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
- {"start_number", "first number in the sequence", OFFSET(start_sequence),AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, E},
- {"hls_time", "segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E},
- {"hls_list_size", "maximum number of playlist entries", OFFSET(size), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E},
- {"hls_wrap", "number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E},
+ {"start_number", "set first number in the sequence", OFFSET(start_sequence),AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, E},
+ {"hls_time", "set segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E},
+ {"hls_list_size", "set maximum number of playlist entries", OFFSET(max_nb_segments), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E},
+ {"hls_ts_options","set hls mpegts list of options for the container format used for hls", OFFSET(format_options_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
+ {"hls_wrap", "set number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E},
{"hls_allow_cache", "explicitly set whether the client MAY (1) or MUST NOT (0) cache media segments", OFFSET(allowcache), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, E},
{"hls_base_url", "url to prepend to each playlist entry", OFFSET(baseurl), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
+ {"hls_segment_filename", "filename template for segment files", OFFSET(segment_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
+ {"hls_flags", "set flags affecting HLS playlist and media file generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "flags"},
+ {"single_file", "generate a single media file indexed with byte ranges", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SINGLE_FILE }, 0, UINT_MAX, E, "flags"},
+ {"delete_segments", "delete segment files that are no longer part of the playlist", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DELETE_SEGMENTS }, 0, UINT_MAX, E, "flags"},
+
{ NULL },
};
diff --git a/libavformat/hlsproto.c b/libavformat/hlsproto.c
index 0eba049797..92843df258 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
*/
@@ -80,7 +80,7 @@ static void free_segment_list(HLSContext *s)
{
int i;
for (i = 0; i < s->n_segments; i++)
- av_free(s->segments[i]);
+ av_freep(&s->segments[i]);
av_freep(&s->segments);
s->n_segments = 0;
}
@@ -89,7 +89,7 @@ static void free_variant_list(HLSContext *s)
{
int i;
for (i = 0; i < s->n_variants; i++)
- av_free(s->variants[i]);
+ av_freep(&s->variants[i]);
av_freep(&s->variants);
s->n_variants = 0;
}
@@ -128,7 +128,7 @@ static int parse_playlist(URLContext *h, const char *url)
free_segment_list(s);
s->finished = 0;
- while (!in->eof_reached) {
+ while (!avio_feof(in)) {
read_chomp_line(in, line, sizeof(line));
if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) {
struct variant_info info = {{0}};
diff --git a/libavformat/hnm.c b/libavformat/hnm.c
index 54ac6c2ce4..1320fa5206 100644
--- a/libavformat/hnm.c
+++ b/libavformat/hnm.c
@@ -3,20 +3,20 @@
*
* Copyright (c) 2012 David Kment
*
- * This file is part of Libav.
+ * This file is part of FFmpeg .
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg ; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -92,7 +92,7 @@ static int hnm_read_header(AVFormatContext *s)
hnm->currentframe = 0;
- if (hnm->width < 320 || hnm->width > 640 ||
+ if (hnm->width < 256 || hnm->width > 640 ||
hnm->height < 150 || hnm->height > 480) {
av_log(s, AV_LOG_ERROR,
"invalid resolution: %ux%u\n", hnm->width, hnm->height);
@@ -150,7 +150,7 @@ static int hnm_read_packet(AVFormatContext *s, AVPacket *pkt)
chunk_id = avio_rl16(pb);
avio_skip(pb, 2);
- if (chunk_size > hnm->superchunk_remaining) {
+ if (chunk_size > hnm->superchunk_remaining || !chunk_size) {
av_log(s, AV_LOG_ERROR,
"invalid chunk size: %"PRIu32", offset: %"PRId64"\n",
chunk_size, avio_tell(pb));
diff --git a/libavformat/http.c b/libavformat/http.c
index 23df1b712e..55dcb6ec67 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -1,21 +1,21 @@
/*
- * HTTP protocol for avconv client
+ * HTTP protocol for ffmpeg client
* Copyright (c) 2000, 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -36,7 +36,7 @@
#include "os_support.h"
#include "url.h"
-/* XXX: POST protocol is not completely implemented because avconv uses
+/* XXX: POST protocol is not completely implemented because ffmpeg uses
* only a subset of it. */
/* The IO buffer size is unrelated to the max URL size in itself, but needs
@@ -64,6 +64,7 @@ typedef struct HTTPContext {
/* Set if the server correctly handles Connection: close and will close
* the connection after feeding us the content. */
int willclose;
+ int seekable; /**< Control seekability, 0 = disable, 1 = enable, -1 = probe. */
int chunked_post;
/* A flag which indicates if the end of chunked encoding has been sent. */
int end_chunked_post;
@@ -73,6 +74,9 @@ typedef struct HTTPContext {
int multiple_requests;
uint8_t *post_data;
int post_datalen;
+ int is_akamai;
+ int is_mediagateway;
+ char *cookies; ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name)
int icy;
/* how much data was read since the last ICY metadata packet */
int icy_data_read;
@@ -97,14 +101,16 @@ typedef struct HTTPContext {
#define DEFAULT_USER_AGENT "Lavf/" AV_STRINGIFY(LIBAVFORMAT_VERSION)
static const AVOption options[] = {
+ { "seekable", "control seekability of connection", OFFSET(seekable), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, D },
{ "chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, E },
{ "headers", "set custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D | E },
{ "content_type", "set a specific content type for the POST messages", OFFSET(content_type), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D | E },
{ "user_agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, { .str = DEFAULT_USER_AGENT }, 0, 0, D },
- { "user-agent", "override User-Agent header, for compatibility with ffmpeg", OFFSET(user_agent), AV_OPT_TYPE_STRING, { .str = DEFAULT_USER_AGENT }, 0, 0, D },
+ { "user-agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, { .str = DEFAULT_USER_AGENT }, 0, 0, D },
{ "multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, D | E },
{ "post_data", "set custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D | E },
{ "mime_type", "export the MIME type", OFFSET(mime_type), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY },
+ { "cookies", "set cookies to be sent in applicable future requests, use newline delimited Set-Cookie HTTP field value syntax", OFFSET(cookies), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D },
{ "icy", "request ICY metadata", OFFSET(icy), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, D },
{ "icy_metadata_headers", "return ICY metadata headers", OFFSET(icy_metadata_headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_EXPORT },
{ "icy_metadata_packet", "return current ICY metadata packet", OFFSET(icy_metadata_packet), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_EXPORT },
@@ -201,6 +207,8 @@ static int http_open_cnx(URLContext *h, AVDictionary **options)
HTTPContext *s = h->priv_data;
int location_changed, attempts = 0, redirects = 0;
redo:
+ av_dict_copy(options, s->chained_options, 0);
+
cur_auth_type = s->auth_state.auth_type;
cur_proxy_auth_type = s->auth_state.auth_type;
@@ -212,8 +220,7 @@ redo:
if (s->http_code == 401) {
if ((cur_auth_type == HTTP_AUTH_NONE || s->auth_state.stale) &&
s->auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
- ffurl_close(s->hd);
- s->hd = NULL;
+ ffurl_closep(&s->hd);
goto redo;
} else
goto fail;
@@ -221,8 +228,7 @@ redo:
if (s->http_code == 407) {
if ((cur_proxy_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) &&
s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
- ffurl_close(s->hd);
- s->hd = NULL;
+ ffurl_closep(&s->hd);
goto redo;
} else
goto fail;
@@ -231,8 +237,7 @@ redo:
s->http_code == 303 || s->http_code == 307) &&
location_changed == 1) {
/* url moved, get next */
- ffurl_close(s->hd);
- s->hd = NULL;
+ ffurl_closep(&s->hd);
if (redirects++ >= MAX_REDIRECTS)
return AVERROR(EIO);
/* Restart the authentication process with the new target, which
@@ -246,9 +251,10 @@ redo:
fail:
if (s->hd)
- ffurl_close(s->hd);
- s->hd = NULL;
- return AVERROR(EIO);
+ ffurl_closep(&s->hd);
+ if (location_changed < 0)
+ return location_changed;
+ return ff_http_averror(s->http_code, AVERROR(EIO));
}
int ff_http_do_new_request(URLContext *h, const char *uri)
@@ -264,19 +270,38 @@ int ff_http_do_new_request(URLContext *h, const char *uri)
if (!s->location)
return AVERROR(ENOMEM);
- av_dict_copy(&options, s->chained_options, 0);
ret = http_open_cnx(h, &options);
av_dict_free(&options);
return ret;
}
+int ff_http_averror(int status_code, int default_averror)
+{
+ switch (status_code) {
+ case 400: return AVERROR_HTTP_BAD_REQUEST;
+ case 401: return AVERROR_HTTP_UNAUTHORIZED;
+ case 403: return AVERROR_HTTP_FORBIDDEN;
+ case 404: return AVERROR_HTTP_NOT_FOUND;
+ default: break;
+ }
+ if (status_code >= 400 && status_code <= 499)
+ return AVERROR_HTTP_OTHER_4XX;
+ else if (status_code >= 500)
+ return AVERROR_HTTP_SERVER_ERROR;
+ else
+ return default_averror;
+}
+
static int http_open(URLContext *h, const char *uri, int flags,
AVDictionary **options)
{
HTTPContext *s = h->priv_data;
int ret;
- h->is_streamed = 1;
+ if( s->seekable == 1 )
+ h->is_streamed = 0;
+ else
+ h->is_streamed = 1;
s->filesize = -1;
s->location = av_strdup(uri);
@@ -349,7 +374,7 @@ static int check_http_code(URLContext *h, int http_code, const char *end)
(http_code != 407 || s->proxy_auth_state.auth_type != HTTP_AUTH_NONE)) {
end += strspn(end, SPACE_CHARS);
av_log(h, AV_LOG_WARNING, "HTTP error %d %s\n", http_code, end);
- return AVERROR(EIO);
+ return ff_http_averror(http_code, AVERROR(EIO));
}
return 0;
}
@@ -379,7 +404,8 @@ static void parse_content_range(URLContext *h, const char *p)
if ((slash = strchr(p, '/')) && strlen(slash) > 0)
s->filesize = strtoll(slash + 1, NULL, 10);
}
- h->is_streamed = 0; /* we _can_ in fact seek */
+ if (s->seekable == -1 && (!s->is_akamai || s->filesize != 2147483647))
+ h->is_streamed = 0; /* we _can_ in fact seek */
}
static int parse_content_encoding(URLContext *h, const char *p)
@@ -411,7 +437,6 @@ static int parse_content_encoding(URLContext *h, const char *p)
// the header at all if this is the case).
} else {
av_log(h, AV_LOG_WARNING, "Unknown content coding: %s\n", p);
- return AVERROR(ENOSYS);
}
return 0;
}
@@ -460,7 +485,7 @@ static int process_line(URLContext *h, char *line, int line_count,
p++;
s->http_code = strtol(p, &end, 10);
- av_dlog(NULL, "http_code=%d\n", s->http_code);
+ av_log(h, AV_LOG_DEBUG, "http_code=%d\n", s->http_code);
if ((ret = check_http_code(h, s->http_code, end)) < 0)
return ret;
@@ -484,7 +509,8 @@ static int process_line(URLContext *h, char *line, int line_count,
} else if (!av_strcasecmp(tag, "Content-Range")) {
parse_content_range(h, p);
} else if (!av_strcasecmp(tag, "Accept-Ranges") &&
- !strncmp(p, "bytes", 5)) {
+ !strncmp(p, "bytes", 5) &&
+ s->seekable == -1) {
h->is_streamed = 0;
} else if (!av_strcasecmp(tag, "Transfer-Encoding") &&
!av_strncasecmp(p, "chunked", 7)) {
@@ -499,9 +525,29 @@ static int process_line(URLContext *h, char *line, int line_count,
} else if (!av_strcasecmp(tag, "Connection")) {
if (!strcmp(p, "close"))
s->willclose = 1;
+ } else if (!av_strcasecmp(tag, "Server")) {
+ if (!av_strcasecmp(p, "AkamaiGHost")) {
+ s->is_akamai = 1;
+ } else if (!av_strncasecmp(p, "MediaGateway", 12)) {
+ s->is_mediagateway = 1;
+ }
} else if (!av_strcasecmp(tag, "Content-Type")) {
av_free(s->mime_type);
s->mime_type = av_strdup(p);
+ } else if (!av_strcasecmp(tag, "Set-Cookie")) {
+ if (!s->cookies) {
+ if (!(s->cookies = av_strdup(p)))
+ return AVERROR(ENOMEM);
+ } else {
+ char *tmp = s->cookies;
+ size_t str_size = strlen(tmp) + strlen(p) + 2;
+ if (!(s->cookies = av_malloc(str_size))) {
+ s->cookies = tmp;
+ return AVERROR(ENOMEM);
+ }
+ snprintf(s->cookies, str_size, "%s\n%s", tmp, p);
+ av_free(tmp);
+ }
} else if (!av_strcasecmp(tag, "Icy-MetaInt")) {
s->icy_metaint = strtoll(p, NULL, 10);
} else if (!av_strncasecmp(tag, "Icy-", 4)) {
@@ -515,6 +561,103 @@ static int process_line(URLContext *h, char *line, int line_count,
return 1;
}
+/**
+ * Create a string containing cookie values for use as a HTTP cookie header
+ * field value for a particular path and domain from the cookie values stored in
+ * the HTTP protocol context. The cookie string is stored in *cookies.
+ *
+ * @return a negative value if an error condition occurred, 0 otherwise
+ */
+static int get_cookies(HTTPContext *s, char **cookies, const char *path,
+ const char *domain)
+{
+ // cookie strings will look like Set-Cookie header field values. Multiple
+ // Set-Cookie fields will result in multiple values delimited by a newline
+ int ret = 0;
+ char *next, *cookie, *set_cookies = av_strdup(s->cookies), *cset_cookies = set_cookies;
+
+ if (!set_cookies) return AVERROR(EINVAL);
+
+ *cookies = NULL;
+ while ((cookie = av_strtok(set_cookies, "\n", &next))) {
+ int domain_offset = 0;
+ char *param, *next_param, *cdomain = NULL, *cpath = NULL, *cvalue = NULL;
+ set_cookies = NULL;
+
+ while ((param = av_strtok(cookie, "; ", &next_param))) {
+ if (cookie) {
+ // first key-value pair is the actual cookie value
+ cvalue = av_strdup(param);
+ cookie = NULL;
+ } else if (!av_strncasecmp("path=", param, 5)) {
+ av_free(cpath);
+ cpath = av_strdup(&param[5]);
+ } else if (!av_strncasecmp("domain=", param, 7)) {
+ // if the cookie specifies a sub-domain, skip the leading dot thereby
+ // supporting URLs that point to sub-domains and the master domain
+ int leading_dot = (param[7] == '.');
+ av_free(cdomain);
+ cdomain = av_strdup(&param[7+leading_dot]);
+ } else {
+ // ignore unknown attributes
+ }
+ }
+ if (!cdomain)
+ cdomain = av_strdup(domain);
+
+ // ensure all of the necessary values are valid
+ if (!cdomain || !cpath || !cvalue) {
+ av_log(s, AV_LOG_WARNING,
+ "Invalid cookie found, no value, path or domain specified\n");
+ goto done_cookie;
+ }
+
+ // check if the request path matches the cookie path
+ if (av_strncasecmp(path, cpath, strlen(cpath)))
+ goto done_cookie;
+
+ // the domain should be at least the size of our cookie domain
+ domain_offset = strlen(domain) - strlen(cdomain);
+ if (domain_offset < 0)
+ goto done_cookie;
+
+ // match the cookie domain
+ if (av_strcasecmp(&domain[domain_offset], cdomain))
+ goto done_cookie;
+
+ // cookie parameters match, so copy the value
+ if (!*cookies) {
+ if (!(*cookies = av_strdup(cvalue))) {
+ ret = AVERROR(ENOMEM);
+ goto done_cookie;
+ }
+ } else {
+ char *tmp = *cookies;
+ size_t str_size = strlen(cvalue) + strlen(*cookies) + 3;
+ if (!(*cookies = av_malloc(str_size))) {
+ ret = AVERROR(ENOMEM);
+ goto done_cookie;
+ }
+ snprintf(*cookies, str_size, "%s; %s", tmp, cvalue);
+ av_free(tmp);
+ }
+
+ done_cookie:
+ av_freep(&cdomain);
+ av_freep(&cpath);
+ av_freep(&cvalue);
+ if (ret < 0) {
+ if (*cookies) av_freep(cookies);
+ av_free(cset_cookies);
+ return ret;
+ }
+ }
+
+ av_free(cset_cookies);
+
+ return 0;
+}
+
static inline int has_header(const char *str, const char *header)
{
/* header + 2 to skip over CRLF prefix. (make sure you have one!) */
@@ -535,7 +678,7 @@ static int http_read_header(URLContext *h, int *new_location)
if ((err = http_get_line(s, line, sizeof(line))) < 0)
return err;
- av_dlog(NULL, "header='%s'\n", line);
+ av_log(h, AV_LOG_DEBUG, "header='%s'\n", line);
err = process_line(h, line, s->line_count, new_location);
if (err < 0)
@@ -545,6 +688,9 @@ static int http_read_header(URLContext *h, int *new_location)
s->line_count++;
}
+ if (s->seekable == -1 && s->is_mediagateway && s->filesize == 2000000000)
+ h->is_streamed = 1; /* we can in fact _not_ seek */
+
return err;
}
@@ -602,7 +748,7 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
// Note: we send this on purpose even when s->off is 0 when we're probing,
// since it allows us to detect more reliably if a (non-conforming)
// server supports seeking by analysing the reply headers.
- if (!has_header(s->headers, "\r\nRange: ") && !post) {
+ if (!has_header(s->headers, "\r\nRange: ") && !post && (s->off > 0 || s->end_off || s->seekable == -1)) {
len += av_strlcatf(headers + len, sizeof(headers) - len,
"Range: bytes=%"PRId64"-", s->off);
if (s->end_off)
@@ -634,6 +780,14 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
if (!has_header(s->headers, "\r\nContent-Type: ") && s->content_type)
len += av_strlcatf(headers + len, sizeof(headers) - len,
"Content-Type: %s\r\n", s->content_type);
+ if (!has_header(s->headers, "\r\nCookie: ") && s->cookies) {
+ char *cookies = NULL;
+ if (!get_cookies(s, &cookies, path, hoststr) && cookies) {
+ len += av_strlcatf(headers + len, sizeof(headers) - len,
+ "Cookie: %s\r\n", cookies);
+ av_free(cookies);
+ }
+ }
if (!has_header(s->headers, "\r\nIcy-MetaData: ") && s->icy)
len += av_strlcatf(headers + len, sizeof(headers) - len,
"Icy-MetaData: %d\r\n", 1);
@@ -656,14 +810,14 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
authstr ? authstr : "",
proxyauthstr ? "Proxy-" : "", proxyauthstr ? proxyauthstr : "");
- av_freep(&authstr);
- av_freep(&proxyauthstr);
+ av_log(h, AV_LOG_DEBUG, "request: %s\n", s->buffer);
+
if ((err = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0)
- return err;
+ goto done;
if (s->post_data)
if ((err = ffurl_write(s->hd, s->post_data, s->post_datalen)) < 0)
- return err;
+ goto done;
/* init input buffer */
s->buf_ptr = s->buffer;
@@ -680,15 +834,20 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
* we've still to send the POST data, but the code calling this
* function will check http_code after we return. */
s->http_code = 200;
- return 0;
+ err = 0;
+ goto done;
}
/* wait for header */
err = http_read_header(h, new_location);
if (err < 0)
- return err;
+ goto done;
- return (off == s->off) ? 0 : -1;
+ err = (off == s->off) ? 0 : -1;
+done:
+ av_freep(&authstr);
+ av_freep(&proxyauthstr);
+ return err;
}
static int http_buf_read(URLContext *h, uint8_t *buf, int size)
@@ -767,7 +926,6 @@ static int http_read_stream(URLContext *h, uint8_t *buf, int size)
if (!s->chunksize) {
char line[32];
- for (;;) {
do {
if ((err = http_get_line(s, line, sizeof(line))) < 0)
return err;
@@ -780,8 +938,6 @@ static int http_read_stream(URLContext *h, uint8_t *buf, int size)
if (!s->chunksize)
return 0;
- break;
- }
}
size = FFMIN(size, s->chunksize);
}
@@ -792,6 +948,8 @@ static int http_read_stream(URLContext *h, uint8_t *buf, int size)
return http_buf_read(h, buf, size);
}
+// Like http_read_stream(), but no short reads.
+// Assumes partial reads are an error.
static int http_read_stream_all(URLContext *h, uint8_t *buf, int size)
{
int pos = 0;
@@ -941,7 +1099,7 @@ static int http_close(URLContext *h)
ret = http_shutdown(h, h->flags);
if (s->hd)
- ffurl_close(s->hd);
+ ffurl_closep(&s->hd);
av_dict_free(&s->chained_options);
return ret;
}
@@ -963,18 +1121,22 @@ static int64_t http_seek(URLContext *h, int64_t off, int whence)
else if ((s->filesize == -1 && whence == SEEK_END) || h->is_streamed)
return AVERROR(ENOSYS);
- /* we save the old context in case the seek fails */
- old_buf_size = s->buf_end - s->buf_ptr;
- memcpy(old_buf, s->buf_ptr, old_buf_size);
- s->hd = NULL;
if (whence == SEEK_CUR)
off += s->off;
else if (whence == SEEK_END)
off += s->filesize;
+ else if (whence != SEEK_SET)
+ return AVERROR(EINVAL);
+ if (off < 0)
+ return AVERROR(EINVAL);
s->off = off;
+ /* we save the old context in case the seek fails */
+ old_buf_size = s->buf_end - s->buf_ptr;
+ memcpy(old_buf, s->buf_ptr, old_buf_size);
+ s->hd = NULL;
+
/* if it fails, continue on old connection */
- av_dict_copy(&options, s->chained_options, 0);
if ((ret = http_open_cnx(h, &options)) < 0) {
av_dict_free(&options);
memcpy(s->buffer, old_buf, old_buf_size);
@@ -1044,7 +1206,7 @@ static int http_proxy_close(URLContext *h)
{
HTTPContext *s = h->priv_data;
if (s->hd)
- ffurl_close(s->hd);
+ ffurl_closep(&s->hd);
return 0;
}
@@ -1059,7 +1221,10 @@ static int http_proxy_open(URLContext *h, const char *uri, int flags)
char *authstr;
int new_loc;
- h->is_streamed = 1;
+ if( s->seekable == 1 )
+ h->is_streamed = 0;
+ else
+ h->is_streamed = 1;
av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
pathbuf, sizeof(pathbuf), uri);
@@ -1115,14 +1280,13 @@ 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;
}
if (s->http_code < 400)
return 0;
- ret = AVERROR(EIO);
+ ret = ff_http_averror(s->http_code, AVERROR(EIO));
fail:
http_proxy_close(h);
diff --git a/libavformat/http.h b/libavformat/http.h
index 3d301a715f..7d02713e31 100644
--- a/libavformat/http.h
+++ b/libavformat/http.h
@@ -2,20 +2,20 @@
* HTTP definitions
* Copyright (c) 2010 Josh Allmann
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -24,7 +24,7 @@
#include "url.h"
-#define HTTP_HEADERS_SIZE 1024
+#define HTTP_HEADERS_SIZE 4096
/**
* Initialize the authentication state based on another HTTP URLContext.
@@ -40,11 +40,13 @@ void ff_http_init_auth_state(URLContext *dest, const URLContext *src);
/**
* Send a new HTTP request, reusing the old connection.
*
- * @param h pointer to the ressource
+ * @param h pointer to the resource
* @param uri uri used to perform the request
* @return a negative value if an error condition occurred, 0
* otherwise
*/
int ff_http_do_new_request(URLContext *h, const char *uri);
+int ff_http_averror(int status_code, int default_averror);
+
#endif /* AVFORMAT_HTTP_H */
diff --git a/libavformat/httpauth.c b/libavformat/httpauth.c
index b96da3ef12..dbe3eff48f 100644
--- a/libavformat/httpauth.c
+++ b/libavformat/httpauth.c
@@ -2,20 +2,20 @@
* HTTP authentication
* Copyright (c) 2010 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -224,8 +224,11 @@ static char *make_digest_auth(HTTPAuthState *state, const char *username,
av_strlcatf(authstr, len, ",nonce=\"%s\"", digest->nonce);
av_strlcatf(authstr, len, ",uri=\"%s\"", uri);
av_strlcatf(authstr, len, ",response=\"%s\"", response);
+
+ // we are violating the RFC and use "" because all others seem to do that too.
if (digest->algorithm[0])
- av_strlcatf(authstr, len, ",algorithm=%s", digest->algorithm);
+ av_strlcatf(authstr, len, ",algorithm=\"%s\"", digest->algorithm);
+
if (digest->opaque[0])
av_strlcatf(authstr, len, ",opaque=\"%s\"", digest->opaque);
if (digest->qop[0]) {
diff --git a/libavformat/httpauth.h b/libavformat/httpauth.h
index 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/icecast.c b/libavformat/icecast.c
index 5e3ce66178..a7c7001f03 100644
--- a/libavformat/icecast.c
+++ b/libavformat/icecast.c
@@ -1,26 +1,27 @@
/*
- * Icecast protocol for Libav
+ * Icecast protocol for FFmpeg
* Copyright (c) 2014 Marvin Scholz
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libavutil/avstring.h"
+#include "libavutil/bprint.h"
#include "libavutil/opt.h"
#include "avformat.h"
@@ -65,27 +66,10 @@ static const AVOption options[] = {
};
-static char *cat_header(char buf[], const char key[], const char value[])
+static void cat_header(AVBPrint *bp, const char key[], const char value[])
{
- if (NOT_EMPTY(value)) {
- int len = strlen(key) + strlen(value) + 5;
- int is_first = !buf;
- char *tmp = NULL;
-
- if (buf)
- len += strlen(buf);
- if (!(tmp = av_realloc(buf, len))) {
- av_freep(&buf);
- return NULL;
- } else {
- buf = tmp;
- }
- if (is_first)
- *buf = '\0';
-
- av_strlcatf(buf, len, "%s: %s\r\n", key, value);
- }
- return buf;
+ if (NOT_EMPTY(value))
+ av_bprintf(bp, "%s: %s\r\n", key, value);
}
static int icecast_close(URLContext *h)
@@ -107,20 +91,24 @@ static int icecast_open(URLContext *h, const char *uri, int flags)
char h_url[1024], host[1024], auth[1024], path[1024];
char *headers = NULL, *user = NULL;
int port, ret;
+ AVBPrint bp;
if (flags & AVIO_FLAG_READ)
return AVERROR(ENOSYS);
+ av_bprint_init(&bp, 0, 1);
+
// Build header strings
- headers = cat_header(headers, "Ice-Name", s->name);
- headers = cat_header(headers, "Ice-Description", s->description);
- headers = cat_header(headers, "Ice-URL", s->url);
- headers = cat_header(headers, "Ice-Genre", s->genre);
- headers = cat_header(headers, "Ice-Public", s->public ? "1" : "0");
- if (!headers) {
+ cat_header(&bp, "Ice-Name", s->name);
+ cat_header(&bp, "Ice-Description", s->description);
+ cat_header(&bp, "Ice-URL", s->url);
+ cat_header(&bp, "Ice-Genre", s->genre);
+ cat_header(&bp, "Ice-Public", s->public ? "1" : "0");
+ if (!av_bprint_is_complete(&bp)) {
ret = AVERROR(ENOMEM);
goto cleanup;
}
+ av_bprint_finalize(&bp, &headers);
// Set options
av_dict_set(&opt_dict, "method", s->legacy_icecast ? "SOURCE" : "PUT", 0);
@@ -141,7 +129,7 @@ static int icecast_open(URLContext *h, const char *uri, int flags)
// Check for auth data in URI
if (auth[0]) {
- char *sep = strchr(auth,':');
+ char *sep = strchr(auth, ':');
if (sep) {
*sep = 0;
sep++;
@@ -179,7 +167,6 @@ static int icecast_open(URLContext *h, const char *uri, int flags)
ret = ffurl_open(&s->hd, h_url, AVIO_FLAG_READ_WRITE, NULL, &opt_dict);
cleanup:
- // Free variables
av_freep(&user);
av_freep(&headers);
av_dict_free(&opt_dict);
diff --git a/libavformat/icodec.c b/libavformat/icodec.c
new file mode 100644
index 0000000000..847f0eea2f
--- /dev/null
+++ b/libavformat/icodec.c
@@ -0,0 +1,182 @@
+/*
+ * Microsoft Windows ICO demuxer
+ * Copyright (c) 2011 Peter Ross (pross@xvid.org)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Microsoft Windows ICO demuxer
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "libavcodec/bytestream.h"
+#include "libavcodec/bmp.h"
+#include "avformat.h"
+#include "internal.h"
+
+typedef struct {
+ int offset;
+ int size;
+ int nb_pal;
+} IcoImage;
+
+typedef struct {
+ int current_image;
+ int nb_images;
+ IcoImage * images;
+} IcoDemuxContext;
+
+static int probe(AVProbeData *p)
+{
+ if (AV_RL16(p->buf) == 0 && AV_RL16(p->buf + 2) == 1 && AV_RL16(p->buf + 4))
+ return AVPROBE_SCORE_MAX / 4;
+ return 0;
+}
+
+static int read_header(AVFormatContext *s)
+{
+ IcoDemuxContext *ico = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int i, codec;
+
+ avio_skip(pb, 4);
+ ico->nb_images = avio_rl16(pb);
+
+ ico->images = av_malloc_array(ico->nb_images, sizeof(IcoImage));
+ if (!ico->images)
+ return AVERROR(ENOMEM);
+
+ for (i = 0; i < ico->nb_images; i++) {
+ AVStream *st;
+ int tmp;
+
+ if (avio_seek(pb, 6 + i * 16, SEEK_SET) < 0)
+ break;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codec->width = avio_r8(pb);
+ st->codec->height = avio_r8(pb);
+ ico->images[i].nb_pal = avio_r8(pb);
+ if (ico->images[i].nb_pal == 255)
+ ico->images[i].nb_pal = 0;
+
+ avio_skip(pb, 5);
+
+ ico->images[i].size = avio_rl32(pb);
+ ico->images[i].offset = avio_rl32(pb);
+
+ if (avio_seek(pb, ico->images[i].offset, SEEK_SET) < 0)
+ break;
+
+ codec = avio_rl32(pb);
+ switch (codec) {
+ case MKTAG(0x89, 'P', 'N', 'G'):
+ st->codec->codec_id = AV_CODEC_ID_PNG;
+ st->codec->width = 0;
+ st->codec->height = 0;
+ break;
+ case 40:
+ if (ico->images[i].size < 40)
+ return AVERROR_INVALIDDATA;
+ st->codec->codec_id = AV_CODEC_ID_BMP;
+ tmp = avio_rl32(pb);
+ if (tmp)
+ st->codec->width = tmp;
+ tmp = avio_rl32(pb);
+ if (tmp)
+ st->codec->height = tmp / 2;
+ break;
+ default:
+ avpriv_request_sample(s, "codec %d", codec);
+ return AVERROR_INVALIDDATA;
+ }
+ }
+
+ return 0;
+}
+
+static int read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ IcoDemuxContext *ico = s->priv_data;
+ IcoImage *image;
+ AVIOContext *pb = s->pb;
+ AVStream *st = s->streams[0];
+ int ret;
+
+ if (ico->current_image >= ico->nb_images)
+ return AVERROR(EIO);
+
+ image = &ico->images[ico->current_image];
+
+ if ((ret = avio_seek(pb, image->offset, SEEK_SET)) < 0)
+ return ret;
+
+ if (s->streams[ico->current_image]->codec->codec_id == AV_CODEC_ID_PNG) {
+ if ((ret = av_get_packet(pb, pkt, image->size)) < 0)
+ return ret;
+ } else {
+ uint8_t *buf;
+ if ((ret = av_new_packet(pkt, 14 + image->size)) < 0)
+ return ret;
+ buf = pkt->data;
+
+ /* add BMP header */
+ bytestream_put_byte(&buf, 'B');
+ bytestream_put_byte(&buf, 'M');
+ bytestream_put_le32(&buf, pkt->size);
+ bytestream_put_le16(&buf, 0);
+ bytestream_put_le16(&buf, 0);
+ bytestream_put_le32(&buf, 0);
+
+ if ((ret = avio_read(pb, buf, image->size)) < 0)
+ return ret;
+
+ st->codec->bits_per_coded_sample = AV_RL16(buf + 14);
+
+ if (AV_RL32(buf + 32))
+ image->nb_pal = AV_RL32(buf + 32);
+
+ if (st->codec->bits_per_coded_sample <= 8 && !image->nb_pal) {
+ image->nb_pal = 1 << st->codec->bits_per_coded_sample;
+ AV_WL32(buf + 32, image->nb_pal);
+ }
+
+ AV_WL32(buf - 4, 14 + 40 + image->nb_pal * 4);
+ AV_WL32(buf + 8, AV_RL32(buf + 8) / 2);
+ }
+
+ pkt->stream_index = ico->current_image++;
+ pkt->flags |= AV_PKT_FLAG_KEY;
+
+ return 0;
+}
+
+AVInputFormat ff_ico_demuxer = {
+ .name = "ico",
+ .long_name = NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"),
+ .priv_data_size = sizeof(IcoDemuxContext),
+ .read_probe = probe,
+ .read_header = read_header,
+ .read_packet = read_packet,
+ .flags = AVFMT_NOTIMESTAMPS,
+};
diff --git a/libavformat/icoenc.c b/libavformat/icoenc.c
new file mode 100644
index 0000000000..53d4420f37
--- /dev/null
+++ b/libavformat/icoenc.c
@@ -0,0 +1,203 @@
+/*
+ * Microsoft Windows ICO muxer
+ * Copyright (c) 2012 Michael Bradshaw <mjbshaw gmail com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Microsoft Windows ICO muxer
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/pixdesc.h"
+#include "avformat.h"
+
+typedef struct {
+ int offset;
+ int size;
+ unsigned char width;
+ unsigned char height;
+ short bits;
+} IcoImage;
+
+typedef struct {
+ int current_image;
+ int nb_images;
+ IcoImage *images;
+} IcoMuxContext;
+
+static int ico_check_attributes(AVFormatContext *s, const AVCodecContext *c)
+{
+ if (c->codec_id == AV_CODEC_ID_BMP) {
+ if (c->pix_fmt == AV_PIX_FMT_PAL8 && AV_PIX_FMT_RGB32 != AV_PIX_FMT_BGRA) {
+ av_log(s, AV_LOG_ERROR, "Wrong endianness for bmp pixel format\n");
+ return AVERROR(EINVAL);
+ } else if (c->pix_fmt != AV_PIX_FMT_PAL8 &&
+ c->pix_fmt != AV_PIX_FMT_RGB555LE &&
+ c->pix_fmt != AV_PIX_FMT_BGR24 &&
+ c->pix_fmt != AV_PIX_FMT_BGRA) {
+ av_log(s, AV_LOG_ERROR, "BMP must be 1bit, 4bit, 8bit, 16bit, 24bit, or 32bit\n");
+ return AVERROR(EINVAL);
+ }
+ } else if (c->codec_id == AV_CODEC_ID_PNG) {
+ if (c->pix_fmt != AV_PIX_FMT_RGBA) {
+ av_log(s, AV_LOG_ERROR, "PNG in ico requires pixel format to be rgba\n");
+ return AVERROR(EINVAL);
+ }
+ } else {
+ const AVCodecDescriptor *codesc = avcodec_descriptor_get(c->codec_id);
+ av_log(s, AV_LOG_ERROR, "Unsupported codec %s\n", codesc ? codesc->name : "");
+ return AVERROR(EINVAL);
+ }
+
+ if (c->width > 256 ||
+ c->height > 256) {
+ av_log(s, AV_LOG_ERROR, "Unsupported dimensions %dx%d (dimensions cannot exceed 256x256)\n", c->width, c->height);
+ return AVERROR(EINVAL);
+ }
+
+ return 0;
+}
+
+static int ico_write_header(AVFormatContext *s)
+{
+ IcoMuxContext *ico = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int ret;
+ int i;
+
+ if (!pb->seekable) {
+ av_log(s, AV_LOG_ERROR, "Output is not seekable\n");
+ return AVERROR(EINVAL);
+ }
+
+ ico->current_image = 0;
+ ico->nb_images = s->nb_streams;
+
+ avio_wl16(pb, 0); // reserved
+ avio_wl16(pb, 1); // 1 == icon
+ avio_skip(pb, 2); // skip the number of images
+
+ for (i = 0; i < s->nb_streams; i++) {
+ if (ret = ico_check_attributes(s, s->streams[i]->codec))
+ return ret;
+
+ // Fill in later when writing trailer...
+ avio_skip(pb, 16);
+ }
+
+ ico->images = av_mallocz_array(ico->nb_images, sizeof(IcoMuxContext));
+ if (!ico->images)
+ return AVERROR(ENOMEM);
+
+ avio_flush(pb);
+
+ return 0;
+}
+
+static int ico_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ IcoMuxContext *ico = s->priv_data;
+ IcoImage *image;
+ AVIOContext *pb = s->pb;
+ AVCodecContext *c = s->streams[pkt->stream_index]->codec;
+ int i;
+
+ if (ico->current_image >= ico->nb_images) {
+ av_log(s, AV_LOG_ERROR, "ICO already contains %d images\n", ico->current_image);
+ return AVERROR(EIO);
+ }
+
+ image = &ico->images[ico->current_image++];
+
+ image->offset = avio_tell(pb);
+ image->width = (c->width == 256) ? 0 : c->width;
+ image->height = (c->height == 256) ? 0 : c->height;
+
+ if (c->codec_id == AV_CODEC_ID_PNG) {
+ image->bits = c->bits_per_coded_sample;
+ image->size = pkt->size;
+
+ avio_write(pb, pkt->data, pkt->size);
+ } else { // BMP
+ if (AV_RL32(pkt->data + 14) != 40) { // must be BITMAPINFOHEADER
+ av_log(s, AV_LOG_ERROR, "Invalid BMP\n");
+ return AVERROR(EINVAL);
+ }
+
+ image->bits = AV_RL16(pkt->data + 28); // allows things like 1bit and 4bit images to be preserved
+ image->size = pkt->size - 14 + c->height * (c->width + 7) / 8;
+
+ avio_write(pb, pkt->data + 14, 8); // Skip the BITMAPFILEHEADER header
+ avio_wl32(pb, AV_RL32(pkt->data + 22) * 2); // rewrite height as 2 * height
+ avio_write(pb, pkt->data + 26, pkt->size - 26);
+
+ for (i = 0; i < c->height * (c->width + 7) / 8; ++i)
+ avio_w8(pb, 0x00); // Write bitmask (opaque)
+ }
+
+ return 0;
+}
+
+static int ico_write_trailer(AVFormatContext *s)
+{
+ IcoMuxContext *ico = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int i;
+
+ avio_seek(pb, 4, SEEK_SET);
+
+ avio_wl16(pb, ico->current_image);
+
+ for (i = 0; i < ico->nb_images; i++) {
+ avio_w8(pb, ico->images[i].width);
+ avio_w8(pb, ico->images[i].height);
+
+ if (s->streams[i]->codec->codec_id == AV_CODEC_ID_BMP &&
+ s->streams[i]->codec->pix_fmt == AV_PIX_FMT_PAL8) {
+ avio_w8(pb, (ico->images[i].bits >= 8) ? 0 : 1 << ico->images[i].bits);
+ } else {
+ avio_w8(pb, 0);
+ }
+
+ avio_w8(pb, 0); // reserved
+ avio_wl16(pb, 1); // color planes
+ avio_wl16(pb, ico->images[i].bits);
+ avio_wl32(pb, ico->images[i].size);
+ avio_wl32(pb, ico->images[i].offset);
+ }
+
+ av_freep(&ico->images);
+
+ return 0;
+}
+
+AVOutputFormat ff_ico_muxer = {
+ .name = "ico",
+ .long_name = NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"),
+ .priv_data_size = sizeof(IcoMuxContext),
+ .mime_type = "image/vnd.microsoft.icon",
+ .extensions = "ico",
+ .audio_codec = AV_CODEC_ID_NONE,
+ .video_codec = AV_CODEC_ID_BMP,
+ .write_header = ico_write_header,
+ .write_packet = ico_write_packet,
+ .write_trailer = ico_write_trailer,
+ .flags = AVFMT_NOTIMESTAMPS,
+};
diff --git a/libavformat/id3v1.c b/libavformat/id3v1.c
index 87930ff361..218ed73f81 100644
--- a/libavformat/id3v1.c
+++ b/libavformat/id3v1.c
@@ -2,20 +2,20 @@
* ID3v1 header parser
* Copyright (c) 2003 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -23,6 +23,7 @@
#include "libavcodec/avcodec.h"
#include "libavutil/dict.h"
+/* See Genre List at http://id3.org/id3v2.3.0 */
const char * const ff_id3v1_genre_str[ID3v1_GENRE_MAX + 1] = {
[0] = "Blues",
[1] = "Classic Rock",
@@ -91,7 +92,7 @@ const char * const ff_id3v1_genre_str[ID3v1_GENRE_MAX + 1] = {
[64] = "Native American",
[65] = "Cabaret",
[66] = "New Wave",
- [67] = "Psychadelic",
+ [67] = "Psychadelic", /* sic, the misspelling is used in the specification */
[68] = "Rave",
[69] = "Showtunes",
[70] = "Trailer",
@@ -178,7 +179,7 @@ static void get_string(AVFormatContext *s, const char *key,
const uint8_t *buf, int buf_size)
{
int i, c;
- char *q, str[512];
+ char *q, str[512], *first_free_space = NULL;
q = str;
for(i = 0; i < buf_size; i++) {
@@ -187,10 +188,19 @@ static void get_string(AVFormatContext *s, const char *key,
break;
if ((q - str) >= sizeof(str) - 1)
break;
+ if (c == ' ') {
+ if (!first_free_space)
+ first_free_space = q;
+ } else {
+ first_free_space = NULL;
+ }
*q++ = c;
}
*q = '\0';
+ if (first_free_space)
+ *first_free_space = '\0';
+
if (*str)
av_dict_set(&s->metadata, key, str, 0);
}
@@ -202,7 +212,6 @@ static void get_string(AVFormatContext *s, const char *key,
*/
static int parse_tag(AVFormatContext *s, const uint8_t *buf)
{
- char str[5];
int genre;
if (!(buf[0] == 'T' &&
@@ -215,8 +224,7 @@ static int parse_tag(AVFormatContext *s, const uint8_t *buf)
get_string(s, "date", buf + 93, 4);
get_string(s, "comment", buf + 97, 30);
if (buf[125] == 0 && buf[126] != 0) {
- snprintf(str, sizeof(str), "%d", buf[126]);
- av_dict_set(&s->metadata, "track", str, 0);
+ av_dict_set_int(&s->metadata, "track", buf[126], 0);
}
genre = buf[127];
if (genre <= ID3v1_GENRE_MAX)
diff --git a/libavformat/id3v1.h b/libavformat/id3v1.h
index 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 3c5c06207f..dae4aeb7c9 100644
--- a/libavformat/id3v2.c
+++ b/libavformat/id3v2.c
@@ -1,24 +1,37 @@
/*
- * ID3v2 header parser
* Copyright (c) 2003 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+/**
+ * @file
+ * ID3v2 header parser
+ *
+ * Specifications available at:
+ * http://id3.org/Developer_Information
+ */
+
+#include "config.h"
+
+#if CONFIG_ZLIB
+#include <zlib.h>
+#endif
+
#include "libavutil/avstring.h"
#include "libavutil/dict.h"
#include "libavutil/intreadwrite.h"
@@ -42,12 +55,14 @@ const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
{ "TPUB", "publisher" },
{ "TRCK", "track" },
{ "TSSE", "encoder" },
+ { "USLT", "lyrics" },
{ 0 }
};
const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
- { "TDRL", "date" },
+ { "TCMP", "compilation" },
{ "TDRC", "date" },
+ { "TDRL", "date" },
{ "TDEN", "creation_time" },
{ "TSOA", "album-sort" },
{ "TSOP", "artist-sort" },
@@ -58,6 +73,7 @@ const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
static const AVMetadataConv id3v2_2_metadata_conv[] = {
{ "TAL", "album" },
{ "TCO", "genre" },
+ { "TCP", "compilation" },
{ "TT2", "title" },
{ "TEN", "encoded_by" },
{ "TP1", "artist" },
@@ -155,16 +171,58 @@ static unsigned int get_size(AVIOContext *s, int len)
return v;
}
+static unsigned int size_to_syncsafe(unsigned int size)
+{
+ return (((size) & (0x7f << 0)) >> 0) +
+ (((size) & (0x7f << 8)) >> 1) +
+ (((size) & (0x7f << 16)) >> 2) +
+ (((size) & (0x7f << 24)) >> 3);
+}
+
+/* No real verification, only check that the tag consists of
+ * a combination of capital alpha-numerical characters */
+static int is_tag(const char *buf, unsigned int len)
+{
+ if (!len)
+ return 0;
+
+ while (len--)
+ if ((buf[len] < 'A' ||
+ buf[len] > 'Z') &&
+ (buf[len] < '0' ||
+ buf[len] > '9'))
+ return 0;
+
+ return 1;
+}
+
+/**
+ * Return 1 if the tag of length len at the given offset is valid, 0 if not, -1 on error
+ */
+static int check_tag(AVIOContext *s, int offset, unsigned int len)
+{
+ char tag[4];
+
+ if (len > 4 ||
+ avio_seek(s, offset, SEEK_SET) < 0 ||
+ avio_read(s, tag, len) < len)
+ return -1;
+ else if (!AV_RB32(tag) || is_tag(tag, len))
+ return 1;
+
+ return 0;
+}
+
/**
* Free GEOB type extra metadata.
*/
static void free_geobtag(void *obj)
{
ID3v2ExtraMetaGEOB *geob = obj;
- av_free(geob->mime_type);
- av_free(geob->file_name);
- av_free(geob->description);
- av_free(geob->data);
+ av_freep(&geob->mime_type);
+ av_freep(&geob->file_name);
+ av_freep(&geob->description);
+ av_freep(&geob->data);
av_free(geob);
}
@@ -258,7 +316,7 @@ static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding,
* Parse a text tag.
*/
static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen,
- const char *key)
+ AVDictionary **metadata, const char *key)
{
uint8_t *dst;
int encoding, dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL;
@@ -293,7 +351,52 @@ static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen,
av_freep(&dst);
if (dst)
- av_dict_set(&s->metadata, key, dst, dict_flags);
+ av_dict_set(metadata, key, dst, dict_flags);
+}
+
+static void read_uslt(AVFormatContext *s, AVIOContext *pb, int taglen,
+ AVDictionary **metadata)
+{
+ uint8_t lang[4];
+ uint8_t *descriptor = NULL; // 'Content descriptor'
+ uint8_t *text = NULL;
+ char *key = NULL;
+ int encoding;
+ int ok = 0;
+
+ if (taglen < 1)
+ goto error;
+
+ encoding = avio_r8(pb);
+ taglen--;
+
+ if (avio_read(pb, lang, 3) < 3)
+ goto error;
+ lang[3] = '\0';
+ taglen -= 3;
+
+ if (decode_str(s, pb, encoding, &descriptor, &taglen) < 0)
+ goto error;
+
+ if (decode_str(s, pb, encoding, &text, &taglen) < 0)
+ goto error;
+
+ // FFmpeg does not support hierarchical metadata, so concatenate the keys.
+ key = av_asprintf("lyrics-%s%s%s", descriptor[0] ? (char *)descriptor : "",
+ descriptor[0] ? "-" : "",
+ lang);
+ if (!key)
+ goto error;
+
+ av_dict_set(metadata, key, text, 0);
+
+ ok = 1;
+error:
+ if (!ok)
+ av_log(s, AV_LOG_ERROR, "Error reading lyrics, skipped\n");
+ av_free(descriptor);
+ av_free(text);
+ av_free(key);
}
/**
@@ -313,14 +416,14 @@ static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen,
geob_data = av_mallocz(sizeof(ID3v2ExtraMetaGEOB));
if (!geob_data) {
- av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n",
+ av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n",
sizeof(ID3v2ExtraMetaGEOB));
return;
}
new_extra = av_mallocz(sizeof(ID3v2ExtraMeta));
if (!new_extra) {
- av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n",
+ av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n",
sizeof(ID3v2ExtraMeta));
goto fail;
}
@@ -496,7 +599,7 @@ static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen,
}
apic->buf = av_buffer_alloc(taglen + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!apic->buf || avio_read(pb, apic->buf->data, taglen) != taglen)
+ if (!apic->buf || !taglen || avio_read(pb, apic->buf->data, taglen) != taglen)
goto fail;
memset(apic->buf->data + taglen, 0, FF_INPUT_BUFFER_PADDING_SIZE);
@@ -514,6 +617,106 @@ fail:
avio_seek(pb, end, SEEK_SET);
}
+static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, const char *ttag, ID3v2ExtraMeta **extra_meta, int isv34)
+{
+ AVRational time_base = {1, 1000};
+ uint32_t start, end;
+ AVChapter *chapter;
+ uint8_t *dst = NULL;
+ int taglen;
+ char tag[5];
+
+ if (!s) {
+ /* We should probably just put the chapter data to extra_meta here
+ * and do the AVFormatContext-needing part in a separate
+ * ff_id3v2_parse_apic()-like function. */
+ av_log(NULL, AV_LOG_DEBUG, "No AVFormatContext, skipped ID3 chapter data\n");
+ return;
+ }
+
+ if (decode_str(s, pb, 0, &dst, &len) < 0)
+ return;
+ if (len < 16)
+ return;
+
+ start = avio_rb32(pb);
+ end = avio_rb32(pb);
+ avio_skip(pb, 8);
+
+ chapter = avpriv_new_chapter(s, s->nb_chapters + 1, time_base, start, end, dst);
+ if (!chapter) {
+ av_free(dst);
+ return;
+ }
+
+ len -= 16;
+ while (len > 10) {
+ if (avio_read(pb, tag, 4) < 4)
+ goto end;
+ tag[4] = 0;
+ taglen = avio_rb32(pb);
+ avio_skip(pb, 2);
+ len -= 10;
+ if (taglen < 0 || taglen > len)
+ goto end;
+ if (tag[0] == 'T')
+ read_ttag(s, pb, taglen, &chapter->metadata, tag);
+ else
+ avio_skip(pb, taglen);
+ len -= taglen;
+ }
+
+ ff_metadata_conv(&chapter->metadata, NULL, ff_id3v2_34_metadata_conv);
+ ff_metadata_conv(&chapter->metadata, NULL, ff_id3v2_4_metadata_conv);
+end:
+ av_free(dst);
+}
+
+static void free_priv(void *obj)
+{
+ ID3v2ExtraMetaPRIV *priv = obj;
+ av_freep(&priv->owner);
+ av_freep(&priv->data);
+ av_freep(&priv);
+}
+
+static void read_priv(AVFormatContext *s, AVIOContext *pb, int taglen,
+ const char *tag, ID3v2ExtraMeta **extra_meta, int isv34)
+{
+ ID3v2ExtraMeta *meta;
+ ID3v2ExtraMetaPRIV *priv;
+
+ meta = av_mallocz(sizeof(*meta));
+ priv = av_mallocz(sizeof(*priv));
+
+ if (!meta || !priv)
+ goto fail;
+
+ if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &priv->owner, &taglen) < 0)
+ goto fail;
+
+ priv->data = av_malloc(taglen);
+ if (!priv->data)
+ goto fail;
+
+ priv->datasize = taglen;
+
+ if (avio_read(pb, priv->data, priv->datasize) != priv->datasize)
+ goto fail;
+
+ meta->tag = "PRIV";
+ meta->data = priv;
+ meta->next = *extra_meta;
+ *extra_meta = meta;
+
+ return;
+
+fail:
+ if (priv)
+ free_priv(priv);
+ av_freep(&meta);
+}
+
typedef struct ID3v2EMFunc {
const char *tag3;
const char *tag4;
@@ -526,6 +729,8 @@ typedef struct ID3v2EMFunc {
static const ID3v2EMFunc id3v2_extra_meta_funcs[] = {
{ "GEO", "GEOB", read_geobtag, free_geobtag },
{ "PIC", "APIC", read_apic, free_apic },
+ { "CHAP","CHAP", read_chapter, NULL },
+ { "PRIV","PRIV", read_priv, free_priv },
{ NULL }
};
@@ -538,7 +743,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)))
@@ -548,19 +753,25 @@ static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34)
return NULL;
}
-static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
+static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata,
+ AVFormatContext *s, int len, uint8_t version,
uint8_t flags, ID3v2ExtraMeta **extra_meta)
{
- int isv34, tlen, unsync;
+ int isv34, unsync;
+ unsigned tlen;
char tag[5];
- int64_t next, end = avio_tell(s->pb) + len;
+ int64_t next, end = avio_tell(pb) + len;
int taghdrlen;
const char *reason = NULL;
- AVIOContext pb;
+ AVIOContext pb_local;
AVIOContext *pbx;
unsigned char *buffer = NULL;
int buffer_size = 0;
- const ID3v2EMFunc *extra_func;
+ const ID3v2EMFunc *extra_func = NULL;
+ unsigned char *uncompressed_buffer = NULL;
+ av_unused int uncompressed_buffer_size = 0;
+
+ av_log(s, AV_LOG_DEBUG, "id3v2 ver:%d flags:%02X len:%d\n", version, flags, len);
switch (version) {
case 2:
@@ -586,7 +797,7 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
unsync = flags & 0x80;
if (isv34 && flags & 0x40) { /* Extended header present, just skip over it */
- int extlen = get_size(s->pb, 4);
+ int extlen = get_size(pb, 4);
if (version == 4)
/* In v2.4 the length includes the length field we just read. */
extlen -= 4;
@@ -595,35 +806,63 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
reason = "invalid extended header length";
goto error;
}
- avio_skip(s->pb, extlen);
+ avio_skip(pb, extlen);
+ len -= extlen + 4;
+ if (len < 0) {
+ reason = "extended header too long.";
+ goto error;
+ }
}
while (len >= taghdrlen) {
unsigned int tflags = 0;
int tunsync = 0;
+ int tcomp = 0;
+ int tencr = 0;
+ unsigned long av_unused dlen;
if (isv34) {
- avio_read(s->pb, tag, 4);
+ if (avio_read(pb, tag, 4) < 4)
+ break;
tag[4] = 0;
if (version == 3) {
- tlen = avio_rb32(s->pb);
- } else
- tlen = get_size(s->pb, 4);
- tflags = avio_rb16(s->pb);
+ tlen = avio_rb32(pb);
+ } else {
+ /* some encoders incorrectly uses v3 sizes instead of syncsafe ones
+ * so check the next tag to see which one to use */
+ tlen = avio_rb32(pb);
+ if (tlen > 0x7f) {
+ if (tlen < len) {
+ int64_t cur = avio_tell(pb);
+
+ if (ffio_ensure_seekback(pb, 2 /* tflags */ + tlen + 4 /* next tag */))
+ break;
+
+ if (check_tag(pb, cur + 2 + size_to_syncsafe(tlen), 4) == 1)
+ tlen = size_to_syncsafe(tlen);
+ else if (check_tag(pb, cur + 2 + tlen, 4) != 1)
+ break;
+ avio_seek(pb, cur, SEEK_SET);
+ } else
+ tlen = size_to_syncsafe(tlen);
+ }
+ }
+ tflags = avio_rb16(pb);
tunsync = tflags & ID3v2_FLAG_UNSYNCH;
} else {
- avio_read(s->pb, tag, 3);
+ if (avio_read(pb, tag, 3) < 3)
+ break;
tag[3] = 0;
- tlen = avio_rb24(s->pb);
+ tlen = avio_rb24(pb);
}
- if (tlen < 0 || tlen > len - taghdrlen) {
- av_log(s, AV_LOG_WARNING,
- "Invalid size in frame %s, skipping the rest of tag.\n",
- tag);
+ if (tlen > (1<<28))
break;
- }
len -= taghdrlen + tlen;
- next = avio_tell(s->pb) + tlen;
+
+ if (len < 0)
+ break;
+
+ next = avio_tell(pb) + tlen;
if (!tlen) {
if (tag[0])
@@ -633,57 +872,110 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
}
if (tflags & ID3v2_FLAG_DATALEN) {
- avio_rb32(s->pb);
+ if (tlen < 4)
+ break;
+ dlen = avio_rb32(pb);
tlen -= 4;
- }
+ } else
+ dlen = tlen;
+
+ tcomp = tflags & ID3v2_FLAG_COMPRESSION;
+ tencr = tflags & ID3v2_FLAG_ENCRYPTION;
+
+ /* skip encrypted tags and, if no zlib, compressed tags */
+ if (tencr || (!CONFIG_ZLIB && tcomp)) {
+ const char *type;
+ if (!tcomp)
+ type = "encrypted";
+ else if (!tencr)
+ type = "compressed";
+ else
+ type = "encrypted and compressed";
- if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) {
- av_log(s, AV_LOG_WARNING,
- "Skipping encrypted/compressed ID3v2 frame %s.\n", tag);
- avio_skip(s->pb, tlen);
+ av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame %s.\n", type, tag);
+ avio_skip(pb, tlen);
/* check for text tag or supported special meta tag */
} else if (tag[0] == 'T' ||
+ !memcmp(tag, "USLT", 4) ||
(extra_meta &&
(extra_func = get_extra_meta_func(tag, isv34)))) {
- if (unsync || tunsync) {
- int64_t end = avio_tell(s->pb) + tlen;
- uint8_t *b;
+ pbx = pb;
+
+ if (unsync || tunsync || tcomp) {
av_fast_malloc(&buffer, &buffer_size, tlen);
if (!buffer) {
av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", tlen);
goto seek;
}
+ }
+ if (unsync || tunsync) {
+ int64_t end = avio_tell(pb) + tlen;
+ uint8_t *b;
+
b = buffer;
- while (avio_tell(s->pb) < end && !s->pb->eof_reached) {
- *b++ = avio_r8(s->pb);
- if (*(b - 1) == 0xff && avio_tell(s->pb) < end - 1 &&
- !s->pb->eof_reached ) {
- uint8_t val = avio_r8(s->pb);
- *b++ = val ? val : avio_r8(s->pb);
+ while (avio_tell(pb) < end && b - buffer < tlen && !pb->eof_reached) {
+ *b++ = avio_r8(pb);
+ if (*(b - 1) == 0xff && avio_tell(pb) < end - 1 &&
+ b - buffer < tlen &&
+ !pb->eof_reached ) {
+ uint8_t val = avio_r8(pb);
+ *b++ = val ? val : avio_r8(pb);
}
}
- ffio_init_context(&pb, buffer, b - buffer, 0, NULL, NULL, NULL,
+ ffio_init_context(&pb_local, buffer, b - buffer, 0, NULL, NULL, NULL,
NULL);
tlen = b - buffer;
- pbx = &pb; // read from sync buffer
- } else {
- pbx = s->pb; // read straight from input
+ pbx = &pb_local; // read from sync buffer
}
+
+#if CONFIG_ZLIB
+ if (tcomp) {
+ int err;
+
+ av_log(s, AV_LOG_DEBUG, "Compresssed frame %s tlen=%d dlen=%ld\n", tag, tlen, dlen);
+
+ av_fast_malloc(&uncompressed_buffer, &uncompressed_buffer_size, dlen);
+ if (!uncompressed_buffer) {
+ av_log(s, AV_LOG_ERROR, "Failed to alloc %ld bytes\n", dlen);
+ goto seek;
+ }
+
+ if (!(unsync || tunsync)) {
+ err = avio_read(pb, buffer, tlen);
+ if (err < 0) {
+ av_log(s, AV_LOG_ERROR, "Failed to read compressed tag\n");
+ goto seek;
+ }
+ tlen = err;
+ }
+
+ err = uncompress(uncompressed_buffer, &dlen, buffer, tlen);
+ if (err != Z_OK) {
+ av_log(s, AV_LOG_ERROR, "Failed to uncompress tag: %d\n", err);
+ goto seek;
+ }
+ ffio_init_context(&pb_local, uncompressed_buffer, dlen, 0, NULL, NULL, NULL, NULL);
+ tlen = dlen;
+ pbx = &pb_local; // read from sync buffer
+ }
+#endif
if (tag[0] == 'T')
/* parse text tag */
- read_ttag(s, pbx, tlen, tag);
+ read_ttag(s, pbx, tlen, metadata, tag);
+ else if (!memcmp(tag, "USLT", 4))
+ read_uslt(s, pbx, tlen, metadata);
else
/* parse special meta tag */
extra_func->read(s, pbx, tlen, tag, extra_meta, isv34);
} else if (!tag[0]) {
if (tag[1])
- av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
- avio_skip(s->pb, tlen);
+ av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding\n");
+ avio_skip(pb, tlen);
break;
}
/* Skip to end of tag */
seek:
- avio_seek(s->pb, next, SEEK_SET);
+ avio_seek(pb, next, SEEK_SET);
}
/* Footer preset, always 10 bytes, skip over it */
@@ -694,25 +986,38 @@ error:
if (reason)
av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n",
version, reason);
- avio_seek(s->pb, end, SEEK_SET);
+ avio_seek(pb, end, SEEK_SET);
av_free(buffer);
+ av_free(uncompressed_buffer);
return;
}
-void ff_id3v2_read(AVFormatContext *s, const char *magic,
- ID3v2ExtraMeta **extra_meta)
+static void id3v2_read_internal(AVIOContext *pb, AVDictionary **metadata,
+ AVFormatContext *s, const char *magic,
+ ID3v2ExtraMeta **extra_meta, int64_t max_search_size)
{
int len, ret;
uint8_t buf[ID3v2_HEADER_SIZE];
int found_header;
- int64_t off;
+ int64_t start, off;
+
+ if (max_search_size && max_search_size < ID3v2_HEADER_SIZE)
+ return;
+ start = avio_tell(pb);
do {
/* save the current offset in case there's nothing to read/skip */
- off = avio_tell(s->pb);
- ret = avio_read(s->pb, buf, ID3v2_HEADER_SIZE);
- if (ret != ID3v2_HEADER_SIZE)
+ off = avio_tell(pb);
+ if (max_search_size && off - start >= max_search_size - ID3v2_HEADER_SIZE) {
+ avio_seek(pb, off, SEEK_SET);
break;
+ }
+
+ ret = avio_read(pb, buf, ID3v2_HEADER_SIZE);
+ if (ret != ID3v2_HEADER_SIZE) {
+ avio_seek(pb, off, SEEK_SET);
+ break;
+ }
found_header = ff_id3v2_match(buf, magic);
if (found_header) {
/* parse ID3v2 header */
@@ -720,15 +1025,27 @@ void ff_id3v2_read(AVFormatContext *s, const char *magic,
((buf[7] & 0x7f) << 14) |
((buf[8] & 0x7f) << 7) |
(buf[9] & 0x7f);
- id3v2_parse(s, len, buf[3], buf[5], extra_meta);
+ id3v2_parse(pb, metadata, s, len, buf[3], buf[5], extra_meta);
} else {
- avio_seek(s->pb, off, SEEK_SET);
+ avio_seek(pb, off, SEEK_SET);
}
} while (found_header);
- ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv);
- ff_metadata_conv(&s->metadata, NULL, id3v2_2_metadata_conv);
- ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv);
- merge_date(&s->metadata);
+ ff_metadata_conv(metadata, NULL, ff_id3v2_34_metadata_conv);
+ ff_metadata_conv(metadata, NULL, id3v2_2_metadata_conv);
+ ff_metadata_conv(metadata, NULL, ff_id3v2_4_metadata_conv);
+ merge_date(metadata);
+}
+
+void ff_id3v2_read_dict(AVIOContext *pb, AVDictionary **metadata,
+ const char *magic, ID3v2ExtraMeta **extra_meta)
+{
+ id3v2_read_internal(pb, metadata, NULL, magic, extra_meta, 0);
+}
+
+void ff_id3v2_read(AVFormatContext *s, const char *magic,
+ ID3v2ExtraMeta **extra_meta, unsigned int max_search_size)
+{
+ id3v2_read_internal(s->pb, &s->metadata, s, magic, extra_meta, max_search_size);
}
void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta)
@@ -743,6 +1060,8 @@ void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta)
av_freep(&current);
current = next;
}
+
+ *extra_meta = NULL;
}
int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta)
diff --git a/libavformat/id3v2.h b/libavformat/id3v2.h
index 7cb4296f11..9d7bf1c03c 100644
--- a/libavformat/id3v2.h
+++ b/libavformat/id3v2.h
@@ -2,20 +2,20 @@
* ID3v2 header parser
* Copyright (c) 2003 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -73,6 +73,12 @@ typedef struct ID3v2ExtraMetaAPIC {
enum AVCodecID id;
} ID3v2ExtraMetaAPIC;
+typedef struct ID3v2ExtraMetaPRIV {
+ uint8_t *owner;
+ uint8_t *data;
+ uint32_t datasize;
+} ID3v2ExtraMetaPRIV;
+
/**
* Detect ID3v2 Header.
* @param buf must be ID3v2_HEADER_SIZE byte long
@@ -89,11 +95,27 @@ int ff_id3v2_match(const uint8_t *buf, const char *magic);
int ff_id3v2_tag_len(const uint8_t *buf);
/**
- * Read an ID3v2 tag, including supported extra metadata
+ * Read an ID3v2 tag into specified dictionary and retrieve supported extra metadata.
+ *
+ * Chapters are not currently read by this variant.
+ *
+ * @param metadata Parsed metadata is stored here
+ * @param extra_meta If not NULL, extra metadata is parsed into a list of
+ * ID3v2ExtraMeta structs and *extra_meta points to the head of the list
+ */
+void ff_id3v2_read_dict(AVIOContext *pb, AVDictionary **metadata, const char *magic, ID3v2ExtraMeta **extra_meta);
+
+/**
+ * Read an ID3v2 tag, including supported extra metadata and chapters.
+ *
+ * Data is read from and stored to AVFormatContext.
+ *
* @param extra_meta If not NULL, extra metadata is parsed into a list of
* ID3v2ExtraMeta structs and *extra_meta points to the head of the list
+ * @param[opt] max_search_search restrict ID3 magic number search (bytes from start)
*/
-void ff_id3v2_read(AVFormatContext *s, const char *magic, ID3v2ExtraMeta **extra_meta);
+void ff_id3v2_read(AVFormatContext *s, const char *magic, ID3v2ExtraMeta **extra_meta,
+ unsigned int max_search_size);
/**
* Initialize an ID3v2 tag.
@@ -114,7 +136,7 @@ int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt)
/**
* Finalize an opened ID3v2 tag.
*/
-void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb);
+void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb, int padding_bytes);
/**
* Write an ID3v2 tag containing all global metadata from s.
diff --git a/libavformat/id3v2enc.c b/libavformat/id3v2enc.c
index 89181c26a0..8b804c4d4a 100644
--- a/libavformat/id3v2enc.c
+++ b/libavformat/id3v2enc.c
@@ -1,20 +1,20 @@
/*
* ID3v2 header writer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -26,6 +26,7 @@
#include "libavutil/intreadwrite.h"
#include "avformat.h"
#include "avio.h"
+#include "avio_internal.h"
#include "id3v2.h"
static void id3v2_put_size(AVIOContext *pb, int size)
@@ -110,6 +111,44 @@ static int id3v2_check_write_tag(ID3v2EncContext *id3, AVIOContext *pb, AVDictio
return -1;
}
+static void id3v2_3_metadata_split_date(AVDictionary **pm)
+{
+ AVDictionaryEntry *mtag = NULL;
+ AVDictionary *dst = NULL;
+ const char *key, *value;
+ char year[5] = {0}, day_month[5] = {0};
+ int i;
+
+ while ((mtag = av_dict_get(*pm, "", mtag, AV_DICT_IGNORE_SUFFIX))) {
+ key = mtag->key;
+ if (!av_strcasecmp(key, "date")) {
+ /* split date tag using "YYYY-MM-DD" format into year and month/day segments */
+ value = mtag->value;
+ i = 0;
+ while (value[i] >= '0' && value[i] <= '9') i++;
+ if (value[i] == '\0' || value[i] == '-') {
+ av_strlcpy(year, value, sizeof(year));
+ av_dict_set(&dst, "TYER", year, 0);
+
+ if (value[i] == '-' &&
+ value[i+1] >= '0' && value[i+1] <= '1' &&
+ value[i+2] >= '0' && value[i+2] <= '9' &&
+ value[i+3] == '-' &&
+ value[i+4] >= '0' && value[i+4] <= '3' &&
+ value[i+5] >= '0' && value[i+5] <= '9' &&
+ (value[i+6] == '\0' || value[i+6] == ' ')) {
+ snprintf(day_month, sizeof(day_month), "%.2s%.2s", value + i + 4, value + i + 1);
+ av_dict_set(&dst, "TDAT", day_month, 0);
+ }
+ } else
+ av_dict_set(&dst, key, value, 0);
+ } else
+ av_dict_set(&dst, key, mtag->value, 0);
+ }
+ av_dict_free(pm);
+ *pm = dst;
+}
+
void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version,
const char *magic)
{
@@ -124,31 +163,31 @@ void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version,
avio_wb32(pb, 0);
}
-int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3)
+static int write_metadata(AVIOContext *pb, AVDictionary **metadata,
+ ID3v2EncContext *id3, int enc)
{
AVDictionaryEntry *t = NULL;
- int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM :
- ID3v2_ENCODING_UTF8;
-
- ff_metadata_conv(&s->metadata, ff_id3v2_34_metadata_conv, NULL);
- if (id3->version == 4)
- ff_metadata_conv(&s->metadata, ff_id3v2_4_metadata_conv, NULL);
+ int ret;
- while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) {
- int ret;
+ ff_metadata_conv(metadata, ff_id3v2_34_metadata_conv, NULL);
+ if (id3->version == 3)
+ id3v2_3_metadata_split_date(metadata);
+ else if (id3->version == 4)
+ ff_metadata_conv(metadata, ff_id3v2_4_metadata_conv, NULL);
- if ((ret = id3v2_check_write_tag(id3, s->pb, t, ff_id3v2_tags, enc)) > 0) {
+ while ((t = av_dict_get(*metadata, "", t, AV_DICT_IGNORE_SUFFIX))) {
+ if ((ret = id3v2_check_write_tag(id3, pb, t, ff_id3v2_tags, enc)) > 0) {
id3->len += ret;
continue;
}
- if ((ret = id3v2_check_write_tag(id3, s->pb, t, id3->version == 3 ?
- ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) {
+ if ((ret = id3v2_check_write_tag(id3, pb, t, id3->version == 3 ?
+ ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) {
id3->len += ret;
continue;
}
/* unknown tag, write as TXXX frame */
- if ((ret = id3v2_put_ttag(id3, s->pb, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0)
+ if ((ret = id3v2_put_ttag(id3, pb, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0)
return ret;
id3->len += ret;
}
@@ -156,6 +195,64 @@ int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3)
return 0;
}
+static int write_chapter(AVFormatContext *s, ID3v2EncContext *id3, int id, int enc)
+{
+ const AVRational time_base = {1, 1000};
+ AVChapter *ch = s->chapters[id];
+ uint8_t *dyn_buf = NULL;
+ AVIOContext *dyn_bc = NULL;
+ char name[123];
+ int len, start, end, ret;
+
+ if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0)
+ goto fail;
+
+ start = av_rescale_q(ch->start, ch->time_base, time_base);
+ end = av_rescale_q(ch->end, ch->time_base, time_base);
+
+ snprintf(name, 122, "ch%d", id);
+ id3->len += avio_put_str(dyn_bc, name);
+ avio_wb32(dyn_bc, start);
+ avio_wb32(dyn_bc, end);
+ avio_wb32(dyn_bc, 0xFFFFFFFFu);
+ avio_wb32(dyn_bc, 0xFFFFFFFFu);
+
+ if ((ret = write_metadata(dyn_bc, &ch->metadata, id3, enc)) < 0)
+ goto fail;
+
+ len = avio_close_dyn_buf(dyn_bc, &dyn_buf);
+ id3->len += 16 + ID3v2_HEADER_SIZE;
+
+ avio_wb32(s->pb, MKBETAG('C', 'H', 'A', 'P'));
+ avio_wb32(s->pb, len);
+ avio_wb16(s->pb, 0);
+ avio_write(s->pb, dyn_buf, len);
+
+fail:
+ if (dyn_bc && !dyn_buf)
+ avio_close_dyn_buf(dyn_bc, &dyn_buf);
+ av_freep(&dyn_buf);
+
+ return ret;
+}
+
+int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3)
+{
+ int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM :
+ ID3v2_ENCODING_UTF8;
+ int i, ret;
+
+ if ((ret = write_metadata(s->pb, &s->metadata, id3, enc)) < 0)
+ return ret;
+
+ for (i = 0; i < s->nb_chapters; i++) {
+ if ((ret = write_chapter(s, id3, i, enc)) < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt)
{
AVStream *st = s->streams[pkt->stream_index];
@@ -196,6 +293,10 @@ int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt)
if ((e = av_dict_get(st->metadata, "title", NULL, 0)))
desc = e->value;
+ /* use UTF16 only for non-ASCII strings */
+ if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(desc))
+ enc = ID3v2_ENCODING_ISO8859;
+
/* start writing */
if (avio_open_dyn_buf(&dyn_buf) < 0)
return AVERROR(ENOMEM);
@@ -221,9 +322,25 @@ int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt)
return 0;
}
-void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb)
+void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb,
+ int padding_bytes)
{
- int64_t cur_pos = avio_tell(pb);
+ int64_t cur_pos;
+
+ if (padding_bytes < 0)
+ padding_bytes = 10;
+
+ /* The ID3v2.3 specification states that 28 bits are used to represent the
+ * size of the whole tag. Therefore the current size of the tag needs to be
+ * subtracted from the upper limit of 2^28-1 to clip the value correctly. */
+ /* The minimum of 10 is an arbitrary amount of padding at the end of the tag
+ * to fix cover art display with some software such as iTunes, Traktor,
+ * Serato, Torq. */
+ padding_bytes = av_clip(padding_bytes, 10, 268435455 - id3->len);
+ ffio_fill(pb, 0, padding_bytes);
+ id3->len += padding_bytes;
+
+ cur_pos = avio_tell(pb);
avio_seek(pb, id3->size_pos, SEEK_SET);
id3v2_put_size(pb, id3->len);
avio_seek(pb, cur_pos, SEEK_SET);
@@ -238,7 +355,7 @@ int ff_id3v2_write_simple(struct AVFormatContext *s, int id3v2_version,
ff_id3v2_start(&id3, s->pb, id3v2_version, magic);
if ((ret = ff_id3v2_write_metadata(s, &id3)) < 0)
return ret;
- ff_id3v2_finish(&id3, s->pb);
+ ff_id3v2_finish(&id3, s->pb, s->metadata_header_padding);
return 0;
}
diff --git a/libavformat/idcin.c b/libavformat/idcin.c
index 3aa0e3ea50..d7a46a17fd 100644
--- a/libavformat/idcin.c
+++ b/libavformat/idcin.c
@@ -1,21 +1,21 @@
/*
* id Quake II CIN File Demuxer
- * Copyright (c) 2003 The ffmpeg Project
+ * Copyright (c) 2003 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -93,7 +93,9 @@ typedef struct IdcinDemuxContext {
static int idcin_probe(AVProbeData *p)
{
- unsigned int number;
+ unsigned int number, sample_rate;
+ unsigned int w, h;
+ int i;
/*
* This is what you could call a "probabilistic" file check: id CIN
@@ -108,34 +110,41 @@ static int idcin_probe(AVProbeData *p)
/* check we have enough data to do all checks, otherwise the
0-padding may cause a wrong recognition */
- if (p->buf_size < 20)
+ if (p->buf_size < 20 + HUFFMAN_TABLE_SIZE + 12)
return 0;
/* check the video width */
- number = AV_RL32(&p->buf[0]);
- if ((number == 0) || (number > 1024))
+ w = AV_RL32(&p->buf[0]);
+ if ((w == 0) || (w > 1024))
return 0;
/* check the video height */
- number = AV_RL32(&p->buf[4]);
- if ((number == 0) || (number > 1024))
+ h = AV_RL32(&p->buf[4]);
+ if ((h == 0) || (h > 1024))
return 0;
/* check the audio sample rate */
- number = AV_RL32(&p->buf[8]);
- if ((number != 0) && ((number < 8000) | (number > 48000)))
+ sample_rate = AV_RL32(&p->buf[8]);
+ if (sample_rate && (sample_rate < 8000 || sample_rate > 48000))
return 0;
/* check the audio bytes/sample */
number = AV_RL32(&p->buf[12]);
- if (number > 2)
+ if (number > 2 || sample_rate && !number)
return 0;
/* check the audio channels */
number = AV_RL32(&p->buf[16]);
- if (number > 2)
+ if (number > 2 || sample_rate && !number)
return 0;
+ i = 20 + HUFFMAN_TABLE_SIZE;
+ if (AV_RL32(&p->buf[i]) == 1)
+ i += 768;
+
+ if (i+12 > p->buf_size || AV_RL32(&p->buf[i+8]) != w*h)
+ return 1;
+
/* return half certainty since this check is a bit sketchy */
return AVPROBE_SCORE_EXTENSION;
}
@@ -196,15 +205,8 @@ static int idcin_read_header(AVFormatContext *s)
st->codec->height = height;
/* load up the Huffman tables into extradata */
- st->codec->extradata_size = HUFFMAN_TABLE_SIZE;
- st->codec->extradata = av_malloc(HUFFMAN_TABLE_SIZE);
- ret = avio_read(pb, st->codec->extradata, HUFFMAN_TABLE_SIZE);
- if (ret < 0) {
+ if ((ret = ff_get_extradata(st->codec, pb, HUFFMAN_TABLE_SIZE)) < 0)
return ret;
- } else if (ret != HUFFMAN_TABLE_SIZE) {
- av_log(s, AV_LOG_ERROR, "incomplete header\n");
- return AVERROR(EIO);
- }
if (idcin->audio_present) {
idcin->audio_present = 1;
@@ -260,7 +262,7 @@ static int idcin_read_packet(AVFormatContext *s,
unsigned char palette_buffer[768];
uint32_t palette[256];
- if (s->pb->eof_reached)
+ if (avio_feof(s->pb))
return s->pb->error ? s->pb->error : AVERROR_EOF;
if (idcin->next_chunk_is_video) {
@@ -288,7 +290,9 @@ static int idcin_read_packet(AVFormatContext *s,
r = palette_buffer[i * 3 ] << palette_scale;
g = palette_buffer[i * 3 + 1] << palette_scale;
b = palette_buffer[i * 3 + 2] << palette_scale;
- palette[i] = (r << 16) | (g << 8) | (b);
+ palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | (b);
+ if (palette_scale == 2)
+ palette[i] |= palette[i] >> 6 & 0x30303;
}
}
@@ -303,6 +307,8 @@ static int idcin_read_packet(AVFormatContext *s,
}
/* skip the number of decoded bytes (always equal to width * height) */
avio_skip(pb, 4);
+ if (chunk_size < 4)
+ return AVERROR_INVALIDDATA;
chunk_size -= 4;
ret= av_get_packet(pb, pkt, chunk_size);
if (ret < 0)
diff --git a/libavformat/idroqdec.c b/libavformat/idroqdec.c
index 82eff24e94..76bb3924b1 100644
--- a/libavformat/idroqdec.c
+++ b/libavformat/idroqdec.c
@@ -1,21 +1,21 @@
/*
* id RoQ (.roq) File Demuxer
- * Copyright (c) 2003 The ffmpeg Project
+ * Copyright (c) 2003 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -31,6 +31,7 @@
#include "libavutil/intreadwrite.h"
#include "avformat.h"
#include "internal.h"
+#include "avio_internal.h"
#define RoQ_MAGIC_NUMBER 0x1084
#define RoQ_CHUNK_PREAMBLE_SIZE 8
@@ -105,7 +106,7 @@ static int roq_read_packet(AVFormatContext *s,
while (!packet_read) {
- if (s->pb->eof_reached)
+ if (avio_feof(s->pb))
return AVERROR(EIO);
/* get the next chunk preamble */
@@ -118,6 +119,8 @@ static int roq_read_packet(AVFormatContext *s,
if(chunk_size > INT_MAX)
return AVERROR_INVALIDDATA;
+ chunk_size = ffio_limit(pb, chunk_size);
+
switch (chunk_type) {
case RoQ_INFO:
diff --git a/libavformat/idroqenc.c b/libavformat/idroqenc.c
index 2ce4d7d493..28a3aba9d4 100644
--- a/libavformat/idroqenc.c
+++ b/libavformat/idroqenc.c
@@ -2,20 +2,20 @@
* id RoQ (.roq) File muxer
* Copyright (c) 2007 Vitor Sessak
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -25,9 +25,35 @@
static int roq_write_header(struct AVFormatContext *s)
{
- static const uint8_t header[] = {
- 0x84, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0x1E, 0x00
+ uint8_t header[] = {
+ 0x84, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, /* fps: */ 0x1E, 0x00
};
+ int n;
+ AVCodecContext *avctx;
+
+// set the actual fps
+ for(n=0;n<s->nb_streams;n++) {
+ if ((avctx=s->streams[n]->codec)->codec_type == AVMEDIA_TYPE_VIDEO) {
+ unsigned int fps;
+
+ if (avctx->time_base.num != 1) {
+ av_log(avctx, AV_LOG_ERROR, "Frame rate must be integer\n");
+ return AVERROR(EINVAL);
+ }
+
+ if ((fps=avctx->time_base.den) > 255) {
+ av_log(avctx, AV_LOG_ERROR, "Frame rate may not exceed 255fps\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (fps != 30) {
+ av_log(avctx, AV_LOG_WARNING, "For vintage compatibility fps must be 30\n");
+ }
+
+ header[6] = fps;
+ break;
+ }
+ }
avio_write(s->pb, header, 8);
avio_flush(s->pb);
diff --git a/libavformat/iff.c b/libavformat/iff.c
index bf092152bd..7235bc1c9c 100644
--- a/libavformat/iff.c
+++ b/libavformat/iff.c
@@ -1,23 +1,22 @@
/*
- * IFF (.iff) file demuxer
* Copyright (c) 2008 Jaikrishnan Menon <realityman@gmx.net>
* Copyright (c) 2010 Peter Ross <pross@xvid.org>
* Copyright (c) 2010 Sebastian Vater <cdgs.basty@googlemail.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -31,13 +30,20 @@
#include <inttypes.h>
+#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
+#include "libavcodec/bytestream.h"
#include "avformat.h"
+#include "id3v2.h"
#include "internal.h"
#define ID_8SVX MKTAG('8','S','V','X')
+#define ID_16SV MKTAG('1','6','S','V')
+#define ID_MAUD MKTAG('M','A','U','D')
+#define ID_MHDR MKTAG('M','H','D','R')
+#define ID_MDAT MKTAG('M','D','A','T')
#define ID_VHDR MKTAG('V','H','D','R')
#define ID_ATAK MKTAG('A','T','A','K')
#define ID_RLSE MKTAG('R','L','S','E')
@@ -45,9 +51,18 @@
#define ID_PBM MKTAG('P','B','M',' ')
#define ID_ILBM MKTAG('I','L','B','M')
#define ID_BMHD MKTAG('B','M','H','D')
+#define ID_DGBL MKTAG('D','G','B','L')
+#define ID_CAMG MKTAG('C','A','M','G')
#define ID_CMAP MKTAG('C','M','A','P')
+#define ID_ACBM MKTAG('A','C','B','M')
+#define ID_DEEP MKTAG('D','E','E','P')
+#define ID_RGB8 MKTAG('R','G','B','8')
+#define ID_RGBN MKTAG('R','G','B','N')
+#define ID_DSD MKTAG('D','S','D',' ')
+#define ID_ANIM MKTAG('A','N','I','M')
#define ID_FORM MKTAG('F','O','R','M')
+#define ID_FRM8 MKTAG('F','R','M','8')
#define ID_ANNO MKTAG('A','N','N','O')
#define ID_AUTH MKTAG('A','U','T','H')
#define ID_CHRS MKTAG('C','H','R','S')
@@ -56,31 +71,50 @@
#define ID_FVER MKTAG('F','V','E','R')
#define ID_NAME MKTAG('N','A','M','E')
#define ID_TEXT MKTAG('T','E','X','T')
+#define ID_ABIT MKTAG('A','B','I','T')
#define ID_BODY MKTAG('B','O','D','Y')
-#define ID_ANNO MKTAG('A','N','N','O')
+#define ID_DBOD MKTAG('D','B','O','D')
+#define ID_DPEL MKTAG('D','P','E','L')
+#define ID_DLOC MKTAG('D','L','O','C')
+#define ID_TVDC MKTAG('T','V','D','C')
#define LEFT 2
#define RIGHT 4
#define STEREO 6
+/**
+ * This number of bytes if added at the beginning of each AVPacket
+ * which contain additional information about video properties
+ * which has to be shared between demuxer and decoder.
+ * This number may change between frames, e.g. the demuxer might
+ * set it to smallest possible size of 2 to indicate that there's
+ * no extradata changing in this frame.
+ */
+#define IFF_EXTRA_VIDEO_SIZE 41
+
typedef enum {
COMP_NONE,
COMP_FIB,
COMP_EXP
} svx8_compression_type;
-typedef enum {
- BITMAP_RAW,
- BITMAP_BYTERUN1
-} bitmap_compression_type;
-
typedef struct IffDemuxContext {
- uint64_t body_pos;
+ int is_64bit; ///< chunk size is 64-bit
+ int64_t body_pos;
+ int64_t body_end;
uint32_t body_size;
- uint32_t sent_bytes;
+ svx8_compression_type svx8_compression;
+ unsigned maud_bits;
+ unsigned maud_compression;
+ unsigned bitmap_compression; ///< delta compression method used
+ unsigned bpp; ///< bits per plane to decode (differs from bits_per_coded_sample if HAM)
+ unsigned ham; ///< 0 if non-HAM or number of hold bits (6 for bpp > 6, 4 otherwise)
+ unsigned flags; ///< 1 for EHB, 0 is no extra half darkening
+ unsigned transparency; ///< transparency color index in palette
+ unsigned masking; ///< masking method used
+ uint8_t tvdc[32]; ///< TVDC lookup table
} IffDemuxContext;
-
/* Metadata string read */
static int get_metadata(AVFormatContext *s,
const char *const tag,
@@ -91,7 +125,7 @@ static int get_metadata(AVFormatContext *s,
if (!buf)
return AVERROR(ENOMEM);
- if (avio_read(s->pb, buf, data_size) < 0) {
+ if (avio_read(s->pb, buf, data_size) != data_size) {
av_free(buf);
return AVERROR(EIO);
}
@@ -104,19 +138,223 @@ static int iff_probe(AVProbeData *p)
{
const uint8_t *d = p->buf;
- if ( AV_RL32(d) == ID_FORM &&
- (AV_RL32(d+8) == ID_8SVX || AV_RL32(d+8) == ID_PBM || AV_RL32(d+8) == ID_ILBM) )
+ if ( (AV_RL32(d) == ID_FORM &&
+ (AV_RL32(d+8) == ID_8SVX ||
+ AV_RL32(d+8) == ID_16SV ||
+ AV_RL32(d+8) == ID_MAUD ||
+ AV_RL32(d+8) == ID_PBM ||
+ AV_RL32(d+8) == ID_ACBM ||
+ AV_RL32(d+8) == ID_DEEP ||
+ AV_RL32(d+8) == ID_ILBM ||
+ AV_RL32(d+8) == ID_RGB8 ||
+ AV_RL32(d+8) == ID_RGB8 ||
+ AV_RL32(d+8) == ID_ANIM ||
+ AV_RL32(d+8) == ID_RGBN)) ||
+ (AV_RL32(d) == ID_FRM8 && AV_RL32(d+12) == ID_DSD))
return AVPROBE_SCORE_MAX;
return 0;
}
+static const AVCodecTag dsd_codec_tags[] = {
+ { AV_CODEC_ID_DSD_MSBF, ID_DSD },
+ { AV_CODEC_ID_NONE, 0 },
+};
+
+
+#define DSD_SLFT MKTAG('S','L','F','T')
+#define DSD_SRGT MKTAG('S','R','G','T')
+#define DSD_MLFT MKTAG('M','L','F','T')
+#define DSD_MRGT MKTAG('M','R','G','T')
+#define DSD_C MKTAG('C',' ',' ',' ')
+#define DSD_LS MKTAG('L','S',' ',' ')
+#define DSD_RS MKTAG('R','S',' ',' ')
+#define DSD_LFE MKTAG('L','F','E',' ')
+
+static const uint32_t dsd_stereo[] = { DSD_SLFT, DSD_SRGT };
+static const uint32_t dsd_5point0[] = { DSD_MLFT, DSD_MRGT, DSD_C, DSD_LS, DSD_RS };
+static const uint32_t dsd_5point1[] = { DSD_MLFT, DSD_MRGT, DSD_C, DSD_LFE, DSD_LS, DSD_RS };
+
+typedef struct {
+ uint64_t layout;
+ const uint32_t * dsd_layout;
+} DSDLayoutDesc;
+
+static const DSDLayoutDesc dsd_channel_layout[] = {
+ { AV_CH_LAYOUT_STEREO, dsd_stereo },
+ { AV_CH_LAYOUT_5POINT0, dsd_5point0 },
+ { AV_CH_LAYOUT_5POINT1, dsd_5point1 },
+};
+
+static const uint64_t dsd_loudspeaker_config[] = {
+ AV_CH_LAYOUT_STEREO,
+ 0, 0,
+ AV_CH_LAYOUT_5POINT0, AV_CH_LAYOUT_5POINT1,
+};
+
+static const char * dsd_source_comment[] = {
+ "dsd_source_comment",
+ "analogue_source_comment",
+ "pcm_source_comment",
+};
+
+static const char * dsd_history_comment[] = {
+ "general_remark",
+ "operator_name",
+ "creating_machine",
+ "timezone",
+ "file_revision"
+};
+
+static int parse_dsd_diin(AVFormatContext *s, AVStream *st, uint64_t eof)
+{
+ AVIOContext *pb = s->pb;
+
+ while (avio_tell(pb) + 12 <= eof) {
+ uint32_t tag = avio_rl32(pb);
+ uint64_t size = avio_rb64(pb);
+ uint64_t orig_pos = avio_tell(pb);
+ const char * metadata_tag = NULL;
+
+ switch(tag) {
+ case MKTAG('D','I','A','R'): metadata_tag = "artist"; break;
+ case MKTAG('D','I','T','I'): metadata_tag = "title"; break;
+ }
+
+ if (metadata_tag && size > 4) {
+ unsigned int tag_size = avio_rb32(pb);
+ int ret = get_metadata(s, metadata_tag, FFMIN(tag_size, size - 4));
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "cannot allocate metadata tag %s!\n", metadata_tag);
+ return ret;
+ }
+ }
+
+ avio_skip(pb, size - (avio_tell(pb) - orig_pos) + (size & 1));
+ }
+
+ return 0;
+}
+
+static int parse_dsd_prop(AVFormatContext *s, AVStream *st, uint64_t eof)
+{
+ AVIOContext *pb = s->pb;
+ char abss[24];
+ int hour, min, sec, i, ret, config;
+ int dsd_layout[6];
+ ID3v2ExtraMeta *id3v2_extra_meta;
+
+ while (avio_tell(pb) + 12 <= eof) {
+ uint32_t tag = avio_rl32(pb);
+ uint64_t size = avio_rb64(pb);
+ uint64_t orig_pos = avio_tell(pb);
+
+ switch(tag) {
+ case MKTAG('A','B','S','S'):
+ if (size < 8)
+ return AVERROR_INVALIDDATA;
+ hour = avio_rb16(pb);
+ min = avio_r8(pb);
+ sec = avio_r8(pb);
+ snprintf(abss, sizeof(abss), "%02dh:%02dm:%02ds:%d", hour, min, sec, avio_rb32(pb));
+ av_dict_set(&st->metadata, "absolute_start_time", abss, 0);
+ break;
+
+ case MKTAG('C','H','N','L'):
+ if (size < 2)
+ return AVERROR_INVALIDDATA;
+ st->codec->channels = avio_rb16(pb);
+ if (size < 2 + st->codec->channels * 4)
+ return AVERROR_INVALIDDATA;
+ st->codec->channel_layout = 0;
+ if (st->codec->channels > FF_ARRAY_ELEMS(dsd_layout)) {
+ avpriv_request_sample(s, "channel layout");
+ break;
+ }
+ for (i = 0; i < st->codec->channels; i++)
+ dsd_layout[i] = avio_rl32(pb);
+ for (i = 0; i < FF_ARRAY_ELEMS(dsd_channel_layout); i++) {
+ const DSDLayoutDesc * d = &dsd_channel_layout[i];
+ if (av_get_channel_layout_nb_channels(d->layout) == st->codec->channels &&
+ !memcmp(d->dsd_layout, dsd_layout, st->codec->channels * sizeof(uint32_t))) {
+ st->codec->channel_layout = d->layout;
+ break;
+ }
+ }
+ break;
+
+ case MKTAG('C','M','P','R'):
+ if (size < 4)
+ return AVERROR_INVALIDDATA;
+ tag = avio_rl32(pb);
+ st->codec->codec_id = ff_codec_get_id(dsd_codec_tags, tag);
+ if (!st->codec->codec_id) {
+ av_log(s, AV_LOG_ERROR, "'%c%c%c%c' compression is not supported\n",
+ tag&0xFF, (tag>>8)&0xFF, (tag>>16)&0xFF, (tag>>24)&0xFF);
+ return AVERROR_PATCHWELCOME;
+ }
+ break;
+
+ case MKTAG('F','S',' ',' '):
+ if (size < 4)
+ return AVERROR_INVALIDDATA;
+ st->codec->sample_rate = avio_rb32(pb) / 8;
+ break;
+
+ case MKTAG('I','D','3',' '):
+ id3v2_extra_meta = NULL;
+ ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, size);
+ if (id3v2_extra_meta) {
+ if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0) {
+ ff_id3v2_free_extra_meta(&id3v2_extra_meta);
+ return ret;
+ }
+ ff_id3v2_free_extra_meta(&id3v2_extra_meta);
+ }
+
+ if (size < avio_tell(pb) - orig_pos) {
+ av_log(s, AV_LOG_ERROR, "id3 exceeds chunk size\n");
+ return AVERROR_INVALIDDATA;
+ }
+ break;
+
+ case MKTAG('L','S','C','O'):
+ if (size < 2)
+ return AVERROR_INVALIDDATA;
+ config = avio_rb16(pb);
+ if (config != 0xFFFF) {
+ if (config < FF_ARRAY_ELEMS(dsd_loudspeaker_config))
+ st->codec->channel_layout = dsd_loudspeaker_config[config];
+ if (!st->codec->channel_layout)
+ avpriv_request_sample(s, "loudspeaker configuration %d", config);
+ }
+ break;
+ }
+
+ avio_skip(pb, size - (avio_tell(pb) - orig_pos) + (size & 1));
+ }
+
+ return 0;
+}
+
+static const uint8_t deep_rgb24[] = {0, 0, 0, 3, 0, 1, 0, 8, 0, 2, 0, 8, 0, 3, 0, 8};
+static const uint8_t deep_rgba[] = {0, 0, 0, 4, 0, 1, 0, 8, 0, 2, 0, 8, 0, 3, 0, 8};
+static const uint8_t deep_bgra[] = {0, 0, 0, 4, 0, 3, 0, 8, 0, 2, 0, 8, 0, 1, 0, 8};
+static const uint8_t deep_argb[] = {0, 0, 0, 4, 0,17, 0, 8, 0, 1, 0, 8, 0, 2, 0, 8};
+static const uint8_t deep_abgr[] = {0, 0, 0, 4, 0,17, 0, 8, 0, 3, 0, 8, 0, 2, 0, 8};
+
static int iff_read_header(AVFormatContext *s)
{
IffDemuxContext *iff = s->priv_data;
AVIOContext *pb = s->pb;
AVStream *st;
- uint32_t chunk_id, data_size;
- int compression = -1;
+ uint8_t *buf;
+ uint32_t chunk_id;
+ uint64_t data_size;
+ uint32_t screenmode = 0, num, den;
+ unsigned transparency = 0;
+ unsigned masking = 0; // no mask
+ uint8_t fmt[16];
+ int fmt_size;
st = avformat_new_stream(s, NULL);
if (!st)
@@ -124,16 +362,26 @@ static int iff_read_header(AVFormatContext *s)
st->codec->channels = 1;
st->codec->channel_layout = AV_CH_LAYOUT_MONO;
- avio_skip(pb, 8);
+ iff->is_64bit = avio_rl32(pb) == ID_FRM8;
+ avio_skip(pb, iff->is_64bit ? 8 : 4);
// codec_tag used by ByteRun1 decoder to distinguish progressive (PBM) and interlaced (ILBM) content
st->codec->codec_tag = avio_rl32(pb);
+ if (st->codec->codec_tag == ID_ANIM) {
+ avio_skip(pb, 8);
+ st->codec->codec_tag = avio_rl32(pb);
+ }
+ iff->bitmap_compression = -1;
+ iff->svx8_compression = -1;
+ iff->maud_bits = -1;
+ iff->maud_compression = -1;
- while(!pb->eof_reached) {
+ while(!avio_feof(pb)) {
uint64_t orig_pos;
int res;
const char *metadata_tag = NULL;
+ int version, nb_comments, i;
chunk_id = avio_rl32(pb);
- data_size = avio_rb32(pb);
+ data_size = iff->is_64bit ? avio_rb64(pb) : avio_rb32(pb);
orig_pos = avio_tell(pb);
switch(chunk_id) {
@@ -146,12 +394,39 @@ static int iff_read_header(AVFormatContext *s)
st->codec->sample_rate = avio_rb16(pb);
if (data_size >= 16) {
avio_skip(pb, 1);
- compression = avio_r8(pb);
+ iff->svx8_compression = avio_r8(pb);
}
break;
+ case ID_MHDR:
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+
+ if (data_size < 32)
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, 4);
+ iff->maud_bits = avio_rb16(pb);
+ avio_skip(pb, 2);
+ num = avio_rb32(pb);
+ den = avio_rb16(pb);
+ if (!den)
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, 2);
+ st->codec->sample_rate = num / den;
+ st->codec->channels = avio_rb16(pb);
+ iff->maud_compression = avio_rb16(pb);
+ if (st->codec->channels == 1)
+ st->codec->channel_layout = AV_CH_LAYOUT_MONO;
+ else if (st->codec->channels == 2)
+ st->codec->channel_layout = AV_CH_LAYOUT_STEREO;
+ break;
+
+ case ID_ABIT:
case ID_BODY:
+ case ID_DBOD:
+ case ID_DSD:
+ case ID_MDAT:
iff->body_pos = avio_tell(pb);
+ iff->body_end = iff->body_pos + data_size;
iff->body_size = data_size;
break;
@@ -167,17 +442,23 @@ static int iff_read_header(AVFormatContext *s)
}
break;
+ case ID_CAMG:
+ if (data_size < 4)
+ return AVERROR_INVALIDDATA;
+ screenmode = avio_rb32(pb);
+ break;
+
case ID_CMAP:
if (data_size < 3 || data_size > 768 || data_size % 3) {
- av_log(s, AV_LOG_ERROR, "Invalid CMAP chunk size %"PRIu32"\n",
+ av_log(s, AV_LOG_ERROR, "Invalid CMAP chunk size %"PRIu64"\n",
data_size);
return AVERROR_INVALIDDATA;
}
- st->codec->extradata_size = data_size;
- st->codec->extradata = av_malloc(data_size);
+ st->codec->extradata_size = data_size + IFF_EXTRA_VIDEO_SIZE;
+ st->codec->extradata = av_malloc(data_size + IFF_EXTRA_VIDEO_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
if (!st->codec->extradata)
return AVERROR(ENOMEM);
- if (avio_read(pb, st->codec->extradata, data_size) < 0)
+ if (avio_read(pb, st->codec->extradata + IFF_EXTRA_VIDEO_SIZE, data_size) < 0)
return AVERROR(EIO);
break;
@@ -189,38 +470,156 @@ static int iff_read_header(AVFormatContext *s)
st->codec->height = avio_rb16(pb);
avio_skip(pb, 4); // x, y offset
st->codec->bits_per_coded_sample = avio_r8(pb);
- if (data_size >= 11) {
- avio_skip(pb, 1); // masking
- compression = avio_r8(pb);
+ if (data_size >= 10)
+ masking = avio_r8(pb);
+ if (data_size >= 11)
+ iff->bitmap_compression = avio_r8(pb);
+ if (data_size >= 14) {
+ avio_skip(pb, 1); // padding
+ transparency = avio_rb16(pb);
}
if (data_size >= 16) {
- avio_skip(pb, 3); // paddding, transparent
st->sample_aspect_ratio.num = avio_r8(pb);
st->sample_aspect_ratio.den = avio_r8(pb);
}
break;
+ case ID_DPEL:
+ if (data_size < 4 || (data_size & 3))
+ return AVERROR_INVALIDDATA;
+ if ((fmt_size = avio_read(pb, fmt, sizeof(fmt))) < 0)
+ return fmt_size;
+ if (fmt_size == sizeof(deep_rgb24) && !memcmp(fmt, deep_rgb24, sizeof(deep_rgb24)))
+ st->codec->pix_fmt = AV_PIX_FMT_RGB24;
+ else if (fmt_size == sizeof(deep_rgba) && !memcmp(fmt, deep_rgba, sizeof(deep_rgba)))
+ st->codec->pix_fmt = AV_PIX_FMT_RGBA;
+ else if (fmt_size == sizeof(deep_bgra) && !memcmp(fmt, deep_bgra, sizeof(deep_bgra)))
+ st->codec->pix_fmt = AV_PIX_FMT_BGRA;
+ else if (fmt_size == sizeof(deep_argb) && !memcmp(fmt, deep_argb, sizeof(deep_argb)))
+ st->codec->pix_fmt = AV_PIX_FMT_ARGB;
+ else if (fmt_size == sizeof(deep_abgr) && !memcmp(fmt, deep_abgr, sizeof(deep_abgr)))
+ st->codec->pix_fmt = AV_PIX_FMT_ABGR;
+ else {
+ avpriv_request_sample(s, "color format %.16s", fmt);
+ return AVERROR_PATCHWELCOME;
+ }
+ break;
+
+ case ID_DGBL:
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ if (data_size < 8)
+ return AVERROR_INVALIDDATA;
+ st->codec->width = avio_rb16(pb);
+ st->codec->height = avio_rb16(pb);
+ iff->bitmap_compression = avio_rb16(pb);
+ st->sample_aspect_ratio.num = avio_r8(pb);
+ st->sample_aspect_ratio.den = avio_r8(pb);
+ st->codec->bits_per_coded_sample = 24;
+ break;
+
+ case ID_DLOC:
+ if (data_size < 4)
+ return AVERROR_INVALIDDATA;
+ st->codec->width = avio_rb16(pb);
+ st->codec->height = avio_rb16(pb);
+ break;
+
+ case ID_TVDC:
+ if (data_size < sizeof(iff->tvdc))
+ return AVERROR_INVALIDDATA;
+ res = avio_read(pb, iff->tvdc, sizeof(iff->tvdc));
+ if (res < 0)
+ return res;
+ break;
+
case ID_ANNO:
- case ID_TEXT:
- metadata_tag = "comment";
+ case ID_TEXT: metadata_tag = "comment"; break;
+ case ID_AUTH: metadata_tag = "artist"; break;
+ case ID_COPYRIGHT: metadata_tag = "copyright"; break;
+ case ID_NAME: metadata_tag = "title"; break;
+
+ /* DSD tags */
+
+ case MKTAG('F','V','E','R'):
+ if (data_size < 4)
+ return AVERROR_INVALIDDATA;
+ version = avio_rb32(pb);
+ av_log(s, AV_LOG_DEBUG, "DSIFF v%d.%d.%d.%d\n",version >> 24, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
break;
- case ID_AUTH:
- metadata_tag = "artist";
+ case MKTAG('D','I','I','N'):
+ res = parse_dsd_diin(s, st, orig_pos + data_size);
+ if (res < 0)
+ return res;
break;
- case ID_COPYRIGHT:
- metadata_tag = "copyright";
+ case MKTAG('P','R','O','P'):
+ if (data_size < 4)
+ return AVERROR_INVALIDDATA;
+ if (avio_rl32(pb) != MKTAG('S','N','D',' ')) {
+ avpriv_request_sample(s, "unknown property type");
+ break;
+ }
+ res = parse_dsd_prop(s, st, orig_pos + data_size);
+ if (res < 0)
+ return res;
break;
- case ID_NAME:
- metadata_tag = "title";
+ case MKTAG('C','O','M','T'):
+ if (data_size < 2)
+ return AVERROR_INVALIDDATA;
+ nb_comments = avio_rb16(pb);
+ for (i = 0; i < nb_comments; i++) {
+ int year, mon, day, hour, min, type, ref;
+ char tmp[24];
+ const char *tag;
+ int metadata_size;
+
+ year = avio_rb16(pb);
+ mon = avio_r8(pb);
+ day = avio_r8(pb);
+ hour = avio_r8(pb);
+ min = avio_r8(pb);
+ snprintf(tmp, sizeof(tmp), "%04d-%02d-%02d %02d:%02d", year, mon, day, hour, min);
+ av_dict_set(&st->metadata, "comment_time", tmp, 0);
+
+ type = avio_rb16(pb);
+ ref = avio_rb16(pb);
+ switch (type) {
+ case 1:
+ if (!i)
+ tag = "channel_comment";
+ else {
+ snprintf(tmp, sizeof(tmp), "channel%d_comment", ref);
+ tag = tmp;
+ }
+ break;
+ case 2:
+ tag = ref < FF_ARRAY_ELEMS(dsd_source_comment) ? dsd_source_comment[ref] : "source_comment";
+ break;
+ case 3:
+ tag = ref < FF_ARRAY_ELEMS(dsd_history_comment) ? dsd_history_comment[ref] : "file_history";
+ break;
+ default:
+ tag = "comment";
+ }
+
+ metadata_size = avio_rb32(pb);
+ if ((res = get_metadata(s, tag, metadata_size)) < 0) {
+ av_log(s, AV_LOG_ERROR, "cannot allocate metadata tag %s!\n", tag);
+ return res;
+ }
+
+ if (metadata_size & 1)
+ avio_skip(pb, 1);
+ }
break;
}
if (metadata_tag) {
if ((res = get_metadata(s, metadata_tag, data_size)) < 0) {
- av_log(s, AV_LOG_ERROR, "cannot allocate metadata tag %s!", metadata_tag);
+ av_log(s, AV_LOG_ERROR, "cannot allocate metadata tag %s!\n", metadata_tag);
return res;
}
}
@@ -233,38 +632,71 @@ static int iff_read_header(AVFormatContext *s)
case AVMEDIA_TYPE_AUDIO:
avpriv_set_pts_info(st, 32, 1, st->codec->sample_rate);
- switch(compression) {
- case COMP_NONE:
- st->codec->codec_id = AV_CODEC_ID_PCM_S8_PLANAR;
- break;
- case COMP_FIB:
- st->codec->codec_id = AV_CODEC_ID_8SVX_FIB;
- break;
- case COMP_EXP:
- st->codec->codec_id = AV_CODEC_ID_8SVX_EXP;
- break;
- default:
- av_log(s, AV_LOG_ERROR, "unknown compression method\n");
- return -1;
+ if (st->codec->codec_tag == ID_16SV)
+ st->codec->codec_id = AV_CODEC_ID_PCM_S16BE_PLANAR;
+ else if (st->codec->codec_tag == ID_MAUD) {
+ if (iff->maud_bits == 8 && !iff->maud_compression) {
+ st->codec->codec_id = AV_CODEC_ID_PCM_U8;
+ } else if (iff->maud_bits == 16 && !iff->maud_compression) {
+ st->codec->codec_id = AV_CODEC_ID_PCM_S16BE;
+ } else if (iff->maud_bits == 8 && iff->maud_compression == 2) {
+ st->codec->codec_id = AV_CODEC_ID_PCM_ALAW;
+ } else if (iff->maud_bits == 8 && iff->maud_compression == 3) {
+ st->codec->codec_id = AV_CODEC_ID_PCM_MULAW;
+ } else {
+ avpriv_request_sample(s, "compression %d and bit depth %d", iff->maud_compression, iff->maud_bits);
+ return AVERROR_PATCHWELCOME;
+ }
+ } else if (st->codec->codec_tag != ID_DSD) {
+ switch (iff->svx8_compression) {
+ case COMP_NONE:
+ st->codec->codec_id = AV_CODEC_ID_PCM_S8_PLANAR;
+ break;
+ case COMP_FIB:
+ st->codec->codec_id = AV_CODEC_ID_8SVX_FIB;
+ break;
+ case COMP_EXP:
+ st->codec->codec_id = AV_CODEC_ID_8SVX_EXP;
+ break;
+ default:
+ av_log(s, AV_LOG_ERROR,
+ "Unknown SVX8 compression method '%d'\n", iff->svx8_compression);
+ return -1;
+ }
}
- st->codec->bits_per_coded_sample = 8;
+ st->codec->bits_per_coded_sample = av_get_bits_per_sample(st->codec->codec_id);
st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * st->codec->bits_per_coded_sample;
st->codec->block_align = st->codec->channels * st->codec->bits_per_coded_sample;
break;
case AVMEDIA_TYPE_VIDEO:
- switch (compression) {
- case BITMAP_RAW:
- st->codec->codec_id = AV_CODEC_ID_IFF_ILBM;
- break;
- case BITMAP_BYTERUN1:
- st->codec->codec_id = AV_CODEC_ID_IFF_BYTERUN1;
- break;
- default:
- av_log(s, AV_LOG_ERROR, "unknown compression method\n");
- return AVERROR_INVALIDDATA;
+ iff->bpp = st->codec->bits_per_coded_sample;
+ if ((screenmode & 0x800 /* Hold And Modify */) && iff->bpp <= 8) {
+ iff->ham = iff->bpp > 6 ? 6 : 4;
+ st->codec->bits_per_coded_sample = 24;
+ }
+ iff->flags = (screenmode & 0x80 /* Extra HalfBrite */) && iff->bpp <= 8;
+ iff->masking = masking;
+ iff->transparency = transparency;
+
+ if (!st->codec->extradata) {
+ st->codec->extradata_size = IFF_EXTRA_VIDEO_SIZE;
+ st->codec->extradata = av_malloc(IFF_EXTRA_VIDEO_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
+ if (!st->codec->extradata)
+ return AVERROR(ENOMEM);
}
+ av_assert0(st->codec->extradata_size >= IFF_EXTRA_VIDEO_SIZE);
+ buf = st->codec->extradata;
+ bytestream_put_be16(&buf, IFF_EXTRA_VIDEO_SIZE);
+ bytestream_put_byte(&buf, iff->bitmap_compression);
+ bytestream_put_byte(&buf, iff->bpp);
+ bytestream_put_byte(&buf, iff->ham);
+ bytestream_put_byte(&buf, iff->flags);
+ bytestream_put_be16(&buf, iff->transparency);
+ bytestream_put_byte(&buf, iff->masking);
+ bytestream_put_buffer(&buf, iff->tvdc, sizeof(iff->tvdc));
+ st->codec->codec_id = AV_CODEC_ID_IFF_ILBM;
break;
default:
return -1;
@@ -278,19 +710,41 @@ static int iff_read_packet(AVFormatContext *s,
{
IffDemuxContext *iff = s->priv_data;
AVIOContext *pb = s->pb;
+ AVStream *st = s->streams[0];
int ret;
+ int64_t pos = avio_tell(pb);
- if(iff->sent_bytes >= iff->body_size)
+ if (pos >= iff->body_end)
return AVERROR_EOF;
- ret = av_get_packet(pb, pkt, iff->body_size);
- if (ret < 0)
- return ret;
+ if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
+ if (st->codec->codec_tag == ID_DSD || st->codec->codec_tag == ID_MAUD) {
+ ret = av_get_packet(pb, pkt, FFMIN(iff->body_end - pos, 1024 * st->codec->block_align));
+ } else {
+ ret = av_get_packet(pb, pkt, iff->body_size);
+ }
+ } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ uint8_t *buf;
- if(iff->sent_bytes == 0)
- pkt->flags |= AV_PKT_FLAG_KEY;
- iff->sent_bytes = iff->body_size;
+ if (av_new_packet(pkt, iff->body_size + 2) < 0) {
+ return AVERROR(ENOMEM);
+ }
+
+ buf = pkt->data;
+ bytestream_put_be16(&buf, 2);
+ ret = avio_read(pb, buf, iff->body_size);
+ if (ret<0) {
+ av_free_packet(pkt);
+ } else if (ret < iff->body_size)
+ av_shrink_packet(pkt, ret + 2);
+ } else {
+ av_assert0(0);
+ }
+ if (pos == iff->body_pos)
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ if (ret < 0)
+ return ret;
pkt->stream_index = 0;
return ret;
}
@@ -302,4 +756,5 @@ AVInputFormat ff_iff_demuxer = {
.read_probe = iff_probe,
.read_header = iff_read_header,
.read_packet = iff_read_packet,
+ .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK,
};
diff --git a/libavformat/ilbc.c b/libavformat/ilbc.c
index e44af487b2..3f154ce29c 100644
--- a/libavformat/ilbc.c
+++ b/libavformat/ilbc.c
@@ -2,20 +2,20 @@
* iLBC storage file format
* Copyright (c) 2012 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/img2.c b/libavformat/img2.c
index 2128b2ecdf..8493a3e824 100644
--- a/libavformat/img2.c
+++ b/libavformat/img2.c
@@ -3,20 +3,20 @@
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
* Copyright (c) 2004 Michael Niedermayer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -34,6 +34,7 @@ static const IdStrMap img_tags[] = {
{ AV_CODEC_ID_MJPEG, "jps" },
{ AV_CODEC_ID_MJPEG, "mpo" },
{ AV_CODEC_ID_LJPEG, "ljpg" },
+ { AV_CODEC_ID_JPEGLS, "jls" },
{ AV_CODEC_ID_PNG, "png" },
{ AV_CODEC_ID_PNG, "pns" },
{ AV_CODEC_ID_PNG, "mng" },
@@ -49,14 +50,13 @@ static const IdStrMap img_tags[] = {
{ AV_CODEC_ID_MPEG4, "mpg4-img" },
{ AV_CODEC_ID_FFV1, "ffv1-img" },
{ AV_CODEC_ID_RAWVIDEO, "y" },
+ { AV_CODEC_ID_RAWVIDEO, "raw" },
{ AV_CODEC_ID_BMP, "bmp" },
- { AV_CODEC_ID_GIF, "gif" },
{ AV_CODEC_ID_TARGA, "tga" },
{ AV_CODEC_ID_TIFF, "tiff" },
{ AV_CODEC_ID_TIFF, "tif" },
{ AV_CODEC_ID_SGI, "sgi" },
{ AV_CODEC_ID_PTX, "ptx" },
- { AV_CODEC_ID_BRENDER_PIX,"pix" },
{ AV_CODEC_ID_PCX, "pcx" },
{ AV_CODEC_ID_SUNRAST, "sun" },
{ AV_CODEC_ID_SUNRAST, "ras" },
@@ -64,6 +64,7 @@ static const IdStrMap img_tags[] = {
{ AV_CODEC_ID_SUNRAST, "im1" },
{ AV_CODEC_ID_SUNRAST, "im8" },
{ AV_CODEC_ID_SUNRAST, "im24" },
+ { AV_CODEC_ID_SUNRAST, "im32" },
{ AV_CODEC_ID_SUNRAST, "sunras" },
{ AV_CODEC_ID_JPEG2000, "j2c" },
{ AV_CODEC_ID_JPEG2000, "jp2" },
@@ -72,8 +73,10 @@ static const IdStrMap img_tags[] = {
{ AV_CODEC_ID_DPX, "dpx" },
{ AV_CODEC_ID_EXR, "exr" },
{ AV_CODEC_ID_PICTOR, "pic" },
+ { AV_CODEC_ID_V210X, "yuv10" },
{ AV_CODEC_ID_WEBP, "webp" },
{ AV_CODEC_ID_XBM, "xbm" },
+ { AV_CODEC_ID_XFACE, "xface" },
{ AV_CODEC_ID_XWD, "xwd" },
{ AV_CODEC_ID_NONE, NULL }
};
diff --git a/libavformat/img2.h b/libavformat/img2.h
new file mode 100644
index 0000000000..67bd7a325f
--- /dev/null
+++ b/libavformat/img2.h
@@ -0,0 +1,63 @@
+/*
+ * Image format
+ * Copyright (c) 2014 Michael Niedermayer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_IMG2_H
+#define AVFORMAT_IMG2_H
+
+#include <stdint.h>
+#include "avformat.h"
+#include "libavutil/opt.h"
+
+#if HAVE_GLOB
+#include <glob.h>
+#endif
+
+typedef struct VideoDemuxData {
+ const AVClass *class; /**< Class for private options. */
+ int img_first;
+ int img_last;
+ int img_number;
+ int64_t pts;
+ int img_count;
+ int is_pipe;
+ int split_planes; /**< use independent file for each Y, U, V plane */
+ char path[1024];
+ char *pixel_format; /**< Set by a private option. */
+ int width, height; /**< Set by a private option. */
+ AVRational framerate; /**< Set by a private option. */
+ int loop;
+ enum { PT_GLOB_SEQUENCE, PT_GLOB, PT_SEQUENCE } pattern_type;
+ int use_glob;
+#if HAVE_GLOB
+ glob_t globstate;
+#endif
+ int start_number;
+ int start_number_range;
+ int frame_size;
+ int ts_from_file;
+} VideoDemuxData;
+
+extern const AVOption ff_img_options[];
+
+int ff_img_read_header(AVFormatContext *s1);
+
+int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt);
+#endif
diff --git a/libavformat/img2_alias_pix.c b/libavformat/img2_alias_pix.c
new file mode 100644
index 0000000000..c2650ad1b3
--- /dev/null
+++ b/libavformat/img2_alias_pix.c
@@ -0,0 +1,73 @@
+/*
+ * Alias PIX image demuxer
+ * Copyright (c) 2014 Michael Niedermayer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "img2.h"
+#include "libavcodec/bytestream.h"
+
+static int alias_pix_read_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+ const uint8_t *end = b + p->buf_size;
+ int width = bytestream_get_be16(&b);
+ int height = bytestream_get_be16(&b);
+ av_unused int ox = bytestream_get_be16(&b);
+ av_unused int oy = bytestream_get_be16(&b);
+ int bpp = bytestream_get_be16(&b);
+ int x, y;
+
+ if (!width || !height)
+ return 0;
+
+ if (bpp != 24 && bpp != 8)
+ return 0;
+
+ for (y=0; y<2 && y<height; y++) {
+ for (x=0; x<width; ) {
+ int count = *b++;
+ if (count == 0 || x + count > width)
+ return 0;
+ if (b > end)
+ return AVPROBE_SCORE_MAX / 8;
+ b += bpp / 8;
+ x += count;
+ }
+ }
+
+ return AVPROBE_SCORE_EXTENSION + 1;
+}
+
+static const AVClass image2_alias_pix_class = {
+ .class_name = "alias_pix demuxer",
+ .item_name = av_default_item_name,
+ .option = ff_img_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_image2_alias_pix_demuxer = {
+ .name = "alias_pix",
+ .long_name = NULL_IF_CONFIG_SMALL("Alias/Wavefront PIX image"),
+ .priv_data_size = sizeof(VideoDemuxData),
+ .read_probe = alias_pix_read_probe,
+ .read_header = ff_img_read_header,
+ .read_packet = ff_img_read_packet,
+ .raw_codec_id = AV_CODEC_ID_ALIAS_PIX,
+ .priv_class = &image2_alias_pix_class,
+};
diff --git a/libavformat/img2_brender_pix.c b/libavformat/img2_brender_pix.c
new file mode 100644
index 0000000000..ae6b3dd718
--- /dev/null
+++ b/libavformat/img2_brender_pix.c
@@ -0,0 +1,57 @@
+/*
+ * BRender PIX image demuxer
+ * Copyright (c) 2014 Michael Niedermayer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "img2.h"
+#include "libavutil/intreadwrite.h"
+
+static int brender_read_probe(AVProbeData *p)
+{
+ static const uint8_t brender_magic[16] = {
+ 0,0,0,0x12,0,0,0,8,0,0,0,2,0,0,0,2
+ };
+
+ if (memcmp(p->buf, brender_magic, sizeof(brender_magic)))
+ return 0;
+
+ if (AV_RB32(p->buf+16) != 0x03 &&
+ AV_RB32(p->buf+16) != 0x3D)
+ return 0;
+
+ return AVPROBE_SCORE_MAX-10;
+}
+
+static const AVClass image2_brender_pix_class = {
+ .class_name = "brender_pix demuxer",
+ .item_name = av_default_item_name,
+ .option = ff_img_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_image2_brender_pix_demuxer = {
+ .name = "brender_pix",
+ .long_name = NULL_IF_CONFIG_SMALL("BRender PIX image"),
+ .priv_data_size = sizeof(VideoDemuxData),
+ .read_probe = brender_read_probe,
+ .read_header = ff_img_read_header,
+ .read_packet = ff_img_read_packet,
+ .raw_codec_id = AV_CODEC_ID_BRENDER_PIX,
+ .priv_class = &image2_brender_pix_class,
+};
diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c
index fc6da1b7fe..8c5e9d5d56 100644
--- a/libavformat/img2dec.c
+++ b/libavformat/img2dec.c
@@ -3,45 +3,47 @@
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
* Copyright (c) 2004 Michael Niedermayer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#define _BSD_SOURCE
+#include <sys/stat.h>
#include "libavutil/avstring.h"
#include "libavutil/log.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "libavutil/parseutils.h"
+#include "libavutil/intreadwrite.h"
#include "avformat.h"
+#include "avio_internal.h"
#include "internal.h"
+#include "img2.h"
-typedef struct VideoDemuxData {
- const AVClass *class; /**< Class for private options. */
- int img_first;
- int img_last;
- int img_number;
- int img_count;
- int is_pipe;
- char path[1024];
- char *pixel_format; /**< Set by a private option. */
- char *video_size; /**< Set by a private option. */
- char *framerate; /**< Set by a private option. */
- int loop;
- int start_number;
-} VideoDemuxData;
+#if HAVE_GLOB
+/* Locally define as 0 (bitwise-OR no-op) any missing glob options that
+ are non-posix glibc/bsd extensions. */
+#ifndef GLOB_NOMAGIC
+#define GLOB_NOMAGIC 0
+#endif
+#ifndef GLOB_BRACE
+#define GLOB_BRACE 0
+#endif
+
+#endif /* HAVE_GLOB */
static const int sizes[][2] = {
{ 640, 480 },
@@ -70,15 +72,44 @@ static int infer_size(int *width_ptr, int *height_ptr, int size)
return -1;
}
-/* return -1 if no image found */
+static int is_glob(const char *path)
+{
+#if HAVE_GLOB
+ size_t span = 0;
+ const char *p = path;
+
+ while (p = strchr(p, '%')) {
+ if (*(++p) == '%') {
+ ++p;
+ continue;
+ }
+ if (span = strspn(p, "*?[]{}"))
+ break;
+ }
+ /* Did we hit a glob char or get to the end? */
+ return span != 0;
+#else
+ return 0;
+#endif
+}
+
+/**
+ * Get index range of image files matched by path.
+ *
+ * @param pfirst_index pointer to index updated with the first number in the range
+ * @param plast_index pointer to index updated with the last number in the range
+ * @param path path which has to be matched by the image files in the range
+ * @param start_index minimum accepted value for the first index in the range
+ * @return -1 if no image file could be found
+ */
static int find_image_range(int *pfirst_index, int *plast_index,
- const char *path, int max_start)
+ const char *path, int start_index, int start_index_range)
{
char buf[1024];
int range, last_index, range1, first_index;
/* find the first image */
- for (first_index = 0; first_index < max_start; first_index++) {
+ for (first_index = start_index; first_index < start_index + start_index_range; first_index++) {
if (av_get_frame_filename(buf, sizeof(buf), path, first_index) < 0) {
*pfirst_index =
*plast_index = 1;
@@ -89,7 +120,7 @@ static int find_image_range(int *pfirst_index, int *plast_index,
if (avio_check(buf, AVIO_FLAG_READ) > 0)
break;
}
- if (first_index == 5)
+ if (first_index == start_index + start_index_range)
goto fail;
/* find the last image */
@@ -129,20 +160,26 @@ static int img_read_probe(AVProbeData *p)
if (p->filename && ff_guess_image2_codec(p->filename)) {
if (av_filename_number_test(p->filename))
return AVPROBE_SCORE_MAX;
+ else if (is_glob(p->filename))
+ return AVPROBE_SCORE_MAX;
+ else if (p->filename[strcspn(p->filename, "*?{")]) // probably PT_GLOB
+ return AVPROBE_SCORE_EXTENSION + 2; // score chosen to be a tad above the image pipes
+ else if (p->buf_size == 0)
+ return 0;
+ else if (av_match_ext(p->filename, "raw") || av_match_ext(p->filename, "gif"))
+ return 5;
else
return AVPROBE_SCORE_EXTENSION;
}
return 0;
}
-static int img_read_header(AVFormatContext *s1)
+int ff_img_read_header(AVFormatContext *s1)
{
VideoDemuxData *s = s1->priv_data;
- int first_index, last_index, ret = 0;
- int width = 0, height = 0;
+ int first_index, last_index;
AVStream *st;
enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE;
- AVRational framerate;
s1->ctx_flags |= AVFMTCTX_NOHEADER;
@@ -157,17 +194,6 @@ static int img_read_header(AVFormatContext *s1)
s->pixel_format);
return AVERROR(EINVAL);
}
- if (s->video_size &&
- (ret = av_parse_video_size(&width, &height, s->video_size)) < 0) {
- av_log(s, AV_LOG_ERROR,
- "Could not parse video size: %s.\n", s->video_size);
- return ret;
- }
- if ((ret = av_parse_video_rate(&framerate, s->framerate)) < 0) {
- av_log(s, AV_LOG_ERROR,
- "Could not parse framerate: %s.\n", s->framerate);
- return ret;
- }
av_strlcpy(s->path, s1->filename, sizeof(s->path));
s->img_number = 0;
@@ -181,23 +207,94 @@ static int img_read_header(AVFormatContext *s1)
st->need_parsing = AVSTREAM_PARSE_FULL;
}
- avpriv_set_pts_info(st, 60, framerate.den, framerate.num);
+ if (s->ts_from_file == 2) {
+#if !HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ av_log(s1, AV_LOG_ERROR, "POSIX.1-2008 not supported, nanosecond file timestamps unavailable\n");
+ return AVERROR(ENOSYS);
+#endif
+ avpriv_set_pts_info(st, 64, 1, 1000000000);
+ } else if (s->ts_from_file)
+ avpriv_set_pts_info(st, 64, 1, 1);
+ else
+ avpriv_set_pts_info(st, 64, s->framerate.den, s->framerate.num);
- if (width && height) {
- st->codec->width = width;
- st->codec->height = height;
+ if (s->width && s->height) {
+ st->codec->width = s->width;
+ st->codec->height = s->height;
}
if (!s->is_pipe) {
- if (find_image_range(&first_index, &last_index, s->path,
- FFMAX(s->start_number, 5)) < 0)
- return AVERROR(ENOENT);
+ if (s->pattern_type == PT_GLOB_SEQUENCE) {
+ s->use_glob = is_glob(s->path);
+ if (s->use_glob) {
+#if HAVE_GLOB
+ char *p = s->path, *q, *dup;
+ int gerr;
+#endif
+
+ av_log(s1, AV_LOG_WARNING, "Pattern type 'glob_sequence' is deprecated: "
+ "use pattern_type 'glob' instead\n");
+#if HAVE_GLOB
+ dup = q = av_strdup(p);
+ while (*q) {
+ /* Do we have room for the next char and a \ insertion? */
+ if ((p - s->path) >= (sizeof(s->path) - 2))
+ break;
+ if (*q == '%' && strspn(q + 1, "%*?[]{}"))
+ ++q;
+ else if (strspn(q, "\\*?[]{}"))
+ *p++ = '\\';
+ *p++ = *q++;
+ }
+ *p = 0;
+ av_free(dup);
+
+ gerr = glob(s->path, GLOB_NOCHECK|GLOB_BRACE|GLOB_NOMAGIC, NULL, &s->globstate);
+ if (gerr != 0) {
+ return AVERROR(ENOENT);
+ }
+ first_index = 0;
+ last_index = s->globstate.gl_pathc - 1;
+#endif
+ }
+ }
+ if ((s->pattern_type == PT_GLOB_SEQUENCE && !s->use_glob) || s->pattern_type == PT_SEQUENCE) {
+ if (find_image_range(&first_index, &last_index, s->path,
+ s->start_number, s->start_number_range) < 0) {
+ av_log(s1, AV_LOG_ERROR,
+ "Could find no file with path '%s' and index in the range %d-%d\n",
+ s->path, s->start_number, s->start_number + s->start_number_range - 1);
+ return AVERROR(ENOENT);
+ }
+ } else if (s->pattern_type == PT_GLOB) {
+#if HAVE_GLOB
+ int gerr;
+ gerr = glob(s->path, GLOB_NOCHECK|GLOB_BRACE|GLOB_NOMAGIC, NULL, &s->globstate);
+ if (gerr != 0) {
+ return AVERROR(ENOENT);
+ }
+ first_index = 0;
+ last_index = s->globstate.gl_pathc - 1;
+ s->use_glob = 1;
+#else
+ av_log(s1, AV_LOG_ERROR,
+ "Pattern type 'glob' was selected but globbing "
+ "is not supported by this libavformat build\n");
+ return AVERROR(ENOSYS);
+#endif
+ } else if (s->pattern_type != PT_GLOB_SEQUENCE) {
+ av_log(s1, AV_LOG_ERROR,
+ "Unknown value '%d' for pattern_type option\n", s->pattern_type);
+ return AVERROR(EINVAL);
+ }
s->img_first = first_index;
s->img_last = last_index;
- s->img_number = s->start_number != 1 ? s->start_number : first_index;
+ s->img_number = first_index;
/* compute duration */
- st->start_time = 0;
- st->duration = last_index - first_index + 1;
+ if (!s->ts_from_file) {
+ st->start_time = 0;
+ st->duration = last_index - first_index + 1;
+ }
}
if (s1->video_codec_id) {
@@ -206,9 +303,52 @@ static int img_read_header(AVFormatContext *s1)
} else if (s1->audio_codec_id) {
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = s1->audio_codec_id;
+ } else if (s1->iformat->raw_codec_id) {
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codec->codec_id = s1->iformat->raw_codec_id;
} else {
+ const char *str = strrchr(s->path, '.');
+ s->split_planes = str && !av_strcasecmp(str + 1, "y");
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
- st->codec->codec_id = ff_guess_image2_codec(s->path);
+ if (s1->pb) {
+ int probe_buffer_size = 2048;
+ uint8_t *probe_buffer = av_realloc(NULL, probe_buffer_size + AVPROBE_PADDING_SIZE);
+ AVInputFormat *fmt = NULL;
+ AVProbeData pd = { 0 };
+
+ if (!probe_buffer)
+ return AVERROR(ENOMEM);
+
+ probe_buffer_size = avio_read(s1->pb, probe_buffer, probe_buffer_size);
+ if (probe_buffer_size < 0) {
+ av_free(probe_buffer);
+ return probe_buffer_size;
+ }
+ memset(probe_buffer + probe_buffer_size, 0, AVPROBE_PADDING_SIZE);
+
+ pd.buf = probe_buffer;
+ pd.buf_size = probe_buffer_size;
+ pd.filename = s1->filename;
+
+ while ((fmt = av_iformat_next(fmt))) {
+ if (fmt->read_header != ff_img_read_header ||
+ !fmt->read_probe ||
+ (fmt->flags & AVFMT_NOFILE) ||
+ !fmt->raw_codec_id)
+ continue;
+ if (fmt->read_probe(&pd) > 0) {
+ st->codec->codec_id = fmt->raw_codec_id;
+ break;
+ }
+ }
+ ffio_rewind_with_probe_data(s1->pb, &probe_buffer, probe_buffer_size);
+ }
+ if (st->codec->codec_id == AV_CODEC_ID_NONE)
+ st->codec->codec_id = ff_guess_image2_codec(s->path);
+ if (st->codec->codec_id == AV_CODEC_ID_LJPEG)
+ st->codec->codec_id = AV_CODEC_ID_MJPEG;
+ if (st->codec->codec_id == AV_CODEC_ID_ALIAS_PIX) // we cannot distingiush this from BRENDER_PIX
+ st->codec->codec_id = AV_CODEC_ID_NONE;
}
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
pix_fmt != AV_PIX_FMT_NONE)
@@ -217,10 +357,11 @@ static int img_read_header(AVFormatContext *s1)
return 0;
}
-static int img_read_packet(AVFormatContext *s1, AVPacket *pkt)
+int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt)
{
VideoDemuxData *s = s1->priv_data;
- char filename[1024];
+ char filename_bytes[1024];
+ char *filename = filename_bytes;
int i, res;
int size[3] = { 0 }, ret[3] = { 0 };
AVIOContext *f[3] = { NULL };
@@ -233,10 +374,16 @@ static int img_read_packet(AVFormatContext *s1, AVPacket *pkt)
}
if (s->img_number > s->img_last)
return AVERROR_EOF;
- if (av_get_frame_filename(filename, sizeof(filename),
+ if (s->use_glob) {
+#if HAVE_GLOB
+ filename = s->globstate.gl_pathv[s->img_number];
+#endif
+ } else {
+ if (av_get_frame_filename(filename_bytes, sizeof(filename_bytes),
s->path,
s->img_number) < 0 && s->img_number > 1)
return AVERROR(EIO);
+ }
for (i = 0; i < 3; i++) {
if (avio_open2(&f[i], filename, AVIO_FLAG_READ,
&s1->interrupt_callback, NULL) < 0) {
@@ -248,18 +395,47 @@ static int img_read_packet(AVFormatContext *s1, AVPacket *pkt)
}
size[i] = avio_size(f[i]);
- if (codec->codec_id != AV_CODEC_ID_RAWVIDEO)
+ if (!s->split_planes)
break;
filename[strlen(filename) - 1] = 'U' + i;
}
+ if (codec->codec_id == AV_CODEC_ID_NONE) {
+ AVProbeData pd = { 0 };
+ AVInputFormat *ifmt;
+ uint8_t header[PROBE_BUF_MIN + AVPROBE_PADDING_SIZE];
+ int ret;
+ int score = 0;
+
+ ret = avio_read(f[0], header, PROBE_BUF_MIN);
+ if (ret < 0)
+ return ret;
+ memset(header + ret, 0, sizeof(header) - ret);
+ avio_skip(f[0], -ret);
+ pd.buf = header;
+ pd.buf_size = ret;
+ pd.filename = filename;
+
+ ifmt = av_probe_input_format3(&pd, 1, &score);
+ if (ifmt && ifmt->read_packet == ff_img_read_packet && ifmt->raw_codec_id)
+ codec->codec_id = ifmt->raw_codec_id;
+ }
+
if (codec->codec_id == AV_CODEC_ID_RAWVIDEO && !codec->width)
infer_size(&codec->width, &codec->height, size[0]);
} else {
f[0] = s1->pb;
- if (f[0]->eof_reached)
- return AVERROR(EIO);
- size[0] = 4096;
+ if (avio_feof(f[0]) && s->loop && s->is_pipe)
+ avio_seek(f[0], 0, SEEK_SET);
+ if (avio_feof(f[0]))
+ return AVERROR_EOF;
+ if (s->frame_size > 0) {
+ size[0] = s->frame_size;
+ } else if (!s1->streams[0]->parser) {
+ size[0] = avio_size(s1->pb);
+ } else {
+ size[0] = 4096;
+ }
}
res = av_new_packet(pkt, size[0] + size[1] + size[2]);
@@ -267,13 +443,35 @@ static int img_read_packet(AVFormatContext *s1, AVPacket *pkt)
return res;
pkt->stream_index = 0;
pkt->flags |= AV_PKT_FLAG_KEY;
+ if (s->ts_from_file) {
+ struct stat img_stat;
+ if (stat(filename, &img_stat))
+ return AVERROR(EIO);
+ pkt->pts = (int64_t)img_stat.st_mtime;
+#if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ if (s->ts_from_file == 2)
+ pkt->pts = 1000000000*pkt->pts + img_stat.st_mtim.tv_nsec;
+#endif
+ av_add_index_entry(s1->streams[0], s->img_number, pkt->pts, 0, 0, AVINDEX_KEYFRAME);
+ } else if (!s->is_pipe) {
+ pkt->pts = s->pts;
+ }
+
+ if (s->is_pipe)
+ pkt->pos = avio_tell(f[0]);
pkt->size = 0;
for (i = 0; i < 3; i++) {
if (f[i]) {
ret[i] = avio_read(f[i], pkt->data + pkt->size, size[i]);
+ if (s->loop && s->is_pipe && ret[i] == AVERROR_EOF) {
+ if (avio_seek(f[i], 0, SEEK_SET) >= 0) {
+ pkt->pos = 0;
+ ret[i] = avio_read(f[i], pkt->data + pkt->size, size[i]);
+ }
+ }
if (!s->is_pipe)
- avio_close(f[i]);
+ avio_closep(&f[i]);
if (ret[i] > 0)
pkt->size += ret[i];
}
@@ -281,22 +479,72 @@ static int img_read_packet(AVFormatContext *s1, AVPacket *pkt)
if (ret[0] <= 0 || ret[1] < 0 || ret[2] < 0) {
av_free_packet(pkt);
- return AVERROR(EIO); /* signal EOF */
+ if (ret[0] < 0) {
+ return ret[0];
+ } else if (ret[1] < 0) {
+ return ret[1];
+ } else if (ret[2] < 0)
+ return ret[2];
+ return AVERROR_EOF;
} else {
s->img_count++;
s->img_number++;
+ s->pts++;
return 0;
}
}
+static int img_read_close(struct AVFormatContext* s1)
+{
+#if HAVE_GLOB
+ VideoDemuxData *s = s1->priv_data;
+ if (s->use_glob) {
+ globfree(&s->globstate);
+ }
+#endif
+ return 0;
+}
+
+static int img_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
+{
+ VideoDemuxData *s1 = s->priv_data;
+ AVStream *st = s->streams[0];
+
+ if (s1->ts_from_file) {
+ int index = av_index_search_timestamp(st, timestamp, flags);
+ if(index < 0)
+ return -1;
+ s1->img_number = st->index_entries[index].pos;
+ return 0;
+ }
+
+ if (timestamp < 0 || !s1->loop && timestamp > s1->img_last - s1->img_first)
+ return -1;
+ s1->img_number = timestamp%(s1->img_last - s1->img_first + 1) + s1->img_first;
+ s1->pts = timestamp;
+ return 0;
+}
+
#define OFFSET(x) offsetof(VideoDemuxData, x)
#define DEC AV_OPT_FLAG_DECODING_PARAM
-static const AVOption options[] = {
- { "pixel_format", "", OFFSET(pixel_format), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, DEC },
- { "video_size", "", OFFSET(video_size), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, DEC },
- { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, { .str = "25" }, 0, 0, DEC },
- { "loop", "", OFFSET(loop), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, DEC },
- { "start_number", "first number in the sequence", OFFSET(start_number), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, DEC },
+const AVOption ff_img_options[] = {
+ { "framerate", "set the video framerate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, DEC },
+ { "loop", "force loop over input file sequence", OFFSET(loop), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, DEC },
+
+ { "pattern_type", "set pattern type", OFFSET(pattern_type), AV_OPT_TYPE_INT, {.i64=PT_GLOB_SEQUENCE}, 0, INT_MAX, DEC, "pattern_type"},
+ { "glob_sequence","select glob/sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB_SEQUENCE}, INT_MIN, INT_MAX, DEC, "pattern_type" },
+ { "glob", "select glob pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB }, INT_MIN, INT_MAX, DEC, "pattern_type" },
+ { "sequence", "select sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_SEQUENCE }, INT_MIN, INT_MAX, DEC, "pattern_type" },
+
+ { "pixel_format", "set video pixel format", OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC },
+ { "start_number", "set first number in the sequence", OFFSET(start_number), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, DEC },
+ { "start_number_range", "set range for looking at the first sequence number", OFFSET(start_number_range), AV_OPT_TYPE_INT, {.i64 = 5}, 1, INT_MAX, DEC },
+ { "video_size", "set video size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC },
+ { "frame_size", "force frame size in bytes", OFFSET(frame_size), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, DEC },
+ { "ts_from_file", "set frame timestamp from file's one", OFFSET(ts_from_file), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 2, DEC, "ts_type" },
+ { "none", "none", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 2, DEC, "ts_type" },
+ { "sec", "second precision", 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 2, DEC, "ts_type" },
+ { "ns", "nano second precision", 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 2, DEC, "ts_type" },
{ NULL },
};
@@ -304,7 +552,7 @@ static const AVOption options[] = {
static const AVClass img2_class = {
.class_name = "image2 demuxer",
.item_name = av_default_item_name,
- .option = options,
+ .option = ff_img_options,
.version = LIBAVUTIL_VERSION_INT,
};
AVInputFormat ff_image2_demuxer = {
@@ -312,8 +560,10 @@ AVInputFormat ff_image2_demuxer = {
.long_name = NULL_IF_CONFIG_SMALL("image2 sequence"),
.priv_data_size = sizeof(VideoDemuxData),
.read_probe = img_read_probe,
- .read_header = img_read_header,
- .read_packet = img_read_packet,
+ .read_header = ff_img_read_header,
+ .read_packet = ff_img_read_packet,
+ .read_close = img_read_close,
+ .read_seek = img_read_seek,
.flags = AVFMT_NOFILE,
.priv_class = &img2_class,
};
@@ -322,15 +572,225 @@ AVInputFormat ff_image2_demuxer = {
static const AVClass img2pipe_class = {
.class_name = "image2pipe demuxer",
.item_name = av_default_item_name,
- .option = options,
+ .option = ff_img_options,
.version = LIBAVUTIL_VERSION_INT,
};
AVInputFormat ff_image2pipe_demuxer = {
.name = "image2pipe",
.long_name = NULL_IF_CONFIG_SMALL("piped image2 sequence"),
.priv_data_size = sizeof(VideoDemuxData),
- .read_header = img_read_header,
- .read_packet = img_read_packet,
+ .read_header = ff_img_read_header,
+ .read_packet = ff_img_read_packet,
.priv_class = &img2pipe_class,
};
#endif
+
+static int bmp_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+ int ihsize;
+
+ if (AV_RB16(b) != 0x424d)
+ return 0;
+
+ ihsize = AV_RL32(b+14);
+ if (ihsize < 12 || ihsize > 255)
+ return 0;
+
+ if (!AV_RN32(b + 6)) {
+ return AVPROBE_SCORE_EXTENSION + 1;
+ }
+ return AVPROBE_SCORE_EXTENSION / 4;
+}
+
+static int dpx_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+ int w, h;
+ int is_big = (AV_RN32(b) == AV_RN32("SDPX"));
+
+ if (p->buf_size < 0x304+8)
+ return 0;
+ w = is_big ? AV_RB32(p->buf + 0x304) : AV_RL32(p->buf + 0x304);
+ h = is_big ? AV_RB32(p->buf + 0x308) : AV_RL32(p->buf + 0x308);
+ if (w <= 0 || h <= 0)
+ return 0;
+
+ if (is_big || AV_RN32(b) == AV_RN32("XPDS"))
+ return AVPROBE_SCORE_EXTENSION + 1;
+ return 0;
+}
+
+static int exr_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+
+ if (AV_RL32(b) == 20000630)
+ return AVPROBE_SCORE_EXTENSION + 1;
+ return 0;
+}
+
+static int j2k_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+
+ if (AV_RB64(b) == 0x0000000c6a502020 ||
+ AV_RB32(b) == 0xff4fff51)
+ return AVPROBE_SCORE_EXTENSION + 1;
+ return 0;
+}
+
+static int jpeg_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+ int i, state = 0xD8, exif_size = 0;
+
+ if (AV_RB16(b) != 0xFFD8 ||
+ AV_RB32(b) == 0xFFD8FFF7)
+ return 0;
+
+ b += 2;
+ if (AV_RB16(b) == 0xFFE1 && AV_RB32(b + 4) == AV_RB32("Exif")) {
+ exif_size = AV_RB16(b + 2) + 2;
+ b += exif_size;
+ }
+ for (i = 0; i + exif_size < p->buf_size - 2; i++) {
+ int c;
+ if (b[i] != 0xFF)
+ continue;
+ c = b[i + 1];
+ switch (c) {
+ case 0xD8:
+ return 0;
+ case 0xC0:
+ case 0xC1:
+ case 0xC2:
+ case 0xC3:
+ case 0xC5:
+ case 0xC6:
+ case 0xC7:
+ if (state != 0xD8)
+ return 0;
+ state = 0xC0;
+ break;
+ case 0xDA:
+ if (state != 0xC0)
+ return 0;
+ state = 0xDA;
+ break;
+ case 0xD9:
+ if (state != 0xDA)
+ return 0;
+ state = 0xD9;
+ break;
+ default:
+ if ( (c >= 0x02 && c <= 0xBF)
+ || c == 0xC8)
+ return 0;
+ }
+ }
+
+ if (state == 0xD9)
+ return AVPROBE_SCORE_EXTENSION + 1;
+ return AVPROBE_SCORE_EXTENSION / 8;
+}
+
+static int jpegls_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+
+ if (AV_RB32(b) == 0xffd8fff7)
+ return AVPROBE_SCORE_EXTENSION + 1;
+ return 0;
+}
+
+static int pictor_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+
+ if (AV_RL16(b) == 0x1234)
+ return AVPROBE_SCORE_EXTENSION / 4;
+ return 0;
+}
+
+static int png_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+
+ if (AV_RB64(b) == 0x89504e470d0a1a0a)
+ return AVPROBE_SCORE_MAX - 1;
+ return 0;
+}
+
+static int sgi_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+
+ if (AV_RB16(b) == 474 &&
+ (b[2] & ~1) == 0 &&
+ (b[3] & ~3) == 0 && b[3] &&
+ (AV_RB16(b + 4) & ~7) == 0 && AV_RB16(b + 4))
+ return AVPROBE_SCORE_EXTENSION + 1;
+ return 0;
+}
+
+static int sunrast_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+
+ if (AV_RB32(b) == 0x59a66a95)
+ return AVPROBE_SCORE_EXTENSION + 1;
+ return 0;
+}
+
+static int tiff_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+
+ if (AV_RB32(b) == 0x49492a00 ||
+ AV_RB32(b) == 0x4D4D002a)
+ return AVPROBE_SCORE_EXTENSION + 1;
+ return 0;
+}
+
+static int webp_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+
+ if (AV_RB32(b) == 0x52494646 &&
+ AV_RB32(b + 8) == 0x57454250)
+ return AVPROBE_SCORE_MAX - 1;
+ return 0;
+}
+
+#define IMAGEAUTO_DEMUXER(imgname, codecid)\
+static const AVClass imgname ## _class = {\
+ .class_name = AV_STRINGIFY(imgname) " demuxer",\
+ .item_name = av_default_item_name,\
+ .option = ff_img_options,\
+ .version = LIBAVUTIL_VERSION_INT,\
+};\
+AVInputFormat ff_image_ ## imgname ## _pipe_demuxer = {\
+ .name = AV_STRINGIFY(imgname) "_pipe",\
+ .long_name = NULL_IF_CONFIG_SMALL("piped " AV_STRINGIFY(imgname) " sequence"),\
+ .priv_data_size = sizeof(VideoDemuxData),\
+ .read_probe = imgname ## _probe,\
+ .read_header = ff_img_read_header,\
+ .read_packet = ff_img_read_packet,\
+ .priv_class = & imgname ## _class,\
+ .flags = AVFMT_GENERIC_INDEX, \
+ .raw_codec_id = codecid,\
+};
+
+IMAGEAUTO_DEMUXER(bmp, AV_CODEC_ID_BMP)
+IMAGEAUTO_DEMUXER(dpx, AV_CODEC_ID_DPX)
+IMAGEAUTO_DEMUXER(exr, AV_CODEC_ID_EXR)
+IMAGEAUTO_DEMUXER(j2k, AV_CODEC_ID_JPEG2000)
+IMAGEAUTO_DEMUXER(jpeg, AV_CODEC_ID_MJPEG)
+IMAGEAUTO_DEMUXER(jpegls, AV_CODEC_ID_JPEGLS)
+IMAGEAUTO_DEMUXER(pictor, AV_CODEC_ID_PICTOR)
+IMAGEAUTO_DEMUXER(png, AV_CODEC_ID_PNG)
+IMAGEAUTO_DEMUXER(sgi, AV_CODEC_ID_SGI)
+IMAGEAUTO_DEMUXER(sunrast, AV_CODEC_ID_SUNRAST)
+IMAGEAUTO_DEMUXER(tiff, AV_CODEC_ID_TIFF)
+IMAGEAUTO_DEMUXER(webp, AV_CODEC_ID_WEBP)
diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c
index eeb19c0e25..f56c39e6d3 100644
--- a/libavformat/img2enc.c
+++ b/libavformat/img2enc.c
@@ -3,42 +3,50 @@
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
* Copyright (c) 2004 Michael Niedermayer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libavutil/intreadwrite.h"
+#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/log.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/time_internal.h"
#include "avformat.h"
#include "avio_internal.h"
#include "internal.h"
-#include "libavutil/opt.h"
typedef struct VideoMuxData {
const AVClass *class; /**< Class for private options. */
int img_number;
int is_pipe;
+ int split_planes; /**< use independent file for each Y, U, V plane */
char path[1024];
int update;
+ int use_strftime;
+ const char *muxer;
} VideoMuxData;
static int write_header(AVFormatContext *s)
{
VideoMuxData *img = s->priv_data;
+ AVStream *st = s->streams[0];
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(st->codec->pix_fmt);
av_strlcpy(img->path, s->filename, sizeof(img->path));
@@ -48,79 +56,116 @@ static int write_header(AVFormatContext *s)
else
img->is_pipe = 1;
+ if (st->codec->codec_id == AV_CODEC_ID_GIF) {
+ img->muxer = "gif";
+ } else if (st->codec->codec_id == AV_CODEC_ID_RAWVIDEO) {
+ const char *str = strrchr(img->path, '.');
+ img->split_planes = str
+ && !av_strcasecmp(str + 1, "y")
+ && s->nb_streams == 1
+ && desc
+ &&(desc->flags & AV_PIX_FMT_FLAG_PLANAR)
+ && desc->nb_components >= 3;
+ }
return 0;
}
static int write_packet(AVFormatContext *s, AVPacket *pkt)
{
VideoMuxData *img = s->priv_data;
- AVIOContext *pb[3];
+ AVIOContext *pb[4];
char filename[1024];
AVCodecContext *codec = s->streams[pkt->stream_index]->codec;
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(codec->pix_fmt);
int i;
if (!img->is_pipe) {
if (img->update) {
av_strlcpy(filename, img->path, sizeof(filename));
+ } else if (img->use_strftime) {
+ time_t now0;
+ struct tm *tm, tmpbuf;
+ time(&now0);
+ tm = localtime_r(&now0, &tmpbuf);
+ if (!strftime(filename, sizeof(filename), img->path, tm)) {
+ av_log(s, AV_LOG_ERROR, "Could not get frame filename with strftime\n");
+ return AVERROR(EINVAL);
+ }
} else if (av_get_frame_filename(filename, sizeof(filename), img->path, img->img_number) < 0 &&
img->img_number > 1) {
av_log(s, AV_LOG_ERROR,
- "Could not get frame filename number %d from pattern '%s'\n",
+ "Could not get frame filename number %d from pattern '%s' (either set updatefirst or use a pattern like %%03d within the filename pattern)\n",
img->img_number, img->path);
- return AVERROR(EIO);
+ return AVERROR(EINVAL);
}
- for (i = 0; i < 3; i++) {
+ for (i = 0; i < 4; i++) {
if (avio_open2(&pb[i], filename, AVIO_FLAG_WRITE,
&s->interrupt_callback, NULL) < 0) {
av_log(s, AV_LOG_ERROR, "Could not open file : %s\n", filename);
return AVERROR(EIO);
}
- if (codec->codec_id != AV_CODEC_ID_RAWVIDEO)
+ if (!img->split_planes || i+1 >= desc->nb_components)
break;
- filename[strlen(filename) - 1] = 'U' + i;
+ filename[strlen(filename) - 1] = "UVAx"[i];
}
} else {
pb[0] = s->pb;
}
- if (codec->codec_id == AV_CODEC_ID_RAWVIDEO) {
+ if (img->split_planes) {
int ysize = codec->width * codec->height;
- avio_write(pb[0], pkt->data, ysize);
- avio_write(pb[1], pkt->data + ysize, (pkt->size - ysize) / 2);
- avio_write(pb[2], pkt->data + ysize + (pkt->size - ysize) / 2, (pkt->size - ysize) / 2);
- avio_close(pb[1]);
- avio_close(pb[2]);
- } else {
- if (ff_guess_image2_codec(s->filename) == AV_CODEC_ID_JPEG2000) {
- AVStream *st = s->streams[0];
- if (st->codec->extradata_size > 8 &&
- AV_RL32(st->codec->extradata + 4) == MKTAG('j', 'p', '2', 'h')) {
- if (pkt->size < 8 ||
- AV_RL32(pkt->data + 4) != MKTAG('j', 'p', '2', 'c'))
- goto error;
- avio_wb32(pb[0], 12);
- ffio_wfourcc(pb[0], "jP ");
- avio_wb32(pb[0], 0x0D0A870A); // signature
- avio_wb32(pb[0], 20);
- ffio_wfourcc(pb[0], "ftyp");
- ffio_wfourcc(pb[0], "jp2 ");
- avio_wb32(pb[0], 0);
- ffio_wfourcc(pb[0], "jp2 ");
- avio_write(pb[0], st->codec->extradata, st->codec->extradata_size);
- } else if (pkt->size < 8 ||
- (!st->codec->extradata_size &&
- AV_RL32(pkt->data + 4) != MKTAG('j', 'P', ' ', ' '))) { // signature
-error:
- av_log(s, AV_LOG_ERROR, "malformed JPEG 2000 codestream\n");
- return -1;
- }
+ int usize = FF_CEIL_RSHIFT(codec->width, desc->log2_chroma_w) * FF_CEIL_RSHIFT(codec->height, desc->log2_chroma_h);
+ if (desc->comp[0].depth_minus1 >= 8) {
+ ysize *= 2;
+ usize *= 2;
+ }
+ avio_write(pb[0], pkt->data , ysize);
+ avio_write(pb[1], pkt->data + ysize , usize);
+ avio_write(pb[2], pkt->data + ysize + usize, usize);
+ avio_closep(&pb[1]);
+ avio_closep(&pb[2]);
+ if (desc->nb_components > 3) {
+ avio_write(pb[3], pkt->data + ysize + 2*usize, ysize);
+ avio_closep(&pb[3]);
}
+ } else if (img->muxer) {
+ int ret;
+ AVStream *st;
+ AVPacket pkt2 = {0};
+ AVFormatContext *fmt = NULL;
+
+ av_assert0(!img->split_planes);
+
+ ret = avformat_alloc_output_context2(&fmt, NULL, img->muxer, s->filename);
+ if (ret < 0)
+ return ret;
+ st = avformat_new_stream(fmt, NULL);
+ if (!st) {
+ avformat_free_context(fmt);
+ return AVERROR(ENOMEM);
+ }
+ st->id = pkt->stream_index;
+
+ fmt->pb = pb[0];
+ if ((ret = av_copy_packet(&pkt2, pkt)) < 0 ||
+ (ret = av_dup_packet(&pkt2)) < 0 ||
+ (ret = avcodec_copy_context(st->codec, s->streams[0]->codec)) < 0 ||
+ (ret = avformat_write_header(fmt, NULL)) < 0 ||
+ (ret = av_interleaved_write_frame(fmt, &pkt2)) < 0 ||
+ (ret = av_write_trailer(fmt)) < 0) {
+ av_free_packet(&pkt2);
+ avformat_free_context(fmt);
+ return ret;
+ }
+ av_free_packet(&pkt2);
+ avformat_free_context(fmt);
+ } else {
avio_write(pb[0], pkt->data, pkt->size);
}
avio_flush(pb[0]);
if (!img->is_pipe) {
- avio_close(pb[0]);
+ avio_closep(&pb[0]);
}
img->img_number++;
@@ -130,8 +175,10 @@ error:
#define OFFSET(x) offsetof(VideoMuxData, x)
#define ENC AV_OPT_FLAG_ENCODING_PARAM
static const AVOption muxoptions[] = {
- { "start_number", "first number in the sequence", OFFSET(img_number), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, ENC },
+ { "updatefirst", "continuously overwrite one file", OFFSET(update), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, ENC },
{ "update", "continuously overwrite one file", OFFSET(update), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, ENC },
+ { "start_number", "set first number in the sequence", OFFSET(img_number), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, ENC },
+ { "strftime", "use strftime for filename", OFFSET(use_strftime), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, ENC },
{ NULL },
};
@@ -146,9 +193,9 @@ static const AVClass img2mux_class = {
AVOutputFormat ff_image2_muxer = {
.name = "image2",
.long_name = NULL_IF_CONFIG_SMALL("image2 sequence"),
- .extensions = "bmp,dpx,jpeg,jpg,ljpg,pam,pbm,pcx,pgm,pgmyuv,png,"
- "ppm,sgi,tga,tif,tiff,jp2,xwd,sun,ras,rs,im1,im8,im24,"
- "sunras,webp,xbm,j2c,pix",
+ .extensions = "bmp,dpx,jls,jpeg,jpg,ljpg,pam,pbm,pcx,pgm,pgmyuv,png,"
+ "ppm,sgi,tga,tif,tiff,jp2,j2c,j2k,xwd,sun,ras,rs,im1,im8,im24,"
+ "sunras,xbm,xface,pix,y",
.priv_data_size = sizeof(VideoMuxData),
.video_codec = AV_CODEC_ID_MJPEG,
.write_header = write_header,
diff --git a/libavformat/ingenientdec.c b/libavformat/ingenientdec.c
index 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 f08ad90038..76ffcc517e 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
*/
@@ -27,6 +27,10 @@
#define MAX_URL_SIZE 4096
+/** size of probe buffer, for guessing file type from file contents */
+#define PROBE_BUF_MIN 2048
+#define PROBE_BUF_MAX (1 << 20)
+
#ifdef DEBUG
# define hex_dump_debug(class, buf, size) av_hex_dump_log(class, AV_LOG_DEBUG, buf, size)
#else
@@ -83,6 +87,7 @@ struct AVFormatInternal {
/**
* Offset to remap timestamps to be non-negative.
* Expressed in timebase units.
+ * @see AVStream.mux_ts_offset
*/
int64_t offset;
@@ -90,9 +95,9 @@ struct AVFormatInternal {
* Timebase for the timestamp offset.
*/
AVRational offset_timebase;
-};
-void ff_dynarray_add(intptr_t **tab_ptr, int *nb_ptr, intptr_t elem);
+ int inject_global_side_data;
+};
#ifdef __GNUC__
#define dynarray_add(tab, nb_ptr, elem)\
@@ -100,12 +105,12 @@ do {\
__typeof__(tab) _tab = (tab);\
__typeof__(elem) _elem = (elem);\
(void)sizeof(**_tab == _elem); /* check that types are compatible */\
- ff_dynarray_add((intptr_t **)_tab, nb_ptr, (intptr_t)_elem);\
+ av_dynarray_add(_tab, nb_ptr, _elem);\
} while(0)
#else
#define dynarray_add(tab, nb_ptr, elem)\
do {\
- ff_dynarray_add((intptr_t **)(tab), nb_ptr, (intptr_t)(elem));\
+ av_dynarray_add((tab), nb_ptr, (elem));\
} while(0)
#endif
@@ -128,6 +133,7 @@ void ff_program_add_stream_index(AVFormatContext *ac, int progid, unsigned int i
/**
* Add packet to AVFormatContext->packet_buffer list, determining its
* interleaved position using compare() function argument.
+ * @return 0, or < 0 on error
*/
int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt,
int (*compare)(AVFormatContext *, AVPacket *, AVPacket *));
@@ -171,10 +177,11 @@ void ff_sdp_write_media(char *buff, int size, AVStream *st, int idx,
* @param dst_stream the stream index within dst to write the packet to
* @param pkt the packet to be written
* @param src the muxer the packet originally was intended for
+ * @param interleave 0->use av_write_frame, 1->av_interleaved_write_frame
* @return the value av_write_frame returned
*/
int ff_write_chained(AVFormatContext *dst, int dst_stream, AVPacket *pkt,
- AVFormatContext *src);
+ AVFormatContext *src, int interleave);
/**
* Get the length in bytes which is needed to store val as v.
@@ -293,6 +300,9 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index,
*/
void ff_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp);
+int ff_find_last_ts(AVFormatContext *s, int stream_index, int64_t *ts, int64_t *pos,
+ int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t ));
+
/**
* Perform a binary search using read_timestamp().
*
@@ -362,6 +372,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.
*/
@@ -388,6 +400,15 @@ enum AVCodecID ff_codec_get_id(const AVCodecTag *tags, unsigned int tag);
enum AVCodecID ff_get_pcm_codec_id(int bps, int flt, int be, int sflags);
/**
+ * Chooses a timebase for muxing the specified stream.
+ *
+ * The chosen timebase allows sample accurate timestamps based
+ * on the framerate or sample rate for audio streams. It also is
+ * at least as precise as 1/min_precision would be.
+ */
+AVRational ff_choose_timebase(AVFormatContext *s, AVStream *st, int min_precision);
+
+/**
* Generate standard extradata for AVC-Intra based on width/height and field
* order.
*/
@@ -400,11 +421,15 @@ int ff_generate_avci_extradata(AVStream *st);
* @param newpath destination path
* @return 0 or AVERROR on failure
*/
-static inline int ff_rename(const char *oldpath, const char *newpath)
+static inline int ff_rename(const char *oldpath, const char *newpath, void *logctx)
{
- if (rename(oldpath, newpath) == -1)
- return AVERROR(errno);
- return 0;
+ int ret = 0;
+ if (rename(oldpath, newpath) == -1) {
+ ret = AVERROR(errno);
+ if (logctx)
+ av_log(logctx, AV_LOG_ERROR, "failed to rename file %s to %s\n", oldpath, newpath);
+ }
+ return ret;
}
/**
@@ -414,4 +439,50 @@ static inline int ff_rename(const char *oldpath, const char *newpath)
uint8_t *ff_stream_new_side_data(AVStream *st, enum AVPacketSideDataType type,
int size);
+/**
+ * Allocate extradata with additional FF_INPUT_BUFFER_PADDING_SIZE at end
+ * which is always set to 0.
+ *
+ * @param size size of extradata
+ * @return 0 if OK, AVERROR_xxx on error
+ */
+int ff_alloc_extradata(AVCodecContext *avctx, int size);
+
+/**
+ * Allocate extradata with additional FF_INPUT_BUFFER_PADDING_SIZE at end
+ * which is always set to 0 and fill it from pb.
+ *
+ * @param size size of extradata
+ * @return >= 0 if OK, AVERROR_xxx on error
+ */
+int ff_get_extradata(AVCodecContext *avctx, AVIOContext *pb, int size);
+
+/**
+ * add frame for rfps calculation.
+ *
+ * @param dts timestamp of the i-th frame
+ * @return 0 if OK, AVERROR_xxx on error
+ */
+int ff_rfps_add_frame(AVFormatContext *ic, AVStream *st, int64_t dts);
+
+void ff_rfps_calculate(AVFormatContext *ic);
+
+/**
+ * Flags for AVFormatContext.write_uncoded_frame()
+ */
+enum AVWriteUncodedFrameFlags {
+
+ /**
+ * Query whether the feature is possible on this stream.
+ * The frame argument is ignored.
+ */
+ AV_WRITE_UNCODED_FRAME_QUERY = 0x0001,
+
+};
+
+/**
+ * Copies the whilelists from one context to the other
+ */
+int ff_copy_whitelists(AVFormatContext *dst, AVFormatContext *src);
+
#endif /* AVFORMAT_INTERNAL_H */
diff --git a/libavformat/ipmovie.c b/libavformat/ipmovie.c
index 60ae939003..01e70e858f 100644
--- a/libavformat/ipmovie.c
+++ b/libavformat/ipmovie.c
@@ -1,21 +1,21 @@
/*
* Interplay MVE File Demuxer
- * Copyright (c) 2003 The ffmpeg Project
+ * Copyright (c) 2003 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -117,7 +117,7 @@ static int load_ipmovie_packet(IPMVEContext *s, AVIOContext *pb,
int chunk_type;
- if (s->audio_chunk_offset) {
+ if (s->audio_chunk_offset && s->audio_channels && s->audio_bits) {
if (s->audio_type == AV_CODEC_ID_NONE) {
av_log(NULL, AV_LOG_ERROR, "Can not read audio packet before"
"audio codec is known\n");
@@ -237,7 +237,7 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
return chunk_type;
/* read the next chunk, wherever the file happens to be pointing */
- if (pb->eof_reached)
+ if (avio_feof(pb))
return CHUNK_EOF;
if (avio_read(pb, chunk_preamble, CHUNK_PREAMBLE_SIZE) !=
CHUNK_PREAMBLE_SIZE)
@@ -283,7 +283,7 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
while ((chunk_size > 0) && (chunk_type != CHUNK_BAD)) {
/* read the next chunk, wherever the file happens to be pointing */
- if (pb->eof_reached) {
+ if (avio_feof(pb)) {
chunk_type = CHUNK_EOF;
break;
}
@@ -321,7 +321,7 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
case OPCODE_CREATE_TIMER:
av_dlog(NULL, "create timer\n");
- if ((opcode_version > 0) || (opcode_size > 6)) {
+ if ((opcode_version > 0) || (opcode_size != 6)) {
av_dlog(NULL, "bad create_timer opcode\n");
chunk_type = CHUNK_BAD;
break;
@@ -339,7 +339,7 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
case OPCODE_INIT_AUDIO_BUFFERS:
av_dlog(NULL, "initialize audio buffers\n");
- if ((opcode_version > 1) || (opcode_size > 10)) {
+ if (opcode_version > 1 || opcode_size > 10 || opcode_size < 6) {
av_dlog(NULL, "bad init_audio_buffers opcode\n");
chunk_type = CHUNK_BAD;
break;
@@ -376,7 +376,9 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
case OPCODE_INIT_VIDEO_BUFFERS:
av_dlog(NULL, "initialize video buffers\n");
- if ((opcode_version > 2) || (opcode_size > 8)) {
+ if ((opcode_version > 2) || (opcode_size > 8) || opcode_size < 4
+ || opcode_version == 2 && opcode_size < 8
+ ) {
av_dlog(NULL, "bad init_video_buffers opcode\n");
chunk_type = CHUNK_BAD;
break;
@@ -449,8 +451,8 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
av_dlog(NULL, "set palette\n");
/* check for the logical maximum palette size
* (3 * 256 + 4 bytes) */
- if (opcode_size > 0x304) {
- av_dlog(NULL, "demux_ipmovie: set_palette opcode too large\n");
+ if (opcode_size > 0x304 || opcode_size < 4) {
+ av_dlog(NULL, "demux_ipmovie: set_palette opcode with invalid size\n");
chunk_type = CHUNK_BAD;
break;
}
@@ -463,7 +465,8 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
first_color = AV_RL16(&scratch[0]);
last_color = first_color + AV_RL16(&scratch[2]) - 1;
/* sanity check (since they are 16 bit values) */
- if ((first_color > 0xFF) || (last_color > 0xFF)) {
+ if ( (first_color > 0xFF) || (last_color > 0xFF)
+ || (last_color - first_color + 1)*3 + 4 > opcode_size) {
av_dlog(NULL, "demux_ipmovie: set_palette indexes out of range (%d -> %d)\n",
first_color, last_color);
chunk_type = CHUNK_BAD;
@@ -476,7 +479,8 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
r = scratch[j++] * 4;
g = scratch[j++] * 4;
b = scratch[j++] * 4;
- s->palette[i] = (r << 16) | (g << 8) | (b);
+ s->palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | (b);
+ s->palette[i] |= s->palette[i] >> 6 & 0x30303;
}
s->has_palette = 1;
break;
@@ -526,11 +530,12 @@ static const char signature[] = "Interplay MVE File\x1A\0\x1A";
static int ipmovie_probe(AVProbeData *p)
{
- uint8_t *b = p->buf;
- uint8_t *b_end = p->buf + p->buf_size - sizeof(signature);
+ const uint8_t *b = p->buf;
+ const uint8_t *b_end = p->buf + p->buf_size - sizeof(signature);
do {
- if (memcmp(b++, signature, sizeof(signature)) == 0)
+ if (b[0] == signature[0] && memcmp(b, signature, sizeof(signature)) == 0)
return AVPROBE_SCORE_MAX;
+ b++;
} while (b < b_end);
return 0;
@@ -543,14 +548,14 @@ static int ipmovie_read_header(AVFormatContext *s)
AVPacket pkt;
AVStream *st;
unsigned char chunk_preamble[CHUNK_PREAMBLE_SIZE];
- int chunk_type;
+ int chunk_type, i;
uint8_t signature_buffer[sizeof(signature)];
avio_read(pb, signature_buffer, sizeof(signature_buffer));
while (memcmp(signature_buffer, signature, sizeof(signature))) {
memmove(signature_buffer, signature_buffer + 1, sizeof(signature_buffer) - 1);
signature_buffer[sizeof(signature_buffer) - 1] = avio_r8(pb);
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR_EOF;
}
/* initialize private context members */
@@ -561,6 +566,9 @@ static int ipmovie_read_header(AVFormatContext *s)
/* on the first read, this will position the stream at the first chunk */
ipmovie->next_chunk_offset = avio_tell(pb) + 4;
+ for (i = 0; i < 256; i++)
+ ipmovie->palette[i] = 0xFFU << 24;
+
/* process the first chunk which should be CHUNK_INIT_VIDEO */
if (process_ipmovie_chunk(ipmovie, pb, &pkt) != CHUNK_INIT_VIDEO)
return AVERROR_INVALIDDATA;
@@ -622,6 +630,7 @@ static int ipmovie_read_packet(AVFormatContext *s,
AVIOContext *pb = s->pb;
int ret;
+ for (;;) {
ret = process_ipmovie_chunk(ipmovie, pb, pkt);
if (ret == CHUNK_BAD)
ret = AVERROR_INVALIDDATA;
@@ -631,10 +640,13 @@ static int ipmovie_read_packet(AVFormatContext *s,
ret = AVERROR(ENOMEM);
else if (ret == CHUNK_VIDEO)
ret = 0;
+ else if (ret == CHUNK_INIT_VIDEO || ret == CHUNK_INIT_AUDIO)
+ continue;
else
ret = -1;
return ret;
+ }
}
AVInputFormat ff_ipmovie_demuxer = {
diff --git a/libavformat/ircam.c b/libavformat/ircam.c
new file mode 100644
index 0000000000..a267c18a08
--- /dev/null
+++ b/libavformat/ircam.c
@@ -0,0 +1,47 @@
+/*
+ * IRCAM common code
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "internal.h"
+
+const AVCodecTag ff_codec_ircam_le_tags[] = {
+ { AV_CODEC_ID_PCM_ALAW, 0x10001 },
+ { AV_CODEC_ID_PCM_F32LE, 0x00004 },
+ { AV_CODEC_ID_PCM_F64LE, 0x00008 },
+ { AV_CODEC_ID_PCM_MULAW, 0x20001 },
+ { AV_CODEC_ID_PCM_S16LE, 0x00002 },
+ { AV_CODEC_ID_PCM_S24LE, 0x00003 },
+ { AV_CODEC_ID_PCM_S32LE, 0x40004 },
+ { AV_CODEC_ID_PCM_S8, 0x00001 },
+ { AV_CODEC_ID_NONE, 0 },
+};
+
+const AVCodecTag ff_codec_ircam_be_tags[] = {
+ { AV_CODEC_ID_PCM_ALAW, 0x10001 },
+ { AV_CODEC_ID_PCM_F32BE, 0x00004 },
+ { AV_CODEC_ID_PCM_F64BE, 0x00008 },
+ { AV_CODEC_ID_PCM_MULAW, 0x20001 },
+ { AV_CODEC_ID_PCM_S16BE, 0x00002 },
+ { AV_CODEC_ID_PCM_S24BE, 0x00003 },
+ { AV_CODEC_ID_PCM_S32BE, 0x40004 },
+ { AV_CODEC_ID_PCM_S8, 0x00001 },
+ { AV_CODEC_ID_NONE, 0 },
+};
diff --git a/libavformat/ircam.h b/libavformat/ircam.h
new file mode 100644
index 0000000000..f7f9c84b91
--- /dev/null
+++ b/libavformat/ircam.h
@@ -0,0 +1,30 @@
+/*
+ * IRCAM common code
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_IRCAM_H
+#define AVFORMAT_IRCAM_H
+
+#include "internal.h"
+
+extern const AVCodecTag ff_codec_ircam_be_tags[];
+extern const AVCodecTag ff_codec_ircam_le_tags[];
+
+#endif /* AVFORMAT_IRCAM_H */
diff --git a/libavformat/ircamdec.c b/libavformat/ircamdec.c
new file mode 100644
index 0000000000..f9533ec4a5
--- /dev/null
+++ b/libavformat/ircamdec.c
@@ -0,0 +1,115 @@
+/*
+ * IRCAM demuxer
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "internal.h"
+#include "pcm.h"
+#include "ircam.h"
+
+static int ircam_probe(AVProbeData *p)
+{
+ if ((p->buf[0] == 0x64 && p->buf[1] == 0xA3 && p->buf[3] == 0x00 &&
+ p->buf[2] >= 1 && p->buf[2] <= 4) ||
+ (p->buf[3] == 0x64 && p->buf[2] == 0xA3 && p->buf[0] == 0x00 &&
+ p->buf[1] >= 1 && p->buf[1] <= 3) &&
+ AV_RN32(p->buf + 4) && AV_RN32(p->buf + 8))
+ return AVPROBE_SCORE_MAX / 4 * 3;
+ return 0;
+}
+
+static const struct endianess {
+ uint32_t magic;
+ int is_le;
+} table[] = {
+ { 0x64A30100, 0 },
+ { 0x64A30200, 1 },
+ { 0x64A30300, 0 },
+ { 0x64A30400, 1 },
+ { 0x0001A364, 1 },
+ { 0x0002A364, 0 },
+ { 0x0003A364, 1 },
+};
+
+static int ircam_read_header(AVFormatContext *s)
+{
+ uint32_t magic, sample_rate, channels, tag;
+ const AVCodecTag *tags;
+ int le = -1, i;
+ AVStream *st;
+
+ magic = avio_rl32(s->pb);
+ for (i = 0; i < 7; i++) {
+ if (magic == table[i].magic) {
+ le = table[i].is_le;
+ break;
+ }
+ }
+
+ if (le == 1) {
+ sample_rate = av_int2float(avio_rl32(s->pb));
+ channels = avio_rl32(s->pb);
+ tag = avio_rl32(s->pb);
+ tags = ff_codec_ircam_le_tags;
+ } else if (le == 0) {
+ sample_rate = av_int2float(avio_rb32(s->pb));
+ channels = avio_rb32(s->pb);
+ tag = avio_rb32(s->pb);
+ tags = ff_codec_ircam_be_tags;
+ } else {
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (!channels || !sample_rate)
+ return AVERROR_INVALIDDATA;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->channels = channels;
+ st->codec->sample_rate = sample_rate;
+
+ st->codec->codec_id = ff_codec_get_id(tags, tag);
+ if (st->codec->codec_id == AV_CODEC_ID_NONE) {
+ av_log(s, AV_LOG_ERROR, "unknown tag %X\n", tag);
+ return AVERROR_INVALIDDATA;
+ }
+
+ st->codec->bits_per_coded_sample = av_get_bits_per_sample(st->codec->codec_id);
+ st->codec->block_align = st->codec->bits_per_coded_sample * st->codec->channels / 8;
+ avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+ avio_skip(s->pb, 1008);
+
+ return 0;
+}
+
+AVInputFormat ff_ircam_demuxer = {
+ .name = "ircam",
+ .long_name = NULL_IF_CONFIG_SMALL("Berkeley/IRCAM/CARL Sound Format"),
+ .read_probe = ircam_probe,
+ .read_header = ircam_read_header,
+ .read_packet = ff_pcm_read_packet,
+ .read_seek = ff_pcm_read_seek,
+ .extensions = "sf,ircam",
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/ircamenc.c b/libavformat/ircamenc.c
new file mode 100644
index 0000000000..38f15fbceb
--- /dev/null
+++ b/libavformat/ircamenc.c
@@ -0,0 +1,62 @@
+/*
+ * IRCAM muxer
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "avio_internal.h"
+#include "internal.h"
+#include "rawenc.h"
+#include "ircam.h"
+
+static int ircam_write_header(AVFormatContext *s)
+{
+ AVCodecContext *codec = s->streams[0]->codec;
+ uint32_t tag;
+
+ if (s->nb_streams != 1) {
+ av_log(s, AV_LOG_ERROR, "only one stream is supported\n");
+ return AVERROR(EINVAL);
+ }
+
+ tag = ff_codec_get_tag(ff_codec_ircam_le_tags, codec->codec_id);
+ if (!tag) {
+ av_log(s, AV_LOG_ERROR, "unsupported codec\n");
+ return AVERROR(EINVAL);
+ }
+
+ avio_wl32(s->pb, 0x0001A364);
+ avio_wl32(s->pb, av_float2int(codec->sample_rate));
+ avio_wl32(s->pb, codec->channels);
+ avio_wl32(s->pb, tag);
+ ffio_fill(s->pb, 0, 1008);
+ return 0;
+}
+
+AVOutputFormat ff_ircam_muxer = {
+ .name = "ircam",
+ .extensions = "sf,ircam",
+ .long_name = NULL_IF_CONFIG_SMALL("Berkeley/IRCAM/CARL Sound Format"),
+ .audio_codec = AV_CODEC_ID_PCM_S16LE,
+ .video_codec = AV_CODEC_ID_NONE,
+ .write_header = ircam_write_header,
+ .write_packet = ff_raw_write_packet,
+ .codec_tag = (const AVCodecTag *const []){ ff_codec_ircam_le_tags, 0 },
+};
diff --git a/libavformat/isom.c b/libavformat/isom.c
index cd532aa23c..fcd696a8b1 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
*/
@@ -57,6 +57,7 @@ const AVCodecTag ff_mp4_obj_type[] = {
{ AV_CODEC_ID_VC1 , 0xA3 },
{ AV_CODEC_ID_DIRAC , 0xA4 },
{ AV_CODEC_ID_AC3 , 0xA5 },
+ { AV_CODEC_ID_EAC3 , 0xA6 },
{ AV_CODEC_ID_DTS , 0xA9 }, /* mp4ra.org */
{ AV_CODEC_ID_VORBIS , 0xDD }, /* non standard, gpac uses it */
{ AV_CODEC_ID_DVD_SUBTITLE, 0xE0 }, /* non standard, see unsupported-embedded-subs-2.mp4 */
@@ -71,7 +72,6 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
{ AV_CODEC_ID_RAWVIDEO, MKTAG('r', 'a', 'w', ' ') }, /* Uncompressed RGB */
{ AV_CODEC_ID_RAWVIDEO, MKTAG('y', 'u', 'v', '2') }, /* Uncompressed YUV422 */
- { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'V', 'U', 'I') }, /* YUV with alpha-channel (AVID Uncompressed) */
{ AV_CODEC_ID_RAWVIDEO, MKTAG('2', 'v', 'u', 'y') }, /* UNCOMPRESSED 8BIT 4:2:2 */
{ AV_CODEC_ID_RAWVIDEO, MKTAG('y', 'u', 'v', 's') }, /* same as 2vuy but byte swapped */
@@ -95,9 +95,17 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
{ AV_CODEC_ID_R10K, MKTAG('R', '1', '0', 'k') }, /* UNCOMPRESSED 10BIT RGB */
{ AV_CODEC_ID_R10K, MKTAG('R', '1', '0', 'g') }, /* UNCOMPRESSED 10BIT RGB */
{ AV_CODEC_ID_R210, MKTAG('r', '2', '1', '0') }, /* UNCOMPRESSED 10BIT RGB */
+ { AV_CODEC_ID_AVUI, MKTAG('A', 'V', 'U', 'I') }, /* AVID Uncompressed deinterleaved UYVY422 */
+ { AV_CODEC_ID_AVRP, MKTAG('A', 'V', 'r', 'p') }, /* Avid 1:1 10-bit RGB Packer */
+ { AV_CODEC_ID_AVRP, MKTAG('S', 'U', 'D', 'S') }, /* Avid DS Uncompressed */
{ AV_CODEC_ID_V210, MKTAG('v', '2', '1', '0') }, /* UNCOMPRESSED 10BIT 4:2:2 */
{ AV_CODEC_ID_V210, MKTAG('b', 'x', 'y', '2') }, /* BOXX 10BIT 4:2:2 */
+ { AV_CODEC_ID_V308, MKTAG('v', '3', '0', '8') }, /* UNCOMPRESSED 8BIT 4:4:4 */
+ { AV_CODEC_ID_V408, MKTAG('v', '4', '0', '8') }, /* UNCOMPRESSED 8BIT 4:4:4:4 */
{ AV_CODEC_ID_V410, MKTAG('v', '4', '1', '0') }, /* UNCOMPRESSED 10BIT 4:4:4 */
+ { AV_CODEC_ID_Y41P, MKTAG('Y', '4', '1', 'P') }, /* UNCOMPRESSED 12BIT 4:1:1 */
+ { AV_CODEC_ID_YUV4, MKTAG('y', 'u', 'v', '4') }, /* libquicktime packed yuv420p */
+ { AV_CODEC_ID_TARGA_Y216, MKTAG('Y', '2', '1', '6') },
{ AV_CODEC_ID_MJPEG, MKTAG('j', 'p', 'e', 'g') }, /* PhotoJPEG */
{ AV_CODEC_ID_MJPEG, MKTAG('m', 'j', 'p', 'a') }, /* Motion-JPEG (format A) */
@@ -165,6 +173,7 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '1', '6') }, /* AVC-Intra 100M 1080i60 */
{ AV_CODEC_ID_H264, MKTAG('A', 'V', 'i', 'n') }, /* AVC-Intra with implicit SPS/PPS */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', 'v', 'x') }, /* XAVC 4:2:2 10bit */
+ { AV_CODEC_ID_H264, MKTAG('r', 'v', '6', '4') }, /* X-Com Radvision */
{ AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', '1', 'v', ' ') },
{ AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', '1', 'v', '1') }, /* Apple MPEG-1 Camcorder */
@@ -300,12 +309,15 @@ const AVCodecTag ff_codec_movaudio_tags[] = {
{ AV_CODEC_ID_SPEEX, MKTAG('s', 'p', 'e', 'x') }, /* Flash Media Server */
{ AV_CODEC_ID_SPEEX, MKTAG('S', 'P', 'X', 'N') }, /* ZygoAudio (quality 10 mode) */
{ AV_CODEC_ID_WMAV2, MKTAG('W', 'M', 'A', '2') },
+ { AV_CODEC_ID_EVRC, MKTAG('s', 'e', 'v', 'c') }, /* 3GPP2 */
+ { AV_CODEC_ID_SMV, MKTAG('s', 's', 'm', 'v') }, /* 3GPP2 */
{ AV_CODEC_ID_NONE, 0 },
};
const AVCodecTag ff_codec_movsubtitle_tags[] = {
{ AV_CODEC_ID_MOV_TEXT, MKTAG('t', 'e', 'x', 't') },
{ AV_CODEC_ID_MOV_TEXT, MKTAG('t', 'x', '3', 'g') },
+ { AV_CODEC_ID_EIA_608, MKTAG('c', '6', '0', '8') },
{ AV_CODEC_ID_NONE, 0 },
};
@@ -433,14 +445,23 @@ static const AVCodecTag mp4_audio_types[] = {
int ff_mp4_read_dec_config_descr(AVFormatContext *fc, AVStream *st, AVIOContext *pb)
{
+ enum AVCodecID codec_id;
int len, tag;
+ int ret;
int object_type_id = avio_r8(pb);
avio_r8(pb); /* stream type */
avio_rb24(pb); /* buffer size db */
avio_rb32(pb); /* max bitrate */
avio_rb32(pb); /* avg bitrate */
- st->codec->codec_id= ff_codec_get_id(ff_mp4_obj_type, object_type_id);
+ if(avcodec_is_open(st->codec)) {
+ av_log(fc, AV_LOG_DEBUG, "codec open in read_dec_config_descr\n");
+ return -1;
+ }
+
+ codec_id= ff_codec_get_id(ff_mp4_obj_type, object_type_id);
+ if (codec_id)
+ st->codec->codec_id= codec_id;
av_dlog(fc, "esds object type id 0x%02x\n", object_type_id);
len = ff_mp4_read_descr(fc, pb, &tag);
if (tag == MP4DecSpecificDescrTag) {
@@ -448,13 +469,10 @@ int ff_mp4_read_dec_config_descr(AVFormatContext *fc, AVStream *st, AVIOContext
if (!len || (uint64_t)len > (1<<30))
return -1;
av_free(st->codec->extradata);
- st->codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
- return AVERROR(ENOMEM);
- avio_read(pb, st->codec->extradata, len);
- st->codec->extradata_size = len;
+ if ((ret = ff_get_extradata(st->codec, pb, len)) < 0)
+ return ret;
if (st->codec->codec_id == AV_CODEC_ID_AAC) {
- MPEG4AudioConfig cfg;
+ MPEG4AudioConfig cfg = {0};
avpriv_mpeg4audio_get_config(&cfg, st->codec->extradata,
st->codec->extradata_size * 8, 1);
st->codec->channels = cfg.channels;
@@ -475,3 +493,104 @@ int ff_mp4_read_dec_config_descr(AVFormatContext *fc, AVStream *st, AVIOContext
}
return 0;
}
+
+typedef struct MovChannelLayout {
+ int64_t channel_layout;
+ uint32_t layout_tag;
+} MovChannelLayout;
+
+static const MovChannelLayout mov_channel_layout[] = {
+ { AV_CH_LAYOUT_MONO, (100<<16) | 1}, // kCAFChannelLayoutTag_Mono
+ { AV_CH_LAYOUT_STEREO, (101<<16) | 2}, // kCAFChannelLayoutTag_Stereo
+ { AV_CH_LAYOUT_STEREO, (102<<16) | 2}, // kCAFChannelLayoutTag_StereoHeadphones
+ { AV_CH_LAYOUT_2_1, (131<<16) | 3}, // kCAFChannelLayoutTag_ITU_2_1
+ { AV_CH_LAYOUT_QUAD, (132<<16) | 4}, // kCAFChannelLayoutTag_ITU_2_2
+ { AV_CH_LAYOUT_2_2, (132<<16) | 4}, // kCAFChannelLayoutTag_ITU_2_2
+ { AV_CH_LAYOUT_QUAD, (108<<16) | 4}, // kCAFChannelLayoutTag_Quadraphonic
+ { AV_CH_LAYOUT_SURROUND, (113<<16) | 3}, // kCAFChannelLayoutTag_MPEG_3_0_A
+ { AV_CH_LAYOUT_4POINT0, (115<<16) | 4}, // kCAFChannelLayoutTag_MPEG_4_0_A
+ { AV_CH_LAYOUT_5POINT0_BACK, (117<<16) | 5}, // kCAFChannelLayoutTag_MPEG_5_0_A
+ { AV_CH_LAYOUT_5POINT0, (117<<16) | 5}, // kCAFChannelLayoutTag_MPEG_5_0_A
+ { AV_CH_LAYOUT_5POINT1_BACK, (121<<16) | 6}, // kCAFChannelLayoutTag_MPEG_5_1_A
+ { AV_CH_LAYOUT_5POINT1, (121<<16) | 6}, // kCAFChannelLayoutTag_MPEG_5_1_A
+ { AV_CH_LAYOUT_7POINT1, (128<<16) | 8}, // kCAFChannelLayoutTag_MPEG_7_1_C
+ { AV_CH_LAYOUT_7POINT1_WIDE, (126<<16) | 8}, // kCAFChannelLayoutTag_MPEG_7_1_A
+ { AV_CH_LAYOUT_5POINT1_BACK|AV_CH_LAYOUT_STEREO_DOWNMIX, (130<<16) | 8}, // kCAFChannelLayoutTag_SMPTE_DTV
+ { AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY, (133<<16) | 3}, // kCAFChannelLayoutTag_DVD_4
+ { AV_CH_LAYOUT_2_1|AV_CH_LOW_FREQUENCY, (134<<16) | 4}, // kCAFChannelLayoutTag_DVD_5
+ { AV_CH_LAYOUT_QUAD|AV_CH_LOW_FREQUENCY, (135<<16) | 4}, // kCAFChannelLayoutTag_DVD_6
+ { AV_CH_LAYOUT_2_2|AV_CH_LOW_FREQUENCY, (135<<16) | 4}, // kCAFChannelLayoutTag_DVD_6
+ { AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY, (136<<16) | 4}, // kCAFChannelLayoutTag_DVD_10
+ { AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY, (137<<16) | 5}, // kCAFChannelLayoutTag_DVD_11
+ { 0, 0},
+};
+#if 0
+int ff_mov_read_chan(AVFormatContext *s, AVStream *st, int64_t size)
+{
+ AVCodecContext *codec= st->codec;
+ uint32_t layout_tag;
+ AVIOContext *pb = s->pb;
+ const MovChannelLayout *layouts = mov_channel_layout;
+
+ if (size < 12)
+ return AVERROR_INVALIDDATA;
+
+ layout_tag = avio_rb32(pb);
+ size -= 4;
+ if (layout_tag == 0) { // kCAFChannelLayoutTag_UseChannelDescriptions
+ // Channel descriptions not implemented
+ av_log_ask_for_sample(s, "Unimplemented container channel layout.\n");
+ avio_skip(pb, size);
+ return 0;
+ }
+ if (layout_tag == 0x10000) { // kCAFChannelLayoutTag_UseChannelBitmap
+ codec->channel_layout = avio_rb32(pb);
+ size -= 4;
+ avio_skip(pb, size);
+ return 0;
+ }
+ while (layouts->channel_layout) {
+ if (layout_tag == layouts->layout_tag) {
+ codec->channel_layout = layouts->channel_layout;
+ break;
+ }
+ layouts++;
+ }
+ if (!codec->channel_layout)
+ av_log(s, AV_LOG_WARNING, "Unknown container channel layout.\n");
+ avio_skip(pb, size);
+
+ return 0;
+}
+#endif
+
+void ff_mov_write_chan(AVIOContext *pb, int64_t channel_layout)
+{
+ const MovChannelLayout *layouts;
+ uint32_t layout_tag = 0;
+
+ for (layouts = mov_channel_layout; layouts->channel_layout; layouts++)
+ if (channel_layout == layouts->channel_layout) {
+ layout_tag = layouts->layout_tag;
+ break;
+ }
+
+ if (layout_tag) {
+ avio_wb32(pb, layout_tag); // mChannelLayoutTag
+ avio_wb32(pb, 0); // mChannelBitmap
+ } else {
+ avio_wb32(pb, 0x10000); // kCAFChannelLayoutTag_UseChannelBitmap
+ avio_wb32(pb, channel_layout);
+ }
+ avio_wb32(pb, 0); // mNumberChannelDescriptions
+}
+
+const struct AVCodecTag *avformat_get_mov_video_tags(void)
+{
+ return ff_codec_movvideo_tags;
+}
+
+const struct AVCodecTag *avformat_get_mov_audio_tags(void)
+{
+ return ff_codec_movaudio_tags;
+}
diff --git a/libavformat/isom.h b/libavformat/isom.h
index c24b962ae5..d233839dab 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
*/
@@ -53,6 +53,12 @@ typedef struct MOVStsc {
int id;
} MOVStsc;
+typedef struct MOVElst {
+ int64_t duration;
+ int64_t time;
+ float rate;
+} MOVElst;
+
typedef struct MOVDref {
uint32_t type;
char *path;
@@ -78,6 +84,7 @@ typedef struct MOVFragment {
unsigned duration;
unsigned size;
unsigned flags;
+ int64_t time;
} MOVFragment;
typedef struct MOVTrackExt {
@@ -93,8 +100,21 @@ typedef struct MOVSbgp {
unsigned int index;
} MOVSbgp;
+typedef struct MOVFragmentIndexItem {
+ int64_t moof_offset;
+ int64_t time;
+} MOVFragmentIndexItem;
+
+typedef struct MOVFragmentIndex {
+ unsigned track_id;
+ unsigned item_count;
+ unsigned current_item;
+ MOVFragmentIndexItem *items;
+} MOVFragmentIndex;
+
typedef struct MOVStreamContext {
AVIOContext *pb;
+ int pb_is_copied;
int ffindex; ///< AVStream index
int next_chunk;
unsigned int chunk_count;
@@ -107,16 +127,19 @@ typedef struct MOVStreamContext {
MOVStsc *stsc_data;
unsigned int stps_count;
unsigned *stps_data; ///< partial sync sample for mpeg-2 open gop
+ MOVElst *elst_data;
+ unsigned int elst_count;
int ctts_index;
int ctts_sample;
- unsigned int sample_size;
+ unsigned int sample_size; ///< may contain value calculated from stsd or value from stsz atom
+ unsigned int stsz_sample_size; ///< always contains sample size from stsz atom
unsigned int sample_count;
int *sample_sizes;
int keyframe_absent;
unsigned int keyframe_count;
int *keyframes;
int time_scale;
- int64_t time_offset; ///< time offset of the first edit list entry
+ int64_t time_offset; ///< time offset of the edit list entries
int current_sample;
unsigned int bytes_per_frame;
unsigned int samples_per_frame;
@@ -126,6 +149,7 @@ typedef struct MOVStreamContext {
unsigned drefs_count;
MOVDref *drefs;
int dref_id;
+ int timecode_track;
int wrong_dts; ///< dts are wrong due to huge ctts offset (iMovie files)
int width; ///< tkhd width
int height; ///< tkhd height
@@ -133,10 +157,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;
+ int nb_frames_for_fps;
+ int64_t duration_for_fps;
+
int32_t *display_matrix;
} MOVStreamContext;
@@ -155,9 +184,19 @@ typedef struct MOVContext {
unsigned trex_count;
int itunes_metadata; ///< metadata are itunes style
int chapter_track;
+ int use_absolute_path;
+ int ignore_editlist;
int64_t next_root_atom; ///< offset of the next root atom
int export_all;
int export_xmp;
+ int *bitrates; ///< bitrates read before streams creation
+ int bitrates_count;
+ int moov_retry;
+ int use_mfra_for;
+ int has_looked_for_mfra;
+ MOVFragmentIndex** fragment_index_data;
+ unsigned fragment_index_count;
+ int atom_depth;
} MOVContext;
int ff_mp4_read_descr_len(AVIOContext *pb);
@@ -215,6 +254,7 @@ void ff_mp4_parse_es_descr(AVIOContext *pb, int *es_id);
(tag) == MKTAG('a', 'i', '1', '3') || \
(tag) == MKTAG('a', 'i', '1', '5') || \
(tag) == MKTAG('a', 'i', '1', '6') || \
+ (tag) == MKTAG('a', 'i', 'v', 'x') || \
(tag) == MKTAG('A', 'V', 'i', 'n'))
@@ -222,5 +262,10 @@ int ff_mov_read_esds(AVFormatContext *fc, AVIOContext *pb);
enum AVCodecID ff_mov_get_lpcm_codec_id(int bps, int flags);
int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries);
+void ff_mov_write_chan(AVIOContext *pb, int64_t channel_layout);
+
+#define FF_MOV_FLAG_MFRA_AUTO -1
+#define FF_MOV_FLAG_MFRA_DTS 1
+#define FF_MOV_FLAG_MFRA_PTS 2
#endif /* AVFORMAT_ISOM_H */
diff --git a/libavformat/iss.c b/libavformat/iss.c
index c86b4250c8..fbde505a2d 100644
--- a/libavformat/iss.c
+++ b/libavformat/iss.c
@@ -2,20 +2,20 @@
* ISS (.iss) file demuxer
* Copyright (c) 2008 Jaikrishnan Menon <realityman@gmx.net>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -76,18 +76,32 @@ static av_cold int iss_read_header(AVFormatContext *s)
get_token(pb, token, sizeof(token)); //"IMA_ADPCM_Sound"
get_token(pb, token, sizeof(token)); //packet size
- sscanf(token, "%d", &iss->packet_size);
+ if (sscanf(token, "%d", &iss->packet_size) != 1) {
+ av_log(s, AV_LOG_ERROR, "Failed parsing packet size\n");
+ return AVERROR_INVALIDDATA;
+ }
get_token(pb, token, sizeof(token)); //File ID
get_token(pb, token, sizeof(token)); //out size
get_token(pb, token, sizeof(token)); //stereo
- sscanf(token, "%d", &stereo);
+ if (sscanf(token, "%d", &stereo) != 1) {
+ av_log(s, AV_LOG_ERROR, "Failed parsing stereo flag\n");
+ return AVERROR_INVALIDDATA;
+ }
get_token(pb, token, sizeof(token)); //Unknown1
get_token(pb, token, sizeof(token)); //RateDivisor
- sscanf(token, "%d", &rate_divisor);
+ if (sscanf(token, "%d", &rate_divisor) != 1) {
+ av_log(s, AV_LOG_ERROR, "Failed parsing rate_divisor\n");
+ return AVERROR_INVALIDDATA;
+ }
get_token(pb, token, sizeof(token)); //Unknown2
get_token(pb, token, sizeof(token)); //Version ID
get_token(pb, token, sizeof(token)); //Size
+ if (iss->packet_size <= 0) {
+ av_log(s, AV_LOG_ERROR, "packet_size %d is invalid\n", iss->packet_size);
+ return AVERROR_INVALIDDATA;
+ }
+
iss->sample_start_pos = avio_tell(pb);
st = avformat_new_stream(s, NULL);
diff --git a/libavformat/iv8.c b/libavformat/iv8.c
index 56909e3a22..38b79609f6 100644
--- a/libavformat/iv8.c
+++ b/libavformat/iv8.c
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2009 Michael Niedermayer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/ivfdec.c b/libavformat/ivfdec.c
index b3555f42a4..c0fdafbf02 100644
--- a/libavformat/ivfdec.c
+++ b/libavformat/ivfdec.c
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2010 David Conrad
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/ivfenc.c b/libavformat/ivfenc.c
index 3cd16166c7..5700f01d3c 100644
--- a/libavformat/ivfenc.c
+++ b/libavformat/ivfenc.c
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2010 Reimar Döffinger
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "avformat.h"
@@ -30,8 +30,9 @@ static int ivf_write_header(AVFormatContext *s)
return AVERROR(EINVAL);
}
ctx = s->streams[0]->codec;
- if (ctx->codec_type != AVMEDIA_TYPE_VIDEO || ctx->codec_id != AV_CODEC_ID_VP8) {
- av_log(s, AV_LOG_ERROR, "Currently only VP8 is supported!\n");
+ if (ctx->codec_type != AVMEDIA_TYPE_VIDEO ||
+ !(ctx->codec_id == AV_CODEC_ID_VP8 || ctx->codec_id == AV_CODEC_ID_VP9)) {
+ av_log(s, AV_LOG_ERROR, "Currently only VP8 and VP9 are supported!\n");
return AVERROR(EINVAL);
}
avio_write(pb, "DKIF", 4);
diff --git a/libavformat/jacosubdec.c b/libavformat/jacosubdec.c
new file mode 100644
index 0000000000..1ca0055877
--- /dev/null
+++ b/libavformat/jacosubdec.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2012 Clément Bœsch
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * JACOsub subtitle demuxer
+ * @see http://unicorn.us.com/jacosub/jscripts.html
+ * @todo Support P[ALETTE] directive.
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "subtitles.h"
+#include "libavcodec/internal.h"
+#include "libavcodec/jacosub.h"
+#include "libavutil/avstring.h"
+#include "libavutil/bprint.h"
+#include "libavutil/intreadwrite.h"
+
+typedef struct {
+ int shift;
+ unsigned timeres;
+ FFDemuxSubtitlesQueue q;
+} JACOsubContext;
+
+static int timed_line(const char *ptr)
+{
+ char c;
+ int fs, fe;
+ return (sscanf(ptr, "%*u:%*u:%*u.%*u %*u:%*u:%*u.%*u %c", &c) == 1 ||
+ (sscanf(ptr, "@%u @%u %c", &fs, &fe, &c) == 3 && fs < fe));
+}
+
+static int jacosub_probe(AVProbeData *p)
+{
+ const char *ptr = p->buf;
+ const char *ptr_end = p->buf + p->buf_size;
+
+ if (AV_RB24(ptr) == 0xEFBBBF)
+ ptr += 3; /* skip UTF-8 BOM */
+
+ while (ptr < ptr_end) {
+ while (jss_whitespace(*ptr))
+ ptr++;
+ if (*ptr != '#' && *ptr != '\n') {
+ if (timed_line(ptr))
+ return AVPROBE_SCORE_EXTENSION + 1;
+ return 0;
+ }
+ ptr += ff_subtitles_next_line(ptr);
+ }
+ return 0;
+}
+
+static const char * const cmds[] = {
+ "CLOCKPAUSE",
+ "DIRECTIVE",
+ "FONT",
+ "HRES",
+ "INCLUDE",
+ "PALETTE",
+ "QUANTIZE",
+ "RAMP",
+ "SHIFT",
+ "TIMERES",
+};
+
+static int get_jss_cmd(char k)
+{
+ int i;
+
+ k = av_toupper(k);
+ for (i = 0; i < FF_ARRAY_ELEMS(cmds); i++)
+ if (k == cmds[i][0])
+ return i;
+ return -1;
+}
+
+static int jacosub_read_close(AVFormatContext *s)
+{
+ JACOsubContext *jacosub = s->priv_data;
+ ff_subtitles_queue_clean(&jacosub->q);
+ return 0;
+}
+
+static const char *read_ts(JACOsubContext *jacosub, const char *buf,
+ int64_t *start, int *duration)
+{
+ int len;
+ unsigned hs, ms, ss, fs; // hours, minutes, seconds, frame start
+ unsigned he, me, se, fe; // hours, minutes, seconds, frame end
+ int ts_start, ts_end;
+
+ /* timed format */
+ if (sscanf(buf, "%u:%u:%u.%u %u:%u:%u.%u %n",
+ &hs, &ms, &ss, &fs,
+ &he, &me, &se, &fe, &len) == 8) {
+ ts_start = (hs*3600 + ms*60 + ss) * jacosub->timeres + fs;
+ ts_end = (he*3600 + me*60 + se) * jacosub->timeres + fe;
+ goto shift_and_ret;
+ }
+
+ /* timestamps format */
+ if (sscanf(buf, "@%u @%u %n", &ts_start, &ts_end, &len) == 2)
+ goto shift_and_ret;
+
+ return NULL;
+
+shift_and_ret:
+ ts_start = (ts_start + jacosub->shift) * 100 / jacosub->timeres;
+ ts_end = (ts_end + jacosub->shift) * 100 / jacosub->timeres;
+ *start = ts_start;
+ *duration = ts_start + ts_end;
+ return buf + len;
+}
+
+static int get_shift(int timeres, const char *buf)
+{
+ int sign = 1;
+ int a = 0, b = 0, c = 0, d = 0;
+#define SSEP "%*1[.:]"
+ int n = sscanf(buf, "%d"SSEP"%d"SSEP"%d"SSEP"%d", &a, &b, &c, &d);
+#undef SSEP
+
+ if (*buf == '-' || a < 0) {
+ sign = -1;
+ a = FFABS(a);
+ }
+
+ switch (n) {
+ case 4: return sign * ((a*3600 + b*60 + c) * timeres + d);
+ case 3: return sign * (( a*60 + b) * timeres + c);
+ case 2: return sign * (( a) * timeres + b);
+ }
+
+ return 0;
+}
+
+static int jacosub_read_header(AVFormatContext *s)
+{
+ AVBPrint header;
+ AVIOContext *pb = s->pb;
+ char line[JSS_MAX_LINESIZE];
+ JACOsubContext *jacosub = s->priv_data;
+ int shift_set = 0; // only the first shift matters
+ int merge_line = 0;
+ int i, ret;
+
+ AVStream *st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 64, 1, 100);
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_JACOSUB;
+
+ jacosub->timeres = 30;
+
+ av_bprint_init(&header, 1024+FF_INPUT_BUFFER_PADDING_SIZE, 4096);
+
+ while (!avio_feof(pb)) {
+ int cmd_len;
+ const char *p = line;
+ int64_t pos = avio_tell(pb);
+ int len = ff_get_line(pb, line, sizeof(line));
+
+ p = jss_skip_whitespace(p);
+
+ /* queue timed line */
+ if (merge_line || timed_line(p)) {
+ AVPacket *sub;
+
+ sub = ff_subtitles_queue_insert(&jacosub->q, line, len, merge_line);
+ if (!sub)
+ return AVERROR(ENOMEM);
+ sub->pos = pos;
+ merge_line = len > 1 && !strcmp(&line[len - 2], "\\\n");
+ continue;
+ }
+
+ /* skip all non-compiler commands and focus on the command */
+ if (*p != '#')
+ continue;
+ p++;
+ i = get_jss_cmd(p[0]);
+ if (i == -1)
+ continue;
+
+ /* trim command + spaces */
+ cmd_len = strlen(cmds[i]);
+ if (av_strncasecmp(p, cmds[i], cmd_len) == 0)
+ p += cmd_len;
+ else
+ p++;
+ p = jss_skip_whitespace(p);
+
+ /* handle commands which affect the whole script */
+ switch (cmds[i][0]) {
+ case 'S': // SHIFT command affect the whole script...
+ if (!shift_set) {
+ jacosub->shift = get_shift(jacosub->timeres, p);
+ shift_set = 1;
+ }
+ av_bprintf(&header, "#S %s", p);
+ break;
+ case 'T': // ...but must be placed after TIMERES
+ jacosub->timeres = strtol(p, NULL, 10);
+ if (!jacosub->timeres)
+ jacosub->timeres = 30;
+ else
+ av_bprintf(&header, "#T %s", p);
+ break;
+ }
+ }
+
+ /* general/essential directives in the extradata */
+ ret = avpriv_bprint_to_extradata(st->codec, &header);
+ if (ret < 0)
+ goto fail;
+
+ /* 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;
+fail:
+ jacosub_read_close(s);
+ return ret;
+}
+
+static int jacosub_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ JACOsubContext *jacosub = s->priv_data;
+ return ff_subtitles_queue_read_packet(&jacosub->q, pkt);
+}
+
+static int jacosub_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ JACOsubContext *jacosub = s->priv_data;
+ return ff_subtitles_queue_seek(&jacosub->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+AVInputFormat ff_jacosub_demuxer = {
+ .name = "jacosub",
+ .long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle format"),
+ .priv_data_size = sizeof(JACOsubContext),
+ .read_probe = jacosub_probe,
+ .read_header = jacosub_read_header,
+ .read_packet = jacosub_read_packet,
+ .read_seek2 = jacosub_read_seek,
+ .read_close = jacosub_read_close,
+};
diff --git a/libavformat/jacosubenc.c b/libavformat/jacosubenc.c
new file mode 100644
index 0000000000..a11e45a4aa
--- /dev/null
+++ b/libavformat/jacosubenc.c
@@ -0,0 +1,42 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "rawenc.h"
+
+static int jacosub_write_header(AVFormatContext *s)
+{
+ const AVCodecContext *avctx = s->streams[0]->codec;
+
+ if (avctx->extradata_size) {
+ avio_write(s->pb, avctx->extradata, avctx->extradata_size - 1);
+ avio_flush(s->pb);
+ }
+ return 0;
+}
+
+AVOutputFormat ff_jacosub_muxer = {
+ .name = "jacosub",
+ .long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle format"),
+ .mime_type = "text/x-jacosub",
+ .extensions = "jss,js",
+ .write_header = jacosub_write_header,
+ .write_packet = ff_raw_write_packet,
+ .flags = AVFMT_TS_NONSTRICT,
+ .subtitle_codec = AV_CODEC_ID_JACOSUB,
+};
diff --git a/libavformat/jvdec.c b/libavformat/jvdec.c
index 8aebbf4de8..64d31e0ee6 100644
--- a/libavformat/jvdec.c
+++ b/libavformat/jvdec.c
@@ -2,20 +2,20 @@
* Bitmap Brothers JV demuxer
* Copyright (c) 2005, 2011 Peter Ross <pross@xvid.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -34,10 +34,10 @@
#define JV_PREAMBLE_SIZE 5
typedef struct JVFrame {
- int audio_size; /** audio packet size (bytes) */
- int video_size; /** video packet size (bytes) */
- int palette_size; /** palette size (bytes) */
- int video_type; /** per-frame video compression type */
+ int audio_size; /**< audio packet size (bytes) */
+ int video_size; /**< video packet size (bytes) */
+ int palette_size; /**< palette size (bytes) */
+ int video_type; /**< per-frame video compression type */
} JVFrame;
typedef struct JVDemuxContext {
@@ -54,8 +54,8 @@ typedef struct JVDemuxContext {
static int read_probe(AVProbeData *pd)
{
- if (pd->buf[0] == 'J' && pd->buf[1] == 'V' &&
- !memcmp(pd->buf + 4, MAGIC, FFMIN(strlen(MAGIC), pd->buf_size - 4)))
+ if (pd->buf[0] == 'J' && pd->buf[1] == 'V' && strlen(MAGIC) <= pd->buf_size - 4 &&
+ !memcmp(pd->buf + 4, MAGIC, strlen(MAGIC)))
return AVPROBE_SCORE_MAX;
return 0;
}
@@ -166,7 +166,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt)
AVIOContext *pb = s->pb;
AVStream *ast = s->streams[0];
- while (!s->pb->eof_reached && jv->pts < ast->nb_index_entries) {
+ while (!avio_feof(s->pb) && jv->pts < ast->nb_index_entries) {
const AVIndexEntry *e = ast->index_entries + jv->pts;
const JVFrame *jvf = jv->frames + jv->pts;
diff --git a/libavformat/latmenc.c b/libavformat/latmenc.c
index 5190ac7b20..17dbf33b72 100644
--- a/libavformat/latmenc.c
+++ b/libavformat/latmenc.c
@@ -2,20 +2,20 @@
* LATM/LOAS muxer
* Copyright (c) 2011 Kieran Kunhya <kieran@kunhya.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -25,6 +25,9 @@
#include "libavcodec/mpeg4audio.h"
#include "libavutil/opt.h"
#include "avformat.h"
+#include "rawenc.h"
+
+#define MAX_EXTRADATA_SIZE 1024
typedef struct LATMContext {
AVClass *av_class;
@@ -33,6 +36,7 @@ typedef struct LATMContext {
int object_type;
int counter;
int mod;
+ uint8_t buffer[0x1fff + MAX_EXTRADATA_SIZE + 1024];
} LATMContext;
static const AVOption options[] = {
@@ -50,15 +54,21 @@ static const AVClass latm_muxer_class = {
static int latm_decode_extradata(LATMContext *ctx, uint8_t *buf, int size)
{
- GetBitContext gb;
MPEG4AudioConfig m4ac;
- init_get_bits(&gb, buf, size * 8);
+ if (size > MAX_EXTRADATA_SIZE) {
+ av_log(ctx, AV_LOG_ERROR, "Extradata is larger than currently supported.\n");
+ return AVERROR_INVALIDDATA;
+ }
ctx->off = avpriv_mpeg4audio_get_config(&m4ac, buf, size * 8, 1);
if (ctx->off < 0)
return ctx->off;
- skip_bits_long(&gb, ctx->off);
+ if (ctx->object_type == AOT_ALS && (ctx->off & 7)) {
+ // as long as avpriv_mpeg4audio_get_config works correctly this is impossible
+ av_log(ctx, AV_LOG_ERROR, "BUG: ALS offset is not byte-aligned\n");
+ return AVERROR_INVALIDDATA;
+ }
/* FIXME: are any formats not allowed in LATM? */
if (m4ac.object_type > AOT_SBR && m4ac.object_type != AOT_ALS) {
@@ -76,6 +86,9 @@ static int latm_write_header(AVFormatContext *s)
LATMContext *ctx = s->priv_data;
AVCodecContext *avctx = s->streams[0]->codec;
+ if (avctx->codec_id == AV_CODEC_ID_AAC_LATM)
+ return 0;
+
if (avctx->extradata_size > 0 &&
latm_decode_extradata(ctx, avctx->extradata, avctx->extradata_size) < 0)
return AVERROR_INVALIDDATA;
@@ -83,19 +96,16 @@ static int latm_write_header(AVFormatContext *s)
return 0;
}
-static int latm_write_frame_header(AVFormatContext *s, PutBitContext *bs)
+static void latm_write_frame_header(AVFormatContext *s, PutBitContext *bs)
{
LATMContext *ctx = s->priv_data;
AVCodecContext *avctx = s->streams[0]->codec;
- GetBitContext gb;
int header_size;
/* AudioMuxElement */
put_bits(bs, 1, !!ctx->counter);
if (!ctx->counter) {
- init_get_bits(&gb, avctx->extradata, avctx->extradata_size * 8);
-
/* StreamMuxConfig */
put_bits(bs, 1, 0); /* audioMuxVersion */
put_bits(bs, 1, 1); /* allStreamsSameTimeFraming */
@@ -105,12 +115,17 @@ static int latm_write_frame_header(AVFormatContext *s, PutBitContext *bs)
/* AudioSpecificConfig */
if (ctx->object_type == AOT_ALS) {
- header_size = avctx->extradata_size-(ctx->off + 7) >> 3;
- avpriv_copy_bits(bs, &avctx->extradata[ctx->off], header_size);
+ header_size = avctx->extradata_size-(ctx->off >> 3);
+ avpriv_copy_bits(bs, &avctx->extradata[ctx->off >> 3], header_size);
} else {
+ // + 3 assumes not scalable and dependsOnCoreCoder == 0,
+ // see decode_ga_specific_config in libavcodec/aacdec.c
avpriv_copy_bits(bs, avctx->extradata, ctx->off + 3);
if (!ctx->channel_conf) {
+ GetBitContext gb;
+ init_get_bits8(&gb, avctx->extradata, avctx->extradata_size);
+ skip_bits_long(&gb, ctx->off + 3);
avpriv_copy_pce_data(bs, &gb);
}
}
@@ -124,28 +139,36 @@ static int latm_write_frame_header(AVFormatContext *s, PutBitContext *bs)
ctx->counter++;
ctx->counter %= ctx->mod;
-
- return 0;
}
static int latm_write_packet(AVFormatContext *s, AVPacket *pkt)
{
+ LATMContext *ctx = s->priv_data;
AVIOContext *pb = s->pb;
PutBitContext bs;
int i, len;
uint8_t loas_header[] = "\x56\xe0\x00";
- uint8_t *buf;
+
+ if (s->streams[0]->codec->codec_id == AV_CODEC_ID_AAC_LATM)
+ return ff_raw_write_packet(s, pkt);
if (pkt->size > 2 && pkt->data[0] == 0xff && (pkt->data[1] >> 4) == 0xf) {
av_log(s, AV_LOG_ERROR, "ADTS header detected - ADTS will not be incorrectly muxed into LATM\n");
return AVERROR_INVALIDDATA;
}
- buf = av_malloc(pkt->size+1024);
- if (!buf)
- return AVERROR(ENOMEM);
+ if (!s->streams[0]->codec->extradata) {
+ if(pkt->size > 2 && pkt->data[0] == 0x56 && (pkt->data[1] >> 4) == 0xe &&
+ (AV_RB16(pkt->data + 1) & 0x1FFF) + 3 == pkt->size)
+ return ff_raw_write_packet(s, pkt);
+ else
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (pkt->size > 0x1fff)
+ goto too_large;
- init_put_bits(&bs, buf, pkt->size+1024);
+ init_put_bits(&bs, ctx->buffer, pkt->size+1024+MAX_EXTRADATA_SIZE);
latm_write_frame_header(s, &bs);
@@ -158,30 +181,46 @@ static int latm_write_packet(AVFormatContext *s, AVPacket *pkt)
/* The LATM payload is written unaligned */
/* PayloadMux() */
- for (i = 0; i < pkt->size; i++)
- put_bits(&bs, 8, pkt->data[i]);
+ if (pkt->size && (pkt->data[0] & 0xe1) == 0x81) {
+ // Convert byte-aligned DSE to non-aligned.
+ // Due to the input format encoding we know that
+ // it is naturally byte-aligned in the input stream,
+ // so there are no padding bits to account for.
+ // To avoid having to add padding bits and rearrange
+ // the whole stream we just remove the byte-align flag.
+ // This allows us to remux our FATE AAC samples into latm
+ // files that are still playable with minimal effort.
+ put_bits(&bs, 8, pkt->data[0] & 0xfe);
+ avpriv_copy_bits(&bs, pkt->data + 1, 8*pkt->size - 8);
+ } else
+ avpriv_copy_bits(&bs, pkt->data, 8*pkt->size);
avpriv_align_put_bits(&bs);
flush_put_bits(&bs);
len = put_bits_count(&bs) >> 3;
+ if (len > 0x1fff)
+ goto too_large;
+
loas_header[1] |= (len >> 8) & 0x1f;
loas_header[2] |= len & 0xff;
avio_write(pb, loas_header, 3);
- avio_write(pb, buf, len);
-
- av_free(buf);
+ avio_write(pb, ctx->buffer, len);
return 0;
+
+too_large:
+ av_log(s, AV_LOG_ERROR, "LATM packet size larger than maximum size 0x1fff\n");
+ return AVERROR_INVALIDDATA;
}
AVOutputFormat ff_latm_muxer = {
.name = "latm",
.long_name = NULL_IF_CONFIG_SMALL("LOAS/LATM"),
.mime_type = "audio/MP4A-LATM",
- .extensions = "latm",
+ .extensions = "latm,loas",
.priv_data_size = sizeof(LATMContext),
.audio_codec = AV_CODEC_ID_AAC,
.video_codec = AV_CODEC_ID_NONE,
diff --git a/libavformat/libavformat.v b/libavformat/libavformat.v
index 6f11d600b9..e90aef79ff 100644
--- a/libavformat/libavformat.v
+++ b/libavformat/libavformat.v
@@ -1,4 +1,20 @@
LIBAVFORMAT_$MAJOR {
global: av*;
+ #FIXME those are for ffserver
+ ff_inet_aton;
+ ff_socket_nonblock;
+ ff_rtsp_parse_line;
+ ff_rtp_get_local_rtp_port;
+ ff_rtp_get_local_rtcp_port;
+ ffio_open_dyn_packet_buf;
+ ffio_set_buf_size;
+ ffurl_close;
+ ffurl_open;
+ ffurl_read_complete;
+ ffurl_seek;
+ ffurl_size;
+ ffurl_write;
+ #those are deprecated, remove on next bump
+ url_feof;
local: *;
};
diff --git a/libavformat/libgme.c b/libavformat/libgme.c
new file mode 100644
index 0000000000..276477b308
--- /dev/null
+++ b/libavformat/libgme.c
@@ -0,0 +1,201 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+* @file
+* libgme demuxer
+*/
+
+#include <gme/gme.h>
+#include "libavutil/avstring.h"
+#include "libavutil/eval.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "internal.h"
+
+typedef struct GMEContext {
+ const AVClass *class;
+ Music_Emu *music_emu;
+ gme_info_t *info; ///< selected track
+
+ /* options */
+ int track_index;
+ int sample_rate;
+ int64_t max_size;
+} GMEContext;
+
+#define OFFSET(x) offsetof(GMEContext, x)
+#define A AV_OPT_FLAG_AUDIO_PARAM
+#define D AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+ {"track_index", "set track that should be played", OFFSET(track_index), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, A|D},
+ {"sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64 = 44100}, 1000, 999999, A|D},
+ {"max_size", "set max file size supported (in bytes)", OFFSET(max_size), AV_OPT_TYPE_INT64, {.i64 = 50 * 1024 * 1024}, 0, SIZE_MAX, A|D},
+ {NULL}
+};
+
+static void add_meta(AVFormatContext *s, const char *name, const char *value)
+{
+ if (value && value[0])
+ av_dict_set(&s->metadata, name, value, 0);
+}
+
+static int load_metadata(AVFormatContext *s)
+{
+ GMEContext *gme = s->priv_data;
+ gme_info_t *info = gme->info;
+ char buf[30];
+
+ add_meta(s, "system", info->system);
+ add_meta(s, "game", info->game);
+ add_meta(s, "song", info->song);
+ add_meta(s, "author", info->author);
+ add_meta(s, "copyright", info->copyright);
+ add_meta(s, "comment", info->comment);
+ add_meta(s, "dumper", info->dumper);
+
+ snprintf(buf, sizeof(buf), "%d", (int)gme_track_count(gme->music_emu));
+ add_meta(s, "tracks", buf);
+
+ return 0;
+}
+
+#define AUDIO_PKT_SIZE 512
+
+static int read_header_gme(AVFormatContext *s)
+{
+ AVStream *st;
+ AVIOContext *pb = s->pb;
+ GMEContext *gme = s->priv_data;
+ int64_t sz = avio_size(pb);
+ char *buf;
+ char dummy;
+
+ if (sz < 0) {
+ av_log(s, AV_LOG_WARNING, "Could not determine file size\n");
+ sz = gme->max_size;
+ } else if (gme->max_size && sz > gme->max_size) {
+ sz = gme->max_size;
+ }
+
+ buf = av_malloc(sz);
+ if (!buf)
+ return AVERROR(ENOMEM);
+ sz = avio_read(pb, buf, sz);
+
+ // Data left means our buffer (the max_size option) is too small
+ if (avio_read(pb, &dummy, 1) == 1) {
+ av_log(s, AV_LOG_ERROR, "File size is larger than max_size option "
+ "value %"PRIi64", consider increasing the max_size option\n",
+ gme->max_size);
+ return AVERROR_BUFFER_TOO_SMALL;
+ }
+
+ if (gme_open_data(buf, sz, &gme->music_emu, gme->sample_rate)) {
+ av_freep(&buf);
+ return AVERROR_INVALIDDATA;
+ }
+ av_freep(&buf);
+
+ if (gme_track_info(gme->music_emu, &gme->info, gme->track_index))
+ return AVERROR_STREAM_NOT_FOUND;
+
+ if (gme_start_track(gme->music_emu, gme->track_index))
+ return AVERROR_UNKNOWN;
+
+ load_metadata(s);
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 64, 1, 1000);
+ if (st->duration > 0)
+ st->duration = gme->info->length;
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->codec_id = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE);
+ st->codec->channels = 2;
+ st->codec->sample_rate = gme->sample_rate;
+
+ return 0;
+}
+
+static int read_packet_gme(AVFormatContext *s, AVPacket *pkt)
+{
+ GMEContext *gme = s->priv_data;
+ int n_samples = AUDIO_PKT_SIZE / 2;
+ int ret;
+
+ if (gme_track_ended(gme->music_emu))
+ return AVERROR_EOF;
+
+ if ((ret = av_new_packet(pkt, AUDIO_PKT_SIZE)) < 0)
+ return ret;
+
+ if (gme_play(gme->music_emu, n_samples, (short *)pkt->data))
+ return AVERROR_EXTERNAL;
+ pkt->size = AUDIO_PKT_SIZE;
+
+ return 0;
+}
+
+static int read_close_gme(AVFormatContext *s)
+{
+ GMEContext *gme = s->priv_data;
+ gme_free_info(gme->info);
+ gme_delete(gme->music_emu);
+ return 0;
+}
+
+static int read_seek_gme(AVFormatContext *s, int stream_idx, int64_t ts, int flags)
+{
+ GMEContext *gme = s->priv_data;
+ if (!gme_seek(gme->music_emu, (int)ts))
+ return AVERROR_EXTERNAL;
+ return 0;
+}
+
+static int probe_gme(AVProbeData *p)
+{
+ // Reads 4 bytes - returns "" if unknown format.
+ if (gme_identify_header(p->buf)[0]) {
+ if (p->buf_size < 16384)
+ return AVPROBE_SCORE_MAX / 4 ;
+ else
+ return AVPROBE_SCORE_MAX / 2;
+ }
+ return 0;
+}
+
+static const AVClass class_gme = {
+ .class_name = "Game Music Emu demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_libgme_demuxer = {
+ .name = "libgme",
+ .long_name = NULL_IF_CONFIG_SMALL("Game Music Emu demuxer"),
+ .priv_data_size = sizeof(GMEContext),
+ .read_probe = probe_gme,
+ .read_header = read_header_gme,
+ .read_packet = read_packet_gme,
+ .read_close = read_close_gme,
+ .read_seek = read_seek_gme,
+ .priv_class = &class_gme,
+};
diff --git a/libavformat/libmodplug.c b/libavformat/libmodplug.c
new file mode 100644
index 0000000000..158a63039f
--- /dev/null
+++ b/libavformat/libmodplug.c
@@ -0,0 +1,382 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+* @file
+* ModPlug demuxer
+* @todo better probing than extensions matching
+*/
+
+#define MODPLUG_STATIC
+#include <libmodplug/modplug.h>
+#include "libavutil/avstring.h"
+#include "libavutil/eval.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "internal.h"
+
+typedef struct ModPlugContext {
+ const AVClass *class;
+ ModPlugFile *f;
+ uint8_t *buf; ///< input file content
+
+ /* options */
+ int noise_reduction;
+ int reverb_depth;
+ int reverb_delay;
+ int bass_amount;
+ int bass_range;
+ int surround_depth;
+ int surround_delay;
+
+ int max_size; ///< max file size to allocate
+
+ /* optional video stream */
+ double ts_per_packet; ///< used to define the pts/dts using packet_count;
+ int packet_count; ///< total number of audio packets
+ int print_textinfo; ///< bool flag for printing speed, tempo, order, ...
+ int video_stream; ///< 1 if the user want a video stream, otherwise 0
+ int w; ///< video stream width in char (one char = 8x8px)
+ int h; ///< video stream height in char (one char = 8x8px)
+ int video_switch; ///< 1 if current packet is video, otherwise 0
+ int fsize; ///< constant frame size
+ int linesize; ///< line size in bytes
+ char *color_eval; ///< color eval user input expression
+ AVExpr *expr; ///< parsed color eval expression
+} ModPlugContext;
+
+static const char * const var_names[] = {
+ "x", "y",
+ "w", "h",
+ "t",
+ "speed", "tempo", "order", "pattern", "row",
+ NULL
+};
+
+enum var_name {
+ VAR_X, VAR_Y,
+ VAR_W, VAR_H,
+ VAR_TIME,
+ VAR_SPEED, VAR_TEMPO, VAR_ORDER, VAR_PATTERN, VAR_ROW,
+ VAR_VARS_NB
+};
+
+#define FF_MODPLUG_MAX_FILE_SIZE (100 * 1<<20) // 100M
+#define FF_MODPLUG_DEF_FILE_SIZE ( 5 * 1<<20) // 5M
+
+#define OFFSET(x) offsetof(ModPlugContext, x)
+#define D AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+ {"noise_reduction", "Enable noise reduction 0(off)-1(on)", OFFSET(noise_reduction), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D},
+ {"reverb_depth", "Reverb level 0(quiet)-100(loud)", OFFSET(reverb_depth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D},
+ {"reverb_delay", "Reverb delay in ms, usually 40-200ms", OFFSET(reverb_delay), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D},
+ {"bass_amount", "XBass level 0(quiet)-100(loud)", OFFSET(bass_amount), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D},
+ {"bass_range", "XBass cutoff in Hz 10-100", OFFSET(bass_range), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D},
+ {"surround_depth", "Surround level 0(quiet)-100(heavy)", OFFSET(surround_depth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D},
+ {"surround_delay", "Surround delay in ms, usually 5-40ms", OFFSET(surround_delay), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D},
+ {"max_size", "Max file size supported (in bytes). Default is 5MB. Set to 0 for no limit (not recommended)",
+ OFFSET(max_size), AV_OPT_TYPE_INT, {.i64 = FF_MODPLUG_DEF_FILE_SIZE}, 0, FF_MODPLUG_MAX_FILE_SIZE, D},
+ {"video_stream_expr", "Color formula", OFFSET(color_eval), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, D},
+ {"video_stream", "Make demuxer output a video stream", OFFSET(video_stream), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D},
+ {"video_stream_w", "Video stream width in char (one char = 8x8px)", OFFSET(w), AV_OPT_TYPE_INT, {.i64 = 30}, 20, 512, D},
+ {"video_stream_h", "Video stream height in char (one char = 8x8px)", OFFSET(h), AV_OPT_TYPE_INT, {.i64 = 30}, 20, 512, D},
+ {"video_stream_ptxt", "Print speed, tempo, order, ... in video stream", OFFSET(print_textinfo), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, D},
+ {NULL},
+};
+
+#define SET_OPT_IF_REQUESTED(libopt, opt, flag) do { \
+ if (modplug->opt) { \
+ settings.libopt = modplug->opt; \
+ settings.mFlags |= flag; \
+ } \
+} while (0)
+
+#define ADD_META_MULTIPLE_ENTRIES(entry_name, fname) do { \
+ if (n_## entry_name ##s) { \
+ unsigned i, n = 0; \
+ \
+ for (i = 0; i < n_## entry_name ##s; i++) { \
+ char item_name[64] = {0}; \
+ fname(f, i, item_name); \
+ if (!*item_name) \
+ continue; \
+ if (n) \
+ av_dict_set(&s->metadata, #entry_name, "\n", AV_DICT_APPEND); \
+ av_dict_set(&s->metadata, #entry_name, item_name, AV_DICT_APPEND); \
+ n++; \
+ } \
+ \
+ extra = av_asprintf(", %u/%u " #entry_name "%s", \
+ n, n_## entry_name ##s, n > 1 ? "s" : ""); \
+ if (!extra) \
+ return AVERROR(ENOMEM); \
+ av_dict_set(&s->metadata, "extra info", extra, AV_DICT_APPEND); \
+ av_free(extra); \
+ } \
+} while (0)
+
+static int modplug_load_metadata(AVFormatContext *s)
+{
+ ModPlugContext *modplug = s->priv_data;
+ ModPlugFile *f = modplug->f;
+ char *extra;
+ const char *name = ModPlug_GetName(f);
+ const char *msg = ModPlug_GetMessage(f);
+
+ unsigned n_instruments = ModPlug_NumInstruments(f);
+ unsigned n_samples = ModPlug_NumSamples(f);
+ unsigned n_patterns = ModPlug_NumPatterns(f);
+ unsigned n_channels = ModPlug_NumChannels(f);
+
+ if (name && *name) av_dict_set(&s->metadata, "name", name, 0);
+ if (msg && *msg) av_dict_set(&s->metadata, "message", msg, 0);
+
+ extra = av_asprintf("%u pattern%s, %u channel%s",
+ n_patterns, n_patterns > 1 ? "s" : "",
+ n_channels, n_channels > 1 ? "s" : "");
+ if (!extra)
+ return AVERROR(ENOMEM);
+ av_dict_set(&s->metadata, "extra info", extra, AV_DICT_DONT_STRDUP_VAL);
+
+ ADD_META_MULTIPLE_ENTRIES(instrument, ModPlug_InstrumentName);
+ ADD_META_MULTIPLE_ENTRIES(sample, ModPlug_SampleName);
+
+ return 0;
+}
+
+#define AUDIO_PKT_SIZE 512
+
+static int modplug_read_header(AVFormatContext *s)
+{
+ AVStream *st;
+ AVIOContext *pb = s->pb;
+ ModPlug_Settings settings;
+ ModPlugContext *modplug = s->priv_data;
+ int64_t sz = avio_size(pb);
+
+ if (sz < 0) {
+ av_log(s, AV_LOG_WARNING, "Could not determine file size\n");
+ sz = modplug->max_size;
+ } else if (modplug->max_size && sz > modplug->max_size) {
+ sz = modplug->max_size;
+ av_log(s, AV_LOG_WARNING, "Max file size reach%s, allocating %"PRIi64"B "
+ "but demuxing is likely to fail due to incomplete buffer\n",
+ sz == FF_MODPLUG_DEF_FILE_SIZE ? " (see -max_size)" : "", sz);
+ }
+
+ if (modplug->color_eval) {
+ int r = av_expr_parse(&modplug->expr, modplug->color_eval, var_names,
+ NULL, NULL, NULL, NULL, 0, s);
+ if (r < 0)
+ return r;
+ }
+
+ modplug->buf = av_malloc(modplug->max_size);
+ if (!modplug->buf)
+ return AVERROR(ENOMEM);
+ sz = avio_read(pb, modplug->buf, sz);
+
+ ModPlug_GetSettings(&settings);
+ settings.mChannels = 2;
+ settings.mBits = 16;
+ settings.mFrequency = 44100;
+ settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; // best quality
+ settings.mLoopCount = 0; // prevents looping forever
+
+ if (modplug->noise_reduction) settings.mFlags |= MODPLUG_ENABLE_NOISE_REDUCTION;
+ SET_OPT_IF_REQUESTED(mReverbDepth, reverb_depth, MODPLUG_ENABLE_REVERB);
+ SET_OPT_IF_REQUESTED(mReverbDelay, reverb_delay, MODPLUG_ENABLE_REVERB);
+ SET_OPT_IF_REQUESTED(mBassAmount, bass_amount, MODPLUG_ENABLE_MEGABASS);
+ SET_OPT_IF_REQUESTED(mBassRange, bass_range, MODPLUG_ENABLE_MEGABASS);
+ SET_OPT_IF_REQUESTED(mSurroundDepth, surround_depth, MODPLUG_ENABLE_SURROUND);
+ SET_OPT_IF_REQUESTED(mSurroundDelay, surround_delay, MODPLUG_ENABLE_SURROUND);
+
+ if (modplug->reverb_depth) settings.mReverbDepth = modplug->reverb_depth;
+ if (modplug->reverb_delay) settings.mReverbDelay = modplug->reverb_delay;
+ if (modplug->bass_amount) settings.mBassAmount = modplug->bass_amount;
+ if (modplug->bass_range) settings.mBassRange = modplug->bass_range;
+ if (modplug->surround_depth) settings.mSurroundDepth = modplug->surround_depth;
+ if (modplug->surround_delay) settings.mSurroundDelay = modplug->surround_delay;
+
+ ModPlug_SetSettings(&settings);
+
+ modplug->f = ModPlug_Load(modplug->buf, sz);
+ if (!modplug->f)
+ return AVERROR_INVALIDDATA;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 64, 1, 1000);
+ st->duration = ModPlug_GetLength(modplug->f);
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
+ st->codec->channels = settings.mChannels;
+ st->codec->sample_rate = settings.mFrequency;
+
+ // timebase = 1/1000, 2ch 16bits 44.1kHz-> 2*2*44100
+ modplug->ts_per_packet = 1000*AUDIO_PKT_SIZE / (4*44100.);
+
+ if (modplug->video_stream) {
+ AVStream *vst = avformat_new_stream(s, NULL);
+ if (!vst)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(vst, 64, 1, 1000);
+ vst->duration = st->duration;
+ vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ vst->codec->codec_id = AV_CODEC_ID_XBIN;
+ vst->codec->width = modplug->w << 3;
+ vst->codec->height = modplug->h << 3;
+ modplug->linesize = modplug->w * 3;
+ modplug->fsize = modplug->linesize * modplug->h;
+ }
+
+ return modplug_load_metadata(s);
+}
+
+static void write_text(uint8_t *dst, const char *s, int linesize, int x, int y)
+{
+ int i;
+ dst += y*linesize + x*3;
+ for (i = 0; s[i]; i++, dst += 3) {
+ dst[0] = 0x0; // count - 1
+ dst[1] = s[i]; // char
+ dst[2] = 0x0f; // background / foreground
+ }
+}
+
+#define PRINT_INFO(line, name, idvalue) do { \
+ snprintf(intbuf, sizeof(intbuf), "%.0f", var_values[idvalue]); \
+ write_text(pkt->data, name ":", modplug->linesize, 0+1, line+1); \
+ write_text(pkt->data, intbuf, modplug->linesize, 10+1, line+1); \
+} while (0)
+
+static int modplug_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ ModPlugContext *modplug = s->priv_data;
+
+ if (modplug->video_stream) {
+ modplug->video_switch ^= 1; // one video packet for one audio packet
+ if (modplug->video_switch) {
+ double var_values[VAR_VARS_NB];
+
+ var_values[VAR_W ] = modplug->w;
+ var_values[VAR_H ] = modplug->h;
+ var_values[VAR_TIME ] = modplug->packet_count * modplug->ts_per_packet;
+ var_values[VAR_SPEED ] = ModPlug_GetCurrentSpeed (modplug->f);
+ var_values[VAR_TEMPO ] = ModPlug_GetCurrentTempo (modplug->f);
+ var_values[VAR_ORDER ] = ModPlug_GetCurrentOrder (modplug->f);
+ var_values[VAR_PATTERN] = ModPlug_GetCurrentPattern(modplug->f);
+ var_values[VAR_ROW ] = ModPlug_GetCurrentRow (modplug->f);
+
+ if (av_new_packet(pkt, modplug->fsize) < 0)
+ return AVERROR(ENOMEM);
+ pkt->stream_index = 1;
+ memset(pkt->data, 0, modplug->fsize);
+
+ if (modplug->print_textinfo) {
+ char intbuf[32];
+ PRINT_INFO(0, "speed", VAR_SPEED);
+ PRINT_INFO(1, "tempo", VAR_TEMPO);
+ PRINT_INFO(2, "order", VAR_ORDER);
+ PRINT_INFO(3, "pattern", VAR_PATTERN);
+ PRINT_INFO(4, "row", VAR_ROW);
+ PRINT_INFO(5, "ts", VAR_TIME);
+ }
+
+ if (modplug->expr) {
+ int x, y;
+ for (y = 0; y < modplug->h; y++) {
+ for (x = 0; x < modplug->w; x++) {
+ double color;
+ var_values[VAR_X] = x;
+ var_values[VAR_Y] = y;
+ color = av_expr_eval(modplug->expr, var_values, NULL);
+ pkt->data[y*modplug->linesize + x*3 + 2] |= av_clip((int)color, 0, 0xf)<<4;
+ }
+ }
+ }
+ pkt->pts = pkt->dts = var_values[VAR_TIME];
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ return 0;
+ }
+ }
+
+ if (av_new_packet(pkt, AUDIO_PKT_SIZE) < 0)
+ return AVERROR(ENOMEM);
+
+ if (modplug->video_stream)
+ pkt->pts = pkt->dts = modplug->packet_count++ * modplug->ts_per_packet;
+
+ pkt->size = ModPlug_Read(modplug->f, pkt->data, AUDIO_PKT_SIZE);
+ if (pkt->size <= 0) {
+ av_free_packet(pkt);
+ return pkt->size == 0 ? AVERROR_EOF : AVERROR(EIO);
+ }
+ return 0;
+}
+
+static int modplug_read_close(AVFormatContext *s)
+{
+ ModPlugContext *modplug = s->priv_data;
+ ModPlug_Unload(modplug->f);
+ av_freep(&modplug->buf);
+ return 0;
+}
+
+static int modplug_read_seek(AVFormatContext *s, int stream_idx, int64_t ts, int flags)
+{
+ ModPlugContext *modplug = s->priv_data;
+ ModPlug_Seek(modplug->f, (int)ts);
+ if (modplug->video_stream)
+ modplug->packet_count = ts / modplug->ts_per_packet;
+ return 0;
+}
+
+static const char modplug_extensions[] = "669,abc,amf,ams,dbm,dmf,dsm,far,it,mdl,med,mid,mod,mt2,mtm,okt,psm,ptm,s3m,stm,ult,umx,xm,itgz,itr,itz,mdgz,mdr,mdz,s3gz,s3r,s3z,xmgz,xmr,xmz";
+
+static int modplug_probe(AVProbeData *p)
+{
+ if (av_match_ext(p->filename, modplug_extensions)) {
+ if (p->buf_size < 16384)
+ return AVPROBE_SCORE_EXTENSION/2-1;
+ else
+ return AVPROBE_SCORE_EXTENSION;
+ }
+ return 0;
+}
+
+static const AVClass modplug_class = {
+ .class_name = "ModPlug demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_libmodplug_demuxer = {
+ .name = "libmodplug",
+ .long_name = NULL_IF_CONFIG_SMALL("ModPlug demuxer"),
+ .priv_data_size = sizeof(ModPlugContext),
+ .read_probe = modplug_probe,
+ .read_header = modplug_read_header,
+ .read_packet = modplug_read_packet,
+ .read_close = modplug_read_close,
+ .read_seek = modplug_read_seek,
+ .extensions = modplug_extensions,
+ .priv_class = &modplug_class,
+};
diff --git a/libavformat/libnut.c b/libavformat/libnut.c
new file mode 100644
index 0000000000..4a9a21a766
--- /dev/null
+++ b/libavformat/libnut.c
@@ -0,0 +1,324 @@
+/*
+ * NUT (de)muxing via libnut
+ * copyright (c) 2006 Oded Shimon <ods15@ods15.dyndns.org>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * NUT demuxing and muxing via libnut.
+ * @author Oded Shimon <ods15@ods15.dyndns.org>
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "riff.h"
+#include <libnut.h>
+
+#define ID_STRING "nut/multimedia container"
+#define ID_LENGTH (strlen(ID_STRING) + 1)
+
+typedef struct {
+ nut_context_tt * nut;
+ nut_stream_header_tt * s;
+} NUTContext;
+
+static const AVCodecTag nut_tags[] = {
+ { AV_CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_MP3, MKTAG('m', 'p', '3', ' ') },
+ { AV_CODEC_ID_VORBIS, MKTAG('v', 'r', 'b', 's') },
+ { 0, 0 },
+};
+
+#if CONFIG_LIBNUT_MUXER
+static int av_write(void * h, size_t len, const uint8_t * buf) {
+ AVIOContext * bc = h;
+ avio_write(bc, buf, len);
+ //avio_flush(bc);
+ return len;
+}
+
+static int nut_write_header(AVFormatContext * avf) {
+ NUTContext * priv = avf->priv_data;
+ AVIOContext * bc = avf->pb;
+ nut_muxer_opts_tt mopts = {
+ .output = {
+ .priv = bc,
+ .write = av_write,
+ },
+ .alloc = { av_malloc, av_realloc, av_free },
+ .write_index = 1,
+ .realtime_stream = 0,
+ .max_distance = 32768,
+ .fti = NULL,
+ };
+ nut_stream_header_tt * s;
+ int i;
+
+ priv->s = s = av_mallocz_array(avf->nb_streams + 1, sizeof*s);
+ if(!s)
+ return AVERROR(ENOMEM);
+
+ for (i = 0; i < avf->nb_streams; i++) {
+ AVCodecContext * codec = avf->streams[i]->codec;
+ int j;
+ int fourcc = 0;
+ int num, denom, ssize;
+
+ s[i].type = codec->codec_type == AVMEDIA_TYPE_VIDEO ? NUT_VIDEO_CLASS : NUT_AUDIO_CLASS;
+
+ if (codec->codec_tag) fourcc = codec->codec_tag;
+ else fourcc = ff_codec_get_tag(nut_tags, codec->codec_id);
+
+ if (!fourcc) {
+ if (codec->codec_type == AVMEDIA_TYPE_VIDEO) fourcc = ff_codec_get_tag(ff_codec_bmp_tags, codec->codec_id);
+ if (codec->codec_type == AVMEDIA_TYPE_AUDIO) fourcc = ff_codec_get_tag(ff_codec_wav_tags, codec->codec_id);
+ }
+
+ s[i].fourcc_len = 4;
+ s[i].fourcc = av_malloc(s[i].fourcc_len);
+ for (j = 0; j < s[i].fourcc_len; j++) s[i].fourcc[j] = (fourcc >> (j*8)) & 0xFF;
+
+ ff_parse_specific_params(codec, &num, &ssize, &denom);
+ avpriv_set_pts_info(avf->streams[i], 60, denom, num);
+
+ s[i].time_base.num = denom;
+ s[i].time_base.den = num;
+
+ s[i].fixed_fps = 0;
+ s[i].decode_delay = codec->has_b_frames;
+ s[i].codec_specific_len = codec->extradata_size;
+ s[i].codec_specific = codec->extradata;
+
+ if (codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ s[i].width = codec->width;
+ s[i].height = codec->height;
+ s[i].sample_width = 0;
+ s[i].sample_height = 0;
+ s[i].colorspace_type = 0;
+ } else {
+ s[i].samplerate_num = codec->sample_rate;
+ s[i].samplerate_denom = 1;
+ s[i].channel_count = codec->channels;
+ }
+ }
+
+ s[avf->nb_streams].type = -1;
+ priv->nut = nut_muxer_init(&mopts, s, NULL);
+
+ return 0;
+}
+
+static int nut_write_packet(AVFormatContext * avf, AVPacket * pkt) {
+ NUTContext * priv = avf->priv_data;
+ nut_packet_tt p;
+
+ p.len = pkt->size;
+ p.stream = pkt->stream_index;
+ p.pts = pkt->pts;
+ p.flags = pkt->flags & AV_PKT_FLAG_KEY ? NUT_FLAG_KEY : 0;
+ p.next_pts = 0;
+
+ nut_write_frame_reorder(priv->nut, &p, pkt->data);
+
+ return 0;
+}
+
+static int nut_write_trailer(AVFormatContext * avf) {
+ AVIOContext * bc = avf->pb;
+ NUTContext * priv = avf->priv_data;
+ int i;
+
+ nut_muxer_uninit_reorder(priv->nut);
+ avio_flush(bc);
+
+ for(i = 0; priv->s[i].type != -1; i++ ) av_freep(&priv->s[i].fourcc);
+ av_freep(&priv->s);
+
+ return 0;
+}
+
+AVOutputFormat ff_libnut_muxer = {
+ .name = "libnut",
+ .long_name = "nut format",
+ .mime_type = "video/x-nut",
+ .extensions = "nut",
+ .priv_data_size = sizeof(NUTContext),
+ .audio_codec = AV_CODEC_ID_VORBIS,
+ .video_codec = AV_CODEC_ID_MPEG4,
+ .write_header = nut_write_header,
+ .write_packet = nut_write_packet,
+ .write_trailer = nut_write_trailer,
+ .flags = AVFMT_GLOBALHEADER,
+};
+#endif /* CONFIG_LIBNUT_MUXER */
+
+static int nut_probe(AVProbeData *p) {
+ if (!memcmp(p->buf, ID_STRING, ID_LENGTH)) return AVPROBE_SCORE_MAX;
+
+ return 0;
+}
+
+static size_t av_read(void * h, size_t len, uint8_t * buf) {
+ AVIOContext * bc = h;
+ return avio_read(bc, buf, len);
+}
+
+static off_t av_seek(void * h, long long pos, int whence) {
+ AVIOContext * bc = h;
+ if (whence == SEEK_END) {
+ pos = avio_size(bc) + pos;
+ whence = SEEK_SET;
+ }
+ return avio_seek(bc, pos, whence);
+}
+
+static int nut_read_header(AVFormatContext * avf) {
+ NUTContext * priv = avf->priv_data;
+ AVIOContext * bc = avf->pb;
+ nut_demuxer_opts_tt dopts = {
+ .input = {
+ .priv = bc,
+ .seek = av_seek,
+ .read = av_read,
+ .eof = NULL,
+ .file_pos = 0,
+ },
+ .alloc = { av_malloc, av_realloc, av_free },
+ .read_index = 1,
+ .cache_syncpoints = 1,
+ };
+ nut_context_tt * nut = priv->nut = nut_demuxer_init(&dopts);
+ nut_stream_header_tt * s;
+ int ret, i;
+
+ if(!nut)
+ return -1;
+
+ if ((ret = nut_read_headers(nut, &s, NULL))) {
+ av_log(avf, AV_LOG_ERROR, " NUT error: %s\n", nut_error(ret));
+ nut_demuxer_uninit(nut);
+ priv->nut = NULL;
+ return -1;
+ }
+
+ priv->s = s;
+
+ for (i = 0; s[i].type != -1 && i < 2; i++) {
+ AVStream * st = avformat_new_stream(avf, NULL);
+ int j;
+
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ for (j = 0; j < s[i].fourcc_len && j < 8; j++) st->codec->codec_tag |= s[i].fourcc[j]<<(j*8);
+
+ st->codec->has_b_frames = s[i].decode_delay;
+
+ st->codec->extradata_size = s[i].codec_specific_len;
+ if (st->codec->extradata_size) {
+ if(ff_alloc_extradata(st->codec, st->codec->extradata_size)){
+ nut_demuxer_uninit(nut);
+ priv->nut = NULL;
+ return AVERROR(ENOMEM);
+ }
+ memcpy(st->codec->extradata, s[i].codec_specific, st->codec->extradata_size);
+ }
+
+ avpriv_set_pts_info(avf->streams[i], 60, s[i].time_base.num, s[i].time_base.den);
+ st->start_time = 0;
+ st->duration = s[i].max_pts;
+
+ st->codec->codec_id = ff_codec_get_id(nut_tags, st->codec->codec_tag);
+
+ switch(s[i].type) {
+ case NUT_AUDIO_CLASS:
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ if (st->codec->codec_id == AV_CODEC_ID_NONE) st->codec->codec_id = ff_codec_get_id(ff_codec_wav_tags, st->codec->codec_tag);
+
+ st->codec->channels = s[i].channel_count;
+ st->codec->sample_rate = s[i].samplerate_num / s[i].samplerate_denom;
+ break;
+ case NUT_VIDEO_CLASS:
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ if (st->codec->codec_id == AV_CODEC_ID_NONE) st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, st->codec->codec_tag);
+
+ st->codec->width = s[i].width;
+ st->codec->height = s[i].height;
+ st->sample_aspect_ratio.num = s[i].sample_width;
+ st->sample_aspect_ratio.den = s[i].sample_height;
+ break;
+ }
+ if (st->codec->codec_id == AV_CODEC_ID_NONE) av_log(avf, AV_LOG_ERROR, "Unknown codec?!\n");
+ }
+
+ return 0;
+}
+
+static int nut_read_packet(AVFormatContext * avf, AVPacket * pkt) {
+ NUTContext * priv = avf->priv_data;
+ nut_packet_tt pd;
+ int ret;
+
+ ret = nut_read_next_packet(priv->nut, &pd);
+
+ if (ret || av_new_packet(pkt, pd.len) < 0) {
+ if (ret != NUT_ERR_EOF)
+ av_log(avf, AV_LOG_ERROR, " NUT error: %s\n", nut_error(ret));
+ return -1;
+ }
+
+ if (pd.flags & NUT_FLAG_KEY) pkt->flags |= AV_PKT_FLAG_KEY;
+ pkt->pts = pd.pts;
+ pkt->stream_index = pd.stream;
+ pkt->pos = avio_tell(avf->pb);
+
+ ret = nut_read_frame(priv->nut, &pd.len, pkt->data);
+
+ return ret;
+}
+
+static int nut_read_seek(AVFormatContext * avf, int stream_index, int64_t target_ts, int flags) {
+ NUTContext * priv = avf->priv_data;
+ int active_streams[] = { stream_index, -1 };
+ double time_pos = target_ts * priv->s[stream_index].time_base.num / (double)priv->s[stream_index].time_base.den;
+
+ if (nut_seek(priv->nut, time_pos, 2*!(flags & AVSEEK_FLAG_BACKWARD), active_streams)) return -1;
+
+ return 0;
+}
+
+static int nut_read_close(AVFormatContext *s) {
+ NUTContext * priv = s->priv_data;
+
+ nut_demuxer_uninit(priv->nut);
+
+ return 0;
+}
+
+AVInputFormat ff_libnut_demuxer = {
+ .name = "libnut",
+ .long_name = NULL_IF_CONFIG_SMALL("NUT format"),
+ .priv_data_size = sizeof(NUTContext),
+ .read_probe = nut_probe,
+ .read_header = nut_read_header,
+ .read_packet = nut_read_packet,
+ .read_close = nut_read_close,
+ .read_seek = nut_read_seek,
+ .extensions = "nut",
+};
diff --git a/libavformat/libquvi.c b/libavformat/libquvi.c
new file mode 100644
index 0000000000..0a593cc9cf
--- /dev/null
+++ b/libavformat/libquvi.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2013 Clément Bœsch
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <quvi/quvi.h>
+
+#include "libavformat/avformat.h"
+#include "libavformat/internal.h"
+#include "libavutil/avassert.h"
+#include "libavutil/opt.h"
+
+typedef struct {
+ const AVClass *class;
+ char *format;
+ AVFormatContext *fmtctx;
+} LibQuviContext;
+
+#define OFFSET(x) offsetof(LibQuviContext, x)
+#define FLAGS AV_OPT_FLAG_DECODING_PARAM
+static const AVOption libquvi_options[] = {
+ { "format", "request specific format", OFFSET(format), AV_OPT_TYPE_STRING, {.str="best"}, .flags = FLAGS },
+ { NULL }
+};
+
+static const AVClass libquvi_context_class = {
+ .class_name = "libquvi",
+ .item_name = av_default_item_name,
+ .option = libquvi_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+static int libquvi_close(AVFormatContext *s)
+{
+ LibQuviContext *qc = s->priv_data;
+ if (qc->fmtctx)
+ avformat_close_input(&qc->fmtctx);
+ return 0;
+}
+
+static int libquvi_read_header(AVFormatContext *s)
+{
+ int i, ret;
+ quvi_t q;
+ quvi_media_t m;
+ QUVIcode rc;
+ LibQuviContext *qc = s->priv_data;
+ char *media_url, *pagetitle;
+
+ rc = quvi_init(&q);
+ if (rc != QUVI_OK)
+ goto quvi_fail;
+
+ quvi_setopt(q, QUVIOPT_FORMAT, qc->format);
+
+ rc = quvi_parse(q, s->filename, &m);
+ if (rc != QUVI_OK)
+ goto quvi_fail;
+
+ rc = quvi_getprop(m, QUVIPROP_MEDIAURL, &media_url);
+ if (rc != QUVI_OK)
+ goto quvi_fail;
+
+ if ((ret = ff_copy_whitelists(qc->fmtctx, s)) < 0)
+ goto end;
+
+ ret = avformat_open_input(&qc->fmtctx, media_url, NULL, NULL);
+ if (ret < 0)
+ goto end;
+
+ rc = quvi_getprop(m, QUVIPROP_PAGETITLE, &pagetitle);
+ if (rc == QUVI_OK)
+ av_dict_set(&s->metadata, "title", pagetitle, 0);
+
+ for (i = 0; i < qc->fmtctx->nb_streams; i++) {
+ AVStream *st = avformat_new_stream(s, NULL);
+ AVStream *ist = qc->fmtctx->streams[i];
+ if (!st) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num, ist->time_base.den);
+ avcodec_copy_context(st->codec, qc->fmtctx->streams[i]->codec);
+ }
+
+ return 0;
+
+quvi_fail:
+ av_log(s, AV_LOG_ERROR, "%s\n", quvi_strerror(q, rc));
+ ret = AVERROR_EXTERNAL;
+
+end:
+ quvi_parse_close(&m);
+ quvi_close(&q);
+ return ret;
+}
+
+static int libquvi_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ LibQuviContext *qc = s->priv_data;
+ return av_read_frame(qc->fmtctx, pkt);
+}
+
+static int libquvi_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
+{
+ LibQuviContext *qc = s->priv_data;
+ return av_seek_frame(qc->fmtctx, stream_index, timestamp, flags);
+}
+
+static int libquvi_probe(AVProbeData *p)
+{
+ int score;
+ quvi_t q;
+ QUVIcode rc;
+
+ rc = quvi_init(&q);
+ if (rc != QUVI_OK)
+ return AVERROR(ENOMEM);
+ score = quvi_supported(q, (char *)p->filename) == QUVI_OK ? AVPROBE_SCORE_EXTENSION : 0;
+ quvi_close(&q);
+ return score;
+}
+
+AVInputFormat ff_libquvi_demuxer = {
+ .name = "libquvi",
+ .long_name = NULL_IF_CONFIG_SMALL("libquvi demuxer"),
+ .priv_data_size = sizeof(LibQuviContext),
+ .read_probe = libquvi_probe,
+ .read_header = libquvi_read_header,
+ .read_packet = libquvi_read_packet,
+ .read_close = libquvi_close,
+ .read_seek = libquvi_read_seek,
+ .priv_class = &libquvi_context_class,
+ .flags = AVFMT_NOFILE,
+};
diff --git a/libavformat/librtmp.c b/libavformat/librtmp.c
index fac3a35196..bfa9a718f1 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
*/
@@ -28,6 +28,9 @@
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
#include "avformat.h"
+#if CONFIG_NETWORK
+#include "network.h"
+#endif
#include "url.h"
#include <librtmp/rtmp.h>
@@ -48,6 +51,7 @@ typedef struct LibRTMPContext {
char *client_buffer_time;
int live;
char *temp_filename;
+ int buffer_size;
} LibRTMPContext;
static void rtmp_log(int level, const char *fmt, va_list args)
@@ -230,10 +234,20 @@ static int rtmp_open(URLContext *s, const char *uri, int flags)
goto fail;
}
+#if CONFIG_NETWORK
+ if (ctx->buffer_size >= 0 && (flags & AVIO_FLAG_WRITE)) {
+ int tmp = ctx->buffer_size;
+ setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp));
+ }
+#endif
+
s->is_streamed = 1;
return 0;
fail:
av_freep(&ctx->temp_filename);
+ if (rc)
+ RTMP_Close(r);
+
return rc;
}
@@ -308,6 +322,9 @@ static const AVOption options[] = {
{"rtmp_swfurl", "URL of the SWF player. By default no value will be sent", OFFSET(swfurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
{"rtmp_swfverify", "URL to player swf file, compute hash/size automatically. (unimplemented)", OFFSET(swfverify), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
{"rtmp_tcurl", "URL of the target stream. Defaults to proto://host[:port]/app.", OFFSET(tcurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
+#if CONFIG_NETWORK
+ {"rtmp_buffer_size", "set buffer size in bytes", OFFSET(buffer_size), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, DEC|ENC },
+#endif
{ NULL },
};
diff --git a/libavformat/libsmbclient.c b/libavformat/libsmbclient.c
new file mode 100644
index 0000000000..8290d75927
--- /dev/null
+++ b/libavformat/libsmbclient.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2014 Lukasz Marek <lukasz.m.luki@gmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <libsmbclient.h>
+#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "internal.h"
+#include "url.h"
+
+typedef struct {
+ const AVClass *class;
+ SMBCCTX *ctx;
+ int fd;
+ int64_t filesize;
+ int trunc;
+ int timeout;
+ char *workgroup;
+} LIBSMBContext;
+
+static void libsmbc_get_auth_data(SMBCCTX *c, const char *server, const char *share,
+ char *workgroup, int workgroup_len,
+ char *username, int username_len,
+ char *password, int password_len)
+{
+ /* Do nothing yet. Credentials are passed via url.
+ * Callback must exists, there might be a segmentation fault otherwise. */
+}
+
+static av_cold int libsmbc_connect(URLContext *h)
+{
+ LIBSMBContext *libsmbc = h->priv_data;
+
+ libsmbc->ctx = smbc_new_context();
+ if (!libsmbc->ctx) {
+ int ret = AVERROR(errno);
+ av_log(h, AV_LOG_ERROR, "Cannot create context: %s.\n", strerror(errno));
+ return ret;
+ }
+ if (!smbc_init_context(libsmbc->ctx)) {
+ int ret = AVERROR(errno);
+ av_log(h, AV_LOG_ERROR, "Cannot initialize context: %s.\n", strerror(errno));
+ return ret;
+ }
+ smbc_set_context(libsmbc->ctx);
+
+ smbc_setOptionUserData(libsmbc->ctx, h);
+ smbc_setFunctionAuthDataWithContext(libsmbc->ctx, libsmbc_get_auth_data);
+
+ if (libsmbc->timeout != -1)
+ smbc_setTimeout(libsmbc->ctx, libsmbc->timeout);
+ if (libsmbc->workgroup)
+ smbc_setWorkgroup(libsmbc->ctx, libsmbc->workgroup);
+
+ if (smbc_init(NULL, 0) < 0) {
+ int ret = AVERROR(errno);
+ av_log(h, AV_LOG_ERROR, "Initialization failed: %s\n", strerror(errno));
+ return ret;
+ }
+ return 0;
+}
+
+static av_cold int libsmbc_close(URLContext *h)
+{
+ LIBSMBContext *libsmbc = h->priv_data;
+ if (libsmbc->fd >= 0) {
+ smbc_close(libsmbc->fd);
+ libsmbc->fd = -1;
+ }
+ if (libsmbc->ctx) {
+ smbc_free_context(libsmbc->ctx, 1);
+ libsmbc->ctx = NULL;
+ }
+ return 0;
+}
+
+static av_cold int libsmbc_open(URLContext *h, const char *url, int flags)
+{
+ LIBSMBContext *libsmbc = h->priv_data;
+ int access, ret;
+ struct stat st;
+
+ libsmbc->fd = -1;
+ libsmbc->filesize = -1;
+
+ if ((ret = libsmbc_connect(h)) < 0)
+ goto fail;
+
+ if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) {
+ access = O_CREAT | O_RDWR;
+ if (libsmbc->trunc)
+ access |= O_TRUNC;
+ } else if (flags & AVIO_FLAG_WRITE) {
+ access = O_CREAT | O_WRONLY;
+ if (libsmbc->trunc)
+ access |= O_TRUNC;
+ } else
+ access = O_RDONLY;
+
+ /* 0666 = -rw-rw-rw- = read+write for everyone, minus umask */
+ if ((libsmbc->fd = smbc_open(url, access, 0666)) < 0) {
+ ret = AVERROR(errno);
+ av_log(h, AV_LOG_ERROR, "File open failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (smbc_fstat(libsmbc->fd, &st) < 0)
+ av_log(h, AV_LOG_WARNING, "Cannot stat file: %s\n", strerror(errno));
+ else
+ libsmbc->filesize = st.st_size;
+
+ return 0;
+ fail:
+ libsmbc_close(h);
+ return ret;
+}
+
+static int64_t libsmbc_seek(URLContext *h, int64_t pos, int whence)
+{
+ LIBSMBContext *libsmbc = h->priv_data;
+ int64_t newpos;
+
+ if (whence == AVSEEK_SIZE) {
+ if (libsmbc->filesize == -1) {
+ av_log(h, AV_LOG_ERROR, "Error during seeking: filesize is unknown.\n");
+ return AVERROR(EIO);
+ } else
+ return libsmbc->filesize;
+ }
+
+ if ((newpos = smbc_lseek(libsmbc->fd, pos, whence)) < 0) {
+ int err = errno;
+ av_log(h, AV_LOG_ERROR, "Error during seeking: %s\n", strerror(err));
+ return AVERROR(err);
+ }
+
+ return newpos;
+}
+
+static int libsmbc_read(URLContext *h, unsigned char *buf, int size)
+{
+ LIBSMBContext *libsmbc = h->priv_data;
+ int bytes_read;
+
+ if ((bytes_read = smbc_read(libsmbc->fd, buf, size)) < 0) {
+ int ret = AVERROR(errno);
+ av_log(h, AV_LOG_ERROR, "Read error: %s\n", strerror(errno));
+ return ret;
+ }
+
+ return bytes_read;
+}
+
+static int libsmbc_write(URLContext *h, const unsigned char *buf, int size)
+{
+ LIBSMBContext *libsmbc = h->priv_data;
+ int bytes_written;
+
+ if ((bytes_written = smbc_write(libsmbc->fd, buf, size)) < 0) {
+ int ret = AVERROR(errno);
+ av_log(h, AV_LOG_ERROR, "Write error: %s\n", strerror(errno));
+ return ret;
+ }
+
+ return bytes_written;
+}
+
+#define OFFSET(x) offsetof(LIBSMBContext, x)
+#define D AV_OPT_FLAG_DECODING_PARAM
+#define E AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ {"timeout", "set timeout in ms of socket I/O operations", OFFSET(timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
+ {"truncate", "truncate existing files on write", OFFSET(trunc), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, E },
+ {"workgroup", "set the workgroup used for making connections", OFFSET(workgroup), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
+ {NULL}
+};
+
+static const AVClass libsmbclient_context_class = {
+ .class_name = "libsmbc",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+URLProtocol ff_libsmbclient_protocol = {
+ .name = "smb",
+ .url_open = libsmbc_open,
+ .url_read = libsmbc_read,
+ .url_write = libsmbc_write,
+ .url_seek = libsmbc_seek,
+ .url_close = libsmbc_close,
+ .priv_data_size = sizeof(LIBSMBContext),
+ .priv_data_class = &libsmbclient_context_class,
+ .flags = URL_PROTOCOL_FLAG_NETWORK,
+};
diff --git a/libavformat/libssh.c b/libavformat/libssh.c
new file mode 100644
index 0000000000..3ec60cb8f4
--- /dev/null
+++ b/libavformat/libssh.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2013 Lukasz Marek <lukasz.m.luki@gmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <fcntl.h>
+#define LIBSSH_STATIC
+#include <libssh/sftp.h>
+#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "libavutil/attributes.h"
+#include "avformat.h"
+#include "internal.h"
+#include "url.h"
+
+typedef struct {
+ const AVClass *class;
+ ssh_session session;
+ sftp_session sftp;
+ sftp_file file;
+ int64_t filesize;
+ int rw_timeout;
+ int trunc;
+ char *priv_key;
+} LIBSSHContext;
+
+static av_cold int libssh_create_ssh_session(LIBSSHContext *libssh, const char* hostname, unsigned int port)
+{
+ static const int verbosity = SSH_LOG_NOLOG;
+
+ if (!(libssh->session = ssh_new())) {
+ av_log(libssh, AV_LOG_ERROR, "SSH session creation failed: %s\n", ssh_get_error(libssh->session));
+ return AVERROR(ENOMEM);
+ }
+ ssh_options_set(libssh->session, SSH_OPTIONS_HOST, hostname);
+ ssh_options_set(libssh->session, SSH_OPTIONS_PORT, &port);
+ ssh_options_set(libssh->session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
+ if (libssh->rw_timeout > 0) {
+ long timeout = libssh->rw_timeout * 1000;
+ ssh_options_set(libssh->session, SSH_OPTIONS_TIMEOUT_USEC, &timeout);
+ }
+
+ if (ssh_connect(libssh->session) != SSH_OK) {
+ av_log(libssh, AV_LOG_ERROR, "Connection failed: %s\n", ssh_get_error(libssh->session));
+ return AVERROR(EIO);
+ }
+
+ return 0;
+}
+
+static av_cold int libssh_authentication(LIBSSHContext *libssh, const char *user, const char *password)
+{
+ int authorized = 0;
+ int auth_methods;
+
+ if (user)
+ ssh_options_set(libssh->session, SSH_OPTIONS_USER, user);
+
+ if (ssh_userauth_none(libssh->session, NULL) == SSH_AUTH_SUCCESS)
+ return 0;
+
+ auth_methods = ssh_userauth_list(libssh->session, NULL);
+
+ if (auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
+ if (libssh->priv_key) {
+ ssh_string pub_key;
+ ssh_private_key priv_key;
+ int type;
+ if (!ssh_try_publickey_from_file(libssh->session, libssh->priv_key, &pub_key, &type)) {
+ priv_key = privatekey_from_file(libssh->session, libssh->priv_key, type, password);
+ if (ssh_userauth_pubkey(libssh->session, NULL, pub_key, priv_key) == SSH_AUTH_SUCCESS) {
+ av_log(libssh, AV_LOG_DEBUG, "Authentication successful with selected private key.\n");
+ authorized = 1;
+ }
+ } else {
+ av_log(libssh, AV_LOG_DEBUG, "Invalid key is provided.\n");
+ return AVERROR(EACCES);
+ }
+ } else if (ssh_userauth_autopubkey(libssh->session, password) == SSH_AUTH_SUCCESS) {
+ av_log(libssh, AV_LOG_DEBUG, "Authentication successful with auto selected key.\n");
+ authorized = 1;
+ }
+ }
+
+ if (!authorized && (auth_methods & SSH_AUTH_METHOD_PASSWORD)) {
+ if (ssh_userauth_password(libssh->session, NULL, password) == SSH_AUTH_SUCCESS) {
+ av_log(libssh, AV_LOG_DEBUG, "Authentication successful with password.\n");
+ authorized = 1;
+ }
+ }
+
+ if (!authorized) {
+ av_log(libssh, AV_LOG_ERROR, "Authentication failed.\n");
+ return AVERROR(EACCES);
+ }
+
+ return 0;
+}
+
+static av_cold int libssh_create_sftp_session(LIBSSHContext *libssh)
+{
+ if (!(libssh->sftp = sftp_new(libssh->session))) {
+ av_log(libssh, AV_LOG_ERROR, "SFTP session creation failed: %s\n", ssh_get_error(libssh->session));
+ return AVERROR(ENOMEM);
+ }
+
+ if (sftp_init(libssh->sftp) != SSH_OK) {
+ av_log(libssh, AV_LOG_ERROR, "Error initializing sftp session: %s\n", ssh_get_error(libssh->session));
+ return AVERROR(EIO);
+ }
+
+ return 0;
+}
+
+static av_cold int libssh_open_file(LIBSSHContext *libssh, int flags, const char *file)
+{
+ int access;
+
+ if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) {
+ access = O_CREAT | O_RDWR;
+ if (libssh->trunc)
+ access |= O_TRUNC;
+ } else if (flags & AVIO_FLAG_WRITE) {
+ access = O_CREAT | O_WRONLY;
+ if (libssh->trunc)
+ access |= O_TRUNC;
+ } else
+ access = O_RDONLY;
+
+ /* 0666 = -rw-rw-rw- = read+write for everyone, minus umask */
+ if (!(libssh->file = sftp_open(libssh->sftp, file, access, 0666))) {
+ av_log(libssh, AV_LOG_ERROR, "Error opening sftp file: %s\n", ssh_get_error(libssh->session));
+ return AVERROR(EIO);
+ }
+
+ return 0;
+}
+
+static av_cold void libssh_stat_file(LIBSSHContext *libssh)
+{
+ sftp_attributes stat;
+
+ if (!(stat = sftp_fstat(libssh->file))) {
+ av_log(libssh, AV_LOG_WARNING, "Cannot stat remote file.\n");
+ libssh->filesize = -1;
+ } else {
+ libssh->filesize = stat->size;
+ sftp_attributes_free(stat);
+ }
+}
+
+static av_cold int libssh_close(URLContext *h)
+{
+ LIBSSHContext *libssh = h->priv_data;
+ if (libssh->file) {
+ sftp_close(libssh->file);
+ libssh->file = NULL;
+ }
+ if (libssh->sftp) {
+ sftp_free(libssh->sftp);
+ libssh->sftp = NULL;
+ }
+ if (libssh->session) {
+ ssh_disconnect(libssh->session);
+ ssh_free(libssh->session);
+ libssh->session = NULL;
+ }
+ return 0;
+}
+
+static av_cold int libssh_open(URLContext *h, const char *url, int flags)
+{
+ LIBSSHContext *libssh = h->priv_data;
+ char proto[10], path[MAX_URL_SIZE], hostname[1024], credencials[1024];
+ int port = 22, ret;
+ const char *user = NULL, *pass = NULL;
+ char *end = NULL;
+
+ av_url_split(proto, sizeof(proto),
+ credencials, sizeof(credencials),
+ hostname, sizeof(hostname),
+ &port,
+ path, sizeof(path),
+ url);
+
+ if (port <= 0 || port > 65535)
+ port = 22;
+
+ if ((ret = libssh_create_ssh_session(libssh, hostname, port)) < 0)
+ goto fail;
+
+ user = av_strtok(credencials, ":", &end);
+ pass = av_strtok(end, ":", &end);
+
+ if ((ret = libssh_authentication(libssh, user, pass)) < 0)
+ goto fail;
+
+ if ((ret = libssh_create_sftp_session(libssh)) < 0)
+ goto fail;
+
+ if ((ret = libssh_open_file(libssh, flags, path)) < 0)
+ goto fail;
+
+ libssh_stat_file(libssh);
+
+ return 0;
+
+ fail:
+ libssh_close(h);
+ return ret;
+}
+
+static int64_t libssh_seek(URLContext *h, int64_t pos, int whence)
+{
+ LIBSSHContext *libssh = h->priv_data;
+ int64_t newpos;
+
+ if (libssh->filesize == -1 && (whence == AVSEEK_SIZE || whence == SEEK_END)) {
+ av_log(h, AV_LOG_ERROR, "Error during seeking.\n");
+ return AVERROR(EIO);
+ }
+
+ switch(whence) {
+ case AVSEEK_SIZE:
+ return libssh->filesize;
+ case SEEK_SET:
+ newpos = pos;
+ break;
+ case SEEK_CUR:
+ newpos = sftp_tell64(libssh->file) + pos;
+ break;
+ case SEEK_END:
+ newpos = libssh->filesize + pos;
+ break;
+ default:
+ return AVERROR(EINVAL);
+ }
+
+ if (newpos < 0) {
+ av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (sftp_seek64(libssh->file, newpos)) {
+ av_log(h, AV_LOG_ERROR, "Error during seeking.\n");
+ return AVERROR(EIO);
+ }
+
+ return newpos;
+}
+
+static int libssh_read(URLContext *h, unsigned char *buf, int size)
+{
+ LIBSSHContext *libssh = h->priv_data;
+ int bytes_read;
+
+ if ((bytes_read = sftp_read(libssh->file, buf, size)) < 0) {
+ av_log(libssh, AV_LOG_ERROR, "Read error.\n");
+ return AVERROR(EIO);
+ }
+ return bytes_read;
+}
+
+static int libssh_write(URLContext *h, const unsigned char *buf, int size)
+{
+ LIBSSHContext *libssh = h->priv_data;
+ int bytes_written;
+
+ if ((bytes_written = sftp_write(libssh->file, buf, size)) < 0) {
+ av_log(libssh, AV_LOG_ERROR, "Write error.\n");
+ return AVERROR(EIO);
+ }
+ return bytes_written;
+}
+
+#define OFFSET(x) offsetof(LIBSSHContext, x)
+#define D AV_OPT_FLAG_DECODING_PARAM
+#define E AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
+ {"truncate", "Truncate existing files on write", OFFSET(trunc), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, E },
+ {"private_key", "set path to private key", OFFSET(priv_key), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D|E },
+ {NULL}
+};
+
+static const AVClass libssh_context_class = {
+ .class_name = "libssh",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+URLProtocol ff_libssh_protocol = {
+ .name = "sftp",
+ .url_open = libssh_open,
+ .url_read = libssh_read,
+ .url_write = libssh_write,
+ .url_seek = libssh_seek,
+ .url_close = libssh_close,
+ .priv_data_size = sizeof(LIBSSHContext),
+ .priv_data_class = &libssh_context_class,
+ .flags = URL_PROTOCOL_FLAG_NETWORK,
+};
diff --git a/libavformat/lmlm4.c b/libavformat/lmlm4.c
index 1fb03e7e11..899f449634 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
*/
@@ -37,7 +37,7 @@
static int lmlm4_probe(AVProbeData *pd)
{
- unsigned char *buf = pd->buf;
+ const unsigned char *buf = pd->buf;
unsigned int frame_type, packet_size;
frame_type = AV_RB16(buf + 2);
@@ -96,8 +96,8 @@ static int lmlm4_read_packet(AVFormatContext *s, AVPacket *pkt)
av_log(s, AV_LOG_ERROR, "invalid or unsupported frame_type\n");
return AVERROR(EIO);
}
- if (packet_size > LMLM4_MAX_PACKET_SIZE) {
- av_log(s, AV_LOG_ERROR, "packet size exceeds maximum\n");
+ if (packet_size > LMLM4_MAX_PACKET_SIZE || packet_size<=8) {
+ av_log(s, AV_LOG_ERROR, "packet size %d is invalid\n", packet_size);
return AVERROR(EIO);
}
diff --git a/libavformat/loasdec.c b/libavformat/loasdec.c
new file mode 100644
index 0000000000..c41809be00
--- /dev/null
+++ b/libavformat/loasdec.c
@@ -0,0 +1,94 @@
+/*
+ * LOAS AudioSyncStream demuxer
+ * Copyright (c) 2008 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/internal.h"
+#include "avformat.h"
+#include "internal.h"
+#include "rawdec.h"
+
+#define LOAS_SYNC_WORD 0x2b7
+
+static int loas_probe(AVProbeData *p)
+{
+ int max_frames = 0, first_frames = 0;
+ int fsize, frames;
+ const uint8_t *buf0 = p->buf;
+ const uint8_t *buf2;
+ const uint8_t *buf;
+ const uint8_t *end = buf0 + p->buf_size - 3;
+ buf = buf0;
+
+ for (; buf < end; buf = buf2 + 1) {
+ buf2 = buf;
+
+ for (frames = 0; buf2 < end; frames++) {
+ uint32_t header = AV_RB24(buf2);
+ if ((header >> 13) != LOAS_SYNC_WORD)
+ break;
+ fsize = (header & 0x1FFF) + 3;
+ if (fsize < 7)
+ break;
+ fsize = FFMIN(fsize, end - buf2);
+ buf2 += fsize;
+ }
+ max_frames = FFMAX(max_frames, frames);
+ if (buf == buf0)
+ first_frames = frames;
+ }
+
+ if (first_frames >= 3)
+ return AVPROBE_SCORE_EXTENSION + 1;
+ else if (max_frames > 100)
+ return AVPROBE_SCORE_EXTENSION;
+ else if (max_frames >= 3)
+ return AVPROBE_SCORE_EXTENSION / 2;
+ else
+ return 0;
+}
+
+static int loas_read_header(AVFormatContext *s)
+{
+ AVStream *st;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->codec_id = s->iformat->raw_codec_id;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
+
+ //LCM of all possible AAC sample rates
+ avpriv_set_pts_info(st, 64, 1, 28224000);
+
+ return 0;
+}
+
+AVInputFormat ff_loas_demuxer = {
+ .name = "loas",
+ .long_name = NULL_IF_CONFIG_SMALL("LOAS AudioSyncStream"),
+ .read_probe = loas_probe,
+ .read_header = loas_read_header,
+ .read_packet = ff_raw_read_partial_packet,
+ .flags= AVFMT_GENERIC_INDEX,
+ .raw_codec_id = AV_CODEC_ID_AAC_LATM,
+};
diff --git a/libavformat/lrc.c b/libavformat/lrc.c
new file mode 100644
index 0000000000..139c6506e4
--- /dev/null
+++ b/libavformat/lrc.c
@@ -0,0 +1,34 @@
+/*
+ * LRC lyrics file format decoder
+ * Copyright (c) 2014 StarBrilliant <m13253@hotmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "metadata.h"
+#include "lrc.h"
+
+const AVMetadataConv ff_lrc_metadata_conv[] = {
+ {"ti", "title"},
+ {"al", "album"},
+ {"ar", "artist"},
+ {"au", "author"},
+ {"by", "creator"},
+ {"re", "encoder"},
+ {"ve", "encoder_version"},
+ {0, 0}
+};
diff --git a/libavformat/lrc.h b/libavformat/lrc.h
new file mode 100644
index 0000000000..0297bc1eb1
--- /dev/null
+++ b/libavformat/lrc.h
@@ -0,0 +1,29 @@
+/*
+ * LRC lyrics file format decoder
+ * Copyright (c) 2014 StarBrilliant <m13253@hotmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_LRC_H
+#define AVFORMAT_LRC_H
+
+#include "metadata.h"
+
+extern const AVMetadataConv ff_lrc_metadata_conv[];
+
+#endif
diff --git a/libavformat/lrcdec.c b/libavformat/lrcdec.c
new file mode 100644
index 0000000000..df61853d2b
--- /dev/null
+++ b/libavformat/lrcdec.c
@@ -0,0 +1,248 @@
+/*
+ * LRC lyrics file format decoder
+ * Copyright (c) 2014 StarBrilliant <m13253@hotmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "avformat.h"
+#include "internal.h"
+#include "lrc.h"
+#include "metadata.h"
+#include "subtitles.h"
+#include "libavutil/bprint.h"
+#include "libavutil/dict.h"
+
+typedef struct LRCContext {
+ FFDemuxSubtitlesQueue q;
+ int64_t ts_offset; // offset metadata item
+} LRCContext;
+
+static int64_t find_header(const char *p)
+{
+ int64_t offset = 0;
+ while(p[offset] == ' ' || p[offset] == '\t') {
+ offset++;
+ }
+ if(p[offset] == '[' && p[offset + 1] >= 'a' && p[offset + 1] <= 'z') {
+ return offset;
+ } else {
+ return -1;
+ }
+}
+
+static int64_t count_ts(const char *p)
+{
+ int64_t offset = 0;
+ int in_brackets = 0;
+
+ for(;;) {
+ if(p[offset] == ' ' || p[offset] == '\t') {
+ offset++;
+ } else if(p[offset] == '[') {
+ offset++;
+ in_brackets++;
+ } else if (p[offset] == ']' && in_brackets) {
+ offset++;
+ in_brackets--;
+ } else if(in_brackets &&
+ (p[offset] == ':' || p[offset] == '.' || p[offset] == '-' ||
+ (p[offset] >= '0' && p[offset] <= '9'))) {
+ offset++;
+ } else {
+ break;
+ }
+ }
+ return offset;
+}
+
+static int64_t read_ts(const char *p, int64_t *start)
+{
+ int64_t offset = 0;
+ uint64_t mm, ss, cs;
+
+ while(p[offset] == ' ' || p[offset] == '\t') {
+ offset++;
+ }
+ if(p[offset] != '[') {
+ return 0;
+ }
+ if(sscanf(p, "[-%"SCNu64":%"SCNu64".%"SCNu64"]", &mm, &ss, &cs) == 3) {
+ /* Just in case negative pts, players may drop it but we won't. */
+ *start = -(int64_t) (mm*60000 + ss*1000 + cs*10);
+ } else if(sscanf(p, "[%"SCNu64":%"SCNu64".%"SCNu64"]", &mm, &ss, &cs) == 3) {
+ *start = mm*60000 + ss*1000 + cs*10;
+ } else {
+ return 0;
+ }
+ do {
+ offset++;
+ } while(p[offset] && p[offset-1] != ']');
+ return offset;
+}
+
+static int64_t read_line(AVBPrint *buf, AVIOContext *pb)
+{
+ int64_t pos = avio_tell(pb);
+
+ av_bprint_clear(buf);
+ while(!avio_feof(pb)) {
+ int c = avio_r8(pb);
+ if(c != '\r') {
+ av_bprint_chars(buf, c, 1);
+ }
+ if(c == '\n') {
+ break;
+ }
+ }
+ return pos;
+}
+
+static int lrc_probe(AVProbeData *p)
+{
+ int64_t offset = 0;
+ int64_t mm;
+ uint64_t ss, cs;
+ const AVMetadataConv *metadata_item;
+
+ if(!memcmp(p->buf, "\xef\xbb\xbf", 3)) { // Skip UTF-8 BOM header
+ offset += 3;
+ }
+ while(p->buf[offset] == '\n' || p->buf[offset] == '\r') {
+ offset++;
+ }
+ if(p->buf[offset] != '[') {
+ return 0;
+ }
+ offset++;
+ // Common metadata item but not exist in ff_lrc_metadata_conv
+ if(!memcmp(p->buf + offset, "offset:", 7)) {
+ return 40;
+ }
+ if(sscanf(p->buf + offset, "%"SCNd64":%"SCNu64".%"SCNu64"]",
+ &mm, &ss, &cs) == 3) {
+ return 50;
+ }
+ // Metadata items exist in ff_lrc_metadata_conv
+ for(metadata_item = ff_lrc_metadata_conv;
+ metadata_item->native; metadata_item++) {
+ size_t metadata_item_len = strlen(metadata_item->native);
+ if(p->buf[offset + metadata_item_len] == ':' &&
+ !memcmp(p->buf + offset, metadata_item->native, metadata_item_len)) {
+ return 40;
+ }
+ }
+ return 5; // Give it 5 scores since it starts with a bracket
+}
+
+static int lrc_read_header(AVFormatContext *s)
+{
+ LRCContext *lrc = s->priv_data;
+ AVBPrint line;
+ AVStream *st;
+
+ st = avformat_new_stream(s, NULL);
+ if(!st) {
+ return AVERROR(ENOMEM);
+ }
+ avpriv_set_pts_info(st, 64, 1, 1000);
+ lrc->ts_offset = 0;
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_TEXT;
+ av_bprint_init(&line, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+ while(!avio_feof(s->pb)) {
+ int64_t pos = read_line(&line, s->pb);
+ int64_t header_offset = find_header(line.str);
+ if(header_offset >= 0) {
+ char *comma_offset = strchr(line.str, ':');
+ if(comma_offset) {
+ char *right_bracket_offset = strchr(line.str, ']');
+ if(!right_bracket_offset) {
+ continue;
+ }
+
+ *right_bracket_offset = *comma_offset = '\0';
+ if(strcmp(line.str + 1, "offset") ||
+ sscanf(comma_offset + 1, "%"SCNd64, &lrc->ts_offset) != 1) {
+ av_dict_set(&s->metadata, line.str + 1, comma_offset + 1, 0);
+ }
+ *comma_offset = ':';
+ *right_bracket_offset = ']';
+ }
+
+ } else {
+ AVPacket *sub;
+ int64_t ts_start = AV_NOPTS_VALUE;
+ int64_t ts_stroffset = 0;
+ int64_t ts_stroffset_incr = 0;
+ int64_t ts_strlength = count_ts(line.str);
+
+ while((ts_stroffset_incr = read_ts(line.str + ts_stroffset,
+ &ts_start)) != 0) {
+ ts_stroffset += ts_stroffset_incr;
+ sub = ff_subtitles_queue_insert(&lrc->q, line.str + ts_strlength,
+ line.len - ts_strlength, 0);
+ if(!sub) {
+ return AVERROR(ENOMEM);
+ }
+ sub->pos = pos;
+ sub->pts = ts_start - lrc->ts_offset;
+ sub->duration = -1;
+ }
+ }
+ }
+ ff_subtitles_queue_finalize(&lrc->q);
+ ff_metadata_conv_ctx(s, NULL, ff_lrc_metadata_conv);
+ return 0;
+}
+
+static int lrc_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ LRCContext *lrc = s->priv_data;
+ return ff_subtitles_queue_read_packet(&lrc->q, pkt);
+}
+
+static int lrc_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ LRCContext *lrc = s->priv_data;
+ return ff_subtitles_queue_seek(&lrc->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int lrc_read_close(AVFormatContext *s)
+{
+ LRCContext *lrc = s->priv_data;
+ ff_subtitles_queue_clean(&lrc->q);
+ return 0;
+}
+
+AVInputFormat ff_lrc_demuxer = {
+ .name = "lrc",
+ .long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"),
+ .priv_data_size = sizeof (LRCContext),
+ .read_probe = lrc_probe,
+ .read_header = lrc_read_header,
+ .read_packet = lrc_read_packet,
+ .read_close = lrc_read_close,
+ .read_seek2 = lrc_read_seek
+};
diff --git a/libavformat/lrcenc.c b/libavformat/lrcenc.c
new file mode 100644
index 0000000000..b316ccd6d8
--- /dev/null
+++ b/libavformat/lrcenc.c
@@ -0,0 +1,152 @@
+/*
+ * LRC lyrics file format decoder
+ * Copyright (c) 2014 StarBrilliant <m13253@hotmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "avformat.h"
+#include "internal.h"
+#include "lrc.h"
+#include "metadata.h"
+#include "subtitles.h"
+#include "version.h"
+#include "libavutil/bprint.h"
+#include "libavutil/dict.h"
+#include "libavutil/log.h"
+#include "libavutil/macros.h"
+
+static int lrc_write_header(AVFormatContext *s)
+{
+ const AVDictionaryEntry *metadata_item;
+
+ if(s->nb_streams != 1 ||
+ s->streams[0]->codec->codec_type != AVMEDIA_TYPE_SUBTITLE) {
+ av_log(s, AV_LOG_ERROR,
+ "LRC supports only a single subtitle stream.\n");
+ return AVERROR(EINVAL);
+ }
+ if(s->streams[0]->codec->codec_id != AV_CODEC_ID_SUBRIP &&
+ s->streams[0]->codec->codec_id != AV_CODEC_ID_TEXT) {
+ av_log(s, AV_LOG_ERROR, "Unsupported subtitle codec: %s\n",
+ avcodec_get_name(s->streams[0]->codec->codec_id));
+ return AVERROR(EINVAL);
+ }
+ avpriv_set_pts_info(s->streams[0], 64, 1, 100);
+
+ ff_metadata_conv_ctx(s, ff_lrc_metadata_conv, NULL);
+ if(!(s->flags & AVFMT_FLAG_BITEXACT)) { // avoid breaking regression tests
+ /* LRC provides a metadata slot for specifying encoder version
+ * in addition to encoder name. We will store LIBAVFORMAT_VERSION
+ * to it.
+ */
+ av_dict_set(&s->metadata, "ve", AV_STRINGIFY(LIBAVFORMAT_VERSION), 0);
+ } else {
+ av_dict_set(&s->metadata, "ve", NULL, 0);
+ }
+ for(metadata_item = NULL;
+ (metadata_item = av_dict_get(s->metadata, "", metadata_item,
+ AV_DICT_IGNORE_SUFFIX));) {
+ char *delim;
+ if(!metadata_item->value[0]) {
+ continue;
+ }
+ while((delim = strchr(metadata_item->value, '\n'))) {
+ *delim = ' ';
+ }
+ while((delim = strchr(metadata_item->value, '\r'))) {
+ *delim = ' ';
+ }
+ avio_printf(s->pb, "[%s:%s]\n",
+ metadata_item->key, metadata_item->value);
+ }
+ avio_printf(s->pb, "\n");
+ return 0;
+}
+
+static int lrc_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ if(pkt->pts != AV_NOPTS_VALUE) {
+ char *data = av_malloc(pkt->size + 1);
+ char *line;
+ char *delim;
+
+ if(!data) {
+ return AVERROR(ENOMEM);
+ }
+ memcpy(data, pkt->data, pkt->size);
+ data[pkt->size] = '\0';
+
+ for(delim = data + pkt->size - 1;
+ delim >= data && (delim[0] == '\n' || delim[0] == '\r'); delim--) {
+ delim[0] = '\0'; // Strip last empty lines
+ }
+ line = data;
+ while(line[0] == '\n' || line[0] == '\r') {
+ line++; // Skip first empty lines
+ }
+
+ while(line) {
+ delim = strchr(line, '\n');
+ if(delim) {
+ if(delim > line && delim[-1] == '\r') {
+ delim[-1] = '\0';
+ }
+ delim[0] = '\0';
+ delim++;
+ }
+ if(line[0] == '[') {
+ av_log(s, AV_LOG_WARNING,
+ "Subtitle starts with '[', may cause problems with LRC format.\n");
+ }
+
+ if(pkt->pts >= 0) {
+ avio_printf(s->pb, "[%02"PRId64":%02"PRId64".%02"PRId64"]",
+ (pkt->pts / 6000),
+ ((pkt->pts / 100) % 60),
+ (pkt->pts % 100));
+ } else {
+ /* Offset feature of LRC can easily make pts negative,
+ * we just output it directly and let the player drop it. */
+ avio_printf(s->pb, "[-%02"PRId64":%02"PRId64".%02"PRId64"]",
+ (-pkt->pts) / 6000,
+ ((-pkt->pts) / 100) % 60,
+ (-pkt->pts) % 100);
+ }
+ avio_printf(s->pb, "%s\n", line);
+ line = delim;
+ }
+ av_free(data);
+ }
+ return 0;
+}
+
+AVOutputFormat ff_lrc_muxer = {
+ .name = "lrc",
+ .long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"),
+ .extensions = "lrc",
+ .priv_data_size = 0,
+ .write_header = lrc_write_header,
+ .write_packet = lrc_write_packet,
+ .flags = AVFMT_VARIABLE_FPS | AVFMT_GLOBALHEADER |
+ AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT,
+ .subtitle_codec = AV_CODEC_ID_SUBRIP
+};
diff --git a/libavformat/lvfdec.c b/libavformat/lvfdec.c
new file mode 100644
index 0000000000..81aec599ba
--- /dev/null
+++ b/libavformat/lvfdec.c
@@ -0,0 +1,152 @@
+/*
+ * LVF demuxer
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "riff.h"
+
+static int lvf_probe(AVProbeData *p)
+{
+ if (AV_RL32(p->buf) != MKTAG('L', 'V', 'F', 'F'))
+ return 0;
+
+ if (!AV_RL32(p->buf + 16) || AV_RL32(p->buf + 16) > 256)
+ return AVPROBE_SCORE_MAX / 8;
+
+ return AVPROBE_SCORE_EXTENSION;
+}
+
+static int lvf_read_header(AVFormatContext *s)
+{
+ AVStream *st;
+ int64_t next_offset;
+ unsigned size, nb_streams, id;
+
+ avio_skip(s->pb, 16);
+ nb_streams = avio_rl32(s->pb);
+ if (!nb_streams)
+ return AVERROR_INVALIDDATA;
+ if (nb_streams > 2) {
+ avpriv_request_sample(s, "%d streams", nb_streams);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ avio_skip(s->pb, 1012);
+
+ while (!avio_feof(s->pb)) {
+ id = avio_rl32(s->pb);
+ size = avio_rl32(s->pb);
+ next_offset = avio_tell(s->pb) + size;
+
+ switch (id) {
+ case MKTAG('0', '0', 'f', 'm'):
+ st = avformat_new_stream(s, 0);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ avio_skip(s->pb, 4);
+ st->codec->width = avio_rl32(s->pb);
+ st->codec->height = avio_rl32(s->pb);
+ avio_skip(s->pb, 4);
+ st->codec->codec_tag = avio_rl32(s->pb);
+ st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags,
+ st->codec->codec_tag);
+ avpriv_set_pts_info(st, 32, 1, 1000);
+ break;
+ case MKTAG('0', '1', 'f', 'm'):
+ st = avformat_new_stream(s, 0);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->codec_tag = avio_rl16(s->pb);
+ st->codec->channels = avio_rl16(s->pb);
+ st->codec->sample_rate = avio_rl16(s->pb);
+ avio_skip(s->pb, 8);
+ st->codec->bits_per_coded_sample = avio_r8(s->pb);
+ st->codec->codec_id = ff_codec_get_id(ff_codec_wav_tags,
+ st->codec->codec_tag);
+ avpriv_set_pts_info(st, 32, 1, 1000);
+ break;
+ case 0:
+ avio_seek(s->pb, 2048 + 8, SEEK_SET);
+ return 0;
+ default:
+ avpriv_request_sample(s, "id %d", id);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ avio_seek(s->pb, next_offset, SEEK_SET);
+ }
+
+ return AVERROR_EOF;
+}
+
+static int lvf_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ unsigned size, flags, timestamp, id;
+ int64_t pos;
+ int ret, is_video = 0;
+
+ pos = avio_tell(s->pb);
+ while (!avio_feof(s->pb)) {
+ id = avio_rl32(s->pb);
+ size = avio_rl32(s->pb);
+
+ if (size == 0xFFFFFFFFu)
+ return AVERROR_EOF;
+
+ switch (id) {
+ case MKTAG('0', '0', 'd', 'c'):
+ is_video = 1;
+ case MKTAG('0', '1', 'w', 'b'):
+ if (size < 8)
+ return AVERROR_INVALIDDATA;
+ timestamp = avio_rl32(s->pb);
+ flags = avio_rl32(s->pb);
+ ret = av_get_packet(s->pb, pkt, size - 8);
+ if (flags & (1 << 12))
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ pkt->stream_index = is_video ? 0 : 1;
+ pkt->pts = timestamp;
+ pkt->pos = pos;
+ return ret;
+ default:
+ ret = avio_skip(s->pb, size);
+ }
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return AVERROR_EOF;
+}
+
+AVInputFormat ff_lvf_demuxer = {
+ .name = "lvf",
+ .long_name = NULL_IF_CONFIG_SMALL("LVF"),
+ .read_probe = lvf_probe,
+ .read_header = lvf_read_header,
+ .read_packet = lvf_read_packet,
+ .extensions = "lvf",
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/lxfdec.c b/libavformat/lxfdec.c
index e25f82e0af..ce5da820d0 100644
--- a/libavformat/lxfdec.c
+++ b/libavformat/lxfdec.c
@@ -2,20 +2,20 @@
* LXF demuxer
* Copyright (c) 2010 Tomas Härdin
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -83,7 +83,7 @@ static int check_checksum(const uint8_t *header, int size)
* @param[out] header where to copy the ident to
* @return 0 if an ident was found, < 0 on I/O error
*/
-static int sync(AVFormatContext *s, uint8_t *header)
+static int lxf_sync(AVFormatContext *s, uint8_t *header)
{
uint8_t buf[LXF_IDENT_LENGTH];
int ret;
@@ -92,7 +92,7 @@ static int sync(AVFormatContext *s, uint8_t *header)
return ret < 0 ? ret : AVERROR_EOF;
while (memcmp(buf, LXF_IDENT, LXF_IDENT_LENGTH)) {
- if (s->pb->eof_reached)
+ if (avio_feof(s->pb))
return AVERROR_EOF;
memmove(buf, &buf[1], LXF_IDENT_LENGTH-1);
@@ -120,7 +120,7 @@ static int get_packet_header(AVFormatContext *s)
const uint8_t *p = header + LXF_IDENT_LENGTH;
//find and read the ident
- if ((ret = sync(s, header)) < 0)
+ if ((ret = lxf_sync(s, header)) < 0)
return ret;
ret = avio_read(pb, header + LXF_IDENT_LENGTH, 8);
@@ -140,8 +140,9 @@ static int get_packet_header(AVFormatContext *s)
}
//read the rest of the packet header
- ret = avio_read(pb, header + (p - header), header_size - (p - header));
- if (ret != header_size - (p - header))
+ if ((ret = avio_read(pb, header + (p - header),
+ header_size - (p - header))) !=
+ header_size - (p - header))
return ret < 0 ? ret : AVERROR_EOF;
if (check_checksum(header, header_size))
@@ -260,6 +261,7 @@ static int lxf_read_header(AVFormatContext *s)
st->codec->bit_rate = 1000000 * ((video_params >> 14) & 0xFF);
st->codec->codec_tag = video_params & 0xF;
st->codec->codec_id = ff_codec_get_id(lxf_tags, st->codec->codec_tag);
+ st->need_parsing = AVSTREAM_PARSE_HEADERS;
av_log(s, AV_LOG_DEBUG, "record: %x = %i-%02i-%02i\n",
record_date, 1900 + (record_date & 0x7F), (record_date >> 7) & 0xF,
diff --git a/libavformat/m4vdec.c b/libavformat/m4vdec.c
index 4a0af3c037..d8ee530010 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
*/
@@ -33,16 +33,18 @@ static int mpeg4video_probe(AVProbeData *probe_packet)
for (i = 0; i < probe_packet->buf_size; i++) {
temp_buffer = (temp_buffer << 8) + probe_packet->buf[i];
- if ((temp_buffer & 0xffffff00) != 0x100)
+ if (temp_buffer & 0xfffffe00)
+ continue;
+ if (temp_buffer < 2)
continue;
if (temp_buffer == VOP_START_CODE)
VOP++;
else if (temp_buffer == VISUAL_OBJECT_START_CODE)
VISO++;
- else if (temp_buffer < 0x120)
+ else if (temp_buffer >= 0x100 && temp_buffer < 0x120)
VO++;
- else if (temp_buffer < 0x130)
+ else if (temp_buffer >= 0x120 && temp_buffer < 0x130)
VOL++;
else if (!(0x1AF < temp_buffer && temp_buffer < 0x1B7) &&
!(0x1B9 < temp_buffer && temp_buffer < 0x1C4))
@@ -50,9 +52,9 @@ static int mpeg4video_probe(AVProbeData *probe_packet)
}
if (VOP >= VISO && VOP >= VOL && VO >= VOL && VOL > 0 && res == 0)
- return AVPROBE_SCORE_EXTENSION;
+ return VOP+VO > 4 ? AVPROBE_SCORE_EXTENSION : AVPROBE_SCORE_EXTENSION/2;
return 0;
}
-FF_DEF_RAWVIDEO_DEMUXER(m4v, "raw MPEG-4 video", mpeg4video_probe, "m4v",
- AV_CODEC_ID_MPEG4)
+FF_DEF_RAWVIDEO_DEMUXER2(m4v, "raw MPEG-4 video", mpeg4video_probe, "m4v",
+ AV_CODEC_ID_MPEG4, AVFMT_GENERIC_INDEX | AVFMT_TS_DISCONT)
diff --git a/libavformat/matroska.c b/libavformat/matroska.c
index 237f26f49c..bc5007a433 100644
--- a/libavformat/matroska.c
+++ b/libavformat/matroska.c
@@ -1,21 +1,21 @@
/*
* Matroska common data
- * Copyright (c) 2003-2004 The ffmpeg Project
+ * Copyright (c) 2003-2004 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -23,6 +23,9 @@
#include "matroska.h"
+/* If you add a tag here that is not in ff_codec_bmp_tags[]
+ or ff_codec_wav_tags[], add it also to additional_audio_tags[]
+ or additional_video_tags[] in matroskaenc.c */
const CodecTags ff_mkv_codec_tags[]={
{"A_AAC" , AV_CODEC_ID_AAC},
{"A_AC3" , AV_CODEC_ID_AC3},
@@ -35,6 +38,7 @@ const CodecTags ff_mkv_codec_tags[]={
{"A_MPEG/L1" , AV_CODEC_ID_MP2},
{"A_MPEG/L3" , AV_CODEC_ID_MP3},
{"A_OPUS" , AV_CODEC_ID_OPUS},
+ {"A_OPUS/EXPERIMENTAL",AV_CODEC_ID_OPUS},
{"A_PCM/FLOAT/IEEE" , AV_CODEC_ID_PCM_F32LE},
{"A_PCM/FLOAT/IEEE" , AV_CODEC_ID_PCM_F64LE},
{"A_PCM/INT/BIG" , AV_CODEC_ID_PCM_S16BE},
@@ -55,14 +59,20 @@ const CodecTags ff_mkv_codec_tags[]={
{"A_VORBIS" , AV_CODEC_ID_VORBIS},
{"A_WAVPACK4" , AV_CODEC_ID_WAVPACK},
+ {"D_WEBVTT/SUBTITLES" , AV_CODEC_ID_WEBVTT},
+ {"D_WEBVTT/CAPTIONS" , AV_CODEC_ID_WEBVTT},
+ {"D_WEBVTT/DESCRIPTIONS", AV_CODEC_ID_WEBVTT},
+ {"D_WEBVTT/METADATA" , AV_CODEC_ID_WEBVTT},
+
+ {"S_TEXT/UTF8" , AV_CODEC_ID_SUBRIP},
{"S_TEXT/UTF8" , AV_CODEC_ID_TEXT},
- {"S_TEXT/UTF8" , AV_CODEC_ID_SRT},
{"S_TEXT/ASCII" , AV_CODEC_ID_TEXT},
- {"S_TEXT/ASS" , AV_CODEC_ID_SSA},
- {"S_TEXT/SSA" , AV_CODEC_ID_SSA},
- {"S_ASS" , AV_CODEC_ID_SSA},
- {"S_SSA" , AV_CODEC_ID_SSA},
+ {"S_TEXT/ASS" , AV_CODEC_ID_ASS},
+ {"S_TEXT/SSA" , AV_CODEC_ID_ASS},
+ {"S_ASS" , AV_CODEC_ID_ASS},
+ {"S_SSA" , AV_CODEC_ID_ASS},
{"S_VOBSUB" , AV_CODEC_ID_DVD_SUBTITLE},
+ {"S_DVBSUB" , AV_CODEC_ID_DVB_SUBTITLE},
{"S_HDMV/PGS" , AV_CODEC_ID_HDMV_PGS_SUBTITLE},
{"V_DIRAC" , AV_CODEC_ID_DIRAC},
@@ -80,6 +90,7 @@ const CodecTags ff_mkv_codec_tags[]={
{"V_REAL/RV20" , AV_CODEC_ID_RV20},
{"V_REAL/RV30" , AV_CODEC_ID_RV30},
{"V_REAL/RV40" , AV_CODEC_ID_RV40},
+ {"V_SNOW" , AV_CODEC_ID_SNOW},
{"V_THEORA" , AV_CODEC_ID_THEORA},
{"V_UNCOMPRESSED" , AV_CODEC_ID_RAWVIDEO},
{"V_VP8" , AV_CODEC_ID_VP8},
@@ -96,6 +107,8 @@ const CodecMime ff_mkv_mime_tags[] = {
{"image/tiff" , AV_CODEC_ID_TIFF},
{"application/x-truetype-font", AV_CODEC_ID_TTF},
{"application/x-font" , AV_CODEC_ID_TTF},
+ {"application/vnd.ms-opentype", AV_CODEC_ID_OTF},
+ {"binary" , AV_CODEC_ID_BIN_DATA},
{"" , AV_CODEC_ID_NONE}
};
@@ -106,6 +119,30 @@ const AVMetadataConv ff_mkv_metadata_conv[] = {
{ 0 }
};
+const char * const ff_matroska_video_stereo_mode[MATROSKA_VIDEO_STEREOMODE_TYPE_NB] = {
+ "mono",
+ "left_right",
+ "bottom_top",
+ "top_bottom",
+ "checkerboard_rl",
+ "checkerboard_lr",
+ "row_interleaved_rl",
+ "row_interleaved_lr",
+ "col_interleaved_rl",
+ "col_interleaved_lr",
+ "anaglyph_cyan_red",
+ "right_left",
+ "anaglyph_green_magenta",
+ "block_lr",
+ "block_rl",
+};
+
+const char * const ff_matroska_video_stereo_plane[MATROSKA_VIDEO_STEREO_PLANE_COUNT] = {
+ "left",
+ "right",
+ "background",
+};
+
int ff_mkv_stereo3d_conv(AVStream *st, MatroskaVideoStereoModeType stereo_mode)
{
AVPacketSideData *sd, *tmp;
diff --git a/libavformat/matroska.h b/libavformat/matroska.h
index d8f4f8ebec..391c56c0aa 100644
--- a/libavformat/matroska.h
+++ b/libavformat/matroska.h
@@ -1,21 +1,21 @@
/*
* Matroska constants
- * Copyright (c) 2003-2004 The ffmpeg Project
+ * Copyright (c) 2003-2004 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -78,8 +78,13 @@
#define MATROSKA_ID_TRACKNUMBER 0xD7
#define MATROSKA_ID_TRACKUID 0x73C5
#define MATROSKA_ID_TRACKTYPE 0x83
-#define MATROSKA_ID_TRACKAUDIO 0xE1
-#define MATROSKA_ID_TRACKVIDEO 0xE0
+#define MATROSKA_ID_TRACKVIDEO 0xE0
+#define MATROSKA_ID_TRACKAUDIO 0xE1
+#define MATROSKA_ID_TRACKOPERATION 0xE2
+#define MATROSKA_ID_TRACKCOMBINEPLANES 0xE3
+#define MATROSKA_ID_TRACKPLANE 0xE4
+#define MATROSKA_ID_TRACKPLANEUID 0xE5
+#define MATROSKA_ID_TRACKPLANETYPE 0xE6
#define MATROSKA_ID_CODECID 0x86
#define MATROSKA_ID_CODECPRIVATE 0x63A2
#define MATROSKA_ID_CODECNAME 0x258688
@@ -87,6 +92,7 @@
#define MATROSKA_ID_CODECDOWNLOADURL 0x26B240
#define MATROSKA_ID_CODECDECODEALL 0xAA
#define MATROSKA_ID_CODECDELAY 0x56AA
+#define MATROSKA_ID_SEEKPREROLL 0x56BB
#define MATROSKA_ID_TRACKNAME 0x536E
#define MATROSKA_ID_TRACKLANGUAGE 0x22B59C
#define MATROSKA_ID_TRACKFLAGENABLED 0xB9
@@ -114,6 +120,7 @@
#define MATROSKA_ID_VIDEODISPLAYUNIT 0x54B2
#define MATROSKA_ID_VIDEOFLAGINTERLACED 0x9A
#define MATROSKA_ID_VIDEOSTEREOMODE 0x53B8
+#define MATROSKA_ID_VIDEOALPHAMODE 0x53C0
#define MATROSKA_ID_VIDEOASPECTRATIO 0x54B3
#define MATROSKA_ID_VIDEOCOLORSPACE 0x2EB524
@@ -132,6 +139,15 @@
#define MATROSKA_ID_ENCODINGCOMPALGO 0x4254
#define MATROSKA_ID_ENCODINGCOMPSETTINGS 0x4255
+#define MATROSKA_ID_ENCODINGENCRYPTION 0x5035
+#define MATROSKA_ID_ENCODINGENCAESSETTINGS 0x47E7
+#define MATROSKA_ID_ENCODINGENCALGO 0x47E1
+#define MATROSKA_ID_ENCODINGENCKEYID 0x47E2
+#define MATROSKA_ID_ENCODINGSIGALGO 0x47E5
+#define MATROSKA_ID_ENCODINGSIGHASHALGO 0x47E6
+#define MATROSKA_ID_ENCODINGSIGKEYID 0x47E4
+#define MATROSKA_ID_ENCODINGSIGNATURE 0x47E3
+
/* ID in the cues master */
#define MATROSKA_ID_POINTENTRY 0xBB
@@ -142,6 +158,8 @@
/* IDs in the cuetrackposition master */
#define MATROSKA_ID_CUETRACK 0xF7
#define MATROSKA_ID_CUECLUSTERPOSITION 0xF1
+#define MATROSKA_ID_CUERELATIVEPOSITION 0xF0
+#define MATROSKA_ID_CUEDURATION 0xB2
#define MATROSKA_ID_CUEBLOCKNUMBER 0x5378
/* IDs in the tags master */
@@ -171,6 +189,10 @@
#define MATROSKA_ID_CLUSTERPOSITION 0xA7
#define MATROSKA_ID_CLUSTERPREVSIZE 0xAB
#define MATROSKA_ID_BLOCKGROUP 0xA0
+#define MATROSKA_ID_BLOCKADDITIONS 0x75A1
+#define MATROSKA_ID_BLOCKMORE 0xA6
+#define MATROSKA_ID_BLOCKADDID 0xEE
+#define MATROSKA_ID_BLOCKADDITIONAL 0xA5
#define MATROSKA_ID_SIMPLEBLOCK 0xA3
/* IDs in the blockgroup master */
@@ -178,6 +200,7 @@
#define MATROSKA_ID_BLOCKDURATION 0x9B
#define MATROSKA_ID_BLOCKREFERENCE 0xFB
#define MATROSKA_ID_CODECSTATE 0xA4
+#define MATROSKA_ID_DISCARDPADDING 0x75A2
/* IDs in the attachments master */
#define MATROSKA_ID_ATTACHEDFILE 0x61A7
@@ -212,6 +235,7 @@ typedef enum {
MATROSKA_TRACK_TYPE_LOGO = 0x10,
MATROSKA_TRACK_TYPE_SUBTITLE = 0x11,
MATROSKA_TRACK_TYPE_CONTROL = 0x20,
+ MATROSKA_TRACK_TYPE_METADATA = 0x21,
} MatroskaTrackType;
typedef enum {
@@ -245,16 +269,32 @@ typedef enum {
*/
typedef struct CodecTags{
- char str[20];
+ char str[22];
enum AVCodecID id;
}CodecTags;
/* max. depth in the EBML tree structure */
#define EBML_MAX_DEPTH 16
+#define MATROSKA_VIDEO_STEREO_PLANE_COUNT 3
+
extern const CodecTags ff_mkv_codec_tags[];
extern const CodecMime ff_mkv_mime_tags[];
extern const AVMetadataConv ff_mkv_metadata_conv[];
+extern const char * const ff_matroska_video_stereo_mode[MATROSKA_VIDEO_STEREOMODE_TYPE_NB];
+extern const char * const ff_matroska_video_stereo_plane[MATROSKA_VIDEO_STEREO_PLANE_COUNT];
+
+/* AVStream Metadata tag keys for WebM Dash Manifest */
+#define INITIALIZATION_RANGE "webm_dash_manifest_initialization_range"
+#define CUES_START "webm_dash_manifest_cues_start"
+#define CUES_END "webm_dash_manifest_cues_end"
+#define FILENAME "webm_dash_manifest_file_name"
+#define BANDWIDTH "webm_dash_manifest_bandwidth"
+#define DURATION "webm_dash_manifest_duration"
+#define CLUSTER_KEYFRAME "webm_dash_manifest_cluster_keyframe"
+#define CUE_TIMESTAMPS "webm_dash_manifest_cue_timestamps"
+#define TRACK_NUMBER "webm_dash_manifest_track_number"
+#define CODEC_PRIVATE_SIZE "webm_dash_manifest_codec_priv_size"
int ff_mkv_stereo3d_conv(AVStream *st, MatroskaVideoStereoModeType stereo_mode);
diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
index 697e14c30c..6c0d0d5c15 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
*/
@@ -32,19 +32,15 @@
#include <inttypes.h>
#include <stdio.h>
-#if CONFIG_BZLIB
-#include <bzlib.h>
-#endif
-#if CONFIG_ZLIB
-#include <zlib.h>
-#endif
#include "libavutil/avstring.h"
+#include "libavutil/base64.h"
#include "libavutil/dict.h"
#include "libavutil/intfloat.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/lzo.h"
#include "libavutil/mathematics.h"
+#include "libavutil/time_internal.h"
#include "libavcodec/bytestream.h"
#include "libavcodec/flac.h"
@@ -60,6 +56,13 @@
#include "riff.h"
#include "rmsipr.h"
+#if CONFIG_BZLIB
+#include <bzlib.h>
+#endif
+#if CONFIG_ZLIB
+#include <zlib.h>
+#endif
+
typedef enum {
EBML_NONE,
EBML_UINT,
@@ -68,8 +71,10 @@ typedef enum {
EBML_UTF8,
EBML_BIN,
EBML_NEST,
+ EBML_LEVEL1,
EBML_PASS,
EBML_STOP,
+ EBML_SINT,
EBML_TYPE_COUNT
} EbmlType;
@@ -110,10 +115,16 @@ typedef struct MatroskaTrackCompression {
EbmlBin settings;
} MatroskaTrackCompression;
+typedef struct MatroskaTrackEncryption {
+ uint64_t algo;
+ EbmlBin key_id;
+} MatroskaTrackEncryption;
+
typedef struct MatroskaTrackEncoding {
uint64_t scope;
uint64_t type;
MatroskaTrackCompression compression;
+ MatroskaTrackEncryption encryption;
} MatroskaTrackEncoding;
typedef struct MatroskaTrackVideo {
@@ -122,8 +133,9 @@ typedef struct MatroskaTrackVideo {
uint64_t display_height;
uint64_t pixel_width;
uint64_t pixel_height;
- uint64_t fourcc;
+ EbmlBin color_space;
uint64_t stereo_mode;
+ uint64_t alpha_mode;
} MatroskaTrackVideo;
typedef struct MatroskaTrackAudio {
@@ -143,6 +155,15 @@ typedef struct MatroskaTrackAudio {
uint8_t *buf;
} MatroskaTrackAudio;
+typedef struct MatroskaTrackPlane {
+ uint64_t uid;
+ uint64_t type;
+} MatroskaTrackPlane;
+
+typedef struct MatroskaTrackOperation {
+ EbmlList combine_planes;
+} MatroskaTrackOperation;
+
typedef struct MatroskaTrack {
uint64_t num;
uint64_t uid;
@@ -155,14 +176,17 @@ typedef struct MatroskaTrack {
uint64_t default_duration;
uint64_t flag_default;
uint64_t flag_forced;
+ uint64_t seek_preroll;
MatroskaTrackVideo video;
MatroskaTrackAudio audio;
+ MatroskaTrackOperation operation;
EbmlList encodings;
uint64_t codec_delay;
AVStream *stream;
int64_t end_timecode;
int ms_compat;
+ uint64_t max_block_additional_id;
} MatroskaTrack;
typedef struct MatroskaAttachment {
@@ -229,6 +253,12 @@ typedef struct MatroskaCluster {
EbmlList blocks;
} MatroskaCluster;
+typedef struct MatroskaLevel1Element {
+ uint64_t id;
+ uint64_t pos;
+ int parsed;
+} MatroskaLevel1Element;
+
typedef struct MatroskaDemuxContext {
AVFormatContext *ctx;
@@ -241,6 +271,8 @@ typedef struct MatroskaDemuxContext {
uint64_t time_scale;
double duration;
char *title;
+ char *muxingapp;
+ EbmlBin date_utc;
EbmlList tracks;
EbmlList attachments;
EbmlList chapters;
@@ -265,6 +297,10 @@ typedef struct MatroskaDemuxContext {
/* File has a CUES element, but we defer parsing until it is needed. */
int cues_parsing_deferred;
+ /* Level1 elements and whether they were read yet */
+ MatroskaLevel1Element level1_elems[64];
+ int num_level1_elems;
+
int current_cluster_num_blocks;
int64_t current_cluster_pos;
MatroskaCluster current_cluster;
@@ -278,6 +314,9 @@ typedef struct MatroskaBlock {
int64_t reference;
uint64_t non_simple;
EbmlBin bin;
+ uint64_t additional_id;
+ EbmlBin additional;
+ int64_t discard_padding;
} MatroskaBlock;
static EbmlSyntax ebml_header[] = {
@@ -301,19 +340,20 @@ static EbmlSyntax matroska_info[] = {
{ MATROSKA_ID_DURATION, EBML_FLOAT, 0, offsetof(MatroskaDemuxContext, duration) },
{ MATROSKA_ID_TITLE, EBML_UTF8, 0, offsetof(MatroskaDemuxContext, title) },
{ MATROSKA_ID_WRITINGAPP, EBML_NONE },
- { MATROSKA_ID_MUXINGAPP, EBML_NONE },
- { MATROSKA_ID_DATEUTC, EBML_NONE },
+ { MATROSKA_ID_MUXINGAPP, EBML_UTF8, 0, offsetof(MatroskaDemuxContext, muxingapp) },
+ { MATROSKA_ID_DATEUTC, EBML_BIN, 0, offsetof(MatroskaDemuxContext, date_utc) },
{ MATROSKA_ID_SEGMENTUID, EBML_NONE },
{ 0 }
};
static EbmlSyntax matroska_track_video[] = {
{ MATROSKA_ID_VIDEOFRAMERATE, EBML_FLOAT, 0, offsetof(MatroskaTrackVideo, frame_rate) },
- { MATROSKA_ID_VIDEODISPLAYWIDTH, EBML_UINT, 0, offsetof(MatroskaTrackVideo, display_width) },
- { MATROSKA_ID_VIDEODISPLAYHEIGHT, EBML_UINT, 0, offsetof(MatroskaTrackVideo, display_height) },
+ { MATROSKA_ID_VIDEODISPLAYWIDTH, EBML_UINT, 0, offsetof(MatroskaTrackVideo, display_width), { .u=-1 } },
+ { MATROSKA_ID_VIDEODISPLAYHEIGHT, EBML_UINT, 0, offsetof(MatroskaTrackVideo, display_height), { .u=-1 } },
{ MATROSKA_ID_VIDEOPIXELWIDTH, EBML_UINT, 0, offsetof(MatroskaTrackVideo, pixel_width) },
{ MATROSKA_ID_VIDEOPIXELHEIGHT, EBML_UINT, 0, offsetof(MatroskaTrackVideo, pixel_height) },
- { MATROSKA_ID_VIDEOCOLORSPACE, EBML_UINT, 0, offsetof(MatroskaTrackVideo, fourcc) },
+ { MATROSKA_ID_VIDEOCOLORSPACE, EBML_BIN, 0, offsetof(MatroskaTrackVideo, color_space) },
+ { MATROSKA_ID_VIDEOALPHAMODE, EBML_UINT, 0, offsetof(MatroskaTrackVideo, alpha_mode) },
{ MATROSKA_ID_VIDEOPIXELCROPB, EBML_NONE },
{ MATROSKA_ID_VIDEOPIXELCROPT, EBML_NONE },
{ MATROSKA_ID_VIDEOPIXELCROPL, EBML_NONE },
@@ -339,10 +379,21 @@ static EbmlSyntax matroska_track_encoding_compression[] = {
{ 0 }
};
+static EbmlSyntax matroska_track_encoding_encryption[] = {
+ { MATROSKA_ID_ENCODINGENCALGO, EBML_UINT, 0, offsetof(MatroskaTrackEncryption,algo), {.u = 0} },
+ { MATROSKA_ID_ENCODINGENCKEYID, EBML_BIN, 0, offsetof(MatroskaTrackEncryption,key_id) },
+ { MATROSKA_ID_ENCODINGENCAESSETTINGS, EBML_NONE },
+ { MATROSKA_ID_ENCODINGSIGALGO, EBML_NONE },
+ { MATROSKA_ID_ENCODINGSIGHASHALGO, EBML_NONE },
+ { MATROSKA_ID_ENCODINGSIGKEYID, EBML_NONE },
+ { MATROSKA_ID_ENCODINGSIGNATURE, EBML_NONE },
+ { 0 }
+};
static EbmlSyntax matroska_track_encoding[] = {
{ MATROSKA_ID_ENCODINGSCOPE, EBML_UINT, 0, offsetof(MatroskaTrackEncoding, scope), { .u = 1 } },
{ MATROSKA_ID_ENCODINGTYPE, EBML_UINT, 0, offsetof(MatroskaTrackEncoding, type), { .u = 0 } },
{ MATROSKA_ID_ENCODINGCOMPRESSION, EBML_NEST, 0, offsetof(MatroskaTrackEncoding, compression), { .n = matroska_track_encoding_compression } },
+ { MATROSKA_ID_ENCODINGENCRYPTION, EBML_NEST, 0, offsetof(MatroskaTrackEncoding, encryption), { .n = matroska_track_encoding_encryption } },
{ MATROSKA_ID_ENCODINGORDER, EBML_NONE },
{ 0 }
};
@@ -352,6 +403,22 @@ static EbmlSyntax matroska_track_encodings[] = {
{ 0 }
};
+static EbmlSyntax matroska_track_plane[] = {
+ { MATROSKA_ID_TRACKPLANEUID, EBML_UINT, 0, offsetof(MatroskaTrackPlane,uid) },
+ { MATROSKA_ID_TRACKPLANETYPE, EBML_UINT, 0, offsetof(MatroskaTrackPlane,type) },
+ { 0 }
+};
+
+static EbmlSyntax matroska_track_combine_planes[] = {
+ { MATROSKA_ID_TRACKPLANE, EBML_NEST, sizeof(MatroskaTrackPlane), offsetof(MatroskaTrackOperation,combine_planes), {.n = matroska_track_plane} },
+ { 0 }
+};
+
+static EbmlSyntax matroska_track_operation[] = {
+ { MATROSKA_ID_TRACKCOMBINEPLANES, EBML_NEST, 0, 0, {.n = matroska_track_combine_planes} },
+ { 0 }
+};
+
static EbmlSyntax matroska_track[] = {
{ MATROSKA_ID_TRACKNUMBER, EBML_UINT, 0, offsetof(MatroskaTrack, num) },
{ MATROSKA_ID_TRACKNAME, EBML_UTF8, 0, offsetof(MatroskaTrack, name) },
@@ -360,14 +427,17 @@ static EbmlSyntax matroska_track[] = {
{ MATROSKA_ID_CODECID, EBML_STR, 0, offsetof(MatroskaTrack, codec_id) },
{ MATROSKA_ID_CODECPRIVATE, EBML_BIN, 0, offsetof(MatroskaTrack, codec_priv) },
{ MATROSKA_ID_CODECDELAY, EBML_UINT, 0, offsetof(MatroskaTrack, codec_delay) },
- { MATROSKA_ID_TRACKLANGUAGE, EBML_UTF8, 0, offsetof(MatroskaTrack, language), { .s = "eng" } },
+ { MATROSKA_ID_TRACKLANGUAGE, EBML_UTF8, 0, offsetof(MatroskaTrack, language), { .s = "eng" } },
{ MATROSKA_ID_TRACKDEFAULTDURATION, EBML_UINT, 0, offsetof(MatroskaTrack, default_duration) },
- { MATROSKA_ID_TRACKTIMECODESCALE, EBML_FLOAT, 0, offsetof(MatroskaTrack, time_scale), { .f = 1.0 } },
- { MATROSKA_ID_TRACKFLAGDEFAULT, EBML_UINT, 0, offsetof(MatroskaTrack, flag_default), { .u = 1 } },
- { MATROSKA_ID_TRACKFLAGFORCED, EBML_UINT, 0, offsetof(MatroskaTrack, flag_forced), { .u = 0 } },
+ { MATROSKA_ID_TRACKTIMECODESCALE, EBML_FLOAT, 0, offsetof(MatroskaTrack, time_scale), { .f = 1.0 } },
+ { MATROSKA_ID_TRACKFLAGDEFAULT, EBML_UINT, 0, offsetof(MatroskaTrack, flag_default), { .u = 1 } },
+ { MATROSKA_ID_TRACKFLAGFORCED, EBML_UINT, 0, offsetof(MatroskaTrack, flag_forced), { .u = 0 } },
{ MATROSKA_ID_TRACKVIDEO, EBML_NEST, 0, offsetof(MatroskaTrack, video), { .n = matroska_track_video } },
{ MATROSKA_ID_TRACKAUDIO, EBML_NEST, 0, offsetof(MatroskaTrack, audio), { .n = matroska_track_audio } },
+ { MATROSKA_ID_TRACKOPERATION, EBML_NEST, 0, offsetof(MatroskaTrack, operation), { .n = matroska_track_operation } },
{ MATROSKA_ID_TRACKCONTENTENCODINGS, EBML_NEST, 0, 0, { .n = matroska_track_encodings } },
+ { MATROSKA_ID_TRACKMAXBLKADDID, EBML_UINT, 0, offsetof(MatroskaTrack, max_block_additional_id) },
+ { MATROSKA_ID_SEEKPREROLL, EBML_UINT, 0, offsetof(MatroskaTrack, seek_preroll) },
{ MATROSKA_ID_TRACKFLAGENABLED, EBML_NONE },
{ MATROSKA_ID_TRACKFLAGLACING, EBML_NONE },
{ MATROSKA_ID_CODECNAME, EBML_NONE },
@@ -376,7 +446,6 @@ static EbmlSyntax matroska_track[] = {
{ MATROSKA_ID_CODECDOWNLOADURL, EBML_NONE },
{ MATROSKA_ID_TRACKMINCACHE, EBML_NONE },
{ MATROSKA_ID_TRACKMAXCACHE, EBML_NONE },
- { MATROSKA_ID_TRACKMAXBLKADDID, EBML_NONE },
{ 0 }
};
@@ -434,6 +503,8 @@ static EbmlSyntax matroska_chapters[] = {
static EbmlSyntax matroska_index_pos[] = {
{ MATROSKA_ID_CUETRACK, EBML_UINT, 0, offsetof(MatroskaIndexPos, track) },
{ MATROSKA_ID_CUECLUSTERPOSITION, EBML_UINT, 0, offsetof(MatroskaIndexPos, pos) },
+ { MATROSKA_ID_CUERELATIVEPOSITION,EBML_NONE },
+ { MATROSKA_ID_CUEDURATION, EBML_NONE },
{ MATROSKA_ID_CUEBLOCKNUMBER, EBML_NONE },
{ 0 }
};
@@ -491,13 +562,13 @@ static EbmlSyntax matroska_seekhead[] = {
};
static EbmlSyntax matroska_segment[] = {
- { MATROSKA_ID_INFO, EBML_NEST, 0, 0, { .n = matroska_info } },
- { MATROSKA_ID_TRACKS, EBML_NEST, 0, 0, { .n = matroska_tracks } },
- { MATROSKA_ID_ATTACHMENTS, EBML_NEST, 0, 0, { .n = matroska_attachments } },
- { MATROSKA_ID_CHAPTERS, EBML_NEST, 0, 0, { .n = matroska_chapters } },
- { MATROSKA_ID_CUES, EBML_NEST, 0, 0, { .n = matroska_index } },
- { MATROSKA_ID_TAGS, EBML_NEST, 0, 0, { .n = matroska_tags } },
- { MATROSKA_ID_SEEKHEAD, EBML_NEST, 0, 0, { .n = matroska_seekhead } },
+ { MATROSKA_ID_INFO, EBML_LEVEL1, 0, 0, { .n = matroska_info } },
+ { MATROSKA_ID_TRACKS, EBML_LEVEL1, 0, 0, { .n = matroska_tracks } },
+ { MATROSKA_ID_ATTACHMENTS, EBML_LEVEL1, 0, 0, { .n = matroska_attachments } },
+ { MATROSKA_ID_CHAPTERS, EBML_LEVEL1, 0, 0, { .n = matroska_chapters } },
+ { MATROSKA_ID_CUES, EBML_LEVEL1, 0, 0, { .n = matroska_index } },
+ { MATROSKA_ID_TAGS, EBML_LEVEL1, 0, 0, { .n = matroska_tags } },
+ { MATROSKA_ID_SEEKHEAD, EBML_LEVEL1, 0, 0, { .n = matroska_seekhead } },
{ MATROSKA_ID_CLUSTER, EBML_STOP },
{ 0 }
};
@@ -507,11 +578,24 @@ static EbmlSyntax matroska_segments[] = {
{ 0 }
};
+static EbmlSyntax matroska_blockmore[] = {
+ { MATROSKA_ID_BLOCKADDID, EBML_UINT, 0, offsetof(MatroskaBlock,additional_id) },
+ { MATROSKA_ID_BLOCKADDITIONAL, EBML_BIN, 0, offsetof(MatroskaBlock,additional) },
+ { 0 }
+};
+
+static EbmlSyntax matroska_blockadditions[] = {
+ { MATROSKA_ID_BLOCKMORE, EBML_NEST, 0, 0, {.n = matroska_blockmore} },
+ { 0 }
+};
+
static EbmlSyntax matroska_blockgroup[] = {
{ MATROSKA_ID_BLOCK, EBML_BIN, 0, offsetof(MatroskaBlock, bin) },
+ { MATROSKA_ID_BLOCKADDITIONS, EBML_NEST, 0, 0, { .n = matroska_blockadditions} },
{ MATROSKA_ID_SIMPLEBLOCK, EBML_BIN, 0, offsetof(MatroskaBlock, bin) },
- { MATROSKA_ID_BLOCKDURATION, EBML_UINT, 0, offsetof(MatroskaBlock, duration), { .u = AV_NOPTS_VALUE } },
- { MATROSKA_ID_BLOCKREFERENCE, EBML_UINT, 0, offsetof(MatroskaBlock, reference) },
+ { MATROSKA_ID_BLOCKDURATION, EBML_UINT, 0, offsetof(MatroskaBlock, duration) },
+ { MATROSKA_ID_DISCARDPADDING, EBML_SINT, 0, offsetof(MatroskaBlock, discard_padding) },
+ { MATROSKA_ID_BLOCKREFERENCE, EBML_SINT, 0, offsetof(MatroskaBlock, reference) },
{ MATROSKA_ID_CODECSTATE, EBML_NONE },
{ 1, EBML_UINT, 0, offsetof(MatroskaBlock, non_simple), { .u = 1 } },
{ 0 }
@@ -583,7 +667,7 @@ static int matroska_resync(MatroskaDemuxContext *matroska, int64_t last_pos)
id = avio_rb32(pb);
// try to find a toplevel element
- while (!pb->eof_reached) {
+ while (!avio_feof(pb)) {
if (id == MATROSKA_ID_INFO || id == MATROSKA_ID_TRACKS ||
id == MATROSKA_ID_CUES || id == MATROSKA_ID_TAGS ||
id == MATROSKA_ID_SEEKHEAD || id == MATROSKA_ID_ATTACHMENTS ||
@@ -636,7 +720,7 @@ static int ebml_read_num(MatroskaDemuxContext *matroska, AVIOContext *pb,
* use it safely here to catch EOS. */
if (!(total = avio_r8(pb))) {
/* we might encounter EOS here */
- if (!pb->eof_reached) {
+ if (!avio_feof(pb)) {
int64_t pos = avio_tell(pb);
av_log(matroska->ctx, AV_LOG_ERROR,
"Read error at pos. %"PRIu64" (0x%"PRIx64")\n",
@@ -700,6 +784,30 @@ static int ebml_read_uint(AVIOContext *pb, int size, uint64_t *num)
}
/*
+ * Read the next element as a signed int.
+ * 0 is success, < 0 is failure.
+ */
+static int ebml_read_sint(AVIOContext *pb, int size, int64_t *num)
+{
+ int n = 1;
+
+ if (size > 8)
+ return AVERROR_INVALIDDATA;
+
+ if (size == 0) {
+ *num = 0;
+ } else {
+ *num = sign_extend(avio_r8(pb), 8);
+
+ /* big-endian ordering; build up number */
+ while (n++ < size)
+ *num = (*num << 8) | avio_r8(pb);
+ }
+
+ return 0;
+}
+
+/*
* Read the next element as a float.
* 0 is success, < 0 is failure.
*/
@@ -746,14 +854,15 @@ static int ebml_read_ascii(AVIOContext *pb, int size, char **str)
*/
static int ebml_read_binary(AVIOContext *pb, int length, EbmlBin *bin)
{
- av_free(bin->data);
- if (!(bin->data = av_mallocz(length + FF_INPUT_BUFFER_PADDING_SIZE)))
+ av_fast_padded_malloc(&bin->data, &bin->size, length);
+ if (!bin->data)
return AVERROR(ENOMEM);
bin->size = length;
bin->pos = avio_tell(pb);
if (avio_read(pb, bin->data, length) != length) {
av_freep(&bin->data);
+ bin->size = 0;
return AVERROR(EIO);
}
@@ -829,9 +938,7 @@ static int ebml_parse_id(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
matroska->levels[matroska->num_levels - 1].length == 0xffffffffffffff)
return 0; // we reached the end of an unknown size cluster
if (!syntax[i].id && id != EBML_ID_VOID && id != EBML_ID_CRC32) {
- av_log(matroska->ctx, AV_LOG_INFO, "Unknown entry 0x%"PRIX32"\n", id);
- if (matroska->ctx->error_recognition & AV_EF_EXPLODE)
- return AVERROR_INVALIDDATA;
+ av_log(matroska->ctx, AV_LOG_DEBUG, "Unknown entry 0x%"PRIX32"\n", id);
}
return ebml_parse_elem(matroska, &syntax[i], data);
}
@@ -880,6 +987,42 @@ static int ebml_parse_nest(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
return res;
}
+/*
+ * Allocate and return the entry for the level1 element with the given ID. If
+ * an entry already exists, return the existing entry.
+ */
+static MatroskaLevel1Element *matroska_find_level1_elem(MatroskaDemuxContext *matroska,
+ uint32_t id)
+{
+ int i;
+ MatroskaLevel1Element *elem;
+
+ // Some files link to all clusters; useless.
+ if (id == MATROSKA_ID_CLUSTER)
+ return NULL;
+
+ // There can be multiple seekheads.
+ if (id != MATROSKA_ID_SEEKHEAD) {
+ for (i = 0; i < matroska->num_level1_elems; i++) {
+ if (matroska->level1_elems[i].id == id)
+ return &matroska->level1_elems[i];
+ }
+ }
+
+ // Only a completely broken file would have more elements.
+ // It also provides a low-effort way to escape from circular seekheads
+ // (every iteration will add a level1 entry).
+ if (matroska->num_level1_elems >= FF_ARRAY_ELEMS(matroska->level1_elems)) {
+ av_log(matroska->ctx, AV_LOG_ERROR, "Too many level1 elements or circular seekheads.\n");
+ return NULL;
+ }
+
+ elem = &matroska->level1_elems[matroska->num_level1_elems++];
+ *elem = (MatroskaLevel1Element){.id = id};
+
+ return elem;
+}
+
static int ebml_parse_elem(MatroskaDemuxContext *matroska,
EbmlSyntax *syntax, void *data)
{
@@ -897,16 +1040,16 @@ static int ebml_parse_elem(MatroskaDemuxContext *matroska,
uint32_t id = syntax->id;
uint64_t length;
int res;
+ void *newelem;
+ MatroskaLevel1Element *level1_elem;
data = (char *) data + syntax->data_offset;
if (syntax->list_elem_size) {
EbmlList *list = data;
- if ((res = av_reallocp_array(&list->elem,
- list->nb_elem + 1,
- syntax->list_elem_size)) < 0) {
- list->nb_elem = 0;
- return res;
- }
+ newelem = av_realloc_array(list->elem, list->nb_elem + 1, syntax->list_elem_size);
+ if (!newelem)
+ return AVERROR(ENOMEM);
+ list->elem = newelem;
data = (char *) list->elem + list->nb_elem * syntax->list_elem_size;
memset(data, 0, syntax->list_elem_size);
list->nb_elem++;
@@ -928,6 +1071,9 @@ static int ebml_parse_elem(MatroskaDemuxContext *matroska,
case EBML_UINT:
res = ebml_read_uint(pb, length, data);
break;
+ case EBML_SINT:
+ res = ebml_read_sint(pb, length, data);
+ break;
case EBML_FLOAT:
res = ebml_read_float(pb, length, data);
break;
@@ -938,17 +1084,28 @@ static int ebml_parse_elem(MatroskaDemuxContext *matroska,
case EBML_BIN:
res = ebml_read_binary(pb, length, data);
break;
+ case EBML_LEVEL1:
case EBML_NEST:
if ((res = ebml_read_master(matroska, length)) < 0)
return res;
if (id == MATROSKA_ID_SEGMENT)
matroska->segment_start = avio_tell(matroska->ctx->pb);
+ if (id == MATROSKA_ID_CUES)
+ matroska->cues_parsing_deferred = 0;
+ if (syntax->type == EBML_LEVEL1 &&
+ (level1_elem = matroska_find_level1_elem(matroska, syntax->id))) {
+ if (level1_elem->parsed)
+ av_log(matroska->ctx, AV_LOG_ERROR, "Duplicate element\n");
+ level1_elem->parsed = 1;
+ }
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:
+ if (ffio_limit(pb, length) != length)
+ return AVERROR(EIO);
return avio_skip(pb, length) < 0 ? AVERROR(EIO) : 0;
}
if (res == AVERROR_INVALIDDATA)
@@ -971,6 +1128,7 @@ static void ebml_free(EbmlSyntax *syntax, void *data)
case EBML_BIN:
av_freep(&((EbmlBin *) data_off)->data);
break;
+ case EBML_LEVEL1:
case EBML_NEST:
if (syntax[i].list_elem_size) {
EbmlList *list = data_off;
@@ -978,7 +1136,7 @@ static void ebml_free(EbmlSyntax *syntax, void *data)
for (j = 0; j < list->nb_elem;
j++, ptr += syntax[i].list_elem_size)
ebml_free(syntax[i].def.n, ptr);
- av_free(list->elem);
+ av_freep(&list->elem);
} else
ebml_free(syntax[i].def.n, data_off);
default:
@@ -1058,7 +1216,7 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size,
int result = 0;
int olen;
- if (pkt_size >= 10000000)
+ if (pkt_size >= 10000000U)
return AVERROR_INVALIDDATA;
switch (encodings[0].compression.algo) {
@@ -1067,6 +1225,11 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size,
int header_size = encodings[0].compression.settings.size;
uint8_t *header = encodings[0].compression.settings.data;
+ if (header_size && !header) {
+ av_log(NULL, AV_LOG_ERROR, "Compression size but no data in headerstrip\n");
+ return -1;
+ }
+
if (!header_size)
return 0;
@@ -1116,7 +1279,10 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size,
pkt_data = newpktdata;
zstream.avail_out = pkt_size - zstream.total_out;
zstream.next_out = pkt_data + zstream.total_out;
- result = inflate(&zstream, Z_NO_FLUSH);
+ if (pkt_data) {
+ result = inflate(&zstream, Z_NO_FLUSH);
+ } else
+ result = Z_MEM_ERROR;
} while (result == Z_OK && pkt_size < 10000000);
pkt_size = zstream.total_out;
inflateEnd(&zstream);
@@ -1148,7 +1314,10 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size,
pkt_data = newpktdata;
bzstream.avail_out = pkt_size - bzstream.total_out_lo32;
bzstream.next_out = pkt_data + bzstream.total_out_lo32;
- result = BZ2_bzDecompress(&bzstream);
+ if (pkt_data) {
+ result = BZ2_bzDecompress(&bzstream);
+ } else
+ result = BZ_MEM_ERROR;
} while (result == BZ_OK && pkt_size < 10000000);
pkt_size = bzstream.total_out_lo32;
BZ2_bzDecompressEnd(&bzstream);
@@ -1175,63 +1344,6 @@ failed:
return result;
}
-static void matroska_fix_ass_packet(MatroskaDemuxContext *matroska,
- AVPacket *pkt, uint64_t display_duration)
-{
- AVBufferRef *line;
- char *layer, *ptr = pkt->data, *end = ptr + pkt->size;
-
- for (; *ptr != ',' && ptr < end - 1; ptr++)
- ;
- if (*ptr == ',')
- layer = ++ptr;
- for (; *ptr != ',' && ptr < end - 1; ptr++)
- ;
- if (*ptr == ',') {
- int64_t end_pts = pkt->pts + display_duration;
- int sc = matroska->time_scale * pkt->pts / 10000000;
- int ec = matroska->time_scale * end_pts / 10000000;
- int sh, sm, ss, eh, em, es, len;
- sh = sc / 360000;
- sc -= 360000 * sh;
- sm = sc / 6000;
- sc -= 6000 * sm;
- ss = sc / 100;
- sc -= 100 * ss;
- eh = ec / 360000;
- ec -= 360000 * eh;
- em = ec / 6000;
- ec -= 6000 * em;
- es = ec / 100;
- ec -= 100 * es;
- *ptr++ = '\0';
- len = 50 + end - ptr + FF_INPUT_BUFFER_PADDING_SIZE;
- if (!(line = av_buffer_alloc(len)))
- return;
- snprintf(line->data, len,
- "Dialogue: %s,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s\r\n",
- layer, sh, sm, ss, sc, eh, em, es, ec, ptr);
- av_buffer_unref(&pkt->buf);
- pkt->buf = line;
- pkt->data = line->data;
- pkt->size = strlen(line->data);
- }
-}
-
-static int matroska_merge_packets(AVPacket *out, AVPacket *in)
-{
- int old_size = out->size;
- int ret = av_grow_packet(out, in->size);
- if (ret < 0)
- return ret;
-
- memcpy(out->data + old_size, in->data, in->size);
-
- av_free_packet(in);
- av_free(in);
- return 0;
-}
-
static void matroska_convert_tag(AVFormatContext *s, EbmlList *list,
AVDictionary **metadata, char *prefix)
{
@@ -1302,24 +1414,17 @@ static void matroska_convert_tags(AVFormatContext *s)
}
static int matroska_parse_seekhead_entry(MatroskaDemuxContext *matroska,
- int idx)
+ uint64_t pos)
{
- EbmlList *seekhead_list = &matroska->seekhead;
uint32_t level_up = matroska->level_up;
uint32_t saved_id = matroska->current_id;
- MatroskaSeekhead *seekhead = seekhead_list->elem;
int64_t before_pos = avio_tell(matroska->ctx->pb);
MatroskaLevel level;
int64_t offset;
int ret = 0;
- if (idx >= seekhead_list->nb_elem ||
- seekhead[idx].id == MATROSKA_ID_SEEKHEAD ||
- seekhead[idx].id == MATROSKA_ID_CLUSTER)
- return 0;
-
/* seek */
- offset = seekhead[idx].pos + matroska->segment_start;
+ offset = pos + matroska->segment_start;
if (avio_seek(matroska->ctx->pb, offset, SEEK_SET) == offset) {
/* We don't want to lose our seekhead level, so we add
* a dummy. This is a crude hack. */
@@ -1356,45 +1461,46 @@ static int matroska_parse_seekhead_entry(MatroskaDemuxContext *matroska,
static void matroska_execute_seekhead(MatroskaDemuxContext *matroska)
{
EbmlList *seekhead_list = &matroska->seekhead;
- int64_t before_pos = avio_tell(matroska->ctx->pb);
int i;
// we should not do any seeking in the streaming case
- if (!matroska->ctx->pb->seekable ||
- (matroska->ctx->flags & AVFMT_FLAG_IGNIDX))
+ if (!matroska->ctx->pb->seekable)
return;
for (i = 0; i < seekhead_list->nb_elem; i++) {
- MatroskaSeekhead *seekhead = seekhead_list->elem;
- if (seekhead[i].pos <= before_pos)
+ MatroskaSeekhead *seekheads = seekhead_list->elem;
+ uint32_t id = seekheads[i].id;
+ uint64_t pos = seekheads[i].pos;
+
+ MatroskaLevel1Element *elem = matroska_find_level1_elem(matroska, id);
+ if (!elem || elem->parsed)
continue;
+ elem->pos = pos;
+
// defer cues parsing until we actually need cue data.
- if (seekhead[i].id == MATROSKA_ID_CUES) {
- matroska->cues_parsing_deferred = 1;
+ if (id == MATROSKA_ID_CUES)
continue;
- }
- if (matroska_parse_seekhead_entry(matroska, i) < 0)
+ if (matroska_parse_seekhead_entry(matroska, pos) < 0) {
+ // mark index as broken
+ matroska->cues_parsing_deferred = -1;
break;
+ }
+
+ elem->parsed = 1;
}
}
-static void matroska_parse_cues(MatroskaDemuxContext *matroska)
+static void matroska_add_index_entries(MatroskaDemuxContext *matroska)
{
- EbmlList *seekhead_list = &matroska->seekhead;
- MatroskaSeekhead *seekhead = seekhead_list->elem;
EbmlList *index_list;
MatroskaIndex *index;
int index_scale = 1;
int i, j;
- for (i = 0; i < seekhead_list->nb_elem; i++)
- if (seekhead[i].id == MATROSKA_ID_CUES)
- break;
- assert(i <= seekhead_list->nb_elem);
-
- matroska_parse_seekhead_entry(matroska, i);
+ if (matroska->ctx->flags & AVFMT_FLAG_IGNIDX)
+ return;
index_list = &matroska->index;
index = index_list->elem;
@@ -1418,6 +1524,25 @@ static void matroska_parse_cues(MatroskaDemuxContext *matroska)
}
}
+static void matroska_parse_cues(MatroskaDemuxContext *matroska) {
+ int i;
+
+ if (matroska->ctx->flags & AVFMT_FLAG_IGNIDX)
+ return;
+
+ for (i = 0; i < matroska->num_level1_elems; i++) {
+ MatroskaLevel1Element *elem = &matroska->level1_elems[i];
+ if (elem->id == MATROSKA_ID_CUES && !elem->parsed) {
+ if (matroska_parse_seekhead_entry(matroska, elem->pos) < 0)
+ matroska->cues_parsing_deferred = -1;
+ elem->parsed = 1;
+ break;
+ }
+ }
+
+ matroska_add_index_entries(matroska);
+}
+
static int matroska_aac_profile(char *codec_id)
{
static const char *const aac_profiles[] = { "MAIN", "LC", "SSR" };
@@ -1439,6 +1564,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 tmpbuf, *ptm = gmtime_r(&creation_time, &tmpbuf);
+ if (!ptm) return;
+ if (strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", ptm))
+ av_dict_set(metadata, "creation_time", buffer, 0);
+}
+
static int matroska_parse_flac(AVFormatContext *s,
MatroskaTrack *track,
int *offset)
@@ -1500,6 +1636,7 @@ static int matroska_parse_tracks(AVFormatContext *s)
MatroskaTrack *tracks = matroska->tracks.elem;
AVStream *st;
int i, j, ret;
+ int k;
for (i = 0; i < matroska->tracks.nb_elem; i++) {
MatroskaTrack *track = &tracks[i];
@@ -1509,12 +1646,16 @@ static int matroska_parse_tracks(AVFormatContext *s)
uint8_t *extradata = NULL;
int extradata_size = 0;
int extradata_offset = 0;
+ uint32_t fourcc = 0;
AVIOContext b;
+ char* key_id_base64 = NULL;
+ int bit_depth = -1;
/* Apply some sanity checks. */
if (track->type != MATROSKA_TRACK_TYPE_VIDEO &&
track->type != MATROSKA_TRACK_TYPE_AUDIO &&
- track->type != MATROSKA_TRACK_TYPE_SUBTITLE) {
+ track->type != MATROSKA_TRACK_TYPE_SUBTITLE &&
+ track->type != MATROSKA_TRACK_TYPE_METADATA) {
av_log(matroska->ctx, AV_LOG_INFO,
"Unknown or unsupported track type %"PRIu64"\n",
track->type);
@@ -1526,10 +1667,12 @@ static int matroska_parse_tracks(AVFormatContext *s)
if (track->type == MATROSKA_TRACK_TYPE_VIDEO) {
if (!track->default_duration && track->video.frame_rate > 0)
track->default_duration = 1000000000 / track->video.frame_rate;
- if (!track->video.display_width)
+ if (track->video.display_width == -1)
track->video.display_width = track->video.pixel_width;
- if (!track->video.display_height)
+ if (track->video.display_height == -1)
track->video.display_height = track->video.pixel_height;
+ if (track->video.color_space.size == 4)
+ fourcc = AV_RL32(track->video.color_space.data);
} else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) {
if (!track->audio.out_samplerate)
track->audio.out_samplerate = track->audio.samplerate;
@@ -1538,8 +1681,24 @@ static int matroska_parse_tracks(AVFormatContext *s)
av_log(matroska->ctx, AV_LOG_ERROR,
"Multiple combined encodings not supported");
} else if (encodings_list->nb_elem == 1) {
- if (encodings[0].type ||
- (
+ if (encodings[0].type) {
+ if (encodings[0].encryption.key_id.size > 0) {
+ /* Save the encryption key id to be stored later as a
+ metadata tag. */
+ const int b64_size = AV_BASE64_SIZE(encodings[0].encryption.key_id.size);
+ key_id_base64 = av_malloc(b64_size);
+ if (key_id_base64 == NULL)
+ return AVERROR(ENOMEM);
+
+ av_base64_encode(key_id_base64, b64_size,
+ encodings[0].encryption.key_id.data,
+ encodings[0].encryption.key_id.size);
+ } else {
+ encodings[0].scope = 0;
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "Unsupported encoding type");
+ }
+ } else if (
#if CONFIG_ZLIB
encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_ZLIB &&
#endif
@@ -1549,7 +1708,7 @@ static int matroska_parse_tracks(AVFormatContext *s)
#if CONFIG_LZO
encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_LZO &&
#endif
- encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP)) {
+ encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP) {
encodings[0].scope = 0;
av_log(matroska->ctx, AV_LOG_ERROR,
"Unsupported encoding type");
@@ -1579,16 +1738,28 @@ static int matroska_parse_tracks(AVFormatContext *s)
}
st = track->stream = avformat_new_stream(s, NULL);
- if (!st)
+ if (!st) {
+ av_free(key_id_base64);
return AVERROR(ENOMEM);
+ }
+
+ if (key_id_base64) {
+ /* export encryption key id as base64 metadata tag */
+ av_dict_set(&st->metadata, "enc_key_id", key_id_base64, 0);
+ av_freep(&key_id_base64);
+ }
if (!strcmp(track->codec_id, "V_MS/VFW/FOURCC") &&
- track->codec_priv.size >= 40 &&
+ track->codec_priv.size >= 40 &&
track->codec_priv.data) {
track->ms_compat = 1;
- track->video.fourcc = AV_RL32(track->codec_priv.data + 16);
+ bit_depth = AV_RL16(track->codec_priv.data + 14);
+ fourcc = AV_RL32(track->codec_priv.data + 16);
codec_id = ff_codec_get_id(ff_codec_bmp_tags,
- track->video.fourcc);
+ fourcc);
+ if (!codec_id)
+ codec_id = ff_codec_get_id(ff_codec_movvideo_tags,
+ fourcc);
extradata_offset = 40;
} else if (!strcmp(track->codec_id, "A_MS/ACM") &&
track->codec_priv.size >= 14 &&
@@ -1597,17 +1768,31 @@ static int matroska_parse_tracks(AVFormatContext *s)
ffio_init_context(&b, track->codec_priv.data,
track->codec_priv.size,
0, NULL, NULL, NULL, NULL);
- ret = ff_get_wav_header(&b, st->codec, track->codec_priv.size);
+ ret = ff_get_wav_header(&b, st->codec, track->codec_priv.size, 0);
if (ret < 0)
return ret;
codec_id = st->codec->codec_id;
extradata_offset = FFMIN(track->codec_priv.size, 18);
+ } else if (!strcmp(track->codec_id, "A_QUICKTIME")
+ && (track->codec_priv.size >= 86)
+ && (track->codec_priv.data)) {
+ fourcc = AV_RL32(track->codec_priv.data + 4);
+ codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc);
+ if (ff_codec_get_id(ff_codec_movaudio_tags, AV_RL32(track->codec_priv.data))) {
+ fourcc = AV_RL32(track->codec_priv.data);
+ codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc);
+ }
} else if (!strcmp(track->codec_id, "V_QUICKTIME") &&
- (track->codec_priv.size >= 86) &&
+ (track->codec_priv.size >= 21) &&
(track->codec_priv.data)) {
- track->video.fourcc = AV_RL32(track->codec_priv.data);
- codec_id = ff_codec_get_id(ff_codec_movvideo_tags,
- track->video.fourcc);
+ fourcc = AV_RL32(track->codec_priv.data + 4);
+ codec_id = ff_codec_get_id(ff_codec_movvideo_tags, fourcc);
+ if (ff_codec_get_id(ff_codec_movvideo_tags, AV_RL32(track->codec_priv.data))) {
+ fourcc = AV_RL32(track->codec_priv.data);
+ codec_id = ff_codec_get_id(ff_codec_movvideo_tags, fourcc);
+ }
+ if (codec_id == AV_CODEC_ID_NONE && AV_RL32(track->codec_priv.data+4) == AV_RL32("SMI "))
+ codec_id = AV_CODEC_ID_SVQ3;
} else if (codec_id == AV_CODEC_ID_PCM_S16BE) {
switch (track->audio.bitdepth) {
case 8:
@@ -1651,7 +1836,7 @@ static int matroska_parse_tracks(AVFormatContext *s)
extradata_size = 5;
} else
extradata_size = 2;
- } else if (codec_id == AV_CODEC_ID_ALAC && track->codec_priv.size) {
+ } else if (codec_id == AV_CODEC_ID_ALAC && track->codec_priv.size && track->codec_priv.size < INT_MAX - 12 - FF_INPUT_BUFFER_PADDING_SIZE) {
/* Only ALAC's magic cookie is stored in Matroska's track headers.
* Create the "atom size", "tag", and "tag version" fields the
* decoder expects manually. */
@@ -1667,7 +1852,7 @@ static int matroska_parse_tracks(AVFormatContext *s)
track->codec_priv.size);
} else if (codec_id == AV_CODEC_ID_TTA) {
extradata_size = 30;
- extradata = av_mallocz(extradata_size);
+ extradata = av_mallocz(extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
if (!extradata)
return AVERROR(ENOMEM);
ffio_init_context(&b, extradata, extradata_size, 1,
@@ -1676,9 +1861,12 @@ static int matroska_parse_tracks(AVFormatContext *s)
avio_wl16(&b, 1);
avio_wl16(&b, track->audio.channels);
avio_wl16(&b, track->audio.bitdepth);
+ if (track->audio.out_samplerate < 0 || track->audio.out_samplerate > INT_MAX)
+ return AVERROR_INVALIDDATA;
avio_wl32(&b, track->audio.out_samplerate);
- avio_wl32(&b, matroska->ctx->duration *
- track->audio.out_samplerate);
+ avio_wl32(&b, av_rescale((matroska->duration * matroska->time_scale),
+ track->audio.out_samplerate,
+ AV_TIME_BASE * 1000));
} else if (codec_id == AV_CODEC_ID_RV10 ||
codec_id == AV_CODEC_ID_RV20 ||
codec_id == AV_CODEC_ID_RV30 ||
@@ -1687,11 +1875,13 @@ static int matroska_parse_tracks(AVFormatContext *s)
} else if (codec_id == AV_CODEC_ID_RA_144) {
track->audio.out_samplerate = 8000;
track->audio.channels = 1;
- } else if (codec_id == AV_CODEC_ID_RA_288 ||
- codec_id == AV_CODEC_ID_COOK ||
- codec_id == AV_CODEC_ID_ATRAC3 ||
- codec_id == AV_CODEC_ID_SIPR) {
+ } else if ((codec_id == AV_CODEC_ID_RA_288 ||
+ codec_id == AV_CODEC_ID_COOK ||
+ codec_id == AV_CODEC_ID_ATRAC3 ||
+ codec_id == AV_CODEC_ID_SIPR)
+ && track->codec_priv.data) {
int flavor;
+
ffio_init_context(&b, track->codec_priv.data,
track->codec_priv.size,
0, NULL, NULL, NULL, NULL);
@@ -1702,20 +1892,22 @@ static int matroska_parse_tracks(AVFormatContext *s)
track->audio.sub_packet_h = avio_rb16(&b);
track->audio.frame_size = avio_rb16(&b);
track->audio.sub_packet_size = avio_rb16(&b);
- if (flavor <= 0 ||
+ if (flavor < 0 ||
track->audio.coded_framesize <= 0 ||
track->audio.sub_packet_h <= 0 ||
track->audio.frame_size <= 0 ||
track->audio.sub_packet_size <= 0)
return AVERROR_INVALIDDATA;
- track->audio.buf = av_malloc(track->audio.frame_size *
- track->audio.sub_packet_h);
+ track->audio.buf = av_malloc_array(track->audio.sub_packet_h,
+ track->audio.frame_size);
+ if (!track->audio.buf)
+ return AVERROR(ENOMEM);
if (codec_id == AV_CODEC_ID_RA_288) {
st->codec->block_align = track->audio.coded_framesize;
track->codec_priv.size = 0;
} else {
if (codec_id == AV_CODEC_ID_SIPR && flavor < 4) {
- const int sipr_bit_rate[4] = { 6504, 8496, 5000, 16000 };
+ static const int sipr_bit_rate[4] = { 6504, 8496, 5000, 16000 };
track->audio.sub_packet_size = ff_sipr_subpk_size[flavor];
st->codec->bit_rate = sipr_bit_rate[flavor];
}
@@ -1726,6 +1918,8 @@ static int matroska_parse_tracks(AVFormatContext *s)
ret = matroska_parse_flac(s, track, &extradata_offset);
if (ret < 0)
return ret;
+ } else if (codec_id == AV_CODEC_ID_PRORES && track->codec_priv.size == 4) {
+ fourcc = AV_RL32(track->codec_priv.data);
}
track->codec_priv.size -= extradata_offset;
@@ -1744,7 +1938,7 @@ static int matroska_parse_tracks(AVFormatContext *s)
st->time_base);
st->codec->codec_id = codec_id;
- st->start_time = 0;
+
if (strcmp(track->language, "und"))
av_dict_set(&st->metadata, "language", track->language, 0);
av_dict_set(&st->metadata, "title", track->name, 0);
@@ -1759,11 +1953,8 @@ static int matroska_parse_tracks(AVFormatContext *s)
st->codec->extradata = extradata;
st->codec->extradata_size = extradata_size;
} else if (track->codec_priv.data && track->codec_priv.size > 0) {
- st->codec->extradata = av_mallocz(track->codec_priv.size +
- FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
+ if (ff_alloc_extradata(st->codec, track->codec_priv.size))
return AVERROR(ENOMEM);
- st->codec->extradata_size = track->codec_priv.size;
memcpy(st->codec->extradata,
track->codec_priv.data + extradata_offset,
track->codec_priv.size);
@@ -1771,8 +1962,12 @@ static int matroska_parse_tracks(AVFormatContext *s)
}
if (track->type == MATROSKA_TRACK_TYPE_VIDEO) {
+ MatroskaTrackPlane *planes = track->operation.combine_planes.elem;
+
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
- st->codec->codec_tag = track->video.fourcc;
+ st->codec->codec_tag = fourcc;
+ if (bit_depth >= 0)
+ st->codec->bits_per_coded_sample = bit_depth;
st->codec->width = track->video.pixel_width;
st->codec->height = track->video.pixel_height;
av_reduce(&st->sample_aspect_ratio.num,
@@ -1780,12 +1975,40 @@ static int matroska_parse_tracks(AVFormatContext *s)
st->codec->height * track->video.display_width,
st->codec->width * track->video.display_height,
255);
- if (st->codec->codec_id != AV_CODEC_ID_H264 &&
- st->codec->codec_id != AV_CODEC_ID_HEVC)
+ if (st->codec->codec_id != AV_CODEC_ID_HEVC)
st->need_parsing = AVSTREAM_PARSE_HEADERS;
+
if (track->default_duration) {
av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
1000000000, track->default_duration, 30000);
+#if FF_API_R_FRAME_RATE
+ if ( st->avg_frame_rate.num < st->avg_frame_rate.den * 1000LL
+ && st->avg_frame_rate.num > st->avg_frame_rate.den * 5LL)
+ st->r_frame_rate = st->avg_frame_rate;
+#endif
+ }
+
+ /* export stereo mode flag as metadata tag */
+ if (track->video.stereo_mode && track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB)
+ av_dict_set(&st->metadata, "stereo_mode", ff_matroska_video_stereo_mode[track->video.stereo_mode], 0);
+
+ /* export alpha mode flag as metadata tag */
+ if (track->video.alpha_mode)
+ av_dict_set(&st->metadata, "alpha_mode", "1", 0);
+
+ /* if we have virtual track, mark the real tracks */
+ for (j=0; j < track->operation.combine_planes.nb_elem; j++) {
+ char buf[32];
+ if (planes[j].type >= MATROSKA_VIDEO_STEREO_PLANE_COUNT)
+ continue;
+ snprintf(buf, sizeof(buf), "%s_%d",
+ ff_matroska_video_stereo_plane[planes[j].type], i);
+ for (k=0; k < matroska->tracks.nb_elem; k++)
+ if (planes[j].uid == tracks[k].uid) {
+ av_dict_set(&s->streams[k]->metadata,
+ "stereo_mode", buf, 0);
+ break;
+ }
}
// add stream level stereo3d side data if it is a supported format
if (track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB &&
@@ -1798,11 +2021,34 @@ static int matroska_parse_tracks(AVFormatContext *s)
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->sample_rate = track->audio.out_samplerate;
st->codec->channels = track->audio.channels;
+ if (!st->codec->bits_per_coded_sample)
+ st->codec->bits_per_coded_sample = track->audio.bitdepth;
if (st->codec->codec_id != AV_CODEC_ID_AAC)
st->need_parsing = AVSTREAM_PARSE_HEADERS;
+ if (track->codec_delay > 0) {
+ st->codec->delay = av_rescale_q(track->codec_delay,
+ st->time_base,
+ (AVRational){1, st->codec->sample_rate});
+ }
+ if (track->seek_preroll > 0) {
+ av_codec_set_seek_preroll(st->codec,
+ av_rescale_q(track->seek_preroll,
+ (AVRational){1, 1000000000},
+ (AVRational){1, st->codec->sample_rate}));
+ }
+ } else if (codec_id == AV_CODEC_ID_WEBVTT) {
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+
+ if (!strcmp(track->codec_id, "D_WEBVTT/CAPTIONS")) {
+ st->disposition |= AV_DISPOSITION_CAPTIONS;
+ } else if (!strcmp(track->codec_id, "D_WEBVTT/DESCRIPTIONS")) {
+ st->disposition |= AV_DISPOSITION_DESCRIPTIONS;
+ } else if (!strcmp(track->codec_id, "D_WEBVTT/METADATA")) {
+ st->disposition |= AV_DISPOSITION_METADATA;
+ }
} else if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE) {
st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
- if (st->codec->codec_id == AV_CODEC_ID_SSA)
+ if (st->codec->codec_id == AV_CODEC_ID_ASS)
matroska->contains_ssa = 1;
}
}
@@ -1823,19 +2069,26 @@ static int matroska_read_header(AVFormatContext *s)
int i, j, res;
matroska->ctx = s;
+ matroska->cues_parsing_deferred = 1;
/* 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 > 3) {
+ ebml.doctype_version > 3 ||
+ !ebml.doctype) {
av_log(matroska->ctx, AV_LOG_ERROR,
"EBML header using unsupported features\n"
"(EBML version %"PRIu64", doctype %s, doc version %"PRIu64")\n",
ebml.version, ebml.doctype, ebml.doctype_version);
ebml_free(ebml_syntax, &ebml);
return AVERROR_PATCHWELCOME;
+ } else if (ebml.doctype_version == 3) {
+ av_log(matroska->ctx, AV_LOG_WARNING,
+ "EBML header using unsupported features\n"
+ "(EBML version %"PRIu64", doctype %s, doc version %"PRIu64")\n",
+ ebml.version, ebml.doctype, ebml.doctype_version);
}
for (i = 0; i < FF_ARRAY_ELEMS(matroska_doctypes); i++)
if (!strcmp(ebml.doctype, matroska_doctypes[i]))
@@ -1868,6 +2121,10 @@ static int matroska_read_header(AVFormatContext *s)
matroska->ctx->duration = matroska->duration * matroska->time_scale *
1000 / AV_TIME_BASE;
av_dict_set(&s->metadata, "title", matroska->title, 0);
+ av_dict_set(&s->metadata, "encoder", matroska->muxingapp, 0);
+
+ if (matroska->date_utc.size == 8)
+ matroska_metadata_creation_time(&s->metadata, AV_RB64(matroska->date_utc.data));
res = matroska_parse_tracks(s);
if (res < 0)
@@ -1886,10 +2143,8 @@ static int matroska_read_header(AVFormatContext *s)
av_dict_set(&st->metadata, "mimetype", attachments[j].mime, 0);
st->codec->codec_id = AV_CODEC_ID_NONE;
st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT;
- st->codec->extradata = av_malloc(attachments[j].bin.size);
- if (!st->codec->extradata)
+ if (ff_alloc_extradata(st->codec, attachments[j].bin.size))
break;
- st->codec->extradata_size = attachments[j].bin.size;
memcpy(st->codec->extradata, attachments[j].bin.data,
attachments[j].bin.size);
@@ -1913,11 +2168,15 @@ static int matroska_read_header(AVFormatContext *s)
(AVRational) { 1, 1000000000 },
chapters[i].start, chapters[i].end,
chapters[i].title);
- av_dict_set(&chapters[i].chapter->metadata,
- "title", chapters[i].title, 0);
+ if (chapters[i].chapter) {
+ av_dict_set(&chapters[i].chapter->metadata,
+ "title", chapters[i].title, 0);
+ }
max_start = chapters[i].start;
}
+ matroska_add_index_entries(matroska);
+
matroska_convert_tags(s);
return 0;
@@ -1932,7 +2191,7 @@ static int matroska_deliver_packet(MatroskaDemuxContext *matroska,
{
if (matroska->num_packets > 0) {
memcpy(pkt, matroska->packets[0], sizeof(AVPacket));
- av_free(matroska->packets[0]);
+ av_freep(&matroska->packets[0]);
if (matroska->num_packets > 1) {
void *newpackets;
memmove(&matroska->packets[0], &matroska->packets[1],
@@ -1963,7 +2222,7 @@ static void matroska_clear_queue(MatroskaDemuxContext *matroska)
int n;
for (n = 0; n < matroska->num_packets; n++) {
av_free_packet(matroska->packets[n]);
- av_free(matroska->packets[n]);
+ av_freep(&matroska->packets[n]);
}
av_freep(&matroska->packets);
matroska->num_packets = 0;
@@ -1988,7 +2247,7 @@ static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf,
return 0;
}
- assert(size > 0);
+ av_assert0(size > 0);
*laces = *data + 1;
data += 1;
size -= 1;
@@ -2003,18 +2262,18 @@ static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf,
uint32_t total = 0;
for (n = 0; res == 0 && n < *laces - 1; n++) {
while (1) {
- if (size == 0) {
- res = AVERROR_EOF;
+ if (size <= total) {
+ res = AVERROR_INVALIDDATA;
break;
}
temp = *data;
+ total += temp;
lace_size[n] += temp;
data += 1;
size -= 1;
if (temp != 0xff)
break;
}
- total += lace_size[n];
}
if (size <= total) {
res = AVERROR_INVALIDDATA;
@@ -2039,10 +2298,10 @@ static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf,
uint64_t num;
uint64_t total;
n = matroska_ebmlnum_uint(matroska, data, size, &num);
- if (n < 0) {
+ if (n < 0 || num > INT_MAX) {
av_log(matroska->ctx, AV_LOG_INFO,
"EBML block data error\n");
- res = n;
+ res = n<0 ? n : AVERROR_INVALIDDATA;
break;
}
data += n;
@@ -2052,10 +2311,10 @@ static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf,
int64_t snum;
int r;
r = matroska_ebmlnum_sint(matroska, data, size, &snum);
- if (r < 0) {
+ if (r < 0 || lace_size[n - 1] + snum > (uint64_t)INT_MAX) {
av_log(matroska->ctx, AV_LOG_INFO,
"EBML block data error\n");
- res = r;
+ res = r<0 ? r : AVERROR_INVALIDDATA;
break;
}
data += r;
@@ -2082,7 +2341,7 @@ static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf,
static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska,
MatroskaTrack *track, AVStream *st,
uint8_t *data, int size, uint64_t timecode,
- uint64_t duration, int64_t pos)
+ int64_t pos)
{
int a = st->codec->block_align;
int sps = track->audio.sub_packet_size;
@@ -2112,7 +2371,7 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska,
}
memcpy(track->audio.buf + y * w, data, w);
} else {
- if (size < sps * w / sps) {
+ if (size < sps * w / sps || h<=0 || w%sps) {
av_log(matroska->ctx, AV_LOG_ERROR,
"Corrupt generic RM-style audio packet size\n");
return AVERROR_INVALIDDATA;
@@ -2236,18 +2495,136 @@ fail:
return ret;
}
+static int matroska_parse_webvtt(MatroskaDemuxContext *matroska,
+ MatroskaTrack *track,
+ AVStream *st,
+ uint8_t *data, int data_len,
+ uint64_t timecode,
+ uint64_t duration,
+ int64_t pos)
+{
+ AVPacket *pkt;
+ uint8_t *id, *settings, *text, *buf;
+ int id_len, settings_len, text_len;
+ uint8_t *p, *q;
+ int err;
+
+ if (data_len <= 0)
+ return AVERROR_INVALIDDATA;
+
+ p = data;
+ q = data + data_len;
+
+ id = p;
+ id_len = -1;
+ while (p < q) {
+ if (*p == '\r' || *p == '\n') {
+ id_len = p - id;
+ if (*p == '\r')
+ p++;
+ break;
+ }
+ p++;
+ }
+
+ if (p >= q || *p != '\n')
+ return AVERROR_INVALIDDATA;
+ p++;
+
+ settings = p;
+ settings_len = -1;
+ while (p < q) {
+ if (*p == '\r' || *p == '\n') {
+ settings_len = p - settings;
+ if (*p == '\r')
+ p++;
+ break;
+ }
+ p++;
+ }
+
+ if (p >= q || *p != '\n')
+ return AVERROR_INVALIDDATA;
+ p++;
+
+ text = p;
+ text_len = q - p;
+ while (text_len > 0) {
+ const int len = text_len - 1;
+ const uint8_t c = p[len];
+ if (c != '\r' && c != '\n')
+ break;
+ text_len = len;
+ }
+
+ if (text_len <= 0)
+ return AVERROR_INVALIDDATA;
+
+ pkt = av_mallocz(sizeof(*pkt));
+ if (!pkt)
+ return AVERROR(ENOMEM);
+ err = av_new_packet(pkt, text_len);
+ if (err < 0) {
+ av_free(pkt);
+ return AVERROR(err);
+ }
+
+ memcpy(pkt->data, text, text_len);
+
+ if (id_len > 0) {
+ buf = av_packet_new_side_data(pkt,
+ AV_PKT_DATA_WEBVTT_IDENTIFIER,
+ id_len);
+ if (!buf) {
+ av_free(pkt);
+ return AVERROR(ENOMEM);
+ }
+ memcpy(buf, id, id_len);
+ }
+
+ if (settings_len > 0) {
+ buf = av_packet_new_side_data(pkt,
+ AV_PKT_DATA_WEBVTT_SETTINGS,
+ settings_len);
+ if (!buf) {
+ av_free(pkt);
+ return AVERROR(ENOMEM);
+ }
+ memcpy(buf, settings, settings_len);
+ }
+
+ // Do we need this for subtitles?
+ // pkt->flags = AV_PKT_FLAG_KEY;
+
+ pkt->stream_index = st->index;
+ pkt->pts = timecode;
+
+ // Do we need this for subtitles?
+ // pkt->dts = timecode;
+
+ pkt->duration = duration;
+ pkt->pos = pos;
+
+ dynarray_add(&matroska->packets, &matroska->num_packets, pkt);
+ matroska->prev_pkt = pkt;
+
+ return 0;
+}
+
static int matroska_parse_frame(MatroskaDemuxContext *matroska,
MatroskaTrack *track, AVStream *st,
uint8_t *data, int pkt_size,
- uint64_t timecode, uint64_t duration,
- int64_t pos, int is_keyframe)
+ uint64_t timecode, uint64_t lace_duration,
+ int64_t pos, int is_keyframe,
+ uint8_t *additional, uint64_t additional_id, int additional_size,
+ int64_t discard_padding)
{
MatroskaTrackEncoding *encodings = track->encodings.elem;
uint8_t *pkt_data = data;
int offset = 0, res;
AVPacket *pkt;
- if (encodings && encodings->scope & 1) {
+ if (encodings && !encodings->type && encodings->scope & 1) {
res = matroska_decode_buffer(&pkt_data, &pkt_size, track);
if (res < 0)
return res;
@@ -2266,18 +2643,21 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska,
pkt_data = wv_data;
}
- if (st->codec->codec_id == AV_CODEC_ID_PRORES)
+ if (st->codec->codec_id == AV_CODEC_ID_PRORES &&
+ AV_RB32(&data[4]) != MKBETAG('i', 'c', 'p', 'f'))
offset = 8;
pkt = av_mallocz(sizeof(AVPacket));
+ if (!pkt)
+ return AVERROR(ENOMEM);
/* XXX: prevent data copy... */
if (av_new_packet(pkt, pkt_size + offset) < 0) {
av_free(pkt);
- av_freep(&pkt_data);
- return AVERROR(ENOMEM);
+ res = AVERROR(ENOMEM);
+ goto fail;
}
- if (st->codec->codec_id == AV_CODEC_ID_PRORES) {
+ if (st->codec->codec_id == AV_CODEC_ID_PRORES && offset == 8) {
uint8_t *buf = pkt->data;
bytestream_put_be32(&buf, pkt_size);
bytestream_put_be32(&buf, MKBETAG('i', 'c', 'p', 'f'));
@@ -2286,35 +2666,71 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska,
memcpy(pkt->data + offset, pkt_data, pkt_size);
if (pkt_data != data)
- av_free(pkt_data);
+ av_freep(&pkt_data);
pkt->flags = is_keyframe;
pkt->stream_index = st->index;
+ if (additional_size > 0) {
+ uint8_t *side_data = av_packet_new_side_data(pkt,
+ AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
+ additional_size + 8);
+ if (!side_data) {
+ av_free_packet(pkt);
+ av_free(pkt);
+ return AVERROR(ENOMEM);
+ }
+ AV_WB64(side_data, additional_id);
+ memcpy(side_data + 8, additional, additional_size);
+ }
+
+ if (discard_padding) {
+ uint8_t *side_data = av_packet_new_side_data(pkt,
+ AV_PKT_DATA_SKIP_SAMPLES,
+ 10);
+ if (!side_data) {
+ av_free_packet(pkt);
+ av_free(pkt);
+ return AVERROR(ENOMEM);
+ }
+ AV_WL32(side_data, 0);
+ AV_WL32(side_data + 4, av_rescale_q(discard_padding,
+ (AVRational){1, 1000000000},
+ (AVRational){1, st->codec->sample_rate}));
+ }
+
if (track->ms_compat)
pkt->dts = timecode;
else
pkt->pts = timecode;
pkt->pos = pos;
- if (st->codec->codec_id == AV_CODEC_ID_TEXT)
- pkt->convergence_duration = duration;
- else if (track->type != MATROSKA_TRACK_TYPE_SUBTITLE)
- pkt->duration = duration;
-
- if (st->codec->codec_id == AV_CODEC_ID_SSA)
- matroska_fix_ass_packet(matroska, pkt, duration);
-
- if (matroska->prev_pkt &&
- timecode != AV_NOPTS_VALUE &&
- matroska->prev_pkt->pts == timecode &&
- matroska->prev_pkt->stream_index == st->index &&
- st->codec->codec_id == AV_CODEC_ID_SSA)
- matroska_merge_packets(matroska->prev_pkt, pkt);
- else {
- dynarray_add(&matroska->packets, &matroska->num_packets, pkt);
- matroska->prev_pkt = pkt;
+ 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;
+ }
+
+ dynarray_add(&matroska->packets, &matroska->num_packets, pkt);
+ matroska->prev_pkt = pkt;
+
return 0;
fail:
@@ -2326,7 +2742,8 @@ fail:
static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data,
int size, int64_t pos, uint64_t cluster_time,
uint64_t block_duration, int is_keyframe,
- int64_t cluster_pos)
+ uint8_t *additional, uint64_t additional_id, int additional_size,
+ int64_t cluster_pos, int64_t discard_padding)
{
uint64_t timecode = AV_NOPTS_VALUE;
MatroskaTrack *track;
@@ -2335,7 +2752,8 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data,
int16_t block_time;
uint32_t *lace_size = NULL;
int n, flags, laces = 0;
- uint64_t num, duration;
+ uint64_t num;
+ int trust_default_duration = 1;
if ((n = matroska_ebmlnum_uint(matroska, data, size, &num)) < 0) {
av_log(matroska->ctx, AV_LOG_ERROR, "EBML block data error\n");
@@ -2354,8 +2772,9 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data,
st = track->stream;
if (st->discard >= AVDISCARD_ALL)
return res;
+ av_assert1(block_duration != AV_NOPTS_VALUE);
- block_time = AV_RB16(data);
+ block_time = sign_extend(AV_RB16(data), 16);
data += 2;
flags = *data++;
size -= 3;
@@ -2375,9 +2794,14 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data,
if (matroska->skip_to_keyframe &&
track->type != MATROSKA_TRACK_TYPE_SUBTITLE) {
- if (!is_keyframe || timecode < matroska->skip_to_timecode)
+ if (timecode < matroska->skip_to_timecode)
return res;
- matroska->skip_to_keyframe = 0;
+ if (is_keyframe)
+ matroska->skip_to_keyframe = 0;
+ else if (!st->skip_to_keyframe) {
+ av_log(matroska->ctx, AV_LOG_ERROR, "File is broken, keyframes not correctly marked!\n");
+ matroska->skip_to_keyframe = 0;
+ }
}
res = matroska_parse_laces(matroska, &data, &size, (flags & 0x06) >> 1,
@@ -2386,22 +2810,29 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data,
if (res)
goto end;
- if (block_duration != AV_NOPTS_VALUE) {
- duration = block_duration / laces;
- if (block_duration != duration * laces) {
- av_log(matroska->ctx, AV_LOG_WARNING,
- "Incorrect block_duration, possibly corrupted container");
+ if (track->audio.samplerate == 8000) {
+ // If this is needed for more codecs, then add them here
+ if (st->codec->codec_id == AV_CODEC_ID_AC3) {
+ if (track->audio.samplerate != st->codec->sample_rate || !st->codec->frame_size)
+ trust_default_duration = 0;
}
- } else {
- duration = track->default_duration / matroska->time_scale;
- block_duration = duration * laces;
}
- if (timecode != AV_NOPTS_VALUE)
+ if (!block_duration && trust_default_duration)
+ block_duration = track->default_duration * laces / matroska->time_scale;
+
+ if (cluster_time != (uint64_t)-1 && (block_time >= 0 || cluster_time >= -block_time))
track->end_timecode =
FFMAX(track->end_timecode, timecode + block_duration);
for (n = 0; n < laces; n++) {
+ int64_t lace_duration = block_duration*(n+1) / laces - block_duration*n / laces;
+
+ if (lace_size[n] > size) {
+ av_log(matroska->ctx, AV_LOG_ERROR, "Invalid packet size\n");
+ break;
+ }
+
if ((st->codec->codec_id == AV_CODEC_ID_RA_288 ||
st->codec->codec_id == AV_CODEC_ID_COOK ||
st->codec->codec_id == AV_CODEC_ID_SIPR ||
@@ -2409,20 +2840,31 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data,
st->codec->block_align && track->audio.sub_packet_size) {
res = matroska_parse_rm_audio(matroska, track, st, data,
lace_size[n],
- timecode, duration, pos);
+ timecode, pos);
+ if (res)
+ goto end;
+
+ } else if (st->codec->codec_id == AV_CODEC_ID_WEBVTT) {
+ res = matroska_parse_webvtt(matroska, track, st,
+ data, lace_size[n],
+ timecode, lace_duration,
+ pos);
if (res)
goto end;
} else {
res = matroska_parse_frame(matroska, track, st, data, lace_size[n],
- timecode, duration, pos,
- !n ? is_keyframe : 0);
+ timecode, lace_duration, pos,
+ !n ? is_keyframe : 0,
+ additional, additional_id, additional_size,
+ discard_padding);
if (res)
goto end;
}
if (timecode != AV_NOPTS_VALUE)
- timecode = duration ? timecode + duration : AV_NOPTS_VALUE;
+ timecode = lace_duration ? timecode + lace_duration : AV_NOPTS_VALUE;
data += lace_size[n];
+ size -= lace_size[n];
}
end:
@@ -2470,18 +2912,21 @@ static int matroska_parse_cluster_incremental(MatroskaDemuxContext *matroska)
i = blocks_list->nb_elem - 1;
if (blocks[i].bin.size > 0 && blocks[i].bin.data) {
int is_keyframe = blocks[i].non_simple ? !blocks[i].reference : -1;
+ uint8_t* additional = blocks[i].additional.size > 0 ?
+ blocks[i].additional.data : NULL;
if (!blocks[i].non_simple)
- blocks[i].duration = AV_NOPTS_VALUE;
+ blocks[i].duration = 0;
res = matroska_parse_block(matroska, blocks[i].bin.data,
blocks[i].bin.size, blocks[i].bin.pos,
matroska->current_cluster.timecode,
blocks[i].duration, is_keyframe,
- matroska->current_cluster_pos);
+ additional, blocks[i].additional_id,
+ blocks[i].additional.size,
+ matroska->current_cluster_pos,
+ blocks[i].discard_padding);
}
}
- if (res < 0)
- matroska->done = 1;
return res;
}
@@ -2502,15 +2947,14 @@ static int matroska_parse_cluster(MatroskaDemuxContext *matroska)
res = ebml_parse(matroska, matroska_clusters, &cluster);
blocks_list = &cluster.blocks;
blocks = blocks_list->elem;
- for (i = 0; i < blocks_list->nb_elem && !res; i++)
+ for (i = 0; i < blocks_list->nb_elem; i++)
if (blocks[i].bin.size > 0 && blocks[i].bin.data) {
int is_keyframe = blocks[i].non_simple ? !blocks[i].reference : -1;
- if (!blocks[i].non_simple)
- blocks[i].duration = AV_NOPTS_VALUE;
res = matroska_parse_block(matroska, blocks[i].bin.data,
blocks[i].bin.size, blocks[i].bin.pos,
cluster.timecode, blocks[i].duration,
- is_keyframe, pos);
+ is_keyframe, NULL, 0, 0, pos,
+ blocks[i].discard_padding);
}
ebml_free(matroska_cluster, &cluster);
return res;
@@ -2519,22 +2963,16 @@ static int matroska_parse_cluster(MatroskaDemuxContext *matroska)
static int matroska_read_packet(AVFormatContext *s, AVPacket *pkt)
{
MatroskaDemuxContext *matroska = s->priv_data;
- int ret = 0;
- while (!ret && matroska_deliver_packet(matroska, pkt)) {
+ while (matroska_deliver_packet(matroska, pkt)) {
int64_t pos = avio_tell(matroska->ctx->pb);
if (matroska->done)
return AVERROR_EOF;
if (matroska_parse_cluster(matroska) < 0)
- ret = matroska_resync(matroska, pos);
+ matroska_resync(matroska, pos);
}
- if (ret == AVERROR_INVALIDDATA && pkt->data) {
- pkt->flags |= AV_PKT_FLAG_CORRUPT;
- return 0;
- }
-
- return ret;
+ return 0;
}
static int matroska_read_seek(AVFormatContext *s, int stream_index,
@@ -2546,20 +2984,20 @@ 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) {
+ if ((index = av_index_search_timestamp(st, timestamp, flags)) < 0 || index == st->nb_index_entries - 1) {
avio_seek(s->pb, st->index_entries[st->nb_index_entries - 1].pos,
SEEK_SET);
matroska->current_id = 0;
- while ((index = av_index_search_timestamp(st, timestamp, flags)) < 0) {
+ while ((index = av_index_search_timestamp(st, timestamp, flags)) < 0 || index == st->nb_index_entries - 1) {
matroska_clear_queue(matroska);
if (matroska_parse_cluster(matroska) < 0)
break;
@@ -2567,8 +3005,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;
tracks = matroska->tracks.elem;
@@ -2582,21 +3020,38 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index,
index_sub = av_index_search_timestamp(
tracks[i].stream, st->index_entries[index].timestamp,
AVSEEK_FLAG_BACKWARD);
- if (index_sub >= 0 &&
- st->index_entries[index_sub].pos < st->index_entries[index_min].pos &&
- st->index_entries[index].timestamp -
- st->index_entries[index_sub].timestamp < 30000000000 / matroska->time_scale)
- index_min = index_sub;
+ while (index_sub >= 0 &&
+ index_min > 0 &&
+ tracks[i].stream->index_entries[index_sub].pos < st->index_entries[index_min].pos &&
+ st->index_entries[index].timestamp - tracks[i].stream->index_entries[index_sub].timestamp < 30000000000 / matroska->time_scale)
+ index_min--;
}
}
avio_seek(s->pb, st->index_entries[index_min].pos, SEEK_SET);
matroska->current_id = 0;
- matroska->skip_to_keyframe = !(flags & AVSEEK_FLAG_ANY);
- matroska->skip_to_timecode = st->index_entries[index].timestamp;
+ if (flags & AVSEEK_FLAG_ANY) {
+ st->skip_to_keyframe = 0;
+ matroska->skip_to_timecode = timestamp;
+ } else {
+ st->skip_to_keyframe = 1;
+ matroska->skip_to_timecode = st->index_entries[index].timestamp;
+ }
+ matroska->skip_to_keyframe = 1;
matroska->done = 0;
+ matroska->num_levels = 0;
ff_update_cur_dts(s, st, st->index_entries[index].timestamp);
return 0;
+err:
+ // slightly hackish but allows proper fallback to
+ // the generic seeking code.
+ matroska_clear_queue(matroska);
+ matroska->current_id = 0;
+ st->skip_to_keyframe =
+ matroska->skip_to_keyframe = 0;
+ matroska->done = 0;
+ matroska->num_levels = 0;
+ return -1;
}
static int matroska_read_close(AVFormatContext *s)
@@ -2609,13 +3064,362 @@ static int matroska_read_close(AVFormatContext *s)
for (n = 0; n < matroska->tracks.nb_elem; n++)
if (tracks[n].type == MATROSKA_TRACK_TYPE_AUDIO)
- av_free(tracks[n].audio.buf);
+ av_freep(&tracks[n].audio.buf);
ebml_free(matroska_cluster, &matroska->current_cluster);
ebml_free(matroska_segment, matroska);
return 0;
}
+typedef struct {
+ int64_t start_time_ns;
+ int64_t end_time_ns;
+ int64_t start_offset;
+ int64_t end_offset;
+} CueDesc;
+
+/* This function searches all the Cues and returns the CueDesc corresponding the
+ * the timestamp ts. Returned CueDesc will be such that start_time_ns <= ts <
+ * end_time_ns. All 4 fields will be set to -1 if ts >= file's duration.
+ */
+static CueDesc get_cue_desc(AVFormatContext *s, int64_t ts, int64_t cues_start) {
+ MatroskaDemuxContext *matroska = s->priv_data;
+ CueDesc cue_desc;
+ int i;
+ int nb_index_entries = s->streams[0]->nb_index_entries;
+ AVIndexEntry *index_entries = s->streams[0]->index_entries;
+ if (ts >= matroska->duration * matroska->time_scale) return (CueDesc) {-1, -1, -1, -1};
+ for (i = 1; i < nb_index_entries; i++) {
+ if (index_entries[i - 1].timestamp * matroska->time_scale <= ts &&
+ index_entries[i].timestamp * matroska->time_scale > ts) {
+ break;
+ }
+ }
+ --i;
+ cue_desc.start_time_ns = index_entries[i].timestamp * matroska->time_scale;
+ cue_desc.start_offset = index_entries[i].pos - matroska->segment_start;
+ if (i != nb_index_entries - 1) {
+ cue_desc.end_time_ns = index_entries[i + 1].timestamp * matroska->time_scale;
+ cue_desc.end_offset = index_entries[i + 1].pos - matroska->segment_start;
+ } else {
+ cue_desc.end_time_ns = matroska->duration * matroska->time_scale;
+ // FIXME: this needs special handling for files where Cues appear
+ // before Clusters. the current logic assumes Cues appear after
+ // Clusters.
+ cue_desc.end_offset = cues_start - matroska->segment_start;
+ }
+ return cue_desc;
+}
+
+static int webm_clusters_start_with_keyframe(AVFormatContext *s)
+{
+ MatroskaDemuxContext *matroska = s->priv_data;
+ int64_t cluster_pos, before_pos;
+ int index, rv = 1;
+ if (s->streams[0]->nb_index_entries <= 0) return 0;
+ // seek to the first cluster using cues.
+ index = av_index_search_timestamp(s->streams[0], 0, 0);
+ if (index < 0) return 0;
+ cluster_pos = s->streams[0]->index_entries[index].pos;
+ before_pos = avio_tell(s->pb);
+ while (1) {
+ int64_t cluster_id = 0, cluster_length = 0;
+ AVPacket *pkt;
+ avio_seek(s->pb, cluster_pos, SEEK_SET);
+ // read cluster id and length
+ ebml_read_num(matroska, matroska->ctx->pb, 4, &cluster_id);
+ ebml_read_length(matroska, matroska->ctx->pb, &cluster_length);
+ if (cluster_id != 0xF43B675) { // done with all clusters
+ break;
+ }
+ avio_seek(s->pb, cluster_pos, SEEK_SET);
+ matroska->current_id = 0;
+ matroska_clear_queue(matroska);
+ if (matroska_parse_cluster(matroska) < 0 ||
+ matroska->num_packets <= 0) {
+ break;
+ }
+ pkt = matroska->packets[0];
+ cluster_pos += cluster_length + 12; // 12 is the offset of the cluster id and length.
+ if (!(pkt->flags & AV_PKT_FLAG_KEY)) {
+ rv = 0;
+ break;
+ }
+ }
+ avio_seek(s->pb, before_pos, SEEK_SET);
+ return rv;
+}
+
+static int buffer_size_after_time_downloaded(int64_t time_ns, double search_sec, int64_t bps,
+ double min_buffer, double* buffer,
+ double* sec_to_download, AVFormatContext *s,
+ int64_t cues_start)
+{
+ double nano_seconds_per_second = 1000000000.0;
+ double time_sec = time_ns / nano_seconds_per_second;
+ int rv = 0;
+ int64_t time_to_search_ns = (int64_t)(search_sec * nano_seconds_per_second);
+ int64_t end_time_ns = time_ns + time_to_search_ns;
+ double sec_downloaded = 0.0;
+ CueDesc desc_curr = get_cue_desc(s, time_ns, cues_start);
+ if (desc_curr.start_time_ns == -1)
+ return -1;
+ *sec_to_download = 0.0;
+
+ // Check for non cue start time.
+ if (time_ns > desc_curr.start_time_ns) {
+ int64_t cue_nano = desc_curr.end_time_ns - time_ns;
+ double percent = (double)(cue_nano) / (desc_curr.end_time_ns - desc_curr.start_time_ns);
+ double cueBytes = (desc_curr.end_offset - desc_curr.start_offset) * percent;
+ double timeToDownload = (cueBytes * 8.0) / bps;
+
+ sec_downloaded += (cue_nano / nano_seconds_per_second) - timeToDownload;
+ *sec_to_download += timeToDownload;
+
+ // Check if the search ends within the first cue.
+ if (desc_curr.end_time_ns >= end_time_ns) {
+ double desc_end_time_sec = desc_curr.end_time_ns / nano_seconds_per_second;
+ double percent_to_sub = search_sec / (desc_end_time_sec - time_sec);
+ sec_downloaded = percent_to_sub * sec_downloaded;
+ *sec_to_download = percent_to_sub * *sec_to_download;
+ }
+
+ if ((sec_downloaded + *buffer) <= min_buffer) {
+ return 1;
+ }
+
+ // Get the next Cue.
+ desc_curr = get_cue_desc(s, desc_curr.end_time_ns, cues_start);
+ }
+
+ while (desc_curr.start_time_ns != -1) {
+ int64_t desc_bytes = desc_curr.end_offset - desc_curr.start_offset;
+ int64_t desc_ns = desc_curr.end_time_ns - desc_curr.start_time_ns;
+ double desc_sec = desc_ns / nano_seconds_per_second;
+ double bits = (desc_bytes * 8.0);
+ double time_to_download = bits / bps;
+
+ sec_downloaded += desc_sec - time_to_download;
+ *sec_to_download += time_to_download;
+
+ if (desc_curr.end_time_ns >= end_time_ns) {
+ double desc_end_time_sec = desc_curr.end_time_ns / nano_seconds_per_second;
+ double percent_to_sub = search_sec / (desc_end_time_sec - time_sec);
+ sec_downloaded = percent_to_sub * sec_downloaded;
+ *sec_to_download = percent_to_sub * *sec_to_download;
+
+ if ((sec_downloaded + *buffer) <= min_buffer)
+ rv = 1;
+ break;
+ }
+
+ if ((sec_downloaded + *buffer) <= min_buffer) {
+ rv = 1;
+ break;
+ }
+
+ desc_curr = get_cue_desc(s, desc_curr.end_time_ns, cues_start);
+ }
+ *buffer = *buffer + sec_downloaded;
+ return rv;
+}
+
+/* This function computes the bandwidth of the WebM file with the help of
+ * buffer_size_after_time_downloaded() function. Both of these functions are
+ * adapted from WebM Tools project and are adapted to work with FFmpeg's
+ * Matroska parsing mechanism.
+ *
+ * Returns the bandwidth of the file on success; -1 on error.
+ * */
+static int64_t webm_dash_manifest_compute_bandwidth(AVFormatContext *s, int64_t cues_start)
+{
+ MatroskaDemuxContext *matroska = s->priv_data;
+ AVStream *st = s->streams[0];
+ double bandwidth = 0.0;
+ int i;
+
+ for (i = 0; i < st->nb_index_entries; i++) {
+ int64_t prebuffer_ns = 1000000000;
+ int64_t time_ns = st->index_entries[i].timestamp * matroska->time_scale;
+ double nano_seconds_per_second = 1000000000.0;
+ int64_t prebuffered_ns = time_ns + prebuffer_ns;
+ double prebuffer_bytes = 0.0;
+ int64_t temp_prebuffer_ns = prebuffer_ns;
+ int64_t pre_bytes, pre_ns;
+ double pre_sec, prebuffer, bits_per_second;
+ CueDesc desc_beg = get_cue_desc(s, time_ns, cues_start);
+
+ // Start with the first Cue.
+ CueDesc desc_end = desc_beg;
+
+ // Figure out how much data we have downloaded for the prebuffer. This will
+ // be used later to adjust the bits per sample to try.
+ while (desc_end.start_time_ns != -1 && desc_end.end_time_ns < prebuffered_ns) {
+ // Prebuffered the entire Cue.
+ prebuffer_bytes += desc_end.end_offset - desc_end.start_offset;
+ temp_prebuffer_ns -= desc_end.end_time_ns - desc_end.start_time_ns;
+ desc_end = get_cue_desc(s, desc_end.end_time_ns, cues_start);
+ }
+ if (desc_end.start_time_ns == -1) {
+ // The prebuffer is larger than the duration.
+ if (matroska->duration * matroska->time_scale >= prebuffered_ns)
+ return -1;
+ bits_per_second = 0.0;
+ } else {
+ // The prebuffer ends in the last Cue. Estimate how much data was
+ // prebuffered.
+ pre_bytes = desc_end.end_offset - desc_end.start_offset;
+ pre_ns = desc_end.end_time_ns - desc_end.start_time_ns;
+ pre_sec = pre_ns / nano_seconds_per_second;
+ prebuffer_bytes +=
+ pre_bytes * ((temp_prebuffer_ns / nano_seconds_per_second) / pre_sec);
+
+ prebuffer = prebuffer_ns / nano_seconds_per_second;
+
+ // Set this to 0.0 in case our prebuffer buffers the entire video.
+ bits_per_second = 0.0;
+ do {
+ int64_t desc_bytes = desc_end.end_offset - desc_beg.start_offset;
+ int64_t desc_ns = desc_end.end_time_ns - desc_beg.start_time_ns;
+ double desc_sec = desc_ns / nano_seconds_per_second;
+ double calc_bits_per_second = (desc_bytes * 8) / desc_sec;
+
+ // Drop the bps by the percentage of bytes buffered.
+ double percent = (desc_bytes - prebuffer_bytes) / desc_bytes;
+ double mod_bits_per_second = calc_bits_per_second * percent;
+
+ if (prebuffer < desc_sec) {
+ double search_sec =
+ (double)(matroska->duration * matroska->time_scale) / nano_seconds_per_second;
+
+ // Add 1 so the bits per second should be a little bit greater than file
+ // datarate.
+ int64_t bps = (int64_t)(mod_bits_per_second) + 1;
+ const double min_buffer = 0.0;
+ double buffer = prebuffer;
+ double sec_to_download = 0.0;
+
+ int rv = buffer_size_after_time_downloaded(prebuffered_ns, search_sec, bps,
+ min_buffer, &buffer, &sec_to_download,
+ s, cues_start);
+ if (rv < 0) {
+ return -1;
+ } else if (rv == 0) {
+ bits_per_second = (double)(bps);
+ break;
+ }
+ }
+
+ desc_end = get_cue_desc(s, desc_end.end_time_ns, cues_start);
+ } while (desc_end.start_time_ns != -1);
+ }
+ if (bandwidth < bits_per_second) bandwidth = bits_per_second;
+ }
+ return (int64_t)bandwidth;
+}
+
+static int webm_dash_manifest_cues(AVFormatContext *s)
+{
+ MatroskaDemuxContext *matroska = s->priv_data;
+ EbmlList *seekhead_list = &matroska->seekhead;
+ MatroskaSeekhead *seekhead = seekhead_list->elem;
+ char *buf;
+ int64_t cues_start = -1, cues_end = -1, before_pos, bandwidth;
+ int i;
+
+ // determine cues start and end positions
+ for (i = 0; i < seekhead_list->nb_elem; i++)
+ if (seekhead[i].id == MATROSKA_ID_CUES)
+ break;
+
+ if (i >= seekhead_list->nb_elem) return -1;
+
+ before_pos = avio_tell(matroska->ctx->pb);
+ cues_start = seekhead[i].pos + matroska->segment_start;
+ if (avio_seek(matroska->ctx->pb, cues_start, SEEK_SET) == cues_start) {
+ // cues_end is computed as cues_start + cues_length + length of the
+ // Cues element ID + EBML length of the Cues element. cues_end is
+ // inclusive and the above sum is reduced by 1.
+ uint64_t cues_length = 0, cues_id = 0, bytes_read = 0;
+ bytes_read += ebml_read_num(matroska, matroska->ctx->pb, 4, &cues_id);
+ bytes_read += ebml_read_length(matroska, matroska->ctx->pb, &cues_length);
+ cues_end = cues_start + cues_length + bytes_read - 1;
+ }
+ avio_seek(matroska->ctx->pb, before_pos, SEEK_SET);
+ if (cues_start == -1 || cues_end == -1) return -1;
+
+ // parse the cues
+ matroska_parse_cues(matroska);
+
+ // cues start
+ av_dict_set_int(&s->streams[0]->metadata, CUES_START, cues_start, 0);
+
+ // cues end
+ av_dict_set_int(&s->streams[0]->metadata, CUES_END, cues_end, 0);
+
+ // bandwidth
+ bandwidth = webm_dash_manifest_compute_bandwidth(s, cues_start);
+ if (bandwidth < 0) return -1;
+ av_dict_set_int(&s->streams[0]->metadata, BANDWIDTH, bandwidth, 0);
+
+ // check if all clusters start with key frames
+ av_dict_set_int(&s->streams[0]->metadata, CLUSTER_KEYFRAME, webm_clusters_start_with_keyframe(s), 0);
+
+ // store cue point timestamps as a comma separated list for checking subsegment alignment in
+ // the muxer. assumes that each timestamp cannot be more than 20 characters long.
+ buf = av_malloc_array(s->streams[0]->nb_index_entries, 20 * sizeof(char));
+ if (!buf) return -1;
+ strcpy(buf, "");
+ for (i = 0; i < s->streams[0]->nb_index_entries; i++) {
+ snprintf(buf, (i + 1) * 20 * sizeof(char),
+ "%s%" PRId64, buf, s->streams[0]->index_entries[i].timestamp);
+ if (i != s->streams[0]->nb_index_entries - 1)
+ strncat(buf, ",", sizeof(char));
+ }
+ av_dict_set(&s->streams[0]->metadata, CUE_TIMESTAMPS, buf, 0);
+ av_free(buf);
+
+ return 0;
+}
+
+static int webm_dash_manifest_read_header(AVFormatContext *s)
+{
+ char *buf;
+ int ret = matroska_read_header(s);
+ MatroskaTrack *tracks;
+ MatroskaDemuxContext *matroska = s->priv_data;
+ if (ret) {
+ av_log(s, AV_LOG_ERROR, "Failed to read file headers\n");
+ return -1;
+ }
+
+ // initialization range
+ // 5 is the offset of Cluster ID.
+ av_dict_set_int(&s->streams[0]->metadata, INITIALIZATION_RANGE, avio_tell(s->pb) - 5, 0);
+
+ // basename of the file
+ buf = strrchr(s->filename, '/');
+ av_dict_set(&s->streams[0]->metadata, FILENAME, buf ? ++buf : s->filename, 0);
+
+ // duration
+ buf = av_asprintf("%g", matroska->duration);
+ if (!buf) return AVERROR(ENOMEM);
+ av_dict_set(&s->streams[0]->metadata, DURATION, buf, 0);
+ av_free(buf);
+
+ // track number
+ tracks = matroska->tracks.elem;
+ av_dict_set_int(&s->streams[0]->metadata, TRACK_NUMBER, tracks[0].num, 0);
+
+ // parse the cues and populate Cue related fields
+ return webm_dash_manifest_cues(s);
+}
+
+static int webm_dash_manifest_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ return AVERROR_EOF;
+}
+
AVInputFormat ff_matroska_demuxer = {
.name = "matroska,webm",
.long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"),
@@ -2628,3 +3432,12 @@ AVInputFormat ff_matroska_demuxer = {
.read_seek = matroska_read_seek,
.mime_type = "audio/webm,audio/x-matroska,video/webm,video/x-matroska"
};
+
+AVInputFormat ff_webm_dash_manifest_demuxer = {
+ .name = "webm_dash_manifest",
+ .long_name = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"),
+ .priv_data_size = sizeof(MatroskaDemuxContext),
+ .read_header = webm_dash_manifest_read_header,
+ .read_packet = webm_dash_manifest_read_packet,
+ .read_close = matroska_read_close,
+};
diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 458a5f6cf7..8690025dc5 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -2,20 +2,20 @@
* Matroska muxer
* Copyright (c) 2007 David Conrad
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -24,12 +24,14 @@
#include "avc.h"
#include "hevc.h"
#include "avformat.h"
+#include "avio_internal.h"
#include "avlanguage.h"
#include "flacenc.h"
#include "internal.h"
#include "isom.h"
#include "matroska.h"
#include "riff.h"
+#include "subtitles.h"
#include "vorbiscomment.h"
#include "wv.h"
@@ -43,10 +45,12 @@
#include "libavutil/opt.h"
#include "libavutil/random_seed.h"
#include "libavutil/samplefmt.h"
+#include "libavutil/sha.h"
#include "libavutil/stereo3d.h"
#include "libavcodec/xiph.h"
#include "libavcodec/mpeg4audio.h"
+#include "libavcodec/internal.h"
typedef struct ebml_master {
int64_t pos; ///< absolute offset in the file where the master's elements start
@@ -69,8 +73,11 @@ typedef struct mkv_seekhead {
typedef struct mkv_cuepoint {
uint64_t pts;
+ int stream_idx;
int tracknum;
int64_t cluster_pos; ///< file offset of the cluster containing the block
+ int64_t relative_pos; ///< relative offset from the position of the cluster containing the block
+ int64_t duration; ///< duration of the block according to time base
} mkv_cuepoint;
typedef struct mkv_cues {
@@ -81,6 +88,7 @@ typedef struct mkv_cues {
typedef struct mkv_track {
int write_dts;
+ int has_cue;
int64_t ts_offset;
} mkv_track;
@@ -110,6 +118,10 @@ typedef struct MatroskaMuxContext {
int cluster_size_limit;
int64_t cues_pos;
int64_t cluster_time_limit;
+ int is_dash;
+ int dash_track_number;
+
+ uint32_t chapter_id_offset;
int wrote_chapters;
} MatroskaMuxContext;
@@ -118,13 +130,16 @@ typedef struct MatroskaMuxContext {
* offset, 4 bytes for target EBML ID */
#define MAX_SEEKENTRY_SIZE 21
-/** per-cuepoint-track - 3 1-byte EBML IDs, 3 1-byte EBML sizes, 2
+/** per-cuepoint-track - 5 1-byte EBML IDs, 5 1-byte EBML sizes, 4
* 8-byte uint max */
-#define MAX_CUETRACKPOS_SIZE 22
+#define MAX_CUETRACKPOS_SIZE 42
/** per-cuepoint - 2 1-byte EBML IDs, 2 1-byte EBML sizes, 8-byte uint max */
#define MAX_CUEPOINT_SIZE(num_tracks) 12 + MAX_CUETRACKPOS_SIZE * num_tracks
+/** Seek preroll value for opus */
+#define OPUS_SEEK_PREROLL 80000000
+
static int ebml_id_size(unsigned int id)
{
return (av_log2(id + 1) - 1) / 7 + 1;
@@ -134,7 +149,7 @@ static void put_ebml_id(AVIOContext *pb, unsigned int id)
{
int i = ebml_id_size(id);
while (i--)
- avio_w8(pb, id >> (i * 8));
+ avio_w8(pb, (uint8_t)(id >> (i * 8)));
}
/**
@@ -144,10 +159,9 @@ static void put_ebml_id(AVIOContext *pb, unsigned int id)
*/
static void put_ebml_size_unknown(AVIOContext *pb, int bytes)
{
- assert(bytes <= 8);
+ av_assert0(bytes <= 8);
avio_w8(pb, 0x1ff >> bytes);
- while (--bytes)
- avio_w8(pb, 0xff);
+ ffio_fill(pb, 0xff, bytes - 1);
}
/**
@@ -172,18 +186,18 @@ static void put_ebml_num(AVIOContext *pb, uint64_t num, int bytes)
int i, needed_bytes = ebml_num_size(num);
// sizes larger than this are currently undefined in EBML
- assert(num < (1ULL << 56) - 1);
+ av_assert0(num < (1ULL << 56) - 1);
if (bytes == 0)
// don't care how many bytes are used, so use the min
bytes = needed_bytes;
// the bytes needed to write the given size would exceed the bytes
// that we need to use, so write unknown size. This shouldn't happen.
- assert(bytes >= needed_bytes);
+ av_assert0(bytes >= needed_bytes);
num |= 1ULL << bytes * 7;
for (i = bytes - 1; i >= 0; i--)
- avio_w8(pb, num >> i * 8);
+ avio_w8(pb, (uint8_t)(num >> i * 8));
}
static void put_ebml_uint(AVIOContext *pb, unsigned int elementid, uint64_t val)
@@ -196,7 +210,20 @@ static void put_ebml_uint(AVIOContext *pb, unsigned int elementid, uint64_t val)
put_ebml_id(pb, elementid);
put_ebml_num(pb, bytes, 0);
for (i = bytes - 1; i >= 0; i--)
- avio_w8(pb, val >> i * 8);
+ avio_w8(pb, (uint8_t)(val >> i * 8));
+}
+
+static void put_ebml_sint(AVIOContext *pb, unsigned int elementid, int64_t val)
+{
+ int i, bytes = 1;
+ uint64_t tmp = 2*(val < 0 ? val^-1 : val);
+
+ while (tmp>>=8) bytes++;
+
+ put_ebml_id(pb, elementid);
+ put_ebml_num(pb, bytes, 0);
+ for (i = bytes - 1; i >= 0; i--)
+ avio_w8(pb, (uint8_t)(val >> i * 8));
}
static void put_ebml_float(AVIOContext *pb, unsigned int elementid, double val)
@@ -230,7 +257,7 @@ static void put_ebml_void(AVIOContext *pb, uint64_t size)
{
int64_t currentpos = avio_tell(pb);
- assert(size >= 2);
+ av_assert0(size >= 2);
put_ebml_id(pb, EBML_ID_VOID);
// we need to subtract the length needed to store the size from the
@@ -240,8 +267,7 @@ static void put_ebml_void(AVIOContext *pb, uint64_t size)
put_ebml_num(pb, size - 1, 0);
else
put_ebml_num(pb, size - 9, 8);
- while (avio_tell(pb) < currentpos + size)
- avio_w8(pb, 0);
+ ffio_fill(pb, 0, currentpos + size - avio_tell(pb));
}
static ebml_master start_ebml_master(AVIOContext *pb, unsigned int elementid,
@@ -265,9 +291,7 @@ static void end_ebml_master(AVIOContext *pb, ebml_master master)
static void put_xiph_size(AVIOContext *pb, int size)
{
- int i;
- for (i = 0; i < size / 255; i++)
- avio_w8(pb, 255);
+ ffio_fill(pb, 255, size / 255);
avio_w8(pb, size % 255);
}
@@ -305,17 +329,16 @@ static mkv_seekhead *mkv_start_seekhead(AVIOContext *pb, int64_t segment_offset,
static int mkv_add_seekhead_entry(mkv_seekhead *seekhead, unsigned int elementid, uint64_t filepos)
{
- int err;
+ mkv_seekhead_entry *entries = seekhead->entries;
// don't store more elements than we reserved space for
if (seekhead->max_entries > 0 && seekhead->max_entries <= seekhead->num_entries)
return -1;
- if ((err = av_reallocp_array(&seekhead->entries, seekhead->num_entries + 1,
- sizeof(*seekhead->entries))) < 0) {
- seekhead->num_entries = 0;
- return err;
- }
+ entries = av_realloc_array(entries, seekhead->num_entries + 1, sizeof(mkv_seekhead_entry));
+ if (!entries)
+ return AVERROR(ENOMEM);
+ seekhead->entries = entries;
seekhead->entries[seekhead->num_entries].elementid = elementid;
seekhead->entries[seekhead->num_entries++].segmentpos = filepos - seekhead->segment_offset;
@@ -370,7 +393,7 @@ static int64_t mkv_write_seekhead(AVIOContext *pb, mkv_seekhead *seekhead)
currentpos = seekhead->filepos;
}
fail:
- av_free(seekhead->entries);
+ av_freep(&seekhead->entries);
av_free(seekhead);
return currentpos;
@@ -386,28 +409,32 @@ static mkv_cues *mkv_start_cues(int64_t segment_offset)
return cues;
}
-static int mkv_add_cuepoint(mkv_cues *cues, int stream, int64_t ts, int64_t cluster_pos)
+static int mkv_add_cuepoint(mkv_cues *cues, int stream, int tracknum, int64_t ts,
+ int64_t cluster_pos, int64_t relative_pos, int64_t duration)
{
- int err;
+ mkv_cuepoint *entries = cues->entries;
if (ts < 0)
return 0;
- if ((err = av_reallocp_array(&cues->entries, cues->num_entries + 1,
- sizeof(*cues->entries))) < 0) {
- cues->num_entries = 0;
- return err;
- }
+ entries = av_realloc_array(entries, cues->num_entries + 1, sizeof(mkv_cuepoint));
+ if (!entries)
+ return AVERROR(ENOMEM);
+ cues->entries = entries;
cues->entries[cues->num_entries].pts = ts;
- cues->entries[cues->num_entries].tracknum = stream + 1;
- cues->entries[cues->num_entries++].cluster_pos = cluster_pos - cues->segment_offset;
+ cues->entries[cues->num_entries].stream_idx = stream;
+ cues->entries[cues->num_entries].tracknum = tracknum;
+ cues->entries[cues->num_entries].cluster_pos = cluster_pos - cues->segment_offset;
+ cues->entries[cues->num_entries].relative_pos = relative_pos;
+ cues->entries[cues->num_entries++].duration = duration;
return 0;
}
-static int64_t mkv_write_cues(AVIOContext *pb, mkv_cues *cues, int num_tracks)
+static int64_t mkv_write_cues(AVFormatContext *s, mkv_cues *cues, mkv_track *tracks, int num_tracks)
{
+ AVIOContext *pb = s->pb;
ebml_master cues_element;
int64_t currentpos;
int i, j;
@@ -419,16 +446,39 @@ static int64_t mkv_write_cues(AVIOContext *pb, mkv_cues *cues, int num_tracks)
ebml_master cuepoint, track_positions;
mkv_cuepoint *entry = &cues->entries[i];
uint64_t pts = entry->pts;
+ int ctp_nb = 0;
+
+ // Calculate the number of entries, so we know the element size
+ for (j = 0; j < num_tracks; j++)
+ tracks[j].has_cue = 0;
+ for (j = 0; j < cues->num_entries - i && entry[j].pts == pts; j++) {
+ int tracknum = entry[j].stream_idx;
+ av_assert0(tracknum>=0 && tracknum<num_tracks);
+ if (tracks[tracknum].has_cue && s->streams[tracknum]->codec->codec_type != AVMEDIA_TYPE_SUBTITLE)
+ continue;
+ tracks[tracknum].has_cue = 1;
+ ctp_nb ++;
+ }
- cuepoint = start_ebml_master(pb, MATROSKA_ID_POINTENTRY, MAX_CUEPOINT_SIZE(num_tracks));
+ cuepoint = start_ebml_master(pb, MATROSKA_ID_POINTENTRY, MAX_CUEPOINT_SIZE(ctp_nb));
put_ebml_uint(pb, MATROSKA_ID_CUETIME, pts);
// put all the entries from different tracks that have the exact same
// timestamp into the same CuePoint
+ for (j = 0; j < num_tracks; j++)
+ tracks[j].has_cue = 0;
for (j = 0; j < cues->num_entries - i && entry[j].pts == pts; j++) {
+ int tracknum = entry[j].stream_idx;
+ av_assert0(tracknum>=0 && tracknum<num_tracks);
+ if (tracks[tracknum].has_cue && s->streams[tracknum]->codec->codec_type != AVMEDIA_TYPE_SUBTITLE)
+ continue;
+ tracks[tracknum].has_cue = 1;
track_positions = start_ebml_master(pb, MATROSKA_ID_CUETRACKPOSITION, MAX_CUETRACKPOS_SIZE);
- put_ebml_uint(pb, MATROSKA_ID_CUETRACK , entry[j].tracknum );
- put_ebml_uint(pb, MATROSKA_ID_CUECLUSTERPOSITION, entry[j].cluster_pos);
+ put_ebml_uint(pb, MATROSKA_ID_CUETRACK , entry[j].tracknum );
+ put_ebml_uint(pb, MATROSKA_ID_CUECLUSTERPOSITION , entry[j].cluster_pos);
+ put_ebml_uint(pb, MATROSKA_ID_CUERELATIVEPOSITION, entry[j].relative_pos);
+ if (entry[j].duration != -1)
+ put_ebml_uint(pb, MATROSKA_ID_CUEDURATION , entry[j].duration);
end_ebml_master(pb, track_positions);
}
i += j - 1;
@@ -441,7 +491,7 @@ static int64_t mkv_write_cues(AVIOContext *pb, mkv_cues *cues, int num_tracks)
static int put_xiph_codecpriv(AVFormatContext *s, AVIOContext *pb, AVCodecContext *codec)
{
- uint8_t *header_start[3];
+ const uint8_t *header_start[3];
int header_len[3];
int first_header_size;
int j;
@@ -490,7 +540,7 @@ static int put_flac_codecpriv(AVFormatContext *s,
if (write_comment) {
const char *vendor = (s->flags & AVFMT_FLAG_BITEXACT) ?
- "Libav" : LIBAVFORMAT_IDENT;
+ "Lavf" : LIBAVFORMAT_IDENT;
AVDictionary *dict = NULL;
uint8_t buf[32], *data, *p;
int len;
@@ -566,8 +616,11 @@ static int mkv_write_native_codecprivate(AVFormatContext *s,
codec->extradata_size - 12);
break;
default:
- if (codec->extradata_size)
- avio_write(dyn_cp, codec->extradata, codec->extradata_size);
+ if (codec->codec_id == AV_CODEC_ID_PRORES &&
+ ff_codec_get_id(ff_codec_movvideo_tags, codec->codec_tag) == AV_CODEC_ID_PRORES) {
+ avio_wl32(dyn_cp, codec->codec_tag);
+ } else if (codec->extradata_size && codec->codec_id != AV_CODEC_ID_TTA)
+ avio_write(dyn_cp, codec->extradata, codec->extradata_size);
}
return 0;
@@ -592,30 +645,46 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb,
if (!codec->codec_tag)
codec->codec_tag = ff_codec_get_tag(ff_codec_movvideo_tags,
codec->codec_id);
- if (codec->extradata_size)
+ if (codec->extradata_size) {
+ if ( ff_codec_get_id(ff_codec_movvideo_tags, codec->codec_tag) == codec->codec_id
+ && ff_codec_get_id(ff_codec_movvideo_tags, AV_RL32(codec->extradata + 4)) != codec->codec_id
+ ) {
+ int i;
+ avio_wb32(dyn_cp, 0x5a + codec->extradata_size);
+ avio_wl32(dyn_cp, codec->codec_tag);
+ for(i = 0; i < 0x5a - 8; i++)
+ avio_w8(dyn_cp, 0);
+ }
avio_write(dyn_cp, codec->extradata, codec->extradata_size);
+ }
} else {
+ if (!ff_codec_get_tag(ff_codec_bmp_tags, codec->codec_id))
+ av_log(s, AV_LOG_WARNING, "codec %s is not supported by this format\n",
+ avcodec_get_name(codec->codec_id));
+
if (!codec->codec_tag)
codec->codec_tag = ff_codec_get_tag(ff_codec_bmp_tags,
codec->codec_id);
- if (!codec->codec_tag) {
- av_log(s, AV_LOG_ERROR, "No bmp codec ID found.\n");
- ret = -1;
+ if (!codec->codec_tag && codec->codec_id != AV_CODEC_ID_RAWVIDEO) {
+ av_log(s, AV_LOG_ERROR, "No bmp codec tag found for codec %s\n",
+ avcodec_get_name(codec->codec_id));
+ ret = AVERROR(EINVAL);
}
- ff_put_bmp_header(dyn_cp, codec, ff_codec_bmp_tags, 0);
+ ff_put_bmp_header(dyn_cp, codec, ff_codec_bmp_tags, 0, 0);
}
} else if (codec->codec_type == AVMEDIA_TYPE_AUDIO) {
unsigned int tag;
tag = ff_codec_get_tag(ff_codec_wav_tags, codec->codec_id);
if (!tag) {
- av_log(s, AV_LOG_ERROR, "No wav codec ID found.\n");
- ret = -1;
+ av_log(s, AV_LOG_ERROR, "No wav codec tag found for codec %s\n",
+ avcodec_get_name(codec->codec_id));
+ ret = AVERROR(EINVAL);
}
if (!codec->codec_tag)
codec->codec_tag = tag;
- ff_put_wav_header(dyn_cp, codec);
+ ff_put_wav_header(dyn_cp, codec, FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX);
}
codecpriv_size = avio_close_dyn_buf(dyn_cp, &codecpriv);
@@ -626,19 +695,28 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb,
return ret;
}
+
static int mkv_write_stereo_mode(AVFormatContext *s, AVIOContext *pb,
- AVStream *st, int mode)
+ AVStream *st, int mode, int *h_width, int *h_height)
{
int i;
- int display_width, display_height;
- int h_width = 1, h_height = 1;
- AVCodecContext *codec = st->codec;
+ int ret = 0;
AVDictionaryEntry *tag;
MatroskaVideoStereoModeType format = MATROSKA_VIDEO_STEREOMODE_TYPE_NB;
+ *h_width = 1;
+ *h_height = 1;
// convert metadata into proper side data and add it to the stream
- if ((tag = av_dict_get(s->metadata, "stereo_mode", NULL, 0))) {
+ if ((tag = av_dict_get(st->metadata, "stereo_mode", NULL, 0)) ||
+ (tag = av_dict_get( s->metadata, "stereo_mode", NULL, 0))) {
int stereo_mode = atoi(tag->value);
+
+ for (i=0; i<MATROSKA_VIDEO_STEREOMODE_TYPE_NB; i++)
+ if (!strcmp(tag->value, ff_matroska_video_stereo_mode[i])){
+ stereo_mode = i;
+ break;
+ }
+
if (stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB &&
stereo_mode != 10 && stereo_mode != 12) {
int ret = ff_mkv_stereo3d_conv(st, stereo_mode);
@@ -661,13 +739,13 @@ static int mkv_write_stereo_mode(AVFormatContext *s, AVIOContext *pb,
format = (stereo->flags & AV_STEREO3D_FLAG_INVERT)
? MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT
: MATROSKA_VIDEO_STEREOMODE_TYPE_LEFT_RIGHT;
- h_width = 2;
+ *h_width = 2;
break;
case AV_STEREO3D_TOPBOTTOM:
format = MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM;
if (stereo->flags & AV_STEREO3D_FLAG_INVERT)
format--;
- h_height = 2;
+ *h_height = 2;
break;
case AV_STEREO3D_CHECKERBOARD:
format = MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_LR;
@@ -678,13 +756,13 @@ static int mkv_write_stereo_mode(AVFormatContext *s, AVIOContext *pb,
format = MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_LR;
if (stereo->flags & AV_STEREO3D_FLAG_INVERT)
format--;
- h_height = 2;
+ *h_height = 2;
break;
case AV_STEREO3D_COLUMNS:
format = MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_LR;
if (stereo->flags & AV_STEREO3D_FLAG_INVERT)
format--;
- h_width = 2;
+ *h_width = 2;
break;
case AV_STEREO3D_FRAMESEQUENCE:
format = MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_LR;
@@ -692,40 +770,32 @@ static int mkv_write_stereo_mode(AVFormatContext *s, AVIOContext *pb,
format++;
break;
}
-
break;
}
}
+ if (format == MATROSKA_VIDEO_STEREOMODE_TYPE_NB)
+ return ret;
+
// if webm, do not write unsupported modes
- if (mode == MODE_WEBM &&
- (format > MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM &&
- format != MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT))
+ if ((mode == MODE_WEBM &&
+ format > MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM &&
+ format != MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT)
+ || format >= MATROSKA_VIDEO_STEREOMODE_TYPE_NB) {
+ av_log(s, AV_LOG_ERROR,
+ "The specified stereo mode is not valid.\n");
format = MATROSKA_VIDEO_STEREOMODE_TYPE_NB;
+ return AVERROR(EINVAL);
+ }
// write StereoMode if format is valid
- if (format < MATROSKA_VIDEO_STEREOMODE_TYPE_NB)
- put_ebml_uint(pb, MATROSKA_ID_VIDEOSTEREOMODE, format);
-
- // write DisplayWidth and DisplayHeight, they contain the size of
- // a single source view and/or the display aspect ratio
- display_width = codec->width / h_width;
- display_height = codec->height / h_height;
- if (st->sample_aspect_ratio.num) {
- display_width *= av_q2d(st->sample_aspect_ratio);
- put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYUNIT, 3); // DAR
- }
- if (st->sample_aspect_ratio.num ||
- format < MATROSKA_VIDEO_STEREOMODE_TYPE_NB) {
- put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYWIDTH, display_width);
- put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYHEIGHT, display_height);
- }
+ put_ebml_uint(pb, MATROSKA_ID_VIDEOSTEREOMODE, format);
- return 0;
+ return ret;
}
static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
- int i, AVIOContext *pb)
+ int i, AVIOContext *pb, int default_stream_exists)
{
AVStream *st = s->streams[i];
AVCodecContext *codec = st->codec;
@@ -735,6 +805,8 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
int bit_depth = av_get_bits_per_sample(codec->codec_id);
int sample_rate = codec->sample_rate;
int output_sample_rate = 0;
+ int display_width_div = 1;
+ int display_height_div = 1;
int j, ret;
AVDictionaryEntry *tag;
@@ -746,8 +818,10 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
return 0;
}
- if (!bit_depth)
+ if (!bit_depth && codec->codec_id != AV_CODEC_ID_ADPCM_G726)
bit_depth = av_get_bytes_per_sample(codec->sample_fmt) << 3;
+ if (!bit_depth)
+ bit_depth = codec->bits_per_coded_sample;
if (codec->codec_id == AV_CODEC_ID_AAC) {
ret = get_aac_sample_rates(s, codec, &sample_rate, &output_sample_rate);
@@ -756,59 +830,96 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
}
track = start_ebml_master(pb, MATROSKA_ID_TRACKENTRY, 0);
- put_ebml_uint (pb, MATROSKA_ID_TRACKNUMBER , i + 1);
- put_ebml_uint (pb, MATROSKA_ID_TRACKUID , i + 1);
+ put_ebml_uint (pb, MATROSKA_ID_TRACKNUMBER,
+ mkv->is_dash ? mkv->dash_track_number : i + 1);
+ put_ebml_uint (pb, MATROSKA_ID_TRACKUID,
+ mkv->is_dash ? mkv->dash_track_number : i + 1);
put_ebml_uint (pb, MATROSKA_ID_TRACKFLAGLACING , 0); // no lacing (yet)
if ((tag = av_dict_get(st->metadata, "title", NULL, 0)))
put_ebml_string(pb, MATROSKA_ID_TRACKNAME, tag->value);
tag = av_dict_get(st->metadata, "language", NULL, 0);
- put_ebml_string(pb, MATROSKA_ID_TRACKLANGUAGE, tag ? tag->value:"und");
+ if (mkv->mode != MODE_WEBM || codec->codec_id != AV_CODEC_ID_WEBVTT) {
+ put_ebml_string(pb, MATROSKA_ID_TRACKLANGUAGE, tag && tag->value ? tag->value:"und");
+ } else if (tag && tag->value) {
+ put_ebml_string(pb, MATROSKA_ID_TRACKLANGUAGE, tag->value);
+ }
// The default value for TRACKFLAGDEFAULT is 1, so add element
// if we need to clear it.
- if (!(st->disposition & AV_DISPOSITION_DEFAULT))
+ if (default_stream_exists && !(st->disposition & AV_DISPOSITION_DEFAULT))
put_ebml_uint(pb, MATROSKA_ID_TRACKFLAGDEFAULT, !!(st->disposition & AV_DISPOSITION_DEFAULT));
- if (codec->codec_type == AVMEDIA_TYPE_AUDIO && codec->initial_padding) {
- mkv->tracks[i].ts_offset = av_rescale_q(codec->initial_padding,
- (AVRational){ 1, codec->sample_rate },
- st->time_base);
+ if (st->disposition & AV_DISPOSITION_FORCED)
+ put_ebml_uint(pb, MATROSKA_ID_TRACKFLAGFORCED, 1);
+
+ if (mkv->mode == MODE_WEBM && codec->codec_id == AV_CODEC_ID_WEBVTT) {
+ const char *codec_id;
+ if (st->disposition & AV_DISPOSITION_CAPTIONS) {
+ codec_id = "D_WEBVTT/CAPTIONS";
+ native_id = MATROSKA_TRACK_TYPE_SUBTITLE;
+ } else if (st->disposition & AV_DISPOSITION_DESCRIPTIONS) {
+ codec_id = "D_WEBVTT/DESCRIPTIONS";
+ native_id = MATROSKA_TRACK_TYPE_METADATA;
+ } else if (st->disposition & AV_DISPOSITION_METADATA) {
+ codec_id = "D_WEBVTT/METADATA";
+ native_id = MATROSKA_TRACK_TYPE_METADATA;
+ } else {
+ codec_id = "D_WEBVTT/SUBTITLES";
+ native_id = MATROSKA_TRACK_TYPE_SUBTITLE;
+ }
+ put_ebml_string(pb, MATROSKA_ID_CODECID, codec_id);
+ } else {
+ // look for a codec ID string specific to mkv to use,
+ // if none are found, use AVI codes
+ for (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) {
+ if (ff_mkv_codec_tags[j].id == codec->codec_id) {
+ put_ebml_string(pb, MATROSKA_ID_CODECID, ff_mkv_codec_tags[j].str);
+ native_id = 1;
+ break;
+ }
+ }
+ if (codec->codec_id == AV_CODEC_ID_RAWVIDEO && !codec->codec_tag)
+ native_id = 0;
+ }
+
+ if (codec->codec_type == AVMEDIA_TYPE_AUDIO && codec->initial_padding && codec->codec_id == AV_CODEC_ID_OPUS) {
+// mkv->tracks[i].ts_offset = av_rescale_q(codec->initial_padding,
+// (AVRational){ 1, codec->sample_rate },
+// st->time_base);
put_ebml_uint(pb, MATROSKA_ID_CODECDELAY,
av_rescale_q(codec->initial_padding,
(AVRational){ 1, codec->sample_rate },
(AVRational){ 1, 1000000000 }));
}
-
- // look for a codec ID string specific to mkv to use,
- // if none are found, use AVI codes
- for (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) {
- if (ff_mkv_codec_tags[j].id == codec->codec_id) {
- put_ebml_string(pb, MATROSKA_ID_CODECID, ff_mkv_codec_tags[j].str);
- native_id = 1;
- break;
- }
+ if (codec->codec_id == AV_CODEC_ID_OPUS) {
+ put_ebml_uint(pb, MATROSKA_ID_SEEKPREROLL, OPUS_SEEK_PREROLL);
}
if (mkv->mode == MODE_WEBM && !(codec->codec_id == AV_CODEC_ID_VP8 ||
codec->codec_id == AV_CODEC_ID_VP9 ||
codec->codec_id == AV_CODEC_ID_OPUS ||
- codec->codec_id == AV_CODEC_ID_VORBIS)) {
+ codec->codec_id == AV_CODEC_ID_VORBIS ||
+ codec->codec_id == AV_CODEC_ID_WEBVTT)) {
av_log(s, AV_LOG_ERROR,
- "Only VP8 or VP9 video and Vorbis or Opus audio are supported for WebM.\n");
+ "Only VP8 or VP9 video and Vorbis or Opus audio and WebVTT subtitles are supported for WebM.\n");
return AVERROR(EINVAL);
}
switch (codec->codec_type) {
case AVMEDIA_TYPE_VIDEO:
put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_VIDEO);
- if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0)
+
+ if( st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0
+ && 1.0/av_q2d(st->avg_frame_rate) > av_q2d(codec->time_base))
put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, 1E9 / av_q2d(st->avg_frame_rate));
+ else
+ put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, av_q2d(codec->time_base)*1E9);
if (!native_id &&
ff_codec_get_tag(ff_codec_movvideo_tags, codec->codec_id) &&
- (!ff_codec_get_tag(ff_codec_bmp_tags, codec->codec_id) ||
+ ((!ff_codec_get_tag(ff_codec_bmp_tags, codec->codec_id) && codec->codec_id != AV_CODEC_ID_RAWVIDEO) ||
codec->codec_id == AV_CODEC_ID_SVQ1 ||
codec->codec_id == AV_CODEC_ID_SVQ3 ||
codec->codec_id == AV_CODEC_ID_CINEPAK))
@@ -829,10 +940,38 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
// check both side data and metadata for stereo information,
// write the result to the bitstream if any is found
- ret = mkv_write_stereo_mode(s, pb, st, mkv->mode);
+ ret = mkv_write_stereo_mode(s, pb, st, mkv->mode,
+ &display_width_div,
+ &display_height_div);
if (ret < 0)
return ret;
+ if (((tag = av_dict_get(st->metadata, "alpha_mode", NULL, 0)) && atoi(tag->value)) ||
+ ((tag = av_dict_get( s->metadata, "alpha_mode", NULL, 0)) && atoi(tag->value)) ||
+ (codec->pix_fmt == AV_PIX_FMT_YUVA420P)) {
+ put_ebml_uint(pb, MATROSKA_ID_VIDEOALPHAMODE, 1);
+ }
+
+ // write DisplayWidth and DisplayHeight, they contain the size of
+ // a single source view and/or the display aspect ratio
+ if (st->sample_aspect_ratio.num) {
+ int64_t d_width = av_rescale(codec->width, st->sample_aspect_ratio.num, st->sample_aspect_ratio.den);
+ if (d_width > INT_MAX) {
+ av_log(s, AV_LOG_ERROR, "Overflow in display width\n");
+ return AVERROR(EINVAL);
+ }
+ put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYWIDTH , d_width / display_width_div);
+ put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYHEIGHT, codec->height / display_height_div);
+ } else if (display_width_div != 1 || display_height_div != 1) {
+ put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYWIDTH , codec->width / display_width_div);
+ put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYHEIGHT, codec->height / display_height_div);
+ }
+
+ if (codec->codec_id == AV_CODEC_ID_RAWVIDEO) {
+ uint32_t color_space = av_le2ne32(codec->codec_tag);
+ put_ebml_binary(pb, MATROSKA_ID_VIDEOCOLORSPACE, &color_space, sizeof(color_space));
+ }
+
end_ebml_master(pb, subinfo);
break;
@@ -854,19 +993,26 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
break;
case AVMEDIA_TYPE_SUBTITLE:
- put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_SUBTITLE);
if (!native_id) {
av_log(s, AV_LOG_ERROR, "Subtitle codec %d is not supported.\n", codec->codec_id);
return AVERROR(ENOSYS);
}
+
+ if (mkv->mode != MODE_WEBM || codec->codec_id != AV_CODEC_ID_WEBVTT)
+ native_id = MATROSKA_TRACK_TYPE_SUBTITLE;
+
+ put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, native_id);
break;
default:
av_log(s, AV_LOG_ERROR, "Only audio, video, and subtitles are supported for Matroska.\n");
- break;
+ return AVERROR(EINVAL);
+ }
+
+ if (mkv->mode != MODE_WEBM || codec->codec_id != AV_CODEC_ID_WEBVTT) {
+ ret = mkv_write_codecprivate(s, pb, codec, native_id, qt_id);
+ if (ret < 0)
+ return ret;
}
- ret = mkv_write_codecprivate(s, pb, codec, native_id, qt_id);
- if (ret < 0)
- return ret;
end_ebml_master(pb, track);
@@ -878,7 +1024,7 @@ static int mkv_write_tracks(AVFormatContext *s)
MatroskaMuxContext *mkv = s->priv_data;
AVIOContext *pb = s->pb;
ebml_master tracks;
- int i, ret;
+ int i, ret, default_stream_exists = 0;
ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_TRACKS, avio_tell(pb));
if (ret < 0)
@@ -886,7 +1032,11 @@ static int mkv_write_tracks(AVFormatContext *s)
tracks = start_ebml_master(pb, MATROSKA_ID_TRACKS, 0);
for (i = 0; i < s->nb_streams; i++) {
- ret = mkv_write_track(s, mkv, i, pb);
+ AVStream *st = s->streams[i];
+ default_stream_exists |= st->disposition & AV_DISPOSITION_DEFAULT;
+ }
+ for (i = 0; i < s->nb_streams; i++) {
+ ret = mkv_write_track(s, mkv, i, pb, default_stream_exists);
if (ret < 0)
return ret;
}
@@ -918,7 +1068,7 @@ static int mkv_write_chapters(AVFormatContext *s)
AVDictionaryEntry *t = NULL;
chapteratom = start_ebml_master(pb, MATROSKA_ID_CHAPTERATOM, 0);
- put_ebml_uint(pb, MATROSKA_ID_CHAPTERUID, c->id);
+ put_ebml_uint(pb, MATROSKA_ID_CHAPTERUID, c->id + mkv->chapter_id_offset);
put_ebml_uint(pb, MATROSKA_ID_CHAPTERTIMESTART,
av_rescale_q(c->start, c->time_base, scale));
put_ebml_uint(pb, MATROSKA_ID_CHAPTERTIMEEND,
@@ -997,6 +1147,7 @@ static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, unsigned int eleme
while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX))) {
if (av_strcasecmp(t->key, "title") &&
+ av_strcasecmp(t->key, "stereo_mode") &&
av_strcasecmp(t->key, "encoding_tool")) {
ret = mkv_write_simpletag(s->pb, t);
if (ret < 0)
@@ -1008,14 +1159,26 @@ static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, unsigned int eleme
return 0;
}
+static int mkv_check_tag(AVDictionary *m)
+{
+ AVDictionaryEntry *t = NULL;
+
+ while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX)))
+ if (av_strcasecmp(t->key, "title") && av_strcasecmp(t->key, "stereo_mode"))
+ return 1;
+
+ return 0;
+}
+
static int mkv_write_tags(AVFormatContext *s)
{
+ MatroskaMuxContext *mkv = s->priv_data;
ebml_master tags = {0};
int i, ret;
ff_metadata_conv_ctx(s, ff_mkv_metadata_conv, NULL);
- if (av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) {
+ if (mkv_check_tag(s->metadata)) {
ret = mkv_write_tag(s, s->metadata, 0, 0, &tags);
if (ret < 0) return ret;
}
@@ -1023,7 +1186,7 @@ static int mkv_write_tags(AVFormatContext *s)
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
- if (!av_dict_get(st->metadata, "", 0, AV_DICT_IGNORE_SUFFIX))
+ if (!mkv_check_tag(st->metadata))
continue;
ret = mkv_write_tag(s, st->metadata, MATROSKA_ID_TAGTARGETS_TRACKUID, i + 1, &tags);
@@ -1033,10 +1196,10 @@ static int mkv_write_tags(AVFormatContext *s)
for (i = 0; i < s->nb_chapters; i++) {
AVChapter *ch = s->chapters[i];
- if (!av_dict_get(ch->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX))
+ if (!mkv_check_tag(ch->metadata))
continue;
- ret = mkv_write_tag(s, ch->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID, ch->id, &tags);
+ ret = mkv_write_tag(s, ch->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID, ch->id + mkv->chapter_id_offset, &tags);
if (ret < 0) return ret;
}
@@ -1068,6 +1231,7 @@ static int mkv_write_attachments(AVFormatContext *s)
ebml_master attached_file;
AVDictionaryEntry *t;
const char *mimetype = NULL;
+ uint64_t fileuid;
if (st->codec->codec_type != AVMEDIA_TYPE_ATTACHMENT)
continue;
@@ -1097,9 +1261,25 @@ static int mkv_write_attachments(AVFormatContext *s)
return AVERROR(EINVAL);
}
+ if (s->flags & AVFMT_FLAG_BITEXACT) {
+ struct AVSHA *sha = av_sha_alloc();
+ uint8_t digest[20];
+ if (!sha)
+ return AVERROR(ENOMEM);
+ av_sha_init(sha, 160);
+ av_sha_update(sha, st->codec->extradata, st->codec->extradata_size);
+ av_sha_final(sha, digest);
+ av_free(sha);
+ fileuid = AV_RL64(digest);
+ } else {
+ fileuid = av_lfg_get(&c);
+ }
+ av_log(s, AV_LOG_VERBOSE, "Using %.16"PRIx64" for attachment %d\n",
+ fileuid, i);
+
put_ebml_string(pb, MATROSKA_ID_FILEMIMETYPE, mimetype);
put_ebml_binary(pb, MATROSKA_ID_FILEDATA, st->codec->extradata, st->codec->extradata_size);
- put_ebml_uint(pb, MATROSKA_ID_FILEUID, av_lfg_get(&c));
+ put_ebml_uint(pb, MATROSKA_ID_FILEUID, fileuid);
end_ebml_master(pb, attached_file);
}
end_ebml_master(pb, attachments);
@@ -1113,14 +1293,40 @@ static int mkv_write_header(AVFormatContext *s)
AVIOContext *pb = s->pb;
ebml_master ebml_header, segment_info;
AVDictionaryEntry *tag;
- int ret, i;
+ int ret, i, version = 2;
if (!strcmp(s->oformat->name, "webm"))
mkv->mode = MODE_WEBM;
else
mkv->mode = MODE_MATROSKAv2;
- mkv->tracks = av_mallocz(s->nb_streams * sizeof(*mkv->tracks));
+ if (s->avoid_negative_ts < 0)
+ s->avoid_negative_ts = 1;
+
+ if (mkv->mode != MODE_WEBM ||
+ av_dict_get(s->metadata, "stereo_mode", NULL, 0) ||
+ av_dict_get(s->metadata, "alpha_mode", NULL, 0))
+ version = 4;
+
+ for (i = 0; i < s->nb_streams; i++) {
+ if (s->streams[i]->codec->codec_id == AV_CODEC_ID_ATRAC3 ||
+ s->streams[i]->codec->codec_id == AV_CODEC_ID_COOK ||
+ s->streams[i]->codec->codec_id == AV_CODEC_ID_RA_288 ||
+ s->streams[i]->codec->codec_id == AV_CODEC_ID_SIPR ||
+ s->streams[i]->codec->codec_id == AV_CODEC_ID_RV10 ||
+ s->streams[i]->codec->codec_id == AV_CODEC_ID_RV20) {
+ av_log(s, AV_LOG_ERROR,
+ "The Matroska muxer does not yet support muxing %s\n",
+ avcodec_get_name(s->streams[i]->codec->codec_id));
+ return AVERROR_PATCHWELCOME;
+ }
+ if (s->streams[i]->codec->codec_id == AV_CODEC_ID_OPUS ||
+ av_dict_get(s->streams[i]->metadata, "stereo_mode", NULL, 0) ||
+ av_dict_get(s->streams[i]->metadata, "alpha_mode", NULL, 0))
+ version = 4;
+ }
+
+ mkv->tracks = av_mallocz_array(s->nb_streams, sizeof(*mkv->tracks));
if (!mkv->tracks)
return AVERROR(ENOMEM);
@@ -1130,7 +1336,7 @@ static int mkv_write_header(AVFormatContext *s)
put_ebml_uint (pb, EBML_ID_EBMLMAXIDLENGTH , 4);
put_ebml_uint (pb, EBML_ID_EBMLMAXSIZELENGTH , 8);
put_ebml_string (pb, EBML_ID_DOCTYPE , s->oformat->name);
- put_ebml_uint (pb, EBML_ID_DOCTYPEVERSION , 2);
+ put_ebml_uint (pb, EBML_ID_DOCTYPEVERSION , version);
put_ebml_uint (pb, EBML_ID_DOCTYPEREADVERSION , 2);
end_ebml_master(pb, ebml_header);
@@ -1168,6 +1374,18 @@ static int mkv_write_header(AVFormatContext *s)
else
put_ebml_string(pb, MATROSKA_ID_WRITINGAPP, LIBAVFORMAT_IDENT);
put_ebml_binary(pb, MATROSKA_ID_SEGMENTUID, segment_uid, 16);
+ } else {
+ const char *ident = "Lavf";
+ put_ebml_string(pb, MATROSKA_ID_MUXINGAPP , ident);
+ put_ebml_string(pb, MATROSKA_ID_WRITINGAPP, ident);
+ }
+
+ if (tag = av_dict_get(s->metadata, "creation_time", NULL, 0)) {
+ // Adjust time so it's relative to 2001-01-01 and convert to nanoseconds.
+ int64_t date_utc = (ff_iso8601_to_unix_time(tag->value) - 978307200) * 1000000000;
+ uint8_t date_utc_buf[8];
+ AV_WB64(date_utc_buf, date_utc);
+ put_ebml_binary(pb, MATROSKA_ID_DATEUTC, date_utc_buf, 8);
}
// reserve space for the duration
@@ -1180,6 +1398,9 @@ static int mkv_write_header(AVFormatContext *s)
if (ret < 0)
return ret;
+ for (i = 0; i < s->nb_chapters; i++)
+ mkv->chapter_id_offset = FFMAX(mkv->chapter_id_offset, 1LL - s->chapters[i]->id);
+
if (mkv->mode != MODE_WEBM) {
ret = mkv_write_chapters(s);
if (ret < 0)
@@ -1208,6 +1429,7 @@ static int mkv_write_header(AVFormatContext *s)
av_init_packet(&mkv->cur_audio_pkt);
mkv->cur_audio_pkt.size = 0;
+ mkv->cluster_pos = -1;
avio_flush(pb);
@@ -1239,85 +1461,6 @@ static int mkv_blockgroup_size(int pkt_size)
return size;
}
-static int ass_get_duration(AVFormatContext *s, const uint8_t *p)
-{
- int sh, sm, ss, sc, eh, em, es, ec;
- uint64_t start, end;
-
- if (sscanf(p, "%*[^,],%d:%d:%d%*c%d,%d:%d:%d%*c%d",
- &sh, &sm, &ss, &sc, &eh, &em, &es, &ec) != 8)
- return 0;
-
- if (sh > 9 || sm > 59 || ss > 59 || sc > 99 ||
- eh > 9 || em > 59 || es > 59 || ec > 99) {
- av_log(s, AV_LOG_WARNING,
- "Non-standard time reference %d:%d:%d.%d,%d:%d:%d.%d\n",
- sh, sm, ss, sc, eh, em, es, ec);
- return 0;
- }
-
- start = 3600000 * sh + 60000 * sm + 1000 * ss + 10 * sc;
- end = 3600000 * eh + 60000 * em + 1000 * es + 10 * ec;
-
- if (start > end) {
- av_log(s, AV_LOG_WARNING,
- "Unexpected time reference %d:%d:%d.%d,%d:%d:%d.%d\n",
- sh, sm, ss, sc, eh, em, es, ec);
- return 0;
- }
-
- return end - start;
-}
-
-static int mkv_write_ass_blocks(AVFormatContext *s, AVIOContext *pb,
- AVPacket *pkt)
-{
- MatroskaMuxContext *mkv = s->priv_data;
- int i, layer = 0, max_duration = 0, size, line_size, data_size = pkt->size;
- uint8_t *start, *end, *data = pkt->data;
- ebml_master blockgroup;
- char buffer[2048];
-
- while (data_size) {
- int duration = ass_get_duration(s, data);
- max_duration = FFMAX(duration, max_duration);
- end = memchr(data, '\n', data_size);
- size = line_size = end ? end - data + 1 : data_size;
- size -= end ? (end[-1] == '\r') + 1 : 0;
- start = data;
- for (i = 0; i < 3; i++, start++)
- if (!(start = memchr(start, ',', size - (start - data))))
- return max_duration;
- size -= start - data;
- sscanf(data, "Dialogue: %d,", &layer);
- i = snprintf(buffer, sizeof(buffer), "%" PRId64 ",%d,",
- s->streams[pkt->stream_index]->nb_frames, layer);
- size = FFMIN(i + size, sizeof(buffer));
- memcpy(buffer + i, start, size - i);
-
- av_log(s, AV_LOG_DEBUG,
- "Writing block at offset %" PRIu64 ", size %d, "
- "pts %" PRId64 ", duration %d\n",
- avio_tell(pb), size, pkt->pts, duration);
- blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP,
- mkv_blockgroup_size(size));
- put_ebml_id(pb, MATROSKA_ID_BLOCK);
- put_ebml_num(pb, size + 4, 0);
- // this assumes stream_index is less than 126
- avio_w8(pb, 0x80 | (pkt->stream_index + 1));
- avio_wb16(pb, pkt->pts - mkv->cluster_pts);
- avio_w8(pb, 0);
- avio_write(pb, buffer, size);
- put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, duration);
- end_ebml_master(pb, blockgroup);
-
- data += line_size;
- data_size -= line_size;
- }
-
- return max_duration;
-}
-
static int mkv_strip_wavpack(const uint8_t *src, uint8_t **pdst, int *size)
{
uint8_t *dst;
@@ -1376,9 +1519,12 @@ static void mkv_write_block(AVFormatContext *s, AVIOContext *pb,
{
MatroskaMuxContext *mkv = s->priv_data;
AVCodecContext *codec = s->streams[pkt->stream_index]->codec;
- uint8_t *data = NULL;
- int offset = 0, size = pkt->size;
+ uint8_t *data = NULL, *side_data = NULL;
+ int offset = 0, size = pkt->size, side_data_size = 0;
int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts;
+ uint64_t additional_id = 0;
+ int64_t discard_padding = 0;
+ ebml_master block_group, block_additions, block_more;
av_log(s, AV_LOG_DEBUG, "Writing block at offset %" PRIu64 ", size %d, "
"pts %" PRId64 ", dts %" PRId64 ", duration %d, flags %d\n",
@@ -1406,56 +1552,95 @@ static void mkv_write_block(AVFormatContext *s, AVIOContext *pb,
offset = 8;
}
+ side_data = av_packet_get_side_data(pkt,
+ AV_PKT_DATA_SKIP_SAMPLES,
+ &side_data_size);
+
+ if (side_data && side_data_size >= 10) {
+ discard_padding = av_rescale_q(AV_RL32(side_data + 4),
+ (AVRational){1, codec->sample_rate},
+ (AVRational){1, 1000000000});
+ }
+
+ side_data = av_packet_get_side_data(pkt,
+ AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
+ &side_data_size);
+ if (side_data) {
+ additional_id = AV_RB64(side_data);
+ side_data += 8;
+ side_data_size -= 8;
+ }
+
+ if ((side_data_size && additional_id == 1) || discard_padding) {
+ block_group = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, 0);
+ blockid = MATROSKA_ID_BLOCK;
+ }
+
put_ebml_id(pb, blockid);
put_ebml_num(pb, size + 4, 0);
// this assumes stream_index is less than 126
- avio_w8(pb, 0x80 | (pkt->stream_index + 1));
+ avio_w8(pb, 0x80 | (mkv->is_dash ? mkv->dash_track_number : (pkt->stream_index + 1)));
avio_wb16(pb, ts - mkv->cluster_pts);
avio_w8(pb, flags);
avio_write(pb, data + offset, size);
if (data != pkt->data)
av_free(data);
-}
-static int srt_get_duration(uint8_t **buf)
-{
- int i, duration = 0;
-
- for (i = 0; i < 2 && !duration; i++) {
- int s_hour, s_min, s_sec, s_hsec, e_hour, e_min, e_sec, e_hsec;
- if (sscanf(*buf, "%d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d",
- &s_hour, &s_min, &s_sec, &s_hsec,
- &e_hour, &e_min, &e_sec, &e_hsec) == 8) {
- s_min += 60 * s_hour;
- e_min += 60 * e_hour;
- s_sec += 60 * s_min;
-
- e_sec += 60 * e_min;
- s_hsec += 1000 * s_sec;
- e_hsec += 1000 * e_sec;
-
- duration = e_hsec - s_hsec;
- }
- *buf += strcspn(*buf, "\n") + 1;
+ if (discard_padding) {
+ put_ebml_sint(pb, MATROSKA_ID_DISCARDPADDING, discard_padding);
+ }
+
+ if (side_data_size && additional_id == 1) {
+ block_additions = start_ebml_master(pb, MATROSKA_ID_BLOCKADDITIONS, 0);
+ block_more = start_ebml_master(pb, MATROSKA_ID_BLOCKMORE, 0);
+ put_ebml_uint(pb, MATROSKA_ID_BLOCKADDID, 1);
+ put_ebml_id(pb, MATROSKA_ID_BLOCKADDITIONAL);
+ put_ebml_num(pb, side_data_size, 0);
+ avio_write(pb, side_data, side_data_size);
+ end_ebml_master(pb, block_more);
+ end_ebml_master(pb, block_additions);
+ }
+ if ((side_data_size && additional_id == 1) || discard_padding) {
+ end_ebml_master(pb, block_group);
}
- return duration;
}
-static int mkv_write_srt_blocks(AVFormatContext *s, AVIOContext *pb,
- AVPacket *pkt)
+static int mkv_write_vtt_blocks(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt)
{
+ MatroskaMuxContext *mkv = s->priv_data;
ebml_master blockgroup;
- AVPacket pkt2 = *pkt;
- int64_t duration = srt_get_duration(&pkt2.data);
- pkt2.size -= pkt2.data - pkt->data;
-
- blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP,
- mkv_blockgroup_size(pkt2.size));
- mkv_write_block(s, pb, MATROSKA_ID_BLOCK, &pkt2, 0);
- put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, duration);
+ int id_size, settings_size, size;
+ uint8_t *id, *settings;
+ int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts;
+ const int flags = 0;
+
+ id_size = 0;
+ id = av_packet_get_side_data(pkt, AV_PKT_DATA_WEBVTT_IDENTIFIER,
+ &id_size);
+
+ settings_size = 0;
+ settings = av_packet_get_side_data(pkt, AV_PKT_DATA_WEBVTT_SETTINGS,
+ &settings_size);
+
+ size = id_size + 1 + settings_size + 1 + pkt->size;
+
+ av_log(s, AV_LOG_DEBUG, "Writing block at offset %" PRIu64 ", size %d, "
+ "pts %" PRId64 ", dts %" PRId64 ", duration %d, flags %d\n",
+ avio_tell(pb), size, pkt->pts, pkt->dts, pkt->duration, flags);
+
+ blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, mkv_blockgroup_size(size));
+
+ put_ebml_id(pb, MATROSKA_ID_BLOCK);
+ put_ebml_num(pb, size + 4, 0);
+ avio_w8(pb, 0x80 | (pkt->stream_index + 1)); // this assumes stream_index is less than 126
+ avio_wb16(pb, ts - mkv->cluster_pts);
+ avio_w8(pb, flags);
+ avio_printf(pb, "%.*s\n%.*s\n%.*s", id_size, id, settings_size, settings, pkt->size, pkt->data);
+
+ put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, pkt->duration);
end_ebml_master(pb, blockgroup);
- return duration;
+ return pkt->duration;
}
static void mkv_flush_dynbuf(AVFormatContext *s)
@@ -1473,7 +1658,29 @@ static void mkv_flush_dynbuf(AVFormatContext *s)
mkv->dyn_bc = NULL;
}
-static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
+static void mkv_start_new_cluster(AVFormatContext *s, AVPacket *pkt)
+{
+ MatroskaMuxContext *mkv = s->priv_data;
+ AVIOContext *pb;
+
+ if (s->pb->seekable) {
+ pb = s->pb;
+ } else {
+ pb = mkv->dyn_bc;
+ }
+
+ av_log(s, AV_LOG_DEBUG,
+ "Starting new cluster at offset %" PRIu64 " bytes, "
+ "pts %" PRIu64 "dts %" PRIu64 "\n",
+ avio_tell(pb), pkt->pts, pkt->dts);
+ end_ebml_master(pb, mkv->cluster);
+ mkv->cluster_pos = -1;
+ if (mkv->dyn_bc)
+ mkv_flush_dynbuf(s);
+ avio_flush(s->pb);
+}
+
+static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt, int add_cue)
{
MatroskaMuxContext *mkv = s->priv_data;
AVIOContext *pb = s->pb;
@@ -1482,6 +1689,8 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
int duration = pkt->duration;
int ret;
int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts;
+ int64_t relative_packet_pos;
+ int dash_tracknum = mkv->is_dash ? mkv->dash_track_number : pkt->stream_index + 1;
if (ts == AV_NOPTS_VALUE) {
av_log(s, AV_LOG_ERROR, "Can't write packet with unknown timestamp\n");
@@ -1489,42 +1698,59 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
}
ts += mkv->tracks[pkt->stream_index].ts_offset;
+ if (mkv->cluster_pos != -1) {
+ int64_t cluster_time = ts - mkv->cluster_pts + mkv->tracks[pkt->stream_index].ts_offset;
+ if ((int16_t)cluster_time != cluster_time) {
+ av_log(s, AV_LOG_WARNING, "Starting new cluster due to timestamp\n");
+ mkv_start_new_cluster(s, pkt);
+ }
+ }
+
if (!s->pb->seekable) {
if (!mkv->dyn_bc) {
ret = avio_open_dyn_buf(&mkv->dyn_bc);
- if (ret < 0)
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "Failed to open dynamic buffer\n");
return ret;
+ }
}
pb = mkv->dyn_bc;
}
- if (!mkv->cluster_pos) {
+ if (mkv->cluster_pos == -1) {
mkv->cluster_pos = avio_tell(s->pb);
mkv->cluster = start_ebml_master(pb, MATROSKA_ID_CLUSTER, 0);
put_ebml_uint(pb, MATROSKA_ID_CLUSTERTIMECODE, FFMAX(0, ts));
mkv->cluster_pts = FFMAX(0, ts);
}
+ relative_packet_pos = avio_tell(s->pb) - mkv->cluster.pos;
+
if (codec->codec_type != AVMEDIA_TYPE_SUBTITLE) {
mkv_write_block(s, pb, MATROSKA_ID_SIMPLEBLOCK, pkt, keyframe << 7);
- } else if (codec->codec_id == AV_CODEC_ID_SSA) {
- duration = mkv_write_ass_blocks(s, pb, pkt);
- } else if (codec->codec_id == AV_CODEC_ID_SRT) {
- duration = mkv_write_srt_blocks(s, pb, pkt);
+ if (codec->codec_type == AVMEDIA_TYPE_VIDEO && keyframe || add_cue) {
+ ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, dash_tracknum, ts, mkv->cluster_pos, relative_packet_pos, -1);
+ if (ret < 0) return ret;
+ }
+ } else {
+ if (codec->codec_id == AV_CODEC_ID_WEBVTT) {
+ duration = mkv_write_vtt_blocks(s, pb, pkt);
} else {
ebml_master blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP,
mkv_blockgroup_size(pkt->size));
- duration = pkt->convergence_duration;
+ /* For backward compatibility, prefer convergence_duration. */
+ if (pkt->convergence_duration > 0) {
+ duration = pkt->convergence_duration;
+ }
mkv_write_block(s, pb, MATROSKA_ID_BLOCK, pkt, 0);
put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, duration);
end_ebml_master(pb, blockgroup);
}
- if (codec->codec_type == AVMEDIA_TYPE_VIDEO && keyframe) {
- ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, ts,
- mkv->cluster_pos);
- if (ret < 0)
- return ret;
+ ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, dash_tracknum, ts,
+ mkv->cluster_pos, relative_packet_pos, duration);
+ if (ret < 0)
+ return ret;
}
mkv->duration = FFMAX(mkv->duration, ts + duration);
@@ -1538,8 +1764,8 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt)
int keyframe = !!(pkt->flags & AV_PKT_FLAG_KEY);
int cluster_size;
int64_t cluster_time;
- AVIOContext *pb;
int ret;
+ int start_new_cluster;
if (mkv->tracks[pkt->stream_index].write_dts)
cluster_time = pkt->dts - mkv->cluster_pts;
@@ -1550,32 +1776,40 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt)
// start a new cluster every 5 MB or 5 sec, or 32k / 1 sec for streaming or
// after 4k and on a keyframe
if (s->pb->seekable) {
- pb = s->pb;
- cluster_size = avio_tell(pb) - mkv->cluster_pos;
+ cluster_size = avio_tell(s->pb) - mkv->cluster_pos;
} else {
- pb = mkv->dyn_bc;
- cluster_size = avio_tell(pb);
- }
-
- if (mkv->cluster_pos &&
- (cluster_size > mkv->cluster_size_limit ||
- cluster_time > mkv->cluster_time_limit ||
- (codec_type == AVMEDIA_TYPE_VIDEO && keyframe &&
- cluster_size > 4 * 1024))) {
- av_log(s, AV_LOG_DEBUG,
- "Starting new cluster at offset %" PRIu64 " bytes, "
- "pts %" PRIu64 "dts %" PRIu64 "\n",
- avio_tell(pb), pkt->pts, pkt->dts);
- end_ebml_master(pb, mkv->cluster);
- mkv->cluster_pos = 0;
- if (mkv->dyn_bc)
- mkv_flush_dynbuf(s);
- avio_flush(s->pb);
+ cluster_size = avio_tell(mkv->dyn_bc);
+ }
+
+ if (mkv->is_dash && codec_type == AVMEDIA_TYPE_VIDEO) {
+ // WebM DASH specification states that the first block of every cluster
+ // has to be a key frame. So for DASH video, we only create a cluster
+ // on seeing key frames.
+ start_new_cluster = keyframe;
+ } else if (mkv->is_dash && codec_type == AVMEDIA_TYPE_AUDIO &&
+ (mkv->cluster_pos == -1 ||
+ cluster_time > mkv->cluster_time_limit)) {
+ // For DASH audio, we create a Cluster based on cluster_time_limit
+ start_new_cluster = 1;
+ } else if (!mkv->is_dash &&
+ (cluster_size > mkv->cluster_size_limit ||
+ cluster_time > mkv->cluster_time_limit ||
+ (codec_type == AVMEDIA_TYPE_VIDEO && keyframe &&
+ cluster_size > 4 * 1024))) {
+ start_new_cluster = 1;
+ } else {
+ start_new_cluster = 0;
+ }
+
+ if (mkv->cluster_pos != -1 && start_new_cluster) {
+ mkv_start_new_cluster(s, pkt);
}
// check if we have an audio packet cached
if (mkv->cur_audio_pkt.size > 0) {
- ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt);
+ // for DASH audio, a CuePoint has to be added when there is a new cluster.
+ ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt,
+ mkv->is_dash ? start_new_cluster : 0);
av_free_packet(&mkv->cur_audio_pkt);
if (ret < 0) {
av_log(s, AV_LOG_ERROR,
@@ -1593,8 +1827,11 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt)
ret = mkv->cur_audio_pkt.buf ? 0 : AVERROR(ENOMEM);
} else
ret = av_dup_packet(&mkv->cur_audio_pkt);
+ if (mkv->cur_audio_pkt.side_data_elems > 0) {
+ ret = av_copy_packet_side_data(&mkv->cur_audio_pkt, &mkv->cur_audio_pkt);
+ }
} else
- ret = mkv_write_packet_internal(s, pkt);
+ ret = mkv_write_packet_internal(s, pkt, 0);
return ret;
}
@@ -1607,12 +1844,12 @@ static int mkv_write_flush_packet(AVFormatContext *s, AVPacket *pkt)
else
pb = mkv->dyn_bc;
if (!pkt) {
- if (mkv->cluster_pos) {
+ if (mkv->cluster_pos != -1) {
av_log(s, AV_LOG_DEBUG,
"Flushing cluster at offset %" PRIu64 " bytes\n",
avio_tell(pb));
end_ebml_master(pb, mkv->cluster);
- mkv->cluster_pos = 0;
+ mkv->cluster_pos = -1;
if (mkv->dyn_bc)
mkv_flush_dynbuf(s);
avio_flush(s->pb);
@@ -1631,7 +1868,7 @@ static int mkv_write_trailer(AVFormatContext *s)
// check if we have an audio packet cached
if (mkv->cur_audio_pkt.size > 0) {
- ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt);
+ ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt, 0);
av_free_packet(&mkv->cur_audio_pkt);
if (ret < 0) {
av_log(s, AV_LOG_ERROR,
@@ -1643,7 +1880,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);
}
@@ -1661,7 +1898,7 @@ static int mkv_write_trailer(AVFormatContext *s)
currentpos = avio_tell(pb);
avio_seek(pb, mkv->cues_pos, SEEK_SET);
- cuespos = mkv_write_cues(pb, mkv->cues, s->nb_streams);
+ cuespos = mkv_write_cues(s, mkv->cues, mkv->tracks, s->nb_streams);
cues_end = avio_tell(pb);
if (cues_end > cuespos + mkv->reserve_cues_space) {
av_log(s, AV_LOG_ERROR,
@@ -1677,7 +1914,7 @@ static int mkv_write_trailer(AVFormatContext *s)
avio_seek(pb, currentpos, SEEK_SET);
} else {
- cuespos = mkv_write_cues(pb, mkv->cues, s->nb_streams);
+ cuespos = mkv_write_cues(s, mkv->cues, mkv->tracks, s->nb_streams);
}
ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_CUES,
@@ -1698,7 +1935,7 @@ static int mkv_write_trailer(AVFormatContext *s)
}
end_ebml_master(pb, mkv->segment);
- av_free(mkv->tracks);
+ av_freep(&mkv->tracks);
av_freep(&mkv->cues->entries);
av_freep(&mkv->cues);
@@ -1722,12 +1959,39 @@ static int mkv_query_codec(enum AVCodecID codec_id, int std_compliance)
return 0;
}
+static const AVCodecTag additional_audio_tags[] = {
+ { AV_CODEC_ID_ALAC, 0XFFFFFFFF },
+ { AV_CODEC_ID_EAC3, 0XFFFFFFFF },
+ { AV_CODEC_ID_MLP, 0xFFFFFFFF },
+ { AV_CODEC_ID_OPUS, 0xFFFFFFFF },
+ { AV_CODEC_ID_PCM_S16BE, 0xFFFFFFFF },
+ { AV_CODEC_ID_PCM_S24BE, 0xFFFFFFFF },
+ { AV_CODEC_ID_PCM_S32BE, 0xFFFFFFFF },
+ { AV_CODEC_ID_QDM2, 0xFFFFFFFF },
+ { AV_CODEC_ID_RA_144, 0xFFFFFFFF },
+ { AV_CODEC_ID_RA_288, 0xFFFFFFFF },
+ { AV_CODEC_ID_COOK, 0xFFFFFFFF },
+ { AV_CODEC_ID_TRUEHD, 0xFFFFFFFF },
+ { AV_CODEC_ID_NONE, 0xFFFFFFFF }
+};
+
+static const AVCodecTag additional_video_tags[] = {
+ { AV_CODEC_ID_RV10, 0xFFFFFFFF },
+ { AV_CODEC_ID_RV20, 0xFFFFFFFF },
+ { AV_CODEC_ID_RV30, 0xFFFFFFFF },
+ { AV_CODEC_ID_RV40, 0xFFFFFFFF },
+ { AV_CODEC_ID_VP9, 0xFFFFFFFF },
+ { AV_CODEC_ID_NONE, 0xFFFFFFFF }
+};
+
#define OFFSET(x) offsetof(MatroskaMuxContext, x)
#define FLAGS AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
{ "reserve_index_space", "Reserve a given amount of space (in bytes) at the beginning of the file for the index (cues).", OFFSET(reserve_cues_space), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
{ "cluster_size_limit", "Store at most the provided amount of bytes in a cluster. ", OFFSET(cluster_size_limit), AV_OPT_TYPE_INT , { .i64 = -1 }, -1, INT_MAX, FLAGS },
{ "cluster_time_limit", "Store at most the provided number of milliseconds in a cluster.", OFFSET(cluster_time_limit), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, FLAGS },
+ { "dash", "Create a WebM file conforming to WebM DASH specification", OFFSET(is_dash), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS },
+ { "dash_track_number", "Track number for the DASH stream", OFFSET(dash_track_number), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 127, FLAGS },
{ NULL },
};
@@ -1755,9 +2019,10 @@ AVOutputFormat ff_matroska_muxer = {
.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH,
.codec_tag = (const AVCodecTag* const []){
- ff_codec_bmp_tags, ff_codec_wav_tags, 0
+ ff_codec_bmp_tags, ff_codec_wav_tags,
+ additional_audio_tags, additional_video_tags, 0
},
- .subtitle_codec = AV_CODEC_ID_SSA,
+ .subtitle_codec = AV_CODEC_ID_ASS,
.query_codec = mkv_query_codec,
.priv_class = &matroska_class,
};
@@ -1779,6 +2044,7 @@ AVOutputFormat ff_webm_muxer = {
.priv_data_size = sizeof(MatroskaMuxContext),
.audio_codec = AV_CODEC_ID_VORBIS,
.video_codec = AV_CODEC_ID_VP8,
+ .subtitle_codec = AV_CODEC_ID_WEBVTT,
.write_header = mkv_write_header,
.write_packet = mkv_write_flush_packet,
.write_trailer = mkv_write_trailer,
@@ -1797,7 +2063,7 @@ static const AVClass mka_class = {
};
AVOutputFormat ff_matroska_audio_muxer = {
.name = "matroska",
- .long_name = NULL_IF_CONFIG_SMALL("Matroska"),
+ .long_name = NULL_IF_CONFIG_SMALL("Matroska Audio"),
.mime_type = "audio/x-matroska",
.extensions = "mka",
.priv_data_size = sizeof(MatroskaMuxContext),
@@ -1809,7 +2075,9 @@ AVOutputFormat ff_matroska_audio_muxer = {
.write_trailer = mkv_write_trailer,
.flags = AVFMT_GLOBALHEADER | AVFMT_TS_NONSTRICT |
AVFMT_ALLOW_FLUSH,
- .codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, 0 },
+ .codec_tag = (const AVCodecTag* const []){
+ ff_codec_wav_tags, additional_audio_tags, 0
+ },
.priv_class = &mka_class,
};
#endif
diff --git a/libavformat/md5enc.c b/libavformat/md5enc.c
index 92497045eb..8e87f095c9 100644
--- a/libavformat/md5enc.c
+++ b/libavformat/md5enc.c
@@ -2,38 +2,46 @@
* MD5 encoder (for codec/format testing)
* Copyright (c) 2009 Reimar Döffinger, based on crcenc (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "libavutil/md5.h"
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/hash.h"
+#include "libavutil/opt.h"
#include "avformat.h"
#include "internal.h"
struct MD5Context {
- struct AVMD5 *md5;
+ const AVClass *avclass;
+ struct AVHashContext *hash;
+ char *hash_name;
+ int format_version;
};
static void md5_finish(struct AVFormatContext *s, char *buf)
{
struct MD5Context *c = s->priv_data;
- uint8_t md5[16];
+ uint8_t md5[AV_HASH_MAX_SIZE];
int i, offset = strlen(buf);
- av_md5_final(c->md5, md5);
- for (i = 0; i < sizeof(md5); i++) {
+ int len = av_hash_get_size(c->hash);
+ av_assert0(len > 0 && len <= sizeof(md5));
+ av_hash_final(c->hash, md5);
+ for (i = 0; i < len; i++) {
snprintf(buf + offset, 3, "%02"PRIx8, md5[i]);
offset += 2;
}
@@ -44,39 +52,55 @@ static void md5_finish(struct AVFormatContext *s, char *buf)
avio_flush(s->pb);
}
+#define OFFSET(x) offsetof(struct MD5Context, x)
+#define ENC AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption hash_options[] = {
+ { "hash", "set hash to use", OFFSET(hash_name), AV_OPT_TYPE_STRING, {.str = "md5"}, 0, 0, ENC },
+ { "format_version", "file format version", OFFSET(format_version), AV_OPT_TYPE_INT, {.i64 = 1}, 1, 1, ENC },
+ { NULL },
+};
+
+static const AVClass md5enc_class = {
+ .class_name = "hash encoder class",
+ .item_name = av_default_item_name,
+ .option = hash_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
#if CONFIG_MD5_MUXER
static int write_header(struct AVFormatContext *s)
{
struct MD5Context *c = s->priv_data;
- c->md5 = av_md5_alloc();
- if (!c->md5)
- return AVERROR(ENOMEM);
- av_md5_init(c->md5);
+ int res = av_hash_alloc(&c->hash, c->hash_name);
+ if (res < 0)
+ return res;
+ av_hash_init(c->hash);
return 0;
}
static int write_packet(struct AVFormatContext *s, AVPacket *pkt)
{
struct MD5Context *c = s->priv_data;
- av_md5_update(c->md5, pkt->data, pkt->size);
+ av_hash_update(c->hash, pkt->data, pkt->size);
return 0;
}
static int write_trailer(struct AVFormatContext *s)
{
struct MD5Context *c = s->priv_data;
- char buf[64] = "MD5=";
+ char buf[256];
+ av_strlcpy(buf, av_hash_get_name(c->hash), sizeof(buf) - 200);
+ av_strlcat(buf, "=", sizeof(buf) - 200);
md5_finish(s, buf);
- av_freep(&c->md5);
+ av_hash_freep(&c->hash);
return 0;
}
AVOutputFormat ff_md5_muxer = {
.name = "md5",
.long_name = NULL_IF_CONFIG_SMALL("MD5 testing"),
- .extensions = "",
.priv_data_size = sizeof(struct MD5Context),
.audio_codec = AV_CODEC_ID_PCM_S16LE,
.video_codec = AV_CODEC_ID_RAWVIDEO,
@@ -84,6 +108,7 @@ AVOutputFormat ff_md5_muxer = {
.write_packet = write_packet,
.write_trailer = write_trailer,
.flags = AVFMT_NOTIMESTAMPS,
+ .priv_class = &md5enc_class,
};
#endif
@@ -91,18 +116,23 @@ AVOutputFormat ff_md5_muxer = {
static int framemd5_write_header(struct AVFormatContext *s)
{
struct MD5Context *c = s->priv_data;
- c->md5 = av_md5_alloc();
- if (!c->md5)
- return AVERROR(ENOMEM);
- return ff_framehash_write_header(s);
+ int res = av_hash_alloc(&c->hash, c->hash_name);
+ if (res < 0)
+ return res;
+ avio_printf(s->pb, "#format: frame checksums\n");
+ avio_printf(s->pb, "#version: %d\n", c->format_version);
+ avio_printf(s->pb, "#hash: %s\n", av_hash_get_name(c->hash));
+ ff_framehash_write_header(s);
+ avio_printf(s->pb, "#stream#, dts, pts, duration, size, hash\n");
+ return 0;
}
static int framemd5_write_packet(struct AVFormatContext *s, AVPacket *pkt)
{
struct MD5Context *c = s->priv_data;
char buf[256];
- av_md5_init(c->md5);
- av_md5_update(c->md5, pkt->data, pkt->size);
+ av_hash_init(c->hash);
+ av_hash_update(c->hash, pkt->data, pkt->size);
snprintf(buf, sizeof(buf) - 64, "%d, %10"PRId64", %10"PRId64", %8d, %8d, ",
pkt->stream_index, pkt->dts, pkt->pts, pkt->duration, pkt->size);
@@ -113,14 +143,20 @@ static int framemd5_write_packet(struct AVFormatContext *s, AVPacket *pkt)
static int framemd5_write_trailer(struct AVFormatContext *s)
{
struct MD5Context *c = s->priv_data;
- av_freep(&c->md5);
+ av_hash_freep(&c->hash);
return 0;
}
+static const AVClass framemd5_class = {
+ .class_name = "frame hash encoder class",
+ .item_name = av_default_item_name,
+ .option = hash_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVOutputFormat ff_framemd5_muxer = {
.name = "framemd5",
.long_name = NULL_IF_CONFIG_SMALL("Per-frame MD5 testing"),
- .extensions = "",
.priv_data_size = sizeof(struct MD5Context),
.audio_codec = AV_CODEC_ID_PCM_S16LE,
.video_codec = AV_CODEC_ID_RAWVIDEO,
@@ -129,5 +165,6 @@ AVOutputFormat ff_framemd5_muxer = {
.write_trailer = framemd5_write_trailer,
.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT |
AVFMT_TS_NEGATIVE,
+ .priv_class = &framemd5_class,
};
#endif
diff --git a/libavformat/md5proto.c b/libavformat/md5proto.c
index 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.c b/libavformat/metadata.c
index 77fb298217..b9b6de7972 100644
--- a/libavformat/metadata.c
+++ b/libavformat/metadata.c
@@ -1,20 +1,20 @@
/*
* copyright (c) 2009 Michael Niedermayer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -33,7 +33,7 @@ void ff_metadata_conv(AVDictionary **pm, const AVMetadataConv *d_conv,
AVDictionary *dst = NULL;
const char *key;
- if (d_conv == s_conv)
+ if (d_conv == s_conv || !pm)
return;
while ((mtag = av_dict_get(*pm, "", mtag, AV_DICT_IGNORE_SUFFIX))) {
diff --git a/libavformat/metadata.h b/libavformat/metadata.h
index 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..8cbc9521d6
--- /dev/null
+++ b/libavformat/mgsts.c
@@ -0,0 +1,106 @@
+/*
+ * Metar Gear Solid: The Twin Snakes demuxer
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/intfloat.h"
+#include "avformat.h"
+#include "riff.h"
+
+static int read_probe(AVProbeData *p)
+{
+ if (AV_RB32(p->buf ) != 0x000E ||
+ AV_RB32(p->buf + 4) != 0x0050 ||
+ AV_RB32(p->buf + 12) != 0x0034)
+ return 0;
+ return AVPROBE_SCORE_MAX;
+}
+
+static int read_header(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ AVStream *st;
+ AVRational fps;
+ uint32_t chunk_size;
+
+ avio_skip(pb, 4);
+ chunk_size = avio_rb32(pb);
+ if (chunk_size != 80)
+ return AVERROR(EIO);
+ avio_skip(pb, 20);
+
+ st = avformat_new_stream(s, 0);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->need_parsing = AVSTREAM_PARSE_HEADERS;
+ st->start_time = 0;
+ st->nb_frames =
+ st->duration = avio_rb32(pb);
+ fps = av_d2q(av_int2float(avio_rb32(pb)), INT_MAX);
+ st->codec->width = avio_rb32(pb);
+ st->codec->height = avio_rb32(pb);
+ avio_skip(pb, 12);
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codec->codec_tag = avio_rb32(pb);
+ st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags,
+ st->codec->codec_tag);
+ avpriv_set_pts_info(st, 64, fps.den, fps.num);
+ avio_skip(pb, 20);
+
+ return 0;
+}
+
+static int read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVIOContext *pb = s->pb;
+ uint32_t chunk_size, payload_size;
+ int ret;
+
+ if (avio_feof(pb))
+ return AVERROR_EOF;
+
+ avio_skip(pb, 4);
+ chunk_size = avio_rb32(pb);
+ avio_skip(pb, 4);
+ payload_size = avio_rb32(pb);
+
+ if (chunk_size < payload_size + 16)
+ return AVERROR(EIO);
+
+ ret = av_get_packet(pb, pkt, payload_size);
+ if (ret < 0)
+ return ret;
+
+ pkt->pos -= 16;
+ pkt->duration = 1;
+ avio_skip(pb, chunk_size - (ret + 16));
+
+ return ret;
+}
+
+AVInputFormat ff_mgsts_demuxer = {
+ .name = "mgsts",
+ .long_name = NULL_IF_CONFIG_SMALL("Metal Gear Solid: The Twin Snakes"),
+ .read_probe = read_probe,
+ .read_header = read_header,
+ .read_packet = read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/microdvddec.c b/libavformat/microdvddec.c
new file mode 100644
index 0000000000..ce3433cf25
--- /dev/null
+++ b/libavformat/microdvddec.c
@@ -0,0 +1,203 @@
+/*
+ * MicroDVD subtitle demuxer
+ * Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org>
+ * Copyright (c) 2012 Clément Bœsch <u pkh me>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "subtitles.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+
+#define MAX_LINESIZE 2048
+
+
+typedef struct {
+ const AVClass *class;
+ FFDemuxSubtitlesQueue q;
+ AVRational frame_rate;
+} MicroDVDContext;
+
+
+static int microdvd_probe(AVProbeData *p)
+{
+ unsigned char c;
+ const uint8_t *ptr = p->buf;
+ int i;
+
+ if (AV_RB24(ptr) == 0xEFBBBF)
+ ptr += 3; /* skip UTF-8 BOM */
+
+ for (i=0; i<3; i++) {
+ if (sscanf(ptr, "{%*d}{}%c", &c) != 1 &&
+ sscanf(ptr, "{%*d}{%*d}%c", &c) != 1 &&
+ sscanf(ptr, "{DEFAULT}{}%c", &c) != 1)
+ return 0;
+ ptr += ff_subtitles_next_line(ptr);
+ }
+ return AVPROBE_SCORE_MAX;
+}
+
+static int64_t get_pts(const char *buf)
+{
+ int frame;
+ char c;
+
+ if (sscanf(buf, "{%d}{%c", &frame, &c) == 2)
+ return frame;
+ return AV_NOPTS_VALUE;
+}
+
+static int get_duration(const char *buf)
+{
+ int frame_start, frame_end;
+
+ if (sscanf(buf, "{%d}{%d}", &frame_start, &frame_end) == 2)
+ return frame_end - frame_start;
+ return -1;
+}
+
+static const char *bom = "\xEF\xBB\xBF";
+
+static int microdvd_read_header(AVFormatContext *s)
+{
+ AVRational pts_info = (AVRational){ 2997, 125 }; /* default: 23.976 fps */
+ MicroDVDContext *microdvd = s->priv_data;
+ AVStream *st = avformat_new_stream(s, NULL);
+ int i = 0;
+ char line_buf[MAX_LINESIZE];
+ int has_real_fps = 0;
+
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ while (!avio_feof(s->pb)) {
+ char *p;
+ AVPacket *sub;
+ int64_t pos = avio_tell(s->pb);
+ int len = ff_get_line(s->pb, line_buf, sizeof(line_buf));
+ char *line = line_buf;
+
+ if (!strncmp(line, bom, 3))
+ line += 3;
+ p = line;
+
+ if (!len)
+ break;
+ line[strcspn(line, "\r\n")] = 0;
+ if (i++ < 3) {
+ int frame;
+ double fps;
+ char c;
+
+ if ((sscanf(line, "{%d}{}%6lf", &frame, &fps) == 2 ||
+ sscanf(line, "{%d}{%*d}%6lf", &frame, &fps) == 2)
+ && frame <= 1 && fps > 3 && fps < 100) {
+ pts_info = av_d2q(fps, 100000);
+ has_real_fps = 1;
+ }
+ if (!st->codec->extradata && sscanf(line, "{DEFAULT}{}%c", &c) == 1) {
+ st->codec->extradata = av_strdup(line + 11);
+ if (!st->codec->extradata)
+ return AVERROR(ENOMEM);
+ st->codec->extradata_size = strlen(st->codec->extradata) + 1;
+ continue;
+ }
+ }
+#define SKIP_FRAME_ID \
+ p = strchr(p, '}'); \
+ if (!p) { \
+ av_log(s, AV_LOG_WARNING, "Invalid event \"%s\"" \
+ " at line %d\n", line, i); \
+ continue; \
+ } \
+ p++
+ SKIP_FRAME_ID;
+ SKIP_FRAME_ID;
+ if (!*p)
+ continue;
+ sub = ff_subtitles_queue_insert(&microdvd->q, p, strlen(p), 0);
+ if (!sub)
+ return AVERROR(ENOMEM);
+ sub->pos = pos;
+ sub->pts = get_pts(line);
+ sub->duration = get_duration(line);
+ }
+ ff_subtitles_queue_finalize(&microdvd->q);
+ if (has_real_fps) {
+ /* export the FPS info only if set in the file */
+ microdvd->frame_rate = pts_info;
+ } else if (microdvd->frame_rate.num) {
+ /* fallback on user specified frame rate */
+ pts_info = microdvd->frame_rate;
+ }
+ avpriv_set_pts_info(st, 64, pts_info.den, pts_info.num);
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_MICRODVD;
+ return 0;
+}
+
+static int microdvd_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ MicroDVDContext *microdvd = s->priv_data;
+ return ff_subtitles_queue_read_packet(&microdvd->q, pkt);
+}
+
+static int microdvd_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ MicroDVDContext *microdvd = s->priv_data;
+ return ff_subtitles_queue_seek(&microdvd->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int microdvd_read_close(AVFormatContext *s)
+{
+ MicroDVDContext *microdvd = s->priv_data;
+ ff_subtitles_queue_clean(&microdvd->q);
+ return 0;
+}
+
+
+#define OFFSET(x) offsetof(MicroDVDContext, x)
+#define SD AV_OPT_FLAG_SUBTITLE_PARAM|AV_OPT_FLAG_DECODING_PARAM
+static const AVOption microdvd_options[] = {
+ { "subfps", "set the movie frame rate fallback", OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, SD },
+ { NULL }
+};
+
+static const AVClass microdvd_class = {
+ .class_name = "microdvddec",
+ .item_name = av_default_item_name,
+ .option = microdvd_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_microdvd_demuxer = {
+ .name = "microdvd",
+ .long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle format"),
+ .priv_data_size = sizeof(MicroDVDContext),
+ .read_probe = microdvd_probe,
+ .read_header = microdvd_read_header,
+ .read_packet = microdvd_read_packet,
+ .read_seek2 = microdvd_read_seek,
+ .read_close = microdvd_read_close,
+ .priv_class = &microdvd_class,
+};
diff --git a/libavformat/microdvdenc.c b/libavformat/microdvdenc.c
new file mode 100644
index 0000000000..4d8438437b
--- /dev/null
+++ b/libavformat/microdvdenc.c
@@ -0,0 +1,67 @@
+/*
+ * MicroDVD subtitle muxer
+ * Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <inttypes.h>
+#include "avformat.h"
+#include "internal.h"
+
+static int microdvd_write_header(struct AVFormatContext *s)
+{
+ AVCodecContext *avctx = s->streams[0]->codec;
+ AVRational tb = avctx->time_base;
+
+ if (s->nb_streams != 1 || avctx->codec_id != AV_CODEC_ID_MICRODVD) {
+ av_log(s, AV_LOG_ERROR, "Exactly one MicroDVD stream is needed.\n");
+ return -1;
+ }
+
+ if (avctx->extradata && avctx->extradata_size > 0) {
+ avio_write(s->pb, "{DEFAULT}{}", 11);
+ avio_write(s->pb, avctx->extradata, avctx->extradata_size);
+ avio_flush(s->pb);
+ }
+
+ avpriv_set_pts_info(s->streams[0], 64, tb.num, tb.den);
+ return 0;
+}
+
+static int microdvd_write_packet(AVFormatContext *avf, AVPacket *pkt)
+{
+ avio_printf(avf->pb, "{%"PRId64"}", pkt->pts);
+ if (pkt->duration < 0)
+ avio_write(avf->pb, "{}", 2);
+ else
+ avio_printf(avf->pb, "{%"PRId64"}", pkt->pts + pkt->duration);
+ avio_write(avf->pb, pkt->data, pkt->size);
+ avio_write(avf->pb, "\n", 1);
+ return 0;
+}
+
+AVOutputFormat ff_microdvd_muxer = {
+ .name = "microdvd",
+ .long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle format"),
+ .mime_type = "text/x-microdvd",
+ .extensions = "sub",
+ .write_header = microdvd_write_header,
+ .write_packet = microdvd_write_packet,
+ .flags = AVFMT_NOTIMESTAMPS,
+ .subtitle_codec = AV_CODEC_ID_MICRODVD,
+};
diff --git a/libavformat/mkvtimestamp_v2.c b/libavformat/mkvtimestamp_v2.c
new file mode 100644
index 0000000000..7ba6691952
--- /dev/null
+++ b/libavformat/mkvtimestamp_v2.c
@@ -0,0 +1,50 @@
+/*
+ * extract pts as timecode v2, as defined by mkvtoolnix
+ * Copyright (c) 2009 David Conrad
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "internal.h"
+
+static int write_header(AVFormatContext *s)
+{
+ static const char *header = "# timecode format v2\n";
+ avio_write(s->pb, header, strlen(header));
+ avpriv_set_pts_info(s->streams[0], 64, 1, 1000);
+ return 0;
+}
+
+static int write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ char buf[256];
+ if (pkt->stream_index)
+ av_log(s, AV_LOG_WARNING, "More than one stream unsupported\n");
+ snprintf(buf, sizeof(buf), "%" PRId64 "\n", pkt->dts);
+ avio_write(s->pb, buf, strlen(buf));
+ return 0;
+}
+
+AVOutputFormat ff_mkvtimestamp_v2_muxer = {
+ .name = "mkvtimestamp_v2",
+ .long_name = NULL_IF_CONFIG_SMALL("extract pts as timecode v2 format, as defined by mkvtoolnix"),
+ .audio_codec = AV_CODEC_ID_NONE,
+ .video_codec = AV_CODEC_ID_RAWVIDEO,
+ .write_header = write_header,
+ .write_packet = write_packet,
+};
diff --git a/libavformat/mlvdec.c b/libavformat/mlvdec.c
new file mode 100644
index 0000000000..9d821bb7b3
--- /dev/null
+++ b/libavformat/mlvdec.c
@@ -0,0 +1,458 @@
+/*
+ * Magic Lantern Video (MLV) demuxer
+ * Copyright (c) 2014 Peter Ross
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Magic Lantern Video (MLV) demuxer
+ */
+
+#include "libavutil/eval.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/rational.h"
+#include "avformat.h"
+#include "internal.h"
+#include "riff.h"
+
+#define MLV_VERSION "v2.0"
+
+#define MLV_VIDEO_CLASS_RAW 1
+#define MLV_VIDEO_CLASS_YUV 2
+#define MLV_VIDEO_CLASS_JPEG 3
+#define MLV_VIDEO_CLASS_H264 4
+
+#define MLV_AUDIO_CLASS_WAV 1
+
+#define MLV_CLASS_FLAG_DELTA 0x40
+#define MLV_CLASS_FLAG_LZMA 0x80
+
+typedef struct {
+ AVIOContext *pb[101];
+ int class[2];
+ int stream_index;
+ uint64_t pts;
+} MlvContext;
+
+static int probe(AVProbeData *p)
+{
+ if (AV_RL32(p->buf) == MKTAG('M','L','V','I') &&
+ AV_RL32(p->buf + 4) >= 52 &&
+ !memcmp(p->buf + 8, MLV_VERSION, 5))
+ return AVPROBE_SCORE_MAX;
+ return 0;
+}
+
+static int check_file_header(AVIOContext *pb, uint64_t guid)
+{
+ unsigned int size;
+ uint8_t version[8];
+
+ avio_skip(pb, 4);
+ size = avio_rl32(pb);
+ if (size < 52)
+ return AVERROR_INVALIDDATA;
+ avio_read(pb, version, 8);
+ if (memcmp(version, MLV_VERSION, 5) || avio_rl64(pb) != guid)
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, size - 24);
+ return 0;
+}
+
+static void read_string(AVFormatContext *avctx, AVIOContext *pb, const char *tag, int size)
+{
+ char * value = av_malloc(size + 1);
+ if (!value) {
+ avio_skip(pb, size);
+ return;
+ }
+
+ avio_read(pb, value, size);
+ if (!value[0]) {
+ av_free(value);
+ return;
+ }
+
+ value[size] = 0;
+ av_dict_set(&avctx->metadata, tag, value, AV_DICT_DONT_STRDUP_VAL);
+}
+
+static void read_uint8(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt)
+{
+ av_dict_set_int(&avctx->metadata, tag, avio_r8(pb), 0);
+}
+
+static void read_uint16(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt)
+{
+ av_dict_set_int(&avctx->metadata, tag, avio_rl16(pb), 0);
+}
+
+static void read_uint32(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt)
+{
+ av_dict_set_int(&avctx->metadata, tag, avio_rl32(pb), 0);
+}
+
+static void read_uint64(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt)
+{
+ av_dict_set_int(&avctx->metadata, tag, avio_rl64(pb), 0);
+}
+
+static int scan_file(AVFormatContext *avctx, AVStream *vst, AVStream *ast, int file)
+{
+ MlvContext *mlv = avctx->priv_data;
+ AVIOContext *pb = mlv->pb[file];
+ int ret;
+ while (!avio_feof(pb)) {
+ int type;
+ unsigned int size;
+ type = avio_rl32(pb);
+ size = avio_rl32(pb);
+ avio_skip(pb, 8); //timestamp
+ if (size < 16)
+ break;
+ size -= 16;
+ if (vst && type == MKTAG('R','A','W','I') && size >= 164) {
+ vst->codec->width = avio_rl16(pb);
+ vst->codec->height = avio_rl16(pb);
+ if (avio_rl32(pb) != 1)
+ avpriv_request_sample(avctx, "raw api version");
+ avio_skip(pb, 20); // pointer, width, height, pitch, frame_size
+ vst->codec->bits_per_coded_sample = avio_rl32(pb);
+ avio_skip(pb, 8 + 16 + 24); // black_level, white_level, xywh, active_area, exposure_bias
+ if (avio_rl32(pb) != 0x2010100) /* RGGB */
+ avpriv_request_sample(avctx, "cfa_pattern");
+ avio_skip(pb, 80); // calibration_illuminant1, color_matrix1, dynamic_range
+ vst->codec->pix_fmt = AV_PIX_FMT_BAYER_RGGB16LE;
+ vst->codec->codec_tag = MKTAG('B', 'I', 'T', 16);
+ size -= 164;
+ } else if (ast && type == MKTAG('W', 'A', 'V', 'I') && size >= 16) {
+ ret = ff_get_wav_header(pb, ast->codec, 16, 0);
+ if (ret < 0)
+ return ret;
+ size -= 16;
+ } else if (type == MKTAG('I','N','F','O')) {
+ if (size > 0)
+ read_string(avctx, pb, "info", size);
+ continue;
+ } else if (type == MKTAG('I','D','N','T') && size >= 36) {
+ read_string(avctx, pb, "cameraName", 32);
+ read_uint32(avctx, pb, "cameraModel", "0x%"PRIx32);
+ size -= 36;
+ if (size >= 32) {
+ read_string(avctx, pb, "cameraSerial", 32);
+ size -= 32;
+ }
+ } else if (type == MKTAG('L','E','N','S') && size >= 48) {
+ read_uint16(avctx, pb, "focalLength", "%i");
+ read_uint16(avctx, pb, "focalDist", "%i");
+ read_uint16(avctx, pb, "aperture", "%i");
+ read_uint8(avctx, pb, "stabilizerMode", "%i");
+ read_uint8(avctx, pb, "autofocusMode", "%i");
+ read_uint32(avctx, pb, "flags", "0x%"PRIx32);
+ read_uint32(avctx, pb, "lensID", "%"PRIi32);
+ read_string(avctx, pb, "lensName", 32);
+ size -= 48;
+ if (size >= 32) {
+ read_string(avctx, pb, "lensSerial", 32);
+ size -= 32;
+ }
+ } else if (vst && type == MKTAG('V', 'I', 'D', 'F') && size >= 4) {
+ uint64_t pts = avio_rl32(pb);
+ ff_add_index_entry(&vst->index_entries, &vst->nb_index_entries, &vst->index_entries_allocated_size,
+ avio_tell(pb) - 20, pts, file, 0, AVINDEX_KEYFRAME);
+ size -= 4;
+ } else if (ast && type == MKTAG('A', 'U', 'D', 'F') && size >= 4) {
+ uint64_t pts = avio_rl32(pb);
+ ff_add_index_entry(&ast->index_entries, &ast->nb_index_entries, &ast->index_entries_allocated_size,
+ avio_tell(pb) - 20, pts, file, 0, AVINDEX_KEYFRAME);
+ size -= 4;
+ } else if (vst && type == MKTAG('W','B','A','L') && size >= 28) {
+ read_uint32(avctx, pb, "wb_mode", "%"PRIi32);
+ read_uint32(avctx, pb, "kelvin", "%"PRIi32);
+ read_uint32(avctx, pb, "wbgain_r", "%"PRIi32);
+ read_uint32(avctx, pb, "wbgain_g", "%"PRIi32);
+ read_uint32(avctx, pb, "wbgain_b", "%"PRIi32);
+ read_uint32(avctx, pb, "wbs_gm", "%"PRIi32);
+ read_uint32(avctx, pb, "wbs_ba", "%"PRIi32);
+ size -= 28;
+ } else if (type == MKTAG('R','T','C','I') && size >= 20) {
+ char str[32];
+ struct tm time = { 0 };
+ time.tm_sec = avio_rl16(pb);
+ time.tm_min = avio_rl16(pb);
+ time.tm_hour = avio_rl16(pb);
+ time.tm_mday = avio_rl16(pb);
+ time.tm_mon = avio_rl16(pb);
+ time.tm_year = avio_rl16(pb);
+ time.tm_wday = avio_rl16(pb);
+ time.tm_yday = avio_rl16(pb);
+ time.tm_isdst = avio_rl16(pb);
+ avio_skip(pb, 2);
+ if (strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", &time))
+ av_dict_set(&avctx->metadata, "time", str, 0);
+ size -= 20;
+ } else if (type == MKTAG('E','X','P','O') && size >= 16) {
+ av_dict_set(&avctx->metadata, "isoMode", avio_rl32(pb) ? "auto" : "manual", 0);
+ read_uint32(avctx, pb, "isoValue", "%"PRIi32);
+ read_uint32(avctx, pb, "isoAnalog", "%"PRIi32);
+ read_uint32(avctx, pb, "digitalGain", "%"PRIi32);
+ size -= 16;
+ if (size >= 8) {
+ read_uint64(avctx, pb, "shutterValue", "%"PRIi64);
+ size -= 8;
+ }
+ } else if (type == MKTAG('S','T','Y','L') && size >= 36) {
+ read_uint32(avctx, pb, "picStyleId", "%"PRIi32);
+ read_uint32(avctx, pb, "contrast", "%"PRIi32);
+ read_uint32(avctx, pb, "sharpness", "%"PRIi32);
+ read_uint32(avctx, pb, "saturation", "%"PRIi32);
+ read_uint32(avctx, pb, "colortone", "%"PRIi32);
+ read_string(avctx, pb, "picStyleName", 16);
+ size -= 36;
+ } else if (type == MKTAG('M','A','R','K')) {
+ } else if (type == MKTAG('N','U','L','L')) {
+ } else if (type == MKTAG('M','L','V','I')) { /* occurs when MLV and Mnn files are concatenated */
+ } else {
+ av_log(avctx, AV_LOG_INFO, "unsupported tag %c%c%c%c, size %u\n", type&0xFF, (type>>8)&0xFF, (type>>16)&0xFF, (type>>24)&0xFF, size);
+ }
+ avio_skip(pb, size);
+ }
+ return 0;
+}
+
+static int read_header(AVFormatContext *avctx)
+{
+ MlvContext *mlv = avctx->priv_data;
+ AVIOContext *pb = avctx->pb;
+ AVStream *vst = NULL, *ast = NULL;
+ int size, ret;
+ unsigned nb_video_frames, nb_audio_frames;
+ uint64_t guid;
+ char guidstr[32];
+
+ avio_skip(pb, 4);
+ size = avio_rl32(pb);
+ if (size < 52)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(pb, 8);
+
+ guid = avio_rl64(pb);
+ snprintf(guidstr, sizeof(guidstr), "0x%"PRIx64, guid);
+ av_dict_set(&avctx->metadata, "guid", guidstr, 0);
+
+ avio_skip(pb, 8); //fileNum, fileCount, fileFlags
+
+ mlv->class[0] = avio_rl16(pb);
+ mlv->class[1] = avio_rl16(pb);
+
+ nb_video_frames = avio_rl32(pb);
+ nb_audio_frames = avio_rl32(pb);
+
+ if (nb_video_frames && mlv->class[0]) {
+ vst = avformat_new_stream(avctx, NULL);
+ if (!vst)
+ return AVERROR(ENOMEM);
+ vst->id = 0;
+ vst->nb_frames = nb_video_frames;
+ if ((mlv->class[0] & (MLV_CLASS_FLAG_DELTA|MLV_CLASS_FLAG_LZMA)))
+ avpriv_request_sample(avctx, "compression");
+ vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ switch (mlv->class[0] & ~(MLV_CLASS_FLAG_DELTA|MLV_CLASS_FLAG_LZMA)) {
+ case MLV_VIDEO_CLASS_RAW:
+ vst->codec->codec_id = AV_CODEC_ID_RAWVIDEO;
+ break;
+ case MLV_VIDEO_CLASS_YUV:
+ vst->codec->pix_fmt = AV_PIX_FMT_YUV420P;
+ vst->codec->codec_id = AV_CODEC_ID_RAWVIDEO;
+ vst->codec->codec_tag = 0;
+ break;
+ case MLV_VIDEO_CLASS_JPEG:
+ vst->codec->codec_id = AV_CODEC_ID_MJPEG;
+ vst->codec->codec_tag = 0;
+ break;
+ case MLV_VIDEO_CLASS_H264:
+ vst->codec->codec_id = AV_CODEC_ID_H264;
+ vst->codec->codec_tag = 0;
+ break;
+ default:
+ avpriv_request_sample(avctx, "unknown video class");
+ }
+ }
+
+ if (nb_audio_frames && mlv->class[1]) {
+ ast = avformat_new_stream(avctx, NULL);
+ if (!ast)
+ return AVERROR(ENOMEM);
+ ast->id = 1;
+ ast->nb_frames = nb_audio_frames;
+ if ((mlv->class[1] & MLV_CLASS_FLAG_LZMA))
+ avpriv_request_sample(avctx, "compression");
+ if ((mlv->class[1] & ~MLV_CLASS_FLAG_LZMA) != MLV_AUDIO_CLASS_WAV)
+ avpriv_request_sample(avctx, "unknown audio class");
+
+ ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ avpriv_set_pts_info(ast, 33, 1, ast->codec->sample_rate);
+ }
+
+ if (vst) {
+ AVRational framerate;
+ framerate.num = avio_rl32(pb);
+ framerate.den = avio_rl32(pb);
+ avpriv_set_pts_info(vst, 64, framerate.den, framerate.num);
+ } else
+ avio_skip(pb, 8);
+
+ avio_skip(pb, size - 52);
+
+ /* scan primary file */
+ mlv->pb[100] = avctx->pb;
+ ret = scan_file(avctx, vst, ast, 100);
+ if (ret < 0)
+ return ret;
+
+ /* scan secondary files */
+ if (strlen(avctx->filename) > 2) {
+ int i;
+ char *filename = av_strdup(avctx->filename);
+ if (!filename)
+ return AVERROR(ENOMEM);
+ for (i = 0; i < 100; i++) {
+ snprintf(filename + strlen(filename) - 2, 3, "%02d", i);
+ if (avio_open2(&mlv->pb[i], filename, AVIO_FLAG_READ, &avctx->interrupt_callback, NULL) < 0)
+ break;
+ if (check_file_header(mlv->pb[i], guid) < 0) {
+ av_log(avctx, AV_LOG_WARNING, "ignoring %s; bad format or guid mismatch\n", filename);
+ avio_closep(&mlv->pb[i]);
+ continue;
+ }
+ av_log(avctx, AV_LOG_INFO, "scanning %s\n", filename);
+ ret = scan_file(avctx, vst, ast, i);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_WARNING, "ignoring %s; %s\n", filename, av_err2str(ret));
+ avio_closep(&mlv->pb[i]);
+ continue;
+ }
+ }
+ av_free(filename);
+ }
+
+ if (vst)
+ vst->duration = vst->nb_index_entries;
+ if (ast)
+ ast->duration = ast->nb_index_entries;
+
+ if (vst && ast)
+ avio_seek(pb, FFMIN(vst->index_entries[0].pos, ast->index_entries[0].pos), SEEK_SET);
+ else if (vst)
+ avio_seek(pb, vst->index_entries[0].pos, SEEK_SET);
+ else if (ast)
+ avio_seek(pb, ast->index_entries[0].pos, SEEK_SET);
+
+ return 0;
+}
+
+static int read_packet(AVFormatContext *avctx, AVPacket *pkt)
+{
+ MlvContext *mlv = avctx->priv_data;
+ AVIOContext *pb;
+ AVStream *st = avctx->streams[mlv->stream_index];
+ int index, ret;
+ unsigned int size, space;
+
+ if (mlv->pts >= st->duration)
+ return AVERROR_EOF;
+
+ index = av_index_search_timestamp(st, mlv->pts, AVSEEK_FLAG_ANY);
+ if (index < 0) {
+ av_log(avctx, AV_LOG_ERROR, "could not find index entry for frame %"PRId64"\n", mlv->pts);
+ return AVERROR(EIO);
+ }
+
+ pb = mlv->pb[st->index_entries[index].size];
+ avio_seek(pb, st->index_entries[index].pos, SEEK_SET);
+
+ avio_skip(pb, 4); // blockType
+ size = avio_rl32(pb);
+ if (size < 16)
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, 12); //timestamp, frameNumber
+ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
+ avio_skip(pb, 8); // cropPosX, cropPosY, panPosX, panPosY
+ space = avio_rl32(pb);
+ avio_skip(pb, space);
+
+ if ((mlv->class[st->id] & (MLV_CLASS_FLAG_DELTA|MLV_CLASS_FLAG_LZMA))) {
+ ret = AVERROR_PATCHWELCOME;
+ } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ ret = av_get_packet(pb, pkt, (st->codec->width * st->codec->height * st->codec->bits_per_coded_sample + 7) >> 3);
+ } else { // AVMEDIA_TYPE_AUDIO
+ if (space > UINT_MAX - 24 || size < (24 + space))
+ return AVERROR_INVALIDDATA;
+ ret = av_get_packet(pb, pkt, size - (24 + space));
+ }
+
+ if (ret < 0)
+ return ret;
+
+ pkt->stream_index = mlv->stream_index;
+ pkt->pts = mlv->pts;
+
+ mlv->stream_index++;
+ if (mlv->stream_index == avctx->nb_streams) {
+ mlv->stream_index = 0;
+ mlv->pts++;
+ }
+ return 0;
+}
+
+static int read_seek(AVFormatContext *avctx, int stream_index, int64_t timestamp, int flags)
+{
+ MlvContext *mlv = avctx->priv_data;
+
+ if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE))
+ return AVERROR(ENOSYS);
+
+ if (!avctx->pb->seekable)
+ return AVERROR(EIO);
+
+ mlv->pts = timestamp;
+ return 0;
+}
+
+static int read_close(AVFormatContext *s)
+{
+ MlvContext *mlv = s->priv_data;
+ int i;
+ for (i = 0; i < 100; i++)
+ if (mlv->pb[i])
+ avio_closep(&mlv->pb[i]);
+ return 0;
+}
+
+AVInputFormat ff_mlv_demuxer = {
+ .name = "mlv",
+ .long_name = NULL_IF_CONFIG_SMALL("Magic Lantern Video (MLV)"),
+ .priv_data_size = sizeof(MlvContext),
+ .read_probe = probe,
+ .read_header = read_header,
+ .read_packet = read_packet,
+ .read_close = read_close,
+ .read_seek = read_seek,
+};
diff --git a/libavformat/mm.c b/libavformat/mm.c
index cdf1010dc6..81ae1a510e 100644
--- a/libavformat/mm.c
+++ b/libavformat/mm.c
@@ -2,20 +2,20 @@
* American Laser Games MM Format Demuxer
* Copyright (c) 2006 Peter Ross
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -176,7 +176,6 @@ static int read_packet(AVFormatContext *s,
case MM_TYPE_AUDIO :
if (av_get_packet(s->pb, pkt, length)<0)
return AVERROR(ENOMEM);
- pkt->size = length;
pkt->stream_index = 1;
pkt->pts = mm->audio_pts++;
return 0;
diff --git a/libavformat/mmf.c b/libavformat/mmf.c
index 6fb63bf9ab..c2acec91eb 100644
--- a/libavformat/mmf.c
+++ b/libavformat/mmf.c
@@ -2,20 +2,20 @@
* Yamaha SMAF format
* Copyright (c) 2005 Vidar Madsen
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -24,11 +24,13 @@
#include "avio_internal.h"
#include "internal.h"
#include "pcm.h"
+#include "rawenc.h"
#include "riff.h"
typedef struct MMFContext {
int64_t atrpos, atsqpos, awapos;
- int64_t data_size;
+ int64_t data_end;
+ int stereo;
} MMFContext;
static const int mmf_rates[] = { 4000, 8000, 11025, 22050, 44100 };
@@ -67,26 +69,38 @@ static int mmf_write_header(AVFormatContext *s)
AVIOContext *pb = s->pb;
int64_t pos;
int rate;
+ const char *version = s->flags & AVFMT_FLAG_BITEXACT ?
+ "VN:Lavf," :
+ "VN:"LIBAVFORMAT_IDENT",";
rate = mmf_rate_code(s->streams[0]->codec->sample_rate);
if (rate < 0) {
- av_log(s, AV_LOG_ERROR, "Unsupported sample rate %d\n",
+ av_log(s, AV_LOG_ERROR, "Unsupported sample rate %d, supported are 4000, 8000, 11025, 22050 and 44100\n",
s->streams[0]->codec->sample_rate);
- return -1;
+ return AVERROR(EINVAL);
+ }
+
+ mmf->stereo = s->streams[0]->codec->channels > 1;
+ if (mmf->stereo &&
+ s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
+ av_log(s, AV_LOG_ERROR, "Yamaha SMAF stereo is experimental, "
+ "add '-strict %d' if you want to use it.\n",
+ FF_COMPLIANCE_EXPERIMENTAL);
+ return AVERROR(EINVAL);
}
ffio_wfourcc(pb, "MMMD");
avio_wb32(pb, 0);
pos = ff_start_tag(pb, "CNTI");
avio_w8(pb, 0); /* class */
- avio_w8(pb, 0); /* type */
- avio_w8(pb, 0); /* code type */
+ avio_w8(pb, 1); /* type */
+ avio_w8(pb, 1); /* code type */
avio_w8(pb, 0); /* status */
avio_w8(pb, 0); /* counts */
end_tag_be(pb, pos);
pos = ff_start_tag(pb, "OPDA");
- avio_write(pb, "VN:libavcodec,", sizeof("VN:libavcodec,") -1); /* metadata ("ST:songtitle,VN:version,...") */
+ avio_write(pb, version, strlen(version)); /* metadata ("ST:songtitle,VN:version,...") */
end_tag_be(pb, pos);
avio_write(pb, "ATR\x00", 4);
@@ -94,7 +108,7 @@ static int mmf_write_header(AVFormatContext *s)
mmf->atrpos = avio_tell(pb);
avio_w8(pb, 0); /* format type */
avio_w8(pb, 0); /* sequence type */
- avio_w8(pb, (0 << 7) | (1 << 4) | rate); /* (channel << 7) | (format << 4) | rate */
+ avio_w8(pb, (mmf->stereo << 7) | (1 << 4) | rate); /* (channel << 7) | (format << 4) | rate */
avio_w8(pb, 0); /* wave base bit */
avio_w8(pb, 2); /* time base d */
avio_w8(pb, 2); /* time base g */
@@ -114,13 +128,6 @@ static int mmf_write_header(AVFormatContext *s)
return 0;
}
-static int mmf_write_packet(AVFormatContext *s, AVPacket *pkt)
-{
- AVIOContext *pb = s->pb;
- avio_write(pb, pkt->data, pkt->size);
- return 0;
-}
-
/* Write a variable-length symbol */
static void put_varlength(AVIOContext *pb, int val)
{
@@ -154,7 +161,7 @@ static int mmf_write_trailer(AVFormatContext *s)
/* "play wav" */
avio_w8(pb, 0); /* start time */
- avio_w8(pb, 1); /* (channel << 6) | wavenum */
+ avio_w8(pb, (mmf->stereo << 6) | 1); /* (channel << 6) | wavenum */
gatetime = size * 500 / s->streams[0]->codec->sample_rate;
put_varlength(pb, gatetime); /* duration */
@@ -197,7 +204,7 @@ static int mmf_read_header(AVFormatContext *s)
tag = avio_rl32(pb);
if (tag != MKTAG('M', 'M', 'M', 'D'))
- return -1;
+ return AVERROR_INVALIDDATA;
avio_skip(pb, 4); /* file_size */
/* Skip some unused chunks that may or may not be present */
@@ -214,11 +221,11 @@ static int mmf_read_header(AVFormatContext *s)
/* Tag = "ATRx", where "x" = track number */
if ((tag & 0xffffff) == MKTAG('M', 'T', 'R', 0)) {
av_log(s, AV_LOG_ERROR, "MIDI like format found, unsupported\n");
- return -1;
+ return AVERROR_PATCHWELCOME;
}
if ((tag & 0xffffff) != MKTAG('A', 'T', 'R', 0)) {
av_log(s, AV_LOG_ERROR, "Unsupported SMAF chunk %08x\n", tag);
- return -1;
+ return AVERROR_PATCHWELCOME;
}
avio_r8(pb); /* format type */
@@ -227,7 +234,7 @@ static int mmf_read_header(AVFormatContext *s)
rate = mmf_rate(params & 0x0f);
if (rate < 0) {
av_log(s, AV_LOG_ERROR, "Invalid sample rate\n");
- return -1;
+ return AVERROR_INVALIDDATA;
}
avio_r8(pb); /* wave base bit */
avio_r8(pb); /* time base d */
@@ -247,9 +254,9 @@ static int mmf_read_header(AVFormatContext *s)
/* Make sure it's followed by an Awa chunk, aka wave data */
if ((tag & 0xffffff) != MKTAG('A', 'w', 'a', 0)) {
av_log(s, AV_LOG_ERROR, "Unexpected SMAF chunk %08x\n", tag);
- return -1;
+ return AVERROR_INVALIDDATA;
}
- mmf->data_size = size;
+ mmf->data_end = avio_tell(pb) + size;
st = avformat_new_stream(s, NULL);
if (!st)
@@ -258,8 +265,8 @@ static int mmf_read_header(AVFormatContext *s)
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = AV_CODEC_ID_ADPCM_YAMAHA;
st->codec->sample_rate = rate;
- st->codec->channels = 1;
- st->codec->channel_layout = AV_CH_LAYOUT_MONO;
+ st->codec->channels = (params >> 7) + 1;
+ st->codec->channel_layout = params >> 7 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO;
st->codec->bits_per_coded_sample = 4;
st->codec->bit_rate = st->codec->sample_rate *
st->codec->bits_per_coded_sample;
@@ -274,29 +281,20 @@ static int mmf_read_header(AVFormatContext *s)
static int mmf_read_packet(AVFormatContext *s, AVPacket *pkt)
{
MMFContext *mmf = s->priv_data;
- int ret, size;
-
- if (s->pb->eof_reached)
- return AVERROR(EIO);
+ int64_t left, size;
+ int ret;
- size = MAX_SIZE;
- if (size > mmf->data_size)
- size = mmf->data_size;
+ left = mmf->data_end - avio_tell(s->pb);
+ size = FFMIN(left, MAX_SIZE);
+ if (avio_feof(s->pb) || size <= 0)
+ return AVERROR_EOF;
- if (!size)
- return AVERROR(EIO);
-
- if (av_new_packet(pkt, size))
- return AVERROR(EIO);
- pkt->stream_index = 0;
-
- ret = avio_read(s->pb, pkt->data, pkt->size);
+ ret = av_get_packet(s->pb, pkt, size);
if (ret < 0)
- av_free_packet(pkt);
+ return ret;
- mmf->data_size -= ret;
+ pkt->stream_index = 0;
- pkt->size = ret;
return ret;
}
@@ -308,7 +306,7 @@ AVInputFormat ff_mmf_demuxer = {
.read_probe = mmf_probe,
.read_header = mmf_read_header,
.read_packet = mmf_read_packet,
- .read_seek = ff_pcm_read_seek,
+ .flags = AVFMT_GENERIC_INDEX,
};
#endif
@@ -322,7 +320,7 @@ AVOutputFormat ff_mmf_muxer = {
.audio_codec = AV_CODEC_ID_ADPCM_YAMAHA,
.video_codec = AV_CODEC_ID_NONE,
.write_header = mmf_write_header,
- .write_packet = mmf_write_packet,
+ .write_packet = ff_raw_write_packet,
.write_trailer = mmf_write_trailer,
};
#endif
diff --git a/libavformat/mms.c b/libavformat/mms.c
index fb16a3c9ef..7e60b3bec6 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 f8803a551c..38e6b319c6 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 MMSHContext {
MMSContext mms;
+ uint8_t location[1024];
int request_seq; ///< request packet sequence
int chunk_seq; ///< data packet sequence
} MMSHContext;
@@ -65,9 +66,9 @@ static int mmsh_close(URLContext *h)
MMSHContext *mmsh = (MMSHContext *)h->priv_data;
MMSContext *mms = &mmsh->mms;
if (mms->mms_hd)
- ffurl_close(mms->mms_hd);
- av_free(mms->streams);
- av_free(mms->asf_header);
+ ffurl_closep(&mms->mms_hd);
+ av_freep(&mms->streams);
+ av_freep(&mms->asf_header);
return 0;
}
@@ -118,7 +119,7 @@ static int read_data_packet(MMSHContext *mmsh, const int len)
int res;
if (len > sizeof(mms->in_buffer)) {
av_log(NULL, AV_LOG_ERROR,
- "Data packet length %d exceeds the in_buffer size %zu\n",
+ "Data packet length %d exceeds the in_buffer size %"SIZE_SPECIFIER"\n",
len, sizeof(mms->in_buffer));
return AVERROR(EIO);
}
@@ -193,7 +194,7 @@ static int get_http_header_data(MMSHContext *mmsh)
if (len) {
if (len > sizeof(mms->in_buffer)) {
av_log(NULL, AV_LOG_ERROR,
- "Other packet len = %d exceed the in_buffer size %zu\n",
+ "Other packet len = %d exceed the in_buffer size %"SIZE_SPECIFIER"\n",
len, sizeof(mms->in_buffer));
return AVERROR(EIO);
}
@@ -210,10 +211,10 @@ static int get_http_header_data(MMSHContext *mmsh)
}
}
-static int mmsh_open(URLContext *h, const char *uri, int flags)
+static int mmsh_open_internal(URLContext *h, const char *uri, int flags, int timestamp, int64_t pos)
{
int i, port, err;
- char httpname[256], path[256], host[128], location[1024];
+ char httpname[256], path[256], host[128];
char *stream_selection = NULL;
char headers[1024];
MMSHContext *mmsh = h->priv_data;
@@ -221,10 +222,10 @@ static int mmsh_open(URLContext *h, const char *uri, int flags)
mmsh->request_seq = h->is_streamed = 1;
mms = &mmsh->mms;
- av_strlcpy(location, uri, sizeof(location));
+ av_strlcpy(mmsh->location, uri, sizeof(mmsh->location));
av_url_split(NULL, 0, NULL, 0,
- host, sizeof(host), &port, path, sizeof(path), location);
+ host, sizeof(host), &port, path, sizeof(path), mmsh->location);
if (port<0)
port = 80; // default mmsh protocol port
ff_url_join(httpname, sizeof(httpname), "http", NULL, host, port, "%s", path);
@@ -241,7 +242,7 @@ static int mmsh_open(URLContext *h, const char *uri, int flags)
"Pragma: no-cache,rate=1.000000,stream-time=0,"
"stream-offset=0:0,request-context=%u,max-duration=0\r\n"
CLIENTGUID
- "Connection: Close\r\n\r\n",
+ "Connection: Close\r\n",
host, port, mmsh->request_seq++);
av_opt_set(mms->mms_hd->priv_data, "headers", headers, 0);
@@ -282,8 +283,9 @@ static int mmsh_open(URLContext *h, const char *uri, int flags)
CLIENTGUID
"Pragma: stream-switch-count=%d\r\n"
"Pragma: stream-switch-entry=%s\r\n"
- "Connection: Close\r\n\r\n",
- host, port, mmsh->request_seq++, mms->stream_num, stream_selection);
+ "Pragma: no-cache,rate=1.000000,stream-time=%u"
+ "Connection: Close\r\n",
+ host, port, mmsh->request_seq++, mms->stream_num, stream_selection, timestamp);
av_freep(&stream_selection);
if (err < 0) {
av_log(NULL, AV_LOG_ERROR, "Build play request failed!\n");
@@ -312,6 +314,11 @@ fail:
return err;
}
+static int mmsh_open(URLContext *h, const char *uri, int flags)
+{
+ return mmsh_open_internal(h, uri, flags, 0, 0);
+}
+
static int handle_chunk_type(MMSHContext *mmsh)
{
MMSContext *mms = &mmsh->mms;
@@ -358,11 +365,49 @@ static int mmsh_read(URLContext *h, uint8_t *buf, int size)
return res;
}
+static int64_t mmsh_read_seek(URLContext *h, int stream_index,
+ int64_t timestamp, int flags)
+{
+ MMSHContext *mmsh_old = h->priv_data;
+ MMSHContext *mmsh = av_mallocz(sizeof(*mmsh));
+ int ret;
+
+ if (!mmsh)
+ return AVERROR(ENOMEM);
+
+ h->priv_data = mmsh;
+ ret= mmsh_open_internal(h, mmsh_old->location, 0, FFMAX(timestamp, 0), 0);
+ if(ret>=0){
+ h->priv_data = mmsh_old;
+ mmsh_close(h);
+ h->priv_data = mmsh;
+ av_free(mmsh_old);
+ mmsh->mms.asf_header_read_size = mmsh->mms.asf_header_size;
+ }else {
+ h->priv_data = mmsh_old;
+ av_free(mmsh);
+ }
+
+ return ret;
+}
+
+static int64_t mmsh_seek(URLContext *h, int64_t pos, int whence)
+{
+ MMSHContext *mmsh = h->priv_data;
+ MMSContext *mms = &mmsh->mms;
+
+ if(pos == 0 && whence == SEEK_CUR)
+ return mms->asf_header_read_size + mms->remaining_in_len + mmsh->chunk_seq * (int64_t)mms->asf_packet_len;
+ return AVERROR(ENOSYS);
+}
+
URLProtocol ff_mmsh_protocol = {
.name = "mmsh",
.url_open = mmsh_open,
.url_read = mmsh_read,
+ .url_seek = mmsh_seek,
.url_close = mmsh_close,
+ .url_read_seek = mmsh_read_seek,
.priv_data_size = sizeof(MMSHContext),
.flags = URL_PROTOCOL_FLAG_NETWORK,
};
diff --git a/libavformat/mmst.c b/libavformat/mmst.c
index d45f7ba83b..65c4f4438e 100644
--- a/libavformat/mmst.c
+++ b/libavformat/mmst.c
@@ -4,20 +4,20 @@
* Copyright (c) 2007 Björn Axelsson
* Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -152,7 +152,7 @@ static int send_command_packet(MMSTContext *mmst)
return 0;
}
-static void mms_put_utf16(MMSContext *mms, uint8_t *src)
+static int mms_put_utf16(MMSContext *mms, const uint8_t *src)
{
AVIOContext bic;
int size = mms->write_out_ptr - mms->out_buffer;
@@ -161,7 +161,10 @@ static void mms_put_utf16(MMSContext *mms, uint8_t *src)
sizeof(mms->out_buffer) - size, 1, NULL, NULL, NULL, NULL);
len = avio_put_str16le(&bic, src);
+ if (len < 0)
+ return len;
mms->write_out_ptr += len;
+ return 0;
}
static int send_time_test_data(MMSTContext *mmst)
@@ -173,6 +176,7 @@ static int send_time_test_data(MMSTContext *mmst)
static int send_protocol_select(MMSTContext *mmst)
{
+ int ret;
char data_string[256];
MMSContext *mms = &mmst->mms;
@@ -189,18 +193,21 @@ static int send_protocol_select(MMSTContext *mmst)
"TCP", // or UDP
LOCAL_PORT);
- mms_put_utf16(mms, data_string);
+ if ((ret = mms_put_utf16(mms, data_string)) < 0)
+ return ret;
return send_command_packet(mmst);
}
static int send_media_file_request(MMSTContext *mmst)
{
+ int ret;
MMSContext *mms = &mmst->mms;
start_command_packet(mmst, CS_PKT_MEDIA_FILE_REQUEST);
insert_command_prefixes(mms, 1, 0xffffffff);
bytestream_put_le32(&mms->write_out_ptr, 0);
bytestream_put_le32(&mms->write_out_ptr, 0);
- mms_put_utf16(mms, mmst->path + 1); // +1 for skip "/"
+ if ((ret = mms_put_utf16(mms, mmst->path + 1)) < 0) // +1 for skip "/"
+ return ret;
return send_command_packet(mmst);
}
@@ -277,7 +284,7 @@ static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst)
if (length_remaining < 0
|| length_remaining > sizeof(mms->in_buffer) - 12) {
av_log(NULL, AV_LOG_ERROR,
- "Incoming packet length %d exceeds bufsize %zu\n",
+ "Incoming packet length %d exceeds bufsize %"SIZE_SPECIFIER"\n",
length_remaining, sizeof(mms->in_buffer) - 12);
return AVERROR_INVALIDDATA;
}
@@ -313,7 +320,7 @@ static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst)
if (length_remaining < 0
|| length_remaining > sizeof(mms->in_buffer) - 8) {
av_log(NULL, AV_LOG_ERROR,
- "Data length %d is invalid or too large (max=%zu)\n",
+ "Data length %d is invalid or too large (max=%"SIZE_SPECIFIER")\n",
length_remaining, sizeof(mms->in_buffer));
return AVERROR_INVALIDDATA;
}
@@ -417,6 +424,7 @@ static int send_media_header_request(MMSTContext *mmst)
static int send_startup_packet(MMSTContext *mmst)
{
char data_string[256];
+ int ret;
MMSContext *mms = &mmst->mms;
// SubscriberName is defined in MS specification linked below.
// The guid value can be any valid value.
@@ -429,7 +437,8 @@ static int send_startup_packet(MMSTContext *mmst)
start_command_packet(mmst, CS_PKT_INITIAL);
insert_command_prefixes(mms, 0, 0x0004000b);
bytestream_put_le32(&mms->write_out_ptr, 0x0003001c);
- mms_put_utf16(mms, data_string);
+ if ((ret = mms_put_utf16(mms, data_string)) < 0)
+ return ret;
return send_command_packet(mmst);
}
@@ -468,8 +477,8 @@ static int mms_close(URLContext *h)
}
/* free all separately allocated pointers in mms */
- av_free(mms->streams);
- av_free(mms->asf_header);
+ av_freep(&mms->streams);
+ av_freep(&mms->asf_header);
return 0;
}
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 7a140a81a4..636c2bbb0a 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -6,20 +6,20 @@
* first version by Francois Revol <revol@free.fr>
* seek function by Gael Chardon <gael.dev@4now.net>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -35,7 +35,9 @@
#include "libavutil/time_internal.h"
#include "libavutil/avstring.h"
#include "libavutil/dict.h"
+#include "libavutil/display.h"
#include "libavutil/opt.h"
+#include "libavutil/timecode.h"
#include "libavcodec/ac3tab.h"
#include "avformat.h"
#include "internal.h"
@@ -53,10 +55,6 @@
#include "qtpalette.h"
-
-#undef NDEBUG
-#include <assert.h>
-
/* those functions parse an atom */
/* links atom IDs to parse functions */
typedef struct MOVParseTableEntry {
@@ -65,6 +63,7 @@ typedef struct MOVParseTableEntry {
} MOVParseTableEntry;
static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom);
+static int mov_read_mfra(MOVContext *c, AVIOContext *f);
static int mov_metadata_track_or_disc_number(MOVContext *c, AVIOContext *pb,
unsigned len, const char *key)
@@ -89,16 +88,13 @@ static int mov_metadata_track_or_disc_number(MOVContext *c, AVIOContext *pb,
static int mov_metadata_int8_bypass_padding(MOVContext *c, AVIOContext *pb,
unsigned len, const char *key)
{
- char buf[16];
-
/* bypass padding bytes */
avio_r8(pb);
avio_r8(pb);
avio_r8(pb);
- snprintf(buf, sizeof(buf), "%d", avio_r8(pb));
c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
- av_dict_set(&c->fc->metadata, key, buf, 0);
+ av_dict_set_int(&c->fc->metadata, key, avio_r8(pb), 0);
return 0;
}
@@ -106,11 +102,8 @@ static int mov_metadata_int8_bypass_padding(MOVContext *c, AVIOContext *pb,
static int mov_metadata_int8_no_padding(MOVContext *c, AVIOContext *pb,
unsigned len, const char *key)
{
- char buf[16];
-
- snprintf(buf, sizeof(buf), "%d", avio_r8(pb));
c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
- av_dict_set(&c->fc->metadata, key, buf, 0);
+ av_dict_set_int(&c->fc->metadata, key, avio_r8(pb), 0);
return 0;
}
@@ -119,16 +112,14 @@ static int mov_metadata_gnre(MOVContext *c, AVIOContext *pb,
unsigned len, const char *key)
{
short genre;
- char buf[20];
avio_r8(pb); // unknown
genre = avio_r8(pb);
if (genre < 1 || genre > ID3v1_GENRE_MAX)
return 0;
- snprintf(buf, sizeof(buf), "%s", ff_id3v1_genre_str[genre-1]);
c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
- av_dict_set(&c->fc->metadata, key, buf, 0);
+ av_dict_set(&c->fc->metadata, key, ff_id3v1_genre_str[genre-1], 0);
return 0;
}
@@ -163,7 +154,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;
@@ -253,7 +244,8 @@ static int mov_metadata_loci(MOVContext *c, AVIOContext *pb, unsigned len)
static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
char tmp_key[5];
- char *str, key2[32], language[4] = {0};
+ char key2[32], language[4] = {0};
+ char *str = NULL;
const char *key = NULL;
uint16_t langcode = 0;
uint32_t data_type = 0, str_size, str_size_alloc;
@@ -346,7 +338,7 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
case MKTAG(0xa9,'w','r','t'): key = "composer"; break;
case MKTAG(0xa9,'x','y','z'): key = "location"; break;
}
-
+retry:
if (c->itunes_metadata && atom.size > 8) {
int data_size = avio_rb32(pb);
int tag = avio_rl32(pb);
@@ -360,12 +352,18 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
int ret = mov_read_covr(c, pb, data_type, str_size);
if (ret < 0) {
av_log(c->fc, AV_LOG_ERROR, "Error parsing cover art.\n");
- return ret;
}
+ return ret;
}
} else return 0;
} else if (atom.size > 4 && key && !c->itunes_metadata && !raw) {
str_size = avio_rb16(pb); // string length
+ if (str_size > atom.size) {
+ raw = 1;
+ avio_seek(pb, -2, SEEK_CUR);
+ av_log(c->fc, AV_LOG_WARNING, "UDTA parsing failed retrying raw\n");
+ goto retry;
+ }
langcode = avio_rb16(pb);
ff_mov_lang_to_iso639(langcode, language);
atom.size -= 4;
@@ -379,12 +377,12 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (!key)
return 0;
- if (atom.size < 0)
+ if (atom.size < 0 || str_size >= INT_MAX/2)
return AVERROR_INVALIDDATA;
- // allocate twice as much as worst-case
+ // worst-case requirement for output string in case of utf8 coded input
str_size_alloc = (raw ? str_size : str_size * 2) + 1;
- str = av_malloc(str_size_alloc);
+ str = av_mallocz(str_size_alloc);
if (!str)
return AVERROR(ENOMEM);
@@ -394,7 +392,11 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (!raw && (data_type == 3 || (data_type == 0 && (langcode < 0x400 || langcode == 0x7fff)))) { // MAC Encoded
mov_read_mac_string(c, pb, str_size, str, str_size_alloc);
} else {
- avio_read(pb, str, str_size);
+ int ret = avio_read(pb, str, str_size);
+ if (ret != str_size) {
+ av_freep(&str);
+ return ret < 0 ? ret : AVERROR_INVALIDDATA;
+ }
str[str_size] = 0;
}
c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
@@ -462,6 +464,7 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom)
entries >= UINT_MAX / sizeof(*sc->drefs))
return AVERROR_INVALIDDATA;
av_free(sc->drefs);
+ sc->drefs_count = 0;
sc->drefs = av_mallocz(entries * sizeof(*sc->drefs));
if (!sc->drefs)
return AVERROR(ENOMEM);
@@ -511,7 +514,7 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom)
avio_skip(pb, 16);
for (type = 0; type != -1 && avio_tell(pb) < next; ) {
- if (pb->eof_reached)
+ if(avio_feof(pb))
return AVERROR_EOF;
type = avio_rb16(pb);
len = avio_rb16(pb);
@@ -538,7 +541,8 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom)
dref->dir = av_malloc(len+1);
if (!dref->dir)
return AVERROR(ENOMEM);
- avio_read(pb, dref->dir, len);
+ if (avio_read(pb, dref->dir, len) != len)
+ return AVERROR_INVALIDDATA;
dref->dir[len] = 0;
for (j = 0; j < len; j++)
if (dref->dir[j] == ':')
@@ -723,12 +727,16 @@ static int mov_read_chan(MOVContext *c, AVIOContext *pb, MOVAtom atom)
static int mov_read_wfex(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
+ int ret;
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- return ff_get_wav_header(pb, st->codec, atom.size);
+ if ((ret = ff_get_wav_header(pb, st->codec, atom.size, 0)) < 0)
+ av_log(c->fc, AV_LOG_WARNING, "get_wav_header failed\n");
+
+ return ret;
}
static int mov_read_pasp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
@@ -748,8 +756,8 @@ static int mov_read_pasp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
st->sample_aspect_ratio.num, st->sample_aspect_ratio.den,
num, den);
} else if (den != 0) {
- st->sample_aspect_ratio.num = num;
- st->sample_aspect_ratio.den = den;
+ av_reduce(&st->sample_aspect_ratio.num, &st->sample_aspect_ratio.den,
+ num, den, 32767);
}
return 0;
}
@@ -768,7 +776,6 @@ static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
uint32_t minor_ver;
int comp_brand_size;
- char minor_ver_str[11]; /* 32 bit integer -> 10 digits + null */
char* comp_brands_str;
uint8_t type[5] = {0};
@@ -778,8 +785,7 @@ static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_log(c->fc, AV_LOG_DEBUG, "ISO: File Type Major Brand: %.4s\n",(char *)&type);
av_dict_set(&c->fc->metadata, "major_brand", type, 0);
minor_ver = avio_rb32(pb); /* minor version */
- snprintf(minor_ver_str, sizeof(minor_ver_str), "%"PRIu32"", minor_ver);
- av_dict_set(&c->fc->metadata, "minor_version", minor_ver_str, 0);
+ av_dict_set_int(&c->fc->metadata, "minor_version", minor_ver, 0);
comp_brand_size = atom.size - 8;
if (comp_brand_size < 0)
@@ -800,6 +806,12 @@ static int mov_read_moov(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
int ret;
+ if (c->found_moov) {
+ av_log(c->fc, AV_LOG_WARNING, "Found duplicated MOOV Atom. Skipped it\n");
+ avio_skip(pb, atom.size);
+ return 0;
+ }
+
if ((ret = mov_read_default(c, pb, atom)) < 0)
return ret;
/* we parsed the 'moov' atom, we can terminate the parsing as soon as we find the 'mdat' */
@@ -810,18 +822,36 @@ static int mov_read_moov(MOVContext *c, AVIOContext *pb, MOVAtom atom)
static int mov_read_moof(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
+ if (!c->has_looked_for_mfra && c->use_mfra_for > 0) {
+ c->has_looked_for_mfra = 1;
+ if (pb->seekable) {
+ int ret;
+ av_log(c->fc, AV_LOG_VERBOSE, "stream has moof boxes, will look "
+ "for a mfra\n");
+ if ((ret = mov_read_mfra(c, pb)) < 0) {
+ av_log(c->fc, AV_LOG_VERBOSE, "found a moof box but failed to "
+ "read the mfra (may be a live ismv)\n");
+ }
+ } else {
+ av_log(c->fc, AV_LOG_VERBOSE, "found a moof box but stream is not "
+ "seekable, can not look for mfra\n");
+ }
+ }
c->fragment.moof_offset = c->fragment.implicit_offset = avio_tell(pb) - 8;
av_dlog(c->fc, "moof offset %"PRIx64"\n", c->fragment.moof_offset);
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, tmbuf;
- time -= 2082844800; /* seconds between 1904-01-01 and Epoch */
- ptm = gmtime_r(&time, &tmbuf);
+ time_t timet;
+ if(time >= 2082844800)
+ time -= 2082844800; /* seconds between 1904-01-01 and Epoch */
+ timet = time;
+ ptm = gmtime_r(&timet, &tmbuf);
if (!ptm) return;
if (strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", ptm))
av_dict_set(metadata, "creation_time", buffer, 0);
@@ -835,7 +865,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;
@@ -875,7 +905,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 */
@@ -892,6 +922,10 @@ static int mov_read_mvhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_dlog(c->fc, "time scale = %i\n", c->time_scale);
c->duration = (version == 1) ? avio_rb64(pb) : avio_rb32(pb); /* duration */
+ // set the AVCodecContext duration because the duration of individual tracks
+ // may be inaccurate
+ if (c->time_scale > 0 && !c->trex_data)
+ c->fc->duration = av_rescale(c->duration, AV_TIME_BASE, c->time_scale);
avio_rb32(pb); /* preferred scale */
avio_rb16(pb); /* preferred volume */
@@ -907,31 +941,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;
}
@@ -944,7 +953,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) {
@@ -989,12 +998,12 @@ static int mov_read_colr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
color_trc = avio_rb16(pb);
color_matrix = avio_rb16(pb);
- av_dlog(c->fc, "%s: pri %"PRIu16" trc %"PRIu16" matrix %"PRIu16"",
+ av_dlog(c->fc, "%s: pri %d trc %d matrix %d",
color_parameter_type, color_primaries, color_trc, color_matrix);
if (c->isom) {
uint8_t color_range = avio_r8(pb) >> 7;
- av_dlog(c->fc, " full %"PRIu8"", color_range)
+ av_dlog(c->fc, " full %"PRIu8"", color_range);
if (color_range)
st->codec->color_range = AVCOL_RANGE_JPEG;
else
@@ -1031,7 +1040,7 @@ static int mov_read_colr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
case 7: st->codec->colorspace = AVCOL_SPC_SMPTE240M; break;
}
}
- av_dlog(c->fc, "\n")
+ av_dlog(c->fc, "\n");
return 0;
}
@@ -1071,7 +1080,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;
@@ -1081,6 +1091,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;
@@ -1092,10 +1106,83 @@ static int mov_read_extradata(MOVContext *c, AVIOContext *pb, MOVAtom atom)
st->codec->extradata_size= size - FF_INPUT_BUFFER_PADDING_SIZE;
AV_WB32( buf , atom.size + 8);
AV_WL32( buf + 4, atom.type);
- avio_read(pb, buf + 8, atom.size);
+ err = avio_read(pb, buf + 8, atom.size);
+ if (err < 0) {
+ return err;
+ } else if (err < atom.size) {
+ av_log(c->fc, AV_LOG_WARNING, "truncated extradata\n");
+ st->codec->extradata_size -= atom.size - err;
+ }
+ memset(buf + 8 + err, 0, FF_INPUT_BUFFER_PADDING_SIZE);
return 0;
}
+/* wrapper functions for reading ALAC/AVS/MJPEG/MJPEG2000 extradata atoms only for those codecs */
+static int mov_read_alac(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ return mov_read_extradata(c, pb, atom, AV_CODEC_ID_ALAC);
+}
+
+static int mov_read_avss(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ return mov_read_extradata(c, pb, atom, AV_CODEC_ID_AVS);
+}
+
+static int mov_read_jp2h(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ return mov_read_extradata(c, pb, atom, AV_CODEC_ID_JPEG2000);
+}
+
+static int mov_read_dpxe(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ return mov_read_extradata(c, pb, atom, AV_CODEC_ID_R10K);
+}
+
+static int mov_read_avid(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ int ret = mov_read_extradata(c, pb, atom, AV_CODEC_ID_AVUI);
+ if(ret == 0)
+ ret = mov_read_extradata(c, pb, atom, AV_CODEC_ID_DNXHD);
+ return ret;
+}
+
+static int mov_read_targa_y216(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ int ret = mov_read_extradata(c, pb, atom, AV_CODEC_ID_TARGA_Y216);
+
+ if (!ret && c->fc->nb_streams >= 1) {
+ AVCodecContext *avctx = c->fc->streams[c->fc->nb_streams-1]->codec;
+ if (avctx->extradata_size >= 40) {
+ avctx->height = AV_RB16(&avctx->extradata[36]);
+ avctx->width = AV_RB16(&avctx->extradata[38]);
+ }
+ }
+ return ret;
+}
+
+static int mov_read_ares(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ if (c->fc->nb_streams >= 1) {
+ AVCodecContext *codec = c->fc->streams[c->fc->nb_streams-1]->codec;
+ if (codec->codec_tag == MKTAG('A', 'V', 'i', 'n') &&
+ codec->codec_id == AV_CODEC_ID_H264 &&
+ atom.size > 11) {
+ avio_skip(pb, 10);
+ /* For AVID AVCI50, force width of 1440 to be able to select the correct SPS and PPS */
+ if (avio_rb16(pb) == 0xd4d)
+ codec->width = 1440;
+ return 0;
+ }
+ }
+
+ return mov_read_avid(c, pb, atom);
+}
+
+static int mov_read_svq3(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ return mov_read_extradata(c, pb, atom, AV_CODEC_ID_SVQ3);
+}
+
static int mov_read_wave(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
@@ -1107,14 +1194,13 @@ static int mov_read_wave(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if ((uint64_t)atom.size > (1<<30))
return AVERROR_INVALIDDATA;
- if (st->codec->codec_id == AV_CODEC_ID_QDM2 || st->codec->codec_id == AV_CODEC_ID_QDMC) {
+ if (st->codec->codec_id == AV_CODEC_ID_QDM2 ||
+ st->codec->codec_id == AV_CODEC_ID_QDMC ||
+ st->codec->codec_id == AV_CODEC_ID_SPEEX) {
// pass all frma atom to codec, needed at least for QDMC and QDM2
- av_free(st->codec->extradata);
- st->codec->extradata = av_mallocz(atom.size + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
+ av_freep(&st->codec->extradata);
+ if (ff_get_extradata(st->codec, pb, atom.size) < 0)
return AVERROR(ENOMEM);
- st->codec->extradata_size = atom.size;
- avio_read(pb, st->codec->extradata, atom.size);
} else if (atom.size > 8) { /* to read frma, esds atoms */
int ret;
if ((ret = mov_read_default(c, pb, atom)) < 0)
@@ -1148,12 +1234,14 @@ static int mov_read_glbl(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (type == MKTAG('f','i','e','l') && size == atom.size)
return mov_read_default(c, pb, atom);
}
- av_free(st->codec->extradata);
- st->codec->extradata = av_mallocz(atom.size + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
+ if (st->codec->extradata_size > 1 && st->codec->extradata) {
+ av_log(c, AV_LOG_WARNING, "ignoring multiple glbl\n");
+ return 0;
+ }
+ av_freep(&st->codec->extradata);
+ if (ff_get_extradata(st->codec, pb, atom.size) < 0)
return AVERROR(ENOMEM);
- st->codec->extradata_size = atom.size;
- avio_read(pb, st->codec->extradata, atom.size);
+
return 0;
}
@@ -1161,6 +1249,7 @@ static int mov_read_dvc1(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
uint8_t profile_level;
+ int ret;
if (c->fc->nb_streams < 1)
return 0;
@@ -1173,13 +1262,11 @@ static int mov_read_dvc1(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if ((profile_level & 0xf0) != 0xc0)
return 0;
- av_free(st->codec->extradata);
- st->codec->extradata = av_mallocz(atom.size - 7 + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
- return AVERROR(ENOMEM);
- st->codec->extradata_size = atom.size - 7;
avio_seek(pb, 6, SEEK_CUR);
- avio_read(pb, st->codec->extradata, st->codec->extradata_size);
+ av_freep(&st->codec->extradata);
+ if ((ret = ff_get_extradata(st->codec, pb, atom.size - 7)) < 0)
+ return ret;
+
return 0;
}
@@ -1201,13 +1288,10 @@ static int mov_read_strf(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if ((uint64_t)atom.size > (1<<30))
return AVERROR_INVALIDDATA;
- av_free(st->codec->extradata);
- st->codec->extradata = av_mallocz(atom.size - 40 + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
- return AVERROR(ENOMEM);
- st->codec->extradata_size = atom.size - 40;
avio_skip(pb, 40);
- avio_read(pb, st->codec->extradata, atom.size - 40);
+ av_freep(&st->codec->extradata);
+ if (ff_get_extradata(st->codec, pb, atom.size - 40) < 0)
+ return AVERROR(ENOMEM);
return 0;
}
@@ -1229,10 +1313,12 @@ static int mov_read_stco(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (!entries)
return 0;
- if (entries >= UINT_MAX/sizeof(int64_t))
- return AVERROR_INVALIDDATA;
- sc->chunk_offsets = av_malloc(entries * sizeof(int64_t));
+ if (sc->chunk_offsets)
+ av_log(c->fc, AV_LOG_WARNING, "Duplicated STCO atom\n");
+ av_free(sc->chunk_offsets);
+ sc->chunk_count = 0;
+ sc->chunk_offsets = av_malloc_array(entries, sizeof(*sc->chunk_offsets));
if (!sc->chunk_offsets)
return AVERROR(ENOMEM);
sc->chunk_count = entries;
@@ -1287,7 +1373,9 @@ static int mov_codec_id(AVStream *st, uint32_t format)
id = ff_codec_get_id(ff_codec_bmp_tags, format);
if (id > 0)
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
- else if (st->codec->codec_type == AVMEDIA_TYPE_DATA) {
+ else if (st->codec->codec_type == AVMEDIA_TYPE_DATA ||
+ (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE &&
+ st->codec->codec_id == AV_CODEC_ID_NONE)) {
id = ff_codec_get_id(ff_codec_movsubtitle_tags, format);
if (id > 0)
st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
@@ -1332,8 +1420,11 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb,
av_dict_set(&st->metadata, "encoder", codec_name, 0);
/* codec_tag YV12 triggers an UV swap in rawdec.c */
- if (!memcmp(codec_name, "Planar Y'CbCr 8-bit 4:2:0", 25))
+ if (!memcmp(codec_name, "Planar Y'CbCr 8-bit 4:2:0", 25)) {
st->codec->codec_tag = MKTAG('I', '4', '2', '0');
+ st->codec->width &= ~1;
+ st->codec->height &= ~1;
+ }
/* Flash Media Server uses tag H263 with Sorenson Spark */
if (st->codec->codec_tag == MKTAG('H','2','6','3') &&
!memcmp(codec_name, "Sorenson H263", 13))
@@ -1346,12 +1437,15 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb,
/* figure out the palette situation */
color_depth = st->codec->bits_per_coded_sample & 0x1F;
color_greyscale = st->codec->bits_per_coded_sample & 0x20;
+ /* Do not create a greyscale palette for cinepak */
+ if (color_greyscale && st->codec->codec_id == AV_CODEC_ID_CINEPAK)
+ return;
/* if the depth is 2, 4, or 8 bpp, file is palettized */
if ((color_depth == 2) || (color_depth == 4) || (color_depth == 8)) {
/* for palette traversal */
unsigned int color_start, color_count, color_end;
- unsigned char r, g, b;
+ unsigned char a, r, g, b;
if (color_greyscale) {
int color_index, color_dec;
@@ -1362,7 +1456,7 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb,
color_dec = 256 / (color_count - 1);
for (j = 0; j < color_count; j++) {
r = g = b = color_index;
- sc->palette[j] = (r << 16) | (g << 8) | (b);
+ sc->palette[j] = (0xFFU << 24) | (r << 16) | (g << 8) | (b);
color_index -= color_dec;
if (color_index < 0)
color_index = 0;
@@ -1382,7 +1476,7 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb,
r = color_table[j * 3 + 0];
g = color_table[j * 3 + 1];
b = color_table[j * 3 + 2];
- sc->palette[j] = (r << 16) | (g << 8) | (b);
+ sc->palette[j] = (0xFFU << 24) | (r << 16) | (g << 8) | (b);
}
} else {
/* load the palette from the file */
@@ -1391,10 +1485,9 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb,
color_end = avio_rb16(pb);
if ((color_start <= 255) && (color_end <= 255)) {
for (j = color_start; j <= color_end; j++) {
- /* each R, G, or B component is 16 bits;
- * only use the top 8 bits; skip alpha bytes
- * up front */
- avio_r8(pb);
+ /* each A, R, G, or B component is 16 bits;
+ * only use the top 8 bits */
+ a = avio_r8(pb);
avio_r8(pb);
r = avio_r8(pb);
avio_r8(pb);
@@ -1402,7 +1495,7 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb,
avio_r8(pb);
b = avio_r8(pb);
avio_r8(pb);
- sc->palette[j] = (r << 16) | (g << 8) | (b);
+ sc->palette[j] = (a << 24 ) | (r << 16) | (g << 8) | (b);
}
}
}
@@ -1415,6 +1508,7 @@ static void mov_parse_stsd_audio(MOVContext *c, AVIOContext *pb,
{
int bits_per_sample, flags;
uint16_t version = avio_rb16(pb);
+ AVDictionaryEntry *compatible_brands = av_dict_get(c->fc->metadata, "compatible_brands", NULL, AV_DICT_MATCH_CASE);
avio_rb16(pb); /* revision level */
avio_rb32(pb); /* vendor */
@@ -1430,7 +1524,9 @@ static void mov_parse_stsd_audio(MOVContext *c, AVIOContext *pb,
// Read QT version 1 fields. In version 0 these do not exist.
av_dlog(c->fc, "version =%d, isom =%d\n", version, c->isom);
- if (!c->isom) {
+ if (!c->isom ||
+ (compatible_brands && strstr(compatible_brands->value, "qt "))) {
+
if (version == 1) {
sc->samples_per_frame = avio_rb32(pb);
avio_rb32(pb); /* bytes per packet */
@@ -1467,6 +1563,10 @@ static void mov_parse_stsd_audio(MOVContext *c, AVIOContext *pb,
st->codec->codec_id =
st->codec->codec_id == AV_CODEC_ID_PCM_S16BE ?
AV_CODEC_ID_PCM_S24BE : AV_CODEC_ID_PCM_S24LE;
+ else if (st->codec->bits_per_coded_sample == 32)
+ st->codec->codec_id =
+ st->codec->codec_id == AV_CODEC_ID_PCM_S16BE ?
+ AV_CODEC_ID_PCM_S32BE : AV_CODEC_ID_PCM_S32LE;
break;
/* set values for old format before stsd version 1 appeared */
case AV_CODEC_ID_MACE3:
@@ -1498,7 +1598,7 @@ static void mov_parse_stsd_audio(MOVContext *c, AVIOContext *pb,
static void mov_parse_stsd_subtitle(MOVContext *c, AVIOContext *pb,
AVStream *st, MOVStreamContext *sc,
- int size)
+ int64_t size)
{
// ttxt stsd contains display flags, justification, background
// color, fonts, and default styles, so fake an atom to read it
@@ -1563,14 +1663,48 @@ static int mov_rewrite_dvd_sub_extradata(AVStream *st)
static int mov_parse_stsd_data(MOVContext *c, AVIOContext *pb,
AVStream *st, MOVStreamContext *sc,
- int size)
+ int64_t size)
{
if (st->codec->codec_tag == MKTAG('t','m','c','d')) {
- st->codec->extradata_size = size;
- st->codec->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
+ if ((int)size != size || ff_get_extradata(st->codec, pb, size) < 0)
return AVERROR(ENOMEM);
- avio_read(pb, st->codec->extradata, size);
+ if (size > 16) {
+ MOVStreamContext *tmcd_ctx = st->priv_data;
+ int val;
+ val = AV_RB32(st->codec->extradata + 4);
+ tmcd_ctx->tmcd_flags = val;
+ if (val & 1)
+ st->codec->flags2 |= CODEC_FLAG2_DROP_FRAME_TIMECODE;
+ st->codec->time_base.den = st->codec->extradata[16]; /* number of frame */
+ st->codec->time_base.num = 1;
+ /* adjust for per frame dur in counter mode */
+ if (tmcd_ctx->tmcd_flags & 0x0008) {
+ int timescale = AV_RB32(st->codec->extradata + 8);
+ int framedur = AV_RB32(st->codec->extradata + 12);
+ st->codec->time_base.den *= timescale;
+ st->codec->time_base.num *= framedur;
+ }
+ if (size > 30) {
+ uint32_t len = AV_RB32(st->codec->extradata + 18); /* name atom length */
+ uint32_t format = AV_RB32(st->codec->extradata + 22);
+ if (format == AV_RB32("name") && (int64_t)size >= (int64_t)len + 18) {
+ uint16_t str_size = AV_RB16(st->codec->extradata + 26); /* string length */
+ if (str_size > 0 && size >= (int)str_size + 26) {
+ char *reel_name = av_malloc(str_size + 1);
+ if (!reel_name)
+ return AVERROR(ENOMEM);
+ memcpy(reel_name, st->codec->extradata + 30, str_size);
+ reel_name[str_size] = 0; /* Add null terminator */
+ /* don't add reel_name if emtpy string */
+ if (*reel_name == 0) {
+ av_free(reel_name);
+ } else {
+ av_dict_set(&st->metadata, "reel_name", reel_name, AV_DICT_DONT_STRDUP_VAL);
+ }
+ }
+ }
+ }
+ }
} else {
/* other codec type, just skip (rtp, mp4s ...) */
avio_skip(pb, size);
@@ -1605,6 +1739,10 @@ static int mov_finalize_stsd_codec(MOVContext *c, AVIOContext *pb,
// force sample rate for qcelp when not stored in mov
if (st->codec->codec_tag != MKTAG('Q','c','l','p'))
st->codec->sample_rate = 8000;
+ // FIXME: Why is the following needed for some files?
+ sc->samples_per_frame = 160;
+ if (!sc->bytes_per_frame)
+ sc->bytes_per_frame = 35;
break;
case AV_CODEC_ID_AMR_NB:
st->codec->channels = 1;
@@ -1625,6 +1763,9 @@ static int mov_finalize_stsd_codec(MOVContext *c, AVIOContext *pb,
case AV_CODEC_ID_ADPCM_MS:
case AV_CODEC_ID_ADPCM_IMA_WAV:
case AV_CODEC_ID_ILBC:
+ case AV_CODEC_ID_MACE3:
+ case AV_CODEC_ID_MACE6:
+ case AV_CODEC_ID_QDM2:
st->codec->block_align = sc->bytes_per_frame;
break;
case AV_CODEC_ID_ALAC:
@@ -1633,6 +1774,9 @@ static int mov_finalize_stsd_codec(MOVContext *c, AVIOContext *pb,
st->codec->sample_rate = AV_RB32(st->codec->extradata + 32);
}
break;
+ case AV_CODEC_ID_AC3:
+ case AV_CODEC_ID_EAC3:
+ case AV_CODEC_ID_MPEG1VIDEO:
case AV_CODEC_ID_VC1:
st->need_parsing = AVSTREAM_PARSE_FULL;
break;
@@ -1644,17 +1788,14 @@ static int mov_finalize_stsd_codec(MOVContext *c, AVIOContext *pb,
static int mov_skip_multiple_stsd(MOVContext *c, AVIOContext *pb,
int codec_tag, int format,
- int size)
+ int64_t size)
{
int video_codec_id = ff_codec_get_id(ff_codec_movvideo_tags, format);
if (codec_tag &&
- (codec_tag == AV_RL32("avc1") ||
- codec_tag == AV_RL32("hvc1") ||
- codec_tag == AV_RL32("hev1") ||
(codec_tag != format &&
(c->fc->video_codec_id ? video_codec_id != c->fc->video_codec_id
- : codec_tag != MKTAG('j','p','e','g'))))) {
+ : codec_tag != MKTAG('j','p','e','g')))) {
/* Multiple fourcc, we skip JPEG. This is not correct, we should
* export it as a separate AVStream but this needs a few changes
* in the MOV demuxer, patch welcome. */
@@ -1663,6 +1804,11 @@ static int mov_skip_multiple_stsd(MOVContext *c, AVIOContext *pb,
avio_skip(pb, size);
return 1;
}
+ if ( codec_tag == AV_RL32("avc1") ||
+ codec_tag == AV_RL32("hvc1") ||
+ codec_tag == AV_RL32("hev1")
+ )
+ av_log(c->fc, AV_LOG_WARNING, "Concatenated H.264 or H.265 might not play correctly.\n");
return 0;
}
@@ -1686,15 +1832,15 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries)
int ret, dref_id = 1;
MOVAtom a = { AV_RL32("stsd") };
int64_t start_pos = avio_tell(pb);
- uint32_t size = avio_rb32(pb); /* size */
+ int64_t size = avio_rb32(pb); /* size */
uint32_t format = avio_rl32(pb); /* data format */
if (size >= 16) {
avio_rb32(pb); /* reserved */
avio_rb16(pb); /* reserved */
dref_id = avio_rb16(pb);
- } else {
- av_log(c->fc, AV_LOG_ERROR, "invalid size %"PRIu32" in stsd\n", size);
+ }else if (size <= 7){
+ av_log(c->fc, AV_LOG_ERROR, "invalid size %"PRId64" in stsd\n", size);
return AVERROR_INVALIDDATA;
}
@@ -1707,7 +1853,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries)
id = mov_codec_id(st, format);
- av_dlog(c->fc, "size=%"PRIu32" 4CC= %"PRIu8"%"PRIu8"%"PRIu8"%"PRIu8" codec_type=%d\n", size,
+ av_dlog(c->fc, "size=%"PRId64" 4CC= %c%c%c%c codec_type=%d\n", size,
(format >> 0) & 0xff, (format >> 8) & 0xff, (format >> 16) & 0xff,
(format >> 24) & 0xff, st->codec->codec_type);
@@ -1773,9 +1919,11 @@ static int mov_read_stsc(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (!entries)
return 0;
- if (entries >= UINT_MAX / sizeof(*sc->stsc_data))
- return AVERROR_INVALIDDATA;
- sc->stsc_data = av_malloc(entries * sizeof(*sc->stsc_data));
+ if (sc->stsc_data)
+ av_log(c->fc, AV_LOG_WARNING, "Duplicated STSC atom\n");
+ av_free(sc->stsc_data);
+ sc->stsc_count = 0;
+ sc->stsc_data = av_malloc_array(entries, sizeof(*sc->stsc_data));
if (!sc->stsc_data)
return AVERROR(ENOMEM);
@@ -1807,9 +1955,11 @@ static int mov_read_stps(MOVContext *c, AVIOContext *pb, MOVAtom atom)
avio_rb32(pb); // version + flags
entries = avio_rb32(pb);
- if (entries >= UINT_MAX / sizeof(*sc->stps_data))
- return AVERROR_INVALIDDATA;
- sc->stps_data = av_malloc(entries * sizeof(*sc->stps_data));
+ if (sc->stps_data)
+ av_log(c->fc, AV_LOG_WARNING, "Duplicated STPS atom\n");
+ av_free(sc->stps_data);
+ sc->stps_count = 0;
+ sc->stps_data = av_malloc_array(entries, sizeof(*sc->stps_data));
if (!sc->stps_data)
return AVERROR(ENOMEM);
@@ -1847,12 +1997,17 @@ static int mov_read_stss(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (!entries)
{
sc->keyframe_absent = 1;
+ if (!st->need_parsing && st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
+ st->need_parsing = AVSTREAM_PARSE_HEADERS;
return 0;
}
+ if (sc->keyframes)
+ av_log(c->fc, AV_LOG_WARNING, "Duplicated STSS atom\n");
if (entries >= UINT_MAX / sizeof(int))
return AVERROR_INVALIDDATA;
av_freep(&sc->keyframes);
- sc->keyframes = av_malloc(entries * sizeof(int));
+ sc->keyframe_count = 0;
+ sc->keyframes = av_malloc_array(entries, sizeof(*sc->keyframes));
if (!sc->keyframes)
return AVERROR(ENOMEM);
@@ -1889,6 +2044,7 @@ static int mov_read_stsz(MOVContext *c, AVIOContext *pb, MOVAtom atom)
sample_size = avio_rb32(pb);
if (!sc->sample_size) /* do not overwrite value computed in stsd */
sc->sample_size = sample_size;
+ sc->stsz_sample_size = sample_size;
field_size = 32;
} else {
sample_size = 0;
@@ -1910,9 +2066,13 @@ static int mov_read_stsz(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (!entries)
return 0;
- if (entries >= UINT_MAX / sizeof(int) || entries >= (UINT_MAX - 4) / field_size)
+ if (entries >= (UINT_MAX - 4) / field_size)
return AVERROR_INVALIDDATA;
- sc->sample_sizes = av_malloc(entries * sizeof(int));
+ if (sc->sample_sizes)
+ av_log(c->fc, AV_LOG_WARNING, "Duplicated STSZ atom\n");
+ av_free(sc->sample_sizes);
+ sc->sample_count = 0;
+ sc->sample_sizes = av_malloc_array(entries, sizeof(*sc->sample_sizes));
if (!sc->sample_sizes)
return AVERROR(ENOMEM);
@@ -1966,13 +2126,11 @@ 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);
-
+ if (sc->stts_data)
+ av_log(c->fc, AV_LOG_WARNING, "Duplicated STTS atom\n");
av_free(sc->stts_data);
- sc->stts_data = av_malloc(entries * sizeof(*sc->stts_data));
+ sc->stts_count = 0;
+ sc->stts_data = av_malloc_array(entries, sizeof(*sc->stts_data));
if (!sc->stts_data)
return AVERROR(ENOMEM);
@@ -1982,6 +2140,13 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
sample_count=avio_rb32(pb);
sample_duration = avio_rb32(pb);
+
+ /* sample_duration < 0 is invalid based on the spec */
+ if (sample_duration < 0) {
+ av_log(c->fc, AV_LOG_ERROR, "Invalid SampleDelta %d in STTS, at %d st:%d\n",
+ sample_duration, i, c->fc->nb_streams-1);
+ sample_duration = 1;
+ }
if (sample_count < 0) {
av_log(c->fc, AV_LOG_ERROR, "Invalid sample_count=%d\n", sample_count);
return AVERROR_INVALIDDATA;
@@ -1992,12 +2157,21 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_dlog(c->fc, "sample_count=%d, sample_duration=%d\n",
sample_count, sample_duration);
+ if ( i+1 == entries
+ && i
+ && sample_count == 1
+ && total_sample_count > 100
+ && sample_duration/10 > duration / total_sample_count)
+ sample_duration = duration / total_sample_count;
duration+=(int64_t)sample_duration*sample_count;
total_sample_count+=sample_count;
}
sc->stts_count = i;
+ sc->duration_for_fps += duration;
+ sc->nb_frames_for_fps += total_sample_count;
+
if (pb->eof_reached)
return AVERROR_EOF;
@@ -2008,6 +2182,13 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
}
+static void mov_update_dts_shift(MOVStreamContext *sc, int duration)
+{
+ if (duration < 0) {
+ sc->dts_shift = FFMAX(sc->dts_shift, -duration);
+ }
+}
+
static int mov_read_ctts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
@@ -2039,8 +2220,19 @@ static int mov_read_ctts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
sc->ctts_data[i].count = count;
sc->ctts_data[i].duration= duration;
- if (duration < 0)
- sc->dts_shift = FFMAX(sc->dts_shift, -duration);
+
+ av_dlog(c->fc, "count=%d, duration=%d\n",
+ count, duration);
+
+ if (FFABS(duration) > (1<<28) && i+2<entries) {
+ av_log(c->fc, AV_LOG_WARNING, "CTTS invalid\n");
+ av_freep(&sc->ctts_data);
+ sc->ctts_count = 0;
+ return 0;
+ }
+
+ if (i+2<entries)
+ mov_update_dts_shift(sc, duration);
}
sc->ctts_count = i;
@@ -2077,9 +2269,11 @@ static int mov_read_sbgp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
entries = avio_rb32(pb);
if (!entries)
return 0;
- if (entries >= UINT_MAX / sizeof(*sc->rap_group))
- return AVERROR_INVALIDDATA;
- sc->rap_group = av_malloc(entries * sizeof(*sc->rap_group));
+ if (sc->rap_group)
+ av_log(c->fc, AV_LOG_WARNING, "Duplicated SBGP atom\n");
+ av_free(sc->rap_group);
+ sc->rap_group_count = 0;
+ sc->rap_group = av_malloc_array(entries, sizeof(*sc->rap_group));
if (!sc->rap_group)
return AVERROR(ENOMEM);
@@ -2105,17 +2299,40 @@ static void mov_build_index(MOVContext *mov, AVStream *st)
unsigned int i, j;
uint64_t stream_size = 0;
- /* adjust first dts according to edit list */
- if (sc->time_offset && mov->time_scale > 0) {
- if (sc->time_offset < 0)
- sc->time_offset = av_rescale(sc->time_offset, sc->time_scale, mov->time_scale);
- 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) {
- /* more than 16 frames delay, dts are likely wrong
- this happens with files created by iMovie */
- sc->wrong_dts = 1;
- st->codec->has_b_frames = 1;
+ if (sc->elst_count) {
+ int i, edit_start_index = 0, unsupported = 0;
+ int64_t empty_duration = 0; // empty duration of the first edit list entry
+ int64_t start_time = 0; // start time of the media
+
+ for (i = 0; i < sc->elst_count; i++) {
+ const MOVElst *e = &sc->elst_data[i];
+ if (i == 0 && e->time == -1) {
+ /* if empty, the first entry is the start time of the stream
+ * relative to the presentation itself */
+ empty_duration = e->duration;
+ edit_start_index = 1;
+ } else if (i == edit_start_index && e->time >= 0) {
+ start_time = e->time;
+ } else
+ unsupported = 1;
+ }
+ if (unsupported)
+ av_log(mov->fc, AV_LOG_WARNING, "multiple edit list entries, "
+ "a/v desync might occur, patch welcome\n");
+
+ /* adjust first dts according to edit list */
+ if ((empty_duration || start_time) && mov->time_scale > 0) {
+ if (empty_duration)
+ empty_duration = av_rescale(empty_duration, sc->time_scale, mov->time_scale);
+ sc->time_offset = start_time - empty_duration;
+ current_dts = -sc->time_offset;
+ 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;
+ st->codec->has_b_frames = 1;
+ }
}
}
@@ -2129,11 +2346,11 @@ static void mov_build_index(MOVContext *mov, AVStream *st)
unsigned int rap_group_index = 0;
unsigned int rap_group_sample = 0;
int rap_group_present = sc->rap_group_count && sc->rap_group;
- int key_off = (sc->keyframes && sc->keyframes[0] > 0) || (sc->stps_data && sc->stps_data[0] > 0);
+ int key_off = (sc->keyframe_count && sc->keyframes[0] > 0) || (sc->stps_count && sc->stps_data[0] > 0);
current_dts -= sc->dts_shift;
- if (!sc->sample_count)
+ if (!sc->sample_count || st->nb_index_entries)
return;
if (sc->sample_count >= UINT_MAX / sizeof(*st->index_entries) - st->nb_index_entries)
return;
@@ -2146,10 +2363,22 @@ static void mov_build_index(MOVContext *mov, AVStream *st)
st->index_entries_allocated_size = (st->nb_index_entries + sc->sample_count) * sizeof(*st->index_entries);
for (i = 0; i < sc->chunk_count; i++) {
+ int64_t next_offset = i+1 < sc->chunk_count ? sc->chunk_offsets[i+1] : INT64_MAX;
current_offset = sc->chunk_offsets[i];
while (stsc_index + 1 < sc->stsc_count &&
i + 1 == sc->stsc_data[stsc_index + 1].first)
stsc_index++;
+
+ if (next_offset > current_offset && sc->sample_size>0 && sc->sample_size < sc->stsz_sample_size &&
+ sc->stsc_data[stsc_index].count * (int64_t)sc->stsz_sample_size > next_offset - current_offset) {
+ av_log(mov->fc, AV_LOG_WARNING, "STSZ sample size %d invalid (too large), ignoring\n", sc->stsz_sample_size);
+ sc->stsz_sample_size = sc->sample_size;
+ }
+ if (sc->stsz_sample_size>0 && sc->stsz_sample_size < sc->sample_size) {
+ av_log(mov->fc, AV_LOG_WARNING, "STSZ sample size %d invalid (too small), ignoring\n", sc->stsz_sample_size);
+ sc->stsz_sample_size = sc->sample_size;
+ }
+
for (j = 0; j < sc->stsc_data[stsc_index].count; j++) {
int keyframe = 0;
if (current_sample >= sc->sample_count) {
@@ -2174,9 +2403,14 @@ static void mov_build_index(MOVContext *mov, AVStream *st)
rap_group_index++;
}
}
+ if (sc->keyframe_absent
+ && !sc->stps_count
+ && !rap_group_present
+ && (st->codec->codec_type == AVMEDIA_TYPE_AUDIO || (i==0 && j==0)))
+ keyframe = 1;
if (keyframe)
distance = 0;
- sample_size = sc->sample_size > 0 ? sc->sample_size : sc->sample_sizes[current_sample];
+ sample_size = sc->stsz_sample_size > 0 ? sc->stsz_sample_size : sc->sample_sizes[current_sample];
if (sc->pseudo_stream_id == -1 ||
sc->stsc_data[stsc_index].id - 1 == sc->pseudo_stream_id) {
AVIndexEntry *e = &st->index_entries[st->nb_index_entries++];
@@ -2188,6 +2422,8 @@ static void mov_build_index(MOVContext *mov, AVStream *st)
av_dlog(mov->fc, "AVIndex stream %d, sample %d, offset %"PRIx64", dts %"PRId64", "
"size %d, distance %d, keyframe %d\n", st->index, current_sample,
current_offset, current_dts, sample_size, distance, keyframe);
+ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && st->nb_index_entries < 100)
+ ff_rfps_add_frame(mov->fc, st, current_dts);
}
current_offset += sample_size;
@@ -2293,14 +2529,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 */
@@ -2332,11 +2568,26 @@ static int mov_open_dref(AVIOContext **pb, char *src, MOVDref *ref,
if (!avio_open2(pb, filename, AVIO_FLAG_READ, int_cb, NULL))
return 0;
}
+ } else if (use_absolute_path) {
+ av_log(fc, AV_LOG_WARNING, "Using absolute path on user request, "
+ "this is a possible security issue\n");
+ if (!avio_open2(pb, ref->path, AVIO_FLAG_READ, int_cb, NULL))
+ return 0;
}
return AVERROR(ENOENT);
}
+static void fix_timescale(MOVContext *c, MOVStreamContext *sc)
+{
+ if (sc->time_scale <= 0) {
+ av_log(c->fc, AV_LOG_WARNING, "stream %d, timescale not set\n", sc->ffindex);
+ sc->time_scale = c->time_scale;
+ if (sc->time_scale <= 0)
+ sc->time_scale = 1;
+ }
+}
+
static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
@@ -2364,12 +2615,7 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
}
- if (sc->time_scale <= 0) {
- av_log(c->fc, AV_LOG_WARNING, "stream %d, timescale not set\n", st->index);
- sc->time_scale = c->time_scale;
- if (sc->time_scale <= 0)
- sc->time_scale = 1;
- }
+ fix_timescale(c, sc);
avpriv_set_pts_info(st, 64, 1, sc->time_scale);
@@ -2377,21 +2623,31 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (sc->dref_id-1 < sc->drefs_count && sc->drefs[sc->dref_id-1].path) {
MOVDref *dref = &sc->drefs[sc->dref_id - 1];
- if (mov_open_dref(&sc->pb, c->fc->filename, dref, &c->fc->interrupt_callback) < 0)
+ if (mov_open_dref(&sc->pb, c->fc->filename, dref, &c->fc->interrupt_callback,
+ c->use_absolute_path, c->fc) < 0)
av_log(c->fc, AV_LOG_ERROR,
"stream %d, error opening alias: path='%s', dir='%s', "
"filename='%s', volume='%s', nlvl_from=%d, nlvl_to=%d\n",
st->index, dref->path, dref->dir, dref->filename,
dref->volume, dref->nlvl_from, dref->nlvl_to);
- } else
+ } else {
sc->pb = c->fc->pb;
+ sc->pb_is_copied = 1;
+ }
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
- if (!st->sample_aspect_ratio.num &&
+ if (!st->sample_aspect_ratio.num && st->codec->width && st->codec->height &&
+ sc->height && sc->width &&
(st->codec->width != sc->width || st->codec->height != sc->height)) {
st->sample_aspect_ratio = av_d2q(((double)st->codec->height * sc->width) /
((double)st->codec->width * sc->height), INT_MAX);
}
+
+#if FF_API_R_FRAME_RATE
+ if (sc->stts_count == 1 || (sc->stts_count == 2 && sc->stts_data[1].count == 1))
+ av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den,
+ sc->time_scale, sc->stts_data[0].duration, INT_MAX);
+#endif
}
// done for ai5q, ai52, ai55, ai1q, ai12 and ai15.
@@ -2424,6 +2680,7 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_freep(&sc->keyframes);
av_freep(&sc->stts_data);
av_freep(&sc->stps_data);
+ av_freep(&sc->elst_data);
av_freep(&sc->rap_group);
return 0;
@@ -2438,11 +2695,18 @@ static int mov_read_ilst(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return ret;
}
-static int mov_read_replaygain(MOVContext *c, AVIOContext *pb, int size)
+static int mov_read_custom_2plus(MOVContext *c, AVIOContext *pb, int size)
{
int64_t end = avio_tell(pb) + size;
uint8_t *key = NULL, *val = NULL;
int i;
+ AVStream *st;
+ MOVStreamContext *sc;
+
+ if (c->fc->nb_streams < 1)
+ return 0;
+ st = c->fc->streams[c->fc->nb_streams-1];
+ sc = st->priv_data;
for (i = 0; i < 2; i++) {
uint8_t **p;
@@ -2476,9 +2740,18 @@ static int mov_read_replaygain(MOVContext *c, AVIOContext *pb, int size)
}
if (key && val) {
- av_dict_set(&c->fc->metadata, key, val,
- AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
- key = val = NULL;
+ if (strcmp(key, "iTunSMPB") == 0) {
+ int priming, remainder, samples;
+ if(sscanf(val, "%*X %X %X %X", &priming, &remainder, &samples) == 3){
+ if(priming>0 && priming<16384)
+ sc->start_pad = priming;
+ }
+ }
+ if (strcmp(key, "cdec") != 0) {
+ av_dict_set(&c->fc->metadata, key, val,
+ AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
+ key = val = NULL;
+ }
}
avio_seek(pb, end, SEEK_SET);
@@ -2510,8 +2783,7 @@ static int mov_read_custom(MOVContext *c, AVIOContext *pb, MOVAtom atom)
domain_len = avio_get_str(pb, len, domain, sizeof(domain));
avio_skip(pb, len - domain_len);
- if (!strcmp(domain, "org.hydrogenaudio.replaygain"))
- return mov_read_replaygain(c, pb, end - avio_tell(pb));
+ return mov_read_custom_2plus(c, pb, end - avio_tell(pb));
}
fail:
@@ -2539,7 +2811,6 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
int i;
int width;
int height;
- int64_t disp_transform[2];
int display_matrix[3][3];
AVStream *st;
MOVStreamContext *sc;
@@ -2590,7 +2861,8 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
sc->width = width >> 16;
sc->height = height >> 16;
- // save the matrix when it is not the default identity
+ // save the matrix and add rotate metadata when it is not the default
+ // identity
if (display_matrix[0][0] != (1 << 16) ||
display_matrix[1][1] != (1 << 16) ||
display_matrix[2][2] != (1 << 30) ||
@@ -2598,6 +2870,7 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
display_matrix[1][0] || display_matrix[1][2] ||
display_matrix[2][0] || display_matrix[2][1]) {
int i, j;
+ double rotate;
av_freep(&sc->display_matrix);
sc->display_matrix = av_malloc(sizeof(int32_t) * 9);
@@ -2607,23 +2880,33 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
sc->display_matrix[i * 3 + j] = display_matrix[j][i];
+
+ rotate = av_display_rotation_get(sc->display_matrix);
+ if (!isnan(rotate)) {
+ char rotate_buf[64];
+ rotate = -rotate;
+ if (rotate < 0) // for backward compatibility
+ rotate += 360;
+ snprintf(rotate_buf, sizeof(rotate_buf), "%g", rotate);
+ av_dict_set(&st->metadata, "rotate", rotate_buf, 0);
+ }
}
// transform the display width/height according to the matrix
- // skip this when the display matrix is the identity one
// to keep the same scale, use [width height 1<<16]
if (width && height && sc->display_matrix) {
+ double disp_transform[2];
+
+#define SQR(a) ((a)*(double)(a))
for (i = 0; i < 2; i++)
- disp_transform[i] =
- (int64_t) width * display_matrix[0][i] +
- (int64_t) height * display_matrix[1][i] +
- ((int64_t) display_matrix[2][i] << 16);
+ disp_transform[i] = sqrt(SQR(display_matrix[i][0]) + SQR(display_matrix[i][1]));
- //sample aspect ratio is new width/height divided by old width/height
- if (disp_transform[0] > 0 && disp_transform[1] > 0)
+ if (disp_transform[0] > 0 && disp_transform[1] > 0 &&
+ disp_transform[0] < (1<<24) && disp_transform[1] < (1<<24) &&
+ fabs((disp_transform[0] / disp_transform[1]) - 1.0) > 0.01)
st->sample_aspect_ratio = av_d2q(
- ((double) disp_transform[0] * height) /
- ((double) disp_transform[1] * width), INT_MAX);
+ disp_transform[0] / disp_transform[1],
+ INT_MAX);
}
return 0;
}
@@ -2632,6 +2915,7 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
MOVFragment *frag = &c->fragment;
MOVTrackExt *trex = NULL;
+ MOVFragmentIndex* index = NULL;
int flags, track_id, i;
avio_r8(pb); /* version */
@@ -2650,6 +2934,15 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_log(c->fc, AV_LOG_ERROR, "could not find corresponding trex\n");
return AVERROR_INVALIDDATA;
}
+ for (i = 0; i < c->fragment_index_count; i++) {
+ MOVFragmentIndex* candidate = c->fragment_index_data[i];
+ if (candidate->track_id == frag->track_id) {
+ av_log(c->fc, AV_LOG_DEBUG,
+ "found fragment index for track %u\n", frag->track_id);
+ index = candidate;
+ break;
+ }
+ }
frag->base_data_offset = flags & MOV_TFHD_BASE_DATA_OFFSET ?
avio_rb64(pb) : flags & MOV_TFHD_DEFAULT_BASE_IS_MOOF ?
@@ -2662,6 +2955,25 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
avio_rb32(pb) : trex->size;
frag->flags = flags & MOV_TFHD_DEFAULT_FLAGS ?
avio_rb32(pb) : trex->flags;
+ frag->time = AV_NOPTS_VALUE;
+ if (index) {
+ int i, found = 0;
+ for (i = index->current_item; i < index->item_count; i++) {
+ if (frag->implicit_offset == index->items[i].moof_offset) {
+ av_log(c->fc, AV_LOG_DEBUG, "found fragment index entry "
+ "for track %u and moof_offset %"PRId64"\n",
+ frag->track_id, index->items[i].moof_offset);
+ frag->time = index->items[i].time;
+ index->current_item = i + 1;
+ found = 1;
+ }
+ }
+ if (!found) {
+ av_log(c->fc, AV_LOG_WARNING, "track %u has a fragment index "
+ "but it doesn't have an (in-order) entry for moof_offset "
+ "%"PRId64"\n", frag->track_id, frag->implicit_offset);
+ }
+ }
av_dlog(c->fc, "frag flags 0x%x\n", frag->flags);
return 0;
}
@@ -2684,6 +2996,9 @@ static int mov_read_trex(MOVContext *c, AVIOContext *pb, MOVAtom atom)
c->trex_count = 0;
return err;
}
+
+ c->fc->duration = AV_NOPTS_VALUE; // the duration from mvhd is not representing the whole file when fragments are used.
+
trex = &c->trex_data[c->trex_count++];
avio_r8(pb); /* version */
avio_rb24(pb); /* flags */
@@ -2748,7 +3063,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return AVERROR_INVALIDDATA;
}
sc = st->priv_data;
- if (sc->pseudo_stream_id+1 != frag->stsd_id)
+ if (sc->pseudo_stream_id+1 != frag->stsd_id && sc->pseudo_stream_id != -1)
return 0;
avio_r8(pb); /* version */
flags = avio_rb24(pb);
@@ -2796,6 +3111,29 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
sc->ctts_data[sc->ctts_count].count = 1;
sc->ctts_data[sc->ctts_count].duration = (flags & MOV_TRUN_SAMPLE_CTS) ?
avio_rb32(pb) : 0;
+ mov_update_dts_shift(sc, sc->ctts_data[sc->ctts_count].duration);
+ if (frag->time != AV_NOPTS_VALUE) {
+ if (c->use_mfra_for == FF_MOV_FLAG_MFRA_PTS) {
+ int64_t pts = frag->time;
+ av_log(c->fc, AV_LOG_DEBUG, "found frag time %"PRId64
+ " sc->dts_shift %d ctts.duration %d"
+ " sc->time_offset %"PRId64" flags & MOV_TRUN_SAMPLE_CTS %d\n", pts,
+ sc->dts_shift, sc->ctts_data[sc->ctts_count].duration,
+ sc->time_offset, flags & MOV_TRUN_SAMPLE_CTS);
+ dts = pts - sc->dts_shift;
+ if (flags & MOV_TRUN_SAMPLE_CTS) {
+ dts -= sc->ctts_data[sc->ctts_count].duration;
+ } else {
+ dts -= sc->time_offset;
+ }
+ av_log(c->fc, AV_LOG_DEBUG, "calculated into dts %"PRId64"\n", dts);
+ } else {
+ dts = frag->time;
+ av_log(c->fc, AV_LOG_DEBUG, "found frag time %"PRId64
+ ", using it for dts\n", dts);
+ }
+ frag->time = AV_NOPTS_VALUE;
+ }
sc->ctts_count++;
if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO)
keyframe = 1;
@@ -2814,6 +3152,8 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
dts += sample_duration;
offset += sample_size;
sc->data_size += sample_size;
+ sc->duration_for_fps += sample_duration;
+ sc->nb_frames_for_fps ++;
}
if (pb->eof_reached)
@@ -2860,7 +3200,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 */
@@ -2901,7 +3241,7 @@ static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom)
MOVStreamContext *sc;
int i, edit_count, version;
- 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;
@@ -2909,40 +3249,147 @@ static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom)
avio_rb24(pb); /* flags */
edit_count = avio_rb32(pb); /* entries */
- if ((uint64_t)edit_count*12+8 > atom.size)
- return AVERROR_INVALIDDATA;
+ if (!edit_count)
+ return 0;
+ if (sc->elst_data)
+ av_log(c->fc, AV_LOG_WARNING, "Duplicated ELST atom\n");
+ av_free(sc->elst_data);
+ sc->elst_count = 0;
+ sc->elst_data = av_malloc_array(edit_count, sizeof(*sc->elst_data));
+ if (!sc->elst_data)
+ return AVERROR(ENOMEM);
+
+ av_dlog(c->fc, "track[%i].edit_count = %i\n", c->fc->nb_streams-1, edit_count);
+ for (i = 0; i < edit_count && !pb->eof_reached; i++) {
+ MOVElst *e = &sc->elst_data[i];
- for (i=0; i<edit_count; i++){
- int64_t time;
- int64_t duration;
if (version == 1) {
- duration = avio_rb64(pb);
- time = avio_rb64(pb);
+ e->duration = avio_rb64(pb);
+ e->time = avio_rb64(pb);
} else {
- duration = avio_rb32(pb); /* segment duration */
- time = (int32_t)avio_rb32(pb); /* media time */
+ e->duration = avio_rb32(pb); /* segment duration */
+ e->time = (int32_t)avio_rb32(pb); /* media time */
+ }
+ e->rate = avio_rb32(pb) / 65536.0;
+ av_dlog(c->fc, "duration=%"PRId64" time=%"PRId64" rate=%f\n",
+ e->duration, e->time, e->rate);
+ }
+ sc->elst_count = i;
+
+ return 0;
+}
+
+static int mov_read_tmcd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ MOVStreamContext *sc;
+
+ if (c->fc->nb_streams < 1)
+ return AVERROR_INVALIDDATA;
+ sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data;
+ sc->timecode_track = avio_rb32(pb);
+ return 0;
+}
+
+static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ int ret;
+ uint8_t uuid[16];
+ static const uint8_t uuid_isml_manifest[] = {
+ 0xa5, 0xd4, 0x0b, 0x30, 0xe8, 0x14, 0x11, 0xdd,
+ 0xba, 0x2f, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66
+ };
+
+ if (atom.size < sizeof(uuid) || atom.size == INT64_MAX)
+ return AVERROR_INVALIDDATA;
+
+ ret = avio_read(pb, uuid, sizeof(uuid));
+ if (ret < 0) {
+ return ret;
+ } else if (ret != sizeof(uuid)) {
+ return AVERROR_INVALIDDATA;
+ }
+ if (!memcmp(uuid, uuid_isml_manifest, sizeof(uuid))) {
+ uint8_t *buffer, *ptr;
+ char *endptr;
+ size_t len = atom.size - sizeof(uuid);
+
+ if (len < 4) {
+ return AVERROR_INVALIDDATA;
+ }
+ ret = avio_skip(pb, 4); // zeroes
+ len -= 4;
+
+ buffer = av_mallocz(len + 1);
+ if (!buffer) {
+ return AVERROR(ENOMEM);
}
- avio_rb32(pb); /* Media rate */
- if (i == 0 && time >= -1) {
- sc->time_offset = time != -1 ? time : -duration;
+ ret = avio_read(pb, buffer, len);
+ if (ret < 0) {
+ av_free(buffer);
+ return ret;
+ } else if (ret != len) {
+ av_free(buffer);
+ return AVERROR_INVALIDDATA;
}
+
+ ptr = buffer;
+ while ((ptr = av_stristr(ptr, "systemBitrate=\""))) {
+ ptr += sizeof("systemBitrate=\"") - 1;
+ c->bitrates_count++;
+ c->bitrates = av_realloc_f(c->bitrates, c->bitrates_count, sizeof(*c->bitrates));
+ if (!c->bitrates) {
+ c->bitrates_count = 0;
+ av_free(buffer);
+ return AVERROR(ENOMEM);
+ }
+ errno = 0;
+ ret = strtol(ptr, &endptr, 10);
+ if (ret < 0 || errno || *endptr != '"') {
+ c->bitrates[c->bitrates_count - 1] = 0;
+ } else {
+ c->bitrates[c->bitrates_count - 1] = ret;
+ }
+ }
+
+ av_free(buffer);
}
+ return 0;
+}
- if (edit_count > 1)
- av_log(c->fc, AV_LOG_WARNING, "multiple edit list entries, "
- "a/v desync might occur, patch welcome\n");
+static int mov_read_free(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ int ret;
+ uint8_t content[16];
+
+ if (atom.size < 8)
+ return 0;
+
+ ret = avio_read(pb, content, FFMIN(sizeof(content), atom.size));
+ if (ret < 0)
+ return ret;
+
+ if ( !c->found_moov
+ && !c->found_mdat
+ && !memcmp(content, "Anevia\x1A\x1A", 8)
+ && c->use_mfra_for == FF_MOV_FLAG_MFRA_AUTO) {
+ c->use_mfra_for = FF_MOV_FLAG_MFRA_PTS;
+ }
- av_dlog(c->fc, "track[%i].edit_count = %i\n", c->fc->nb_streams-1, edit_count);
return 0;
}
static const MOVParseTableEntry mov_default_parse_table[] = {
-{ MKTAG('a','v','s','s'), mov_read_extradata },
+{ MKTAG('A','C','L','R'), mov_read_avid },
+{ MKTAG('A','P','R','G'), mov_read_avid },
+{ MKTAG('A','A','L','P'), mov_read_avid },
+{ MKTAG('A','R','E','S'), mov_read_ares },
+{ MKTAG('a','v','s','s'), mov_read_avss },
{ MKTAG('c','h','p','l'), mov_read_chpl },
{ MKTAG('c','o','6','4'), mov_read_stco },
{ MKTAG('c','o','l','r'), mov_read_colr },
{ MKTAG('c','t','t','s'), mov_read_ctts }, /* composition time to sample */
{ MKTAG('d','i','n','f'), mov_read_default },
+{ MKTAG('D','p','x','E'), mov_read_dpxe },
{ MKTAG('d','r','e','f'), mov_read_dref },
{ MKTAG('e','d','t','s'), mov_read_default },
{ MKTAG('e','l','s','t'), mov_read_elst },
@@ -2952,7 +3399,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 },
@@ -2962,8 +3409,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 },
@@ -2982,6 +3429,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
{ MKTAG('t','r','a','k'), mov_read_trak },
{ MKTAG('t','r','a','f'), mov_read_default },
{ MKTAG('t','r','e','f'), mov_read_default },
+{ MKTAG('t','m','c','d'), mov_read_tmcd },
{ MKTAG('c','h','a','p'), mov_read_chap },
{ MKTAG('t','r','e','x'), mov_read_trex },
{ MKTAG('t','r','u','n'), mov_read_trun },
@@ -2997,6 +3445,9 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
{ MKTAG('d','v','c','1'), mov_read_dvc1 },
{ MKTAG('s','b','g','p'), mov_read_sbgp },
{ MKTAG('h','v','c','C'), mov_read_glbl },
+{ MKTAG('u','u','i','d'), mov_read_uuid },
+{ MKTAG('C','i','n', 0x8e), mov_read_targa_y216 },
+{ MKTAG('f','r','e','e'), mov_read_free },
{ MKTAG('-','-','-','-'), mov_read_custom },
{ 0, NULL }
};
@@ -3007,27 +3458,55 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom)
MOVAtom a;
int i;
+ if (c->atom_depth > 10) {
+ av_log(c->fc, AV_LOG_ERROR, "Atoms too deeply nested\n");
+ return AVERROR_INVALIDDATA;
+ }
+ c->atom_depth ++;
+
if (atom.size < 0)
atom.size = INT64_MAX;
- while (total_size + 8 < atom.size && !pb->eof_reached) {
+ while (total_size + 8 <= atom.size && !avio_feof(pb)) {
int (*parse)(MOVContext*, AVIOContext*, MOVAtom) = NULL;
a.size = atom.size;
a.type=0;
if (atom.size >= 8) {
a.size = avio_rb32(pb);
a.type = avio_rl32(pb);
+ if (a.type == MKTAG('f','r','e','e') &&
+ a.size >= 8 &&
+ c->moov_retry) {
+ uint8_t buf[8];
+ uint32_t *type = (uint32_t *)buf + 1;
+ avio_read(pb, buf, 8);
+ avio_seek(pb, -8, SEEK_CUR);
+ if (*type == MKTAG('m','v','h','d') ||
+ *type == MKTAG('c','m','o','v')) {
+ av_log(c->fc, AV_LOG_ERROR, "Detected moov in a free atom.\n");
+ a.type = MKTAG('m','o','o','v');
+ }
+ }
+ if (atom.type != MKTAG('r','o','o','t') &&
+ atom.type != MKTAG('m','o','o','v'))
+ {
+ if (a.type == MKTAG('t','r','a','k') || a.type == MKTAG('m','d','a','t'))
+ {
+ av_log(c->fc, AV_LOG_ERROR, "Broken file, trak/mdat not at top-level\n");
+ avio_skip(pb, -8);
+ c->atom_depth --;
+ return 0;
+ }
+ }
+ total_size += 8;
+ if (a.size == 1 && total_size + 8 <= atom.size) { /* 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)
@@ -3051,13 +3530,16 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom)
int64_t start_pos = avio_tell(pb);
int64_t left;
int err = parse(c, pb, a);
- if (err < 0)
+ if (err < 0) {
+ c->atom_depth --;
return err;
+ }
if (c->found_moov && c->found_mdat &&
((!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX) ||
start_pos + a.size == avio_size(pb))) {
if (!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX)
c->next_root_atom = start_pos + a.size;
+ c->atom_depth --;
return 0;
}
left = a.size - avio_tell(pb) + start_pos;
@@ -3077,51 +3559,89 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (total_size < atom.size && atom.size < 0x7ffff)
avio_skip(pb, atom.size - total_size);
+ c->atom_depth --;
return 0;
}
static int mov_probe(AVProbeData *p)
{
- unsigned int offset;
+ int64_t offset;
uint32_t tag;
int score = 0;
+ int moov_offset = -1;
/* check file header */
offset = 0;
for (;;) {
/* ignore invalid offset */
if ((offset + 8) > (unsigned int)p->buf_size)
- return score;
+ break;
tag = AV_RL32(p->buf + offset + 4);
switch(tag) {
/* check for obvious tags */
- case MKTAG('j','P',' ',' '): /* jpeg 2000 signature */
case MKTAG('m','o','o','v'):
+ moov_offset = offset + 4;
case MKTAG('m','d','a','t'):
case MKTAG('p','n','o','t'): /* detect movs with preview pics like ew.mov and april.mov */
case MKTAG('u','d','t','a'): /* Packet Video PVAuthor adds this and a lot of more junk */
case MKTAG('f','t','y','p'):
- return AVPROBE_SCORE_MAX;
+ if (AV_RB32(p->buf+offset) < 8 &&
+ (AV_RB32(p->buf+offset) != 1 ||
+ offset + 12 > (unsigned int)p->buf_size ||
+ AV_RB64(p->buf+offset + 8) == 0)) {
+ score = FFMAX(score, AVPROBE_SCORE_EXTENSION);
+ } else if (tag == MKTAG('f','t','y','p') &&
+ AV_RL32(p->buf + offset + 8) == MKTAG('j','p','2',' ')) {
+ score = FFMAX(score, 5);
+ } else {
+ score = AVPROBE_SCORE_MAX;
+ }
+ offset = FFMAX(4, AV_RB32(p->buf+offset)) + offset;
+ break;
/* those are more common words, so rate then a bit less */
case MKTAG('e','d','i','w'): /* xdcam files have reverted first tags */
case MKTAG('w','i','d','e'):
case MKTAG('f','r','e','e'):
case MKTAG('j','u','n','k'):
case MKTAG('p','i','c','t'):
- return AVPROBE_SCORE_MAX - 5;
+ score = FFMAX(score, AVPROBE_SCORE_MAX - 5);
+ offset = FFMAX(4, AV_RB32(p->buf+offset)) + offset;
+ break;
case MKTAG(0x82,0x82,0x7f,0x7d):
case MKTAG('s','k','i','p'):
case MKTAG('u','u','i','d'):
case MKTAG('p','r','f','l'):
- offset = AV_RB32(p->buf+offset) + offset;
/* if we only find those cause probedata is too small at least rate them */
- score = AVPROBE_SCORE_EXTENSION;
+ score = FFMAX(score, AVPROBE_SCORE_EXTENSION);
+ offset = FFMAX(4, AV_RB32(p->buf+offset)) + offset;
break;
default:
- /* unrecognized tag */
- return score;
+ offset = FFMAX(4, AV_RB32(p->buf+offset)) + offset;
+ }
+ }
+ if(score > AVPROBE_SCORE_MAX - 50 && moov_offset != -1) {
+ /* moov atom in the header - we should make sure that this is not a
+ * MOV-packed MPEG-PS */
+ offset = moov_offset;
+
+ while(offset < (p->buf_size - 16)){ /* Sufficient space */
+ /* We found an actual hdlr atom */
+ if(AV_RL32(p->buf + offset ) == MKTAG('h','d','l','r') &&
+ AV_RL32(p->buf + offset + 8) == MKTAG('m','h','l','r') &&
+ AV_RL32(p->buf + offset + 12) == MKTAG('M','P','E','G')){
+ av_log(NULL, AV_LOG_WARNING, "Found media data tag MPEG indicating this is a MOV-packed MPEG-PS.\n");
+ /* We found a media handler reference atom describing an
+ * MPEG-PS-in-MOV, return a
+ * low score to force expanding the probe window until
+ * mpegps_probe finds what it needs */
+ return 5;
+ }else
+ /* Keep looking */
+ offset+=2;
}
}
+
+ return score;
}
// must be done after parsing all trak because there's no order requirement
@@ -3154,6 +3674,11 @@ static void mov_read_chapters(AVFormatContext *s)
uint16_t ch;
int len, title_len;
+ if (end < sample->timestamp) {
+ av_log(s, AV_LOG_WARNING, "ignoring stream duration which is shorter than chapters\n");
+ end = AV_NOPTS_VALUE;
+ }
+
if (avio_seek(sc->pb, sample->pos, SEEK_SET) != sample->pos) {
av_log(s, AV_LOG_ERROR, "Chapter %d not found in file\n", i);
goto finish;
@@ -3183,7 +3708,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);
}
}
@@ -3194,6 +3719,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;
@@ -3209,15 +3777,20 @@ static int mov_read_close(AVFormatContext *s)
av_freep(&sc->drefs[j].dir);
}
av_freep(&sc->drefs);
- if (sc->pb && sc->pb != s->pb)
- avio_close(sc->pb);
+ sc->drefs_count = 0;
+
+ if (!sc->pb_is_copied)
+ avio_closep(&sc->pb);
+
+ sc->pb = NULL;
av_freep(&sc->chunk_offsets);
av_freep(&sc->stsc_data);
av_freep(&sc->sample_sizes);
av_freep(&sc->keyframes);
av_freep(&sc->stts_data);
av_freep(&sc->stps_data);
+ av_freep(&sc->elst_data);
av_freep(&sc->rap_group);
av_freep(&sc->display_matrix);
}
@@ -3228,15 +3801,163 @@ static int mov_read_close(AVFormatContext *s)
}
av_freep(&mov->trex_data);
+ av_freep(&mov->bitrates);
+
+ for (i = 0; i < mov->fragment_index_count; i++) {
+ MOVFragmentIndex* index = mov->fragment_index_data[i];
+ av_freep(&index->items);
+ av_freep(&mov->fragment_index_data[i]);
+ }
+ av_freep(&mov->fragment_index_data);
return 0;
}
+static int tmcd_is_referenced(AVFormatContext *s, int tmcd_id)
+{
+ int i;
+
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ MOVStreamContext *sc = st->priv_data;
+
+ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
+ sc->timecode_track == tmcd_id)
+ return 1;
+ }
+ return 0;
+}
+
+/* look for a tmcd track not referenced by any video track, and export it globally */
+static void export_orphan_timecode(AVFormatContext *s)
+{
+ int i;
+
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+
+ if (st->codec->codec_tag == MKTAG('t','m','c','d') &&
+ !tmcd_is_referenced(s, i + 1)) {
+ AVDictionaryEntry *tcr = av_dict_get(st->metadata, "timecode", NULL, 0);
+ if (tcr) {
+ av_dict_set(&s->metadata, "timecode", tcr->value, 0);
+ break;
+ }
+ }
+ }
+}
+
+static int read_tfra(MOVContext *mov, AVIOContext *f)
+{
+ MOVFragmentIndex* index = NULL;
+ int version, fieldlength, i, j;
+ int64_t pos = avio_tell(f);
+ uint32_t size = avio_rb32(f);
+ void *tmp;
+
+ if (avio_rb32(f) != MKBETAG('t', 'f', 'r', 'a')) {
+ return 1;
+ }
+ av_log(mov->fc, AV_LOG_VERBOSE, "found tfra\n");
+ index = av_mallocz(sizeof(MOVFragmentIndex));
+ if (!index) {
+ return AVERROR(ENOMEM);
+ }
+
+ tmp = av_realloc_array(mov->fragment_index_data,
+ mov->fragment_index_count + 1,
+ sizeof(MOVFragmentIndex*));
+ if (!tmp) {
+ av_freep(&index);
+ return AVERROR(ENOMEM);
+ }
+ mov->fragment_index_data = tmp;
+ mov->fragment_index_data[mov->fragment_index_count++] = index;
+
+ version = avio_r8(f);
+ avio_rb24(f);
+ index->track_id = avio_rb32(f);
+ fieldlength = avio_rb32(f);
+ index->item_count = avio_rb32(f);
+ index->items = av_mallocz_array(
+ index->item_count, sizeof(MOVFragmentIndexItem));
+ if (!index->items) {
+ index->item_count = 0;
+ return AVERROR(ENOMEM);
+ }
+ for (i = 0; i < index->item_count; i++) {
+ int64_t time, offset;
+ if (version == 1) {
+ time = avio_rb64(f);
+ offset = avio_rb64(f);
+ } else {
+ time = avio_rb32(f);
+ offset = avio_rb32(f);
+ }
+ index->items[i].time = time;
+ index->items[i].moof_offset = offset;
+ for (j = 0; j < ((fieldlength >> 4) & 3) + 1; j++)
+ avio_r8(f);
+ for (j = 0; j < ((fieldlength >> 2) & 3) + 1; j++)
+ avio_r8(f);
+ for (j = 0; j < ((fieldlength >> 0) & 3) + 1; j++)
+ avio_r8(f);
+ }
+
+ avio_seek(f, pos + size, SEEK_SET);
+ return 0;
+}
+
+static int mov_read_mfra(MOVContext *c, AVIOContext *f)
+{
+ int64_t stream_size = avio_size(f);
+ int64_t original_pos = avio_tell(f);
+ int64_t seek_ret;
+ int32_t mfra_size;
+ int ret = -1;
+ if ((seek_ret = avio_seek(f, stream_size - 4, SEEK_SET)) < 0) {
+ ret = seek_ret;
+ goto fail;
+ }
+ mfra_size = avio_rb32(f);
+ if (mfra_size < 0 || mfra_size > stream_size) {
+ av_log(c->fc, AV_LOG_DEBUG, "doesn't look like mfra (unreasonable size)\n");
+ goto fail;
+ }
+ if ((seek_ret = avio_seek(f, -mfra_size, SEEK_CUR)) < 0) {
+ ret = seek_ret;
+ goto fail;
+ }
+ if (avio_rb32(f) != mfra_size) {
+ av_log(c->fc, AV_LOG_DEBUG, "doesn't look like mfra (size mismatch)\n");
+ goto fail;
+ }
+ if (avio_rb32(f) != MKBETAG('m', 'f', 'r', 'a')) {
+ av_log(c->fc, AV_LOG_DEBUG, "doesn't look like mfra (tag mismatch)\n");
+ goto fail;
+ }
+ av_log(c->fc, AV_LOG_VERBOSE, "stream has mfra\n");
+ do {
+ ret = read_tfra(c, f);
+ if (ret < 0)
+ goto fail;
+ } while (!ret);
+ ret = 0;
+fail:
+ seek_ret = avio_seek(f, original_pos, SEEK_SET);
+ if (seek_ret < 0) {
+ av_log(c->fc, AV_LOG_ERROR,
+ "failed to seek back after looking for mfra\n");
+ ret = seek_ret;
+ }
+ return ret;
+}
+
static int mov_read_header(AVFormatContext *s)
{
MOVContext *mov = s->priv_data;
AVIOContext *pb = s->pb;
- int err;
+ int j, err;
MOVAtom atom = { AV_RL32("root") };
int i;
@@ -3248,11 +3969,15 @@ static int mov_read_header(AVFormatContext *s)
atom.size = INT64_MAX;
/* check MOV header */
+ do {
+ if (mov->moov_retry)
+ avio_seek(pb, 0, SEEK_SET);
if ((err = mov_read_default(mov, pb, atom)) < 0) {
- av_log(s, AV_LOG_ERROR, "error reading header: %d\n", err);
+ av_log(s, AV_LOG_ERROR, "error reading header\n");
mov_read_close(s);
return err;
}
+ } while (pb->seekable && !mov->found_moov && !mov->moov_retry++);
if (!mov->found_moov) {
av_log(s, AV_LOG_ERROR, "moov atom not found\n");
mov_read_close(s);
@@ -3260,13 +3985,45 @@ static int mov_read_header(AVFormatContext *s)
}
av_dlog(mov->fc, "on_parse_exit_offset=%"PRId64"\n", avio_tell(pb));
- if (pb->seekable && mov->chapter_track > 0)
- mov_read_chapters(s);
+ if (pb->seekable) {
+ if (mov->chapter_track > 0)
+ mov_read_chapters(s);
+ for (i = 0; i < s->nb_streams; i++)
+ if (s->streams[i]->codec->codec_tag == AV_RL32("tmcd"))
+ mov_read_timecode_track(s, s->streams[i]);
+ }
+ /* copy timecode metadata from tmcd tracks to the related video streams */
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MOVStreamContext *sc = st->priv_data;
+ if (sc->timecode_track > 0) {
+ AVDictionaryEntry *tcr;
+ int tmcd_st_id = -1;
+
+ for (j = 0; j < s->nb_streams; j++)
+ if (s->streams[j]->id == sc->timecode_track)
+ tmcd_st_id = j;
+
+ if (tmcd_st_id < 0 || tmcd_st_id == i)
+ continue;
+ tcr = av_dict_get(s->streams[tmcd_st_id]->metadata, "timecode", NULL, 0);
+ if (tcr)
+ av_dict_set(&st->metadata, "timecode", tcr->value, 0);
+ }
+ }
+ export_orphan_timecode(s);
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ MOVStreamContext *sc = st->priv_data;
+ fix_timescale(mov, sc);
+ if(st->codec->codec_type == AVMEDIA_TYPE_AUDIO && st->codec->codec_id == AV_CODEC_ID_AAC) {
+ st->skip_samples = sc->start_pad;
+ }
+ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && sc->nb_frames_for_fps > 0 && sc->duration_for_fps > 0)
+ av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
+ sc->time_scale*(int64_t)sc->nb_frames_for_fps, sc->duration_for_fps, INT_MAX);
if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
if (st->codec->width <= 0 || st->codec->height <= 0) {
st->codec->width = sc->width;
@@ -3288,6 +4045,25 @@ static int mov_read_header(AVFormatContext *s)
}
}
+ if (mov->use_mfra_for > 0) {
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ MOVStreamContext *sc = st->priv_data;
+ if (sc->duration_for_fps > 0) {
+ st->codec->bit_rate = sc->data_size * 8 * sc->time_scale /
+ sc->duration_for_fps;
+ }
+ }
+ }
+
+ for (i = 0; i < mov->bitrates_count && i < s->nb_streams; i++) {
+ if (mov->bitrates[i]) {
+ s->streams[i]->codec->bit_rate = mov->bitrates[i];
+ }
+ }
+
+ ff_rfps_calculate(s);
+
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MOVStreamContext *sc = st->priv_data;
@@ -3358,6 +4134,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) {
@@ -3367,7 +4144,7 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
avio_seek(s->pb, mov->next_root_atom, SEEK_SET);
mov->next_root_atom = 0;
if (mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX }) < 0 ||
- s->pb->eof_reached)
+ avio_feof(s->pb))
return AVERROR_EOF;
av_dlog(s, "read fragments, offset 0x%"PRIx64"\n", avio_tell(s->pb));
goto retry;
@@ -3376,6 +4153,11 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
/* must be done just before reading, to avoid infinite loop on sample */
sc->current_sample++;
+ if (mov->next_root_atom) {
+ sample->pos = FFMIN(sample->pos, mov->next_root_atom);
+ sample->size = FFMIN(sample->size, (mov->next_root_atom - sample->pos));
+ }
+
if (st->discard != AVDISCARD_ALL) {
if (avio_seek(sc->pb, sample->pos, SEEK_SET) != sample->pos) {
av_log(mov->fc, AV_LOG_ERROR, "stream %d, offset 0x%"PRIx64": partial file\n",
@@ -3398,8 +4180,8 @@ 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);
- av_free(pkt->data);
+ avpriv_dv_produce_packet(mov->dv_demux, pkt, pkt->data, pkt->size, pkt->pos);
+ av_freep(&pkt->data);
pkt->size = 0;
ret = avpriv_dv_get_packet(mov->dv_demux, pkt);
if (ret < 0)
@@ -3475,8 +4257,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);
@@ -3487,7 +4267,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;
@@ -3500,6 +4283,23 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
#define OFFSET(x) offsetof(MOVContext, x)
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
static const AVOption mov_options[] = {
+ {"use_absolute_path",
+ "allow using absolute path when opening alias, this is a possible security issue",
+ OFFSET(use_absolute_path), FF_OPT_TYPE_INT, {.i64 = 0},
+ 0, 1, FLAGS},
+ {"ignore_editlist", "", OFFSET(ignore_editlist), FF_OPT_TYPE_INT, {.i64 = 0},
+ 0, 1, FLAGS},
+ {"use_mfra_for",
+ "use mfra for fragment timestamps",
+ OFFSET(use_mfra_for), FF_OPT_TYPE_INT, {.i64 = FF_MOV_FLAG_MFRA_AUTO},
+ -1, FF_MOV_FLAG_MFRA_PTS, FLAGS,
+ "use_mfra_for"},
+ {"auto", "auto", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_MFRA_AUTO}, 0, 0,
+ FLAGS, "use_mfra_for" },
+ {"dts", "dts", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_MFRA_DTS}, 0, 0,
+ FLAGS, "use_mfra_for" },
+ {"pts", "pts", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_MFRA_PTS}, 0, 0,
+ FLAGS, "use_mfra_for" },
{ "export_all", "Export unrecognized metadata entries", OFFSET(export_all),
AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = FLAGS },
{ "export_xmp", "Export full XMP metadata", OFFSET(export_xmp),
@@ -3525,4 +4325,5 @@ AVInputFormat ff_mov_demuxer = {
.read_packet = mov_read_packet,
.read_close = mov_read_close,
.read_seek = mov_read_seek,
+ .flags = AVFMT_NO_BYTE_SEEK,
};
diff --git a/libavformat/mov_chan.c b/libavformat/mov_chan.c
index aa7ba1079f..3b91ed7054 100644
--- a/libavformat/mov_chan.c
+++ b/libavformat/mov_chan.c
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2011 Justin Ruggles
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -344,7 +344,7 @@ static const struct MovChannelLayoutMap mov_ch_layout_map_9ch[] = {
{ 0, 0 },
};
-static const struct MovChannelLayoutMap *mov_ch_layout_map[] = {
+static const struct MovChannelLayoutMap * const mov_ch_layout_map[] = {
mov_ch_layout_map_misc,
mov_ch_layout_map_1ch,
mov_ch_layout_map_2ch,
@@ -426,6 +426,7 @@ static const enum MovChannelLayoutTag mov_ch_layouts_wav[] = {
MOV_CH_LAYOUT_MPEG_7_1_A,
MOV_CH_LAYOUT_MPEG_7_1_C,
MOV_CH_LAYOUT_SMPTE_DTV,
+ 0,
};
static const struct {
@@ -570,6 +571,7 @@ int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st,
avio_rl32(pb); // mCoordinates[0]
avio_rl32(pb); // mCoordinates[1]
avio_rl32(pb); // mCoordinates[2]
+ size -= 20;
if (layout_tag == 0) {
uint32_t mask_incr = mov_get_channel_label(label);
if (mask_incr == 0) {
@@ -584,6 +586,7 @@ int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st,
st->codec->channel_layout = label_mask;
} else
st->codec->channel_layout = ff_mov_get_channel_layout(layout_tag, bitmap);
+ avio_skip(pb, size - 12);
return 0;
}
diff --git a/libavformat/mov_chan.h b/libavformat/mov_chan.h
index 3fae93961e..ca28345aef 100644
--- a/libavformat/mov_chan.h
+++ b/libavformat/mov_chan.h
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2011 Justin Ruggles
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 6259a8770f..f95d0667b4 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
*/
@@ -31,25 +31,27 @@
#include "avio.h"
#include "isom.h"
#include "avc.h"
+#include "libavcodec/ac3_parser.h"
#include "libavcodec/get_bits.h"
#include "libavcodec/put_bits.h"
#include "libavcodec/vc1_common.h"
+#include "libavcodec/raw.h"
#include "internal.h"
#include "libavutil/avstring.h"
#include "libavutil/intfloat.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
#include "libavutil/dict.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/timecode.h"
#include "hevc.h"
#include "rtpenc.h"
#include "mov_chan.h"
-#undef NDEBUG
-#include <assert.h>
-
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", 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" },
@@ -62,14 +64,16 @@ static const AVOption options[] = {
{ "dash", "Write DASH compatible fragmented MP4", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DASH}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
{ "frag_discont", "Signal that the next fragment is discontinuous from earlier ones", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_DISCONT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
{ "delay_moov", "Delay writing the initial moov until the first fragment is cut, or until the first fragment flush", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DELAY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
+ { "write_colr", "Write colr atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_COLR}, 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},
+ { "video_track_timescale", "set timescale of all video tracks", offsetof(MOVMuxContext, video_track_timescale), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
{ "brand", "Override major brand", offsetof(MOVMuxContext, major_brand), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = AV_OPT_FLAG_ENCODING_PARAM },
{ "use_editlist", "use edit list", offsetof(MOVMuxContext, use_editlist), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM},
{ "fragment_index", "Fragment number of the next fragment", offsetof(MOVMuxContext, fragments), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
@@ -84,6 +88,8 @@ static const AVClass flavor ## _muxer_class = {\
.version = LIBAVUTIL_VERSION_INT,\
};
+static int get_moov_size(AVFormatContext *s);
+
static int utf8len(const uint8_t *b)
{
int len = 0;
@@ -120,13 +126,15 @@ static int mov_write_stco_tag(AVIOContext *pb, MOVTrack *track)
int mode64 = co64_required(track); // use 32 bit size variant if possible
int64_t pos = avio_tell(pb);
avio_wb32(pb, 0); /* size */
- if (mode64) {
+ if (mode64)
ffio_wfourcc(pb, "co64");
- } else
+ else
ffio_wfourcc(pb, "stco");
avio_wb32(pb, 0); /* version & flags */
- avio_wb32(pb, track->entry); /* entry count */
+ avio_wb32(pb, track->chunkCount); /* entry count */
for (i = 0; i < track->entry; i++) {
+ if (!track->cluster[i].chunkNum)
+ continue;
if (mode64 == 1)
avio_wb64(pb, track->cluster[i].pos + track->data_offset);
else
@@ -182,10 +190,10 @@ static int mov_write_stsc_tag(AVIOContext *pb, MOVTrack *track)
ffio_wfourcc(pb, "stsc");
avio_wb32(pb, 0); // version & flags
entryPos = avio_tell(pb);
- avio_wb32(pb, track->entry); // entry count
+ avio_wb32(pb, track->chunkCount); // entry count
for (i = 0; i < track->entry; i++) {
- if (oldval != track->cluster[i].samples_in_chunk) {
- avio_wb32(pb, i + 1); // first chunk
+ if (oldval != track->cluster[i].samples_in_chunk && track->cluster[i].chunkNum) {
+ avio_wb32(pb, track->cluster[i].chunkNum); // first chunk
avio_wb32(pb, track->cluster[i].samples_in_chunk); // samples per chunk
avio_wb32(pb, 0x1); // sample description index
oldval = track->cluster[i].samples_in_chunk;
@@ -282,6 +290,214 @@ static int mov_write_ac3_tag(AVIOContext *pb, MOVTrack *track)
return 11;
}
+struct eac3_info {
+ AVPacket pkt;
+ uint8_t ec3_done;
+ uint8_t num_blocks;
+
+ /* Layout of the EC3SpecificBox */
+ /* maximum bitrate */
+ uint16_t data_rate;
+ /* number of independent substreams */
+ uint8_t num_ind_sub;
+ struct {
+ /* sample rate code (see ff_ac3_sample_rate_tab) 2 bits */
+ uint8_t fscod;
+ /* bit stream identification 5 bits */
+ uint8_t bsid;
+ /* one bit reserved */
+ /* audio service mixing (not supported yet) 1 bit */
+ /* bit stream mode 3 bits */
+ uint8_t bsmod;
+ /* audio coding mode 3 bits */
+ uint8_t acmod;
+ /* sub woofer on 1 bit */
+ uint8_t lfeon;
+ /* 3 bits reserved */
+ /* number of dependent substreams associated with this substream 4 bits */
+ uint8_t num_dep_sub;
+ /* channel locations of the dependent substream(s), if any, 9 bits */
+ uint16_t chan_loc;
+ /* if there is no dependent substream, then one bit reserved instead */
+ } substream[1]; /* TODO: support 8 independent substreams */
+};
+
+#if CONFIG_AC3_PARSER
+static int handle_eac3(MOVMuxContext *mov, AVPacket *pkt, MOVTrack *track)
+{
+ GetBitContext gbc;
+ AC3HeaderInfo tmp, *hdr = &tmp;
+ struct eac3_info *info;
+ int num_blocks;
+
+ if (!track->eac3_priv && !(track->eac3_priv = av_mallocz(sizeof(*info))))
+ return AVERROR(ENOMEM);
+ info = track->eac3_priv;
+
+ init_get_bits(&gbc, pkt->data, pkt->size * 8);
+ if (avpriv_ac3_parse_header2(&gbc, &hdr) < 0) {
+ /* drop the packets until we see a good one */
+ if (!track->entry) {
+ av_log(mov, AV_LOG_WARNING, "Dropping invalid packet from start of the stream\n");
+ return 0;
+ }
+ return AVERROR_INVALIDDATA;
+ }
+
+ info->data_rate = FFMAX(info->data_rate, hdr->bit_rate / 1000);
+ num_blocks = hdr->num_blocks;
+
+ if (!info->ec3_done) {
+ /* AC-3 substream must be the first one */
+ if (hdr->bitstream_id <= 10 && hdr->substreamid != 0)
+ return AVERROR(EINVAL);
+
+ /* this should always be the case, given that our AC-3 parser
+ * concatenates dependent frames to their independent parent */
+ if (hdr->frame_type == EAC3_FRAME_TYPE_INDEPENDENT) {
+ /* substream ids must be incremental */
+ if (hdr->substreamid > info->num_ind_sub + 1)
+ return AVERROR(EINVAL);
+
+ if (hdr->substreamid == info->num_ind_sub + 1) {
+ //info->num_ind_sub++;
+ avpriv_request_sample(track->enc, "Multiple independent substreams");
+ return AVERROR_PATCHWELCOME;
+ } else if (hdr->substreamid < info->num_ind_sub ||
+ hdr->substreamid == 0 && info->substream[0].bsid) {
+ info->ec3_done = 1;
+ goto concatenate;
+ }
+ }
+
+ /* fill the info needed for the "dec3" atom */
+ info->substream[hdr->substreamid].fscod = hdr->sr_code;
+ info->substream[hdr->substreamid].bsid = hdr->bitstream_id;
+ info->substream[hdr->substreamid].bsmod = hdr->bitstream_mode;
+ info->substream[hdr->substreamid].acmod = hdr->channel_mode;
+ info->substream[hdr->substreamid].lfeon = hdr->lfe_on;
+
+ /* Parse dependent substream(s), if any */
+ if (pkt->size != hdr->frame_size) {
+ int cumul_size = hdr->frame_size;
+ int parent = hdr->substreamid;
+
+ while (cumul_size != pkt->size) {
+ int i;
+ init_get_bits(&gbc, pkt->data + cumul_size, (pkt->size - cumul_size) * 8);
+ if (avpriv_ac3_parse_header2(&gbc, &hdr) < 0)
+ return AVERROR_INVALIDDATA;
+ if (hdr->frame_type != EAC3_FRAME_TYPE_DEPENDENT)
+ return AVERROR(EINVAL);
+ cumul_size += hdr->frame_size;
+ info->substream[parent].num_dep_sub++;
+
+ /* header is parsed up to lfeon, but custom channel map may be needed */
+ /* skip bsid */
+ skip_bits(&gbc, 5);
+ /* skip volume control params */
+ for (i = 0; i < (hdr->channel_mode ? 1 : 2); i++) {
+ skip_bits(&gbc, 5); // skip dialog normalization
+ if (get_bits1(&gbc)) {
+ skip_bits(&gbc, 8); // skip compression gain word
+ }
+ }
+ /* get the dependent stream channel map, if exists */
+ if (get_bits1(&gbc))
+ info->substream[parent].chan_loc |= (get_bits(&gbc, 16) >> 5) & 0x1f;
+ else
+ info->substream[parent].chan_loc |= hdr->channel_mode;
+ }
+ }
+ }
+
+concatenate:
+ if (!info->num_blocks && num_blocks == 6)
+ return pkt->size;
+ else if (info->num_blocks + num_blocks > 6)
+ return AVERROR_INVALIDDATA;
+
+ if (!info->num_blocks) {
+ int ret;
+ if ((ret = av_copy_packet(&info->pkt, pkt)) < 0)
+ return ret;
+ info->num_blocks = num_blocks;
+ return 0;
+ } else {
+ int ret;
+ if ((ret = av_grow_packet(&info->pkt, pkt->size)) < 0)
+ return ret;
+ memcpy(info->pkt.data + info->pkt.size - pkt->size, pkt->data, pkt->size);
+ info->num_blocks += num_blocks;
+ info->pkt.duration += pkt->duration;
+ if ((ret = av_copy_packet_side_data(&info->pkt, pkt)) < 0)
+ return ret;
+ if (info->num_blocks != 6)
+ return 0;
+ av_free_packet(pkt);
+ if ((ret = av_copy_packet(pkt, &info->pkt)) < 0)
+ return ret;
+ av_free_packet(&info->pkt);
+ info->num_blocks = 0;
+ }
+
+ return pkt->size;
+}
+#endif
+
+static int mov_write_eac3_tag(AVIOContext *pb, MOVTrack *track)
+{
+ PutBitContext pbc;
+ uint8_t *buf;
+ struct eac3_info *info;
+ int size, i;
+
+ if (!track->eac3_priv)
+ return AVERROR(EINVAL);
+
+ info = track->eac3_priv;
+ size = 2 + 4 * (info->num_ind_sub + 1);
+ buf = av_malloc(size);
+ if (!buf) {
+ size = AVERROR(ENOMEM);
+ goto end;
+ }
+
+ init_put_bits(&pbc, buf, size);
+ put_bits(&pbc, 13, info->data_rate);
+ put_bits(&pbc, 3, info->num_ind_sub);
+ for (i = 0; i <= info->num_ind_sub; i++) {
+ put_bits(&pbc, 2, info->substream[i].fscod);
+ put_bits(&pbc, 5, info->substream[i].bsid);
+ put_bits(&pbc, 1, 0); /* reserved */
+ put_bits(&pbc, 1, 0); /* asvc */
+ put_bits(&pbc, 3, info->substream[i].bsmod);
+ put_bits(&pbc, 3, info->substream[i].acmod);
+ put_bits(&pbc, 1, info->substream[i].lfeon);
+ put_bits(&pbc, 5, 0); /* reserved */
+ put_bits(&pbc, 4, info->substream[i].num_dep_sub);
+ if (!info->substream[i].num_dep_sub) {
+ put_bits(&pbc, 1, 0); /* reserved */
+ size--;
+ } else {
+ put_bits(&pbc, 9, info->substream[i].chan_loc);
+ }
+ }
+ flush_put_bits(&pbc);
+
+ avio_wb32(pb, size + 8);
+ ffio_wfourcc(pb, "dec3");
+ avio_write(pb, buf, size);
+
+ av_free(buf);
+
+end:
+ av_free_packet(&info->pkt);
+ av_freep(&track->eac3_priv);
+
+ return size;
+}
+
/**
* This function writes extradata "as is".
* Extradata must be formatted like a valid atom (with size and tag).
@@ -292,6 +508,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;
@@ -301,10 +533,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");
@@ -337,12 +581,10 @@ static int mov_write_esds_tag(AVIOContext *pb, MOVTrack *track) // Basic
avio_wb24(pb, track->enc->rc_buffer_size >> 3); // Buffersize DB
- avio_wb32(pb, FFMAX(track->enc->bit_rate, track->enc->rc_max_rate)); // maxbitrate (FIXME should be max rate in any 1 sec window)
- if (track->enc->rc_max_rate != track->enc->rc_min_rate ||
- track->enc->rc_min_rate == 0)
- avio_wb32(pb, 0); // vbr
- else
- avio_wb32(pb, track->enc->rc_max_rate); // avg bitrate
+ avg_bitrate = compute_avg_bitrate(track);
+ // maxbitrate (FIXME should be max rate in any 1 sec window)
+ avio_wb32(pb, FFMAX3(track->enc->bit_rate, track->enc->rc_max_rate, avg_bitrate));
+ avio_wb32(pb, avg_bitrate);
if (track->vos_len) {
// DecoderSpecific info descriptor
@@ -356,22 +598,42 @@ static int mov_write_esds_tag(AVIOContext *pb, MOVTrack *track) // Basic
return update_size(pb, pos);
}
+static int mov_pcm_le_gt16(enum AVCodecID codec_id)
+{
+ return codec_id == AV_CODEC_ID_PCM_S24LE ||
+ codec_id == AV_CODEC_ID_PCM_S32LE ||
+ codec_id == AV_CODEC_ID_PCM_F32LE ||
+ codec_id == AV_CODEC_ID_PCM_F64LE;
+}
+
+static int mov_pcm_be_gt16(enum AVCodecID codec_id)
+{
+ return codec_id == AV_CODEC_ID_PCM_S24BE ||
+ codec_id == AV_CODEC_ID_PCM_S32BE ||
+ codec_id == AV_CODEC_ID_PCM_F32BE ||
+ codec_id == AV_CODEC_ID_PCM_F64BE;
+}
+
static int mov_write_ms_tag(AVIOContext *pb, MOVTrack *track)
{
+ int ret;
int64_t pos = avio_tell(pb);
avio_wb32(pb, 0);
avio_wl32(pb, track->tag); // store it byteswapped
track->enc->codec_tag = av_bswap16(track->tag >> 16);
- ff_put_wav_header(pb, track->enc);
+ if ((ret = ff_put_wav_header(pb, track->enc, 0)) < 0)
+ return ret;
return update_size(pb, pos);
}
static int mov_write_wfex_tag(AVIOContext *pb, MOVTrack *track)
{
+ int ret;
int64_t pos = avio_tell(pb);
avio_wb32(pb, 0);
ffio_wfourcc(pb, "wfex");
- ff_put_wav_header(pb, track->enc);
+ if ((ret = ff_put_wav_header(pb, track->enc, FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX)) < 0)
+ return ret;
return update_size(pb, pos);
}
@@ -389,6 +651,9 @@ static int mov_write_chan_tag(AVIOContext *pb, MOVTrack *track)
return 0;
}
+ if (track->multichannel_as_mono)
+ return 0;
+
avio_wb32(pb, 0); // Size
ffio_wfourcc(pb, "chan"); // Type
avio_w8(pb, 0); // Version
@@ -407,9 +672,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 */
@@ -417,11 +684,18 @@ 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_EAC3) {
+ mov_write_eac3_tag(pb, track);
+ } 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) {
@@ -582,13 +856,20 @@ static int get_cluster_duration(MOVTrack *track, int cluster_idx)
else
next_dts = track->cluster[cluster_idx + 1].dts;
- return next_dts - track->cluster[cluster_idx].dts;
+ next_dts -= track->cluster[cluster_idx].dts;
+
+ av_assert0(next_dts >= 0);
+ av_assert0(next_dts <= INT_MAX);
+
+ return next_dts;
}
static int get_samples_per_packet(MOVTrack *track)
{
int i, first_duration;
+// return track->enc->frame_size;
+
/* use 1 for raw PCM */
if (!track->audio_vbr)
return 1;
@@ -611,9 +892,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 */
@@ -642,10 +931,21 @@ 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 if (track->enc->codec_id == AV_CODEC_ID_ADPCM_G726)
+ avio_wb16(pb, track->enc->bits_per_coded_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 ?
@@ -653,13 +953,28 @@ 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_EAC3 ||
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);
@@ -667,6 +982,8 @@ static int mov_write_audio_tag(AVIOContext *pb, MOVTrack *track)
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_EAC3)
+ mov_write_eac3_tag(pb, track);
else if (track->enc->codec_id == AV_CODEC_ID_ALAC)
mov_write_extradata_tag(pb, track);
else if (track->enc->codec_id == AV_CODEC_ID_WMAPRO)
@@ -692,19 +1009,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);
@@ -733,7 +1037,12 @@ static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track)
ffio_wfourcc(pb, "ACLR");
ffio_wfourcc(pb, "ACLR");
ffio_wfourcc(pb, "0001");
- avio_wb32(pb, 2); /* yuv range: full 1 / normal 2 */
+ if (track->enc->color_range == AVCOL_RANGE_MPEG || /* Legal range (16-235) */
+ track->enc->color_range == AVCOL_RANGE_UNSPECIFIED) {
+ avio_wb32(pb, 1); /* Corresponds to 709 in official encoder */
+ } else { /* Full range (0-255) */
+ avio_wb32(pb, 2); /* Corresponds to RGB in official encoder */
+ }
avio_wb32(pb, 0); /* unknown */
avio_wb32(pb, 24); /* size */
@@ -773,6 +1082,19 @@ static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track)
return 0;
}
+static int mov_write_dpxe_tag(AVIOContext *pb, MOVTrack *track)
+{
+ avio_wb32(pb, 12);
+ ffio_wfourcc(pb, "DpxE");
+ if (track->enc->extradata_size >= 12 &&
+ !memcmp(&track->enc->extradata[4], "DpxE", 4)) {
+ avio_wb32(pb, track->enc->extradata[11]);
+ } else {
+ avio_wb32(pb, 1);
+ }
+ return 0;
+}
+
static int mp4_get_codec_tag(AVFormatContext *s, MOVTrack *track)
{
int tag = track->enc->codec_tag;
@@ -783,6 +1105,7 @@ static int mp4_get_codec_tag(AVFormatContext *s, MOVTrack *track)
if (track->enc->codec_id == AV_CODEC_ID_H264) tag = MKTAG('a','v','c','1');
else if (track->enc->codec_id == AV_CODEC_ID_HEVC) tag = MKTAG('h','e','v','1');
else if (track->enc->codec_id == AV_CODEC_ID_AC3) tag = MKTAG('a','c','-','3');
+ else if (track->enc->codec_id == AV_CODEC_ID_EAC3) tag = MKTAG('e','c','-','3');
else if (track->enc->codec_id == AV_CODEC_ID_DIRAC) tag = MKTAG('d','r','a','c');
else if (track->enc->codec_id == AV_CODEC_ID_MOV_TEXT) tag = MKTAG('t','x','3','g');
else if (track->enc->codec_id == AV_CODEC_ID_VC1) tag = MKTAG('v','c','-','1');
@@ -814,8 +1137,10 @@ static int ipod_get_codec_tag(AVFormatContext *s, MOVTrack *track)
tag == MKTAG('t', 'e', 'x', 't'))))
tag = ff_codec_get_tag(codec_ipod_tags, track->enc->codec_id);
- if (!av_match_ext(s->filename, "m4a") && !av_match_ext(s->filename, "m4v"))
- av_log(s, AV_LOG_WARNING, "Warning, extension is not .m4a nor .m4v "
+ if (!av_match_ext(s->filename, "m4a") &&
+ !av_match_ext(s->filename, "m4b") &&
+ !av_match_ext(s->filename, "m4v"))
+ av_log(s, AV_LOG_WARNING, "Warning, extension is not .m4a, .m4v nor .m4b "
"Quicktime/Ipod might not play the file\n");
return tag;
@@ -825,20 +1150,20 @@ static int mov_get_dv_codec_tag(AVFormatContext *s, MOVTrack *track)
{
int tag;
- if (track->enc->width == 720) /* SD */
- if (track->enc->height == 480) /* NTSC */
+ if (track->enc->width == 720) { /* SD */
+ if (track->enc->height == 480) { /* NTSC */
if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','n');
else tag = MKTAG('d','v','c',' ');
- else if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','p');
+ }else if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','p');
else if (track->enc->pix_fmt == AV_PIX_FMT_YUV420P) tag = MKTAG('d','v','c','p');
else tag = MKTAG('d','v','p','p');
- else if (track->enc->height == 720) /* HD 720 line */
+ } else if (track->enc->height == 720) { /* HD 720 line */
if (track->st->time_base.den == 50) tag = MKTAG('d','v','h','q');
else tag = MKTAG('d','v','h','p');
- else if (track->enc->height == 1080) /* HD 1080 line */
+ } else if (track->enc->height == 1080) { /* HD 1080 line */
if (track->st->time_base.den == 25) tag = MKTAG('d','v','h','5');
else tag = MKTAG('d','v','h','6');
- else {
+ } else {
av_log(s, AV_LOG_ERROR, "unsupported height for dv codec\n");
return 0;
}
@@ -846,11 +1171,145 @@ static int mov_get_dv_codec_tag(AVFormatContext *s, MOVTrack *track)
return tag;
}
+static AVRational find_fps(AVFormatContext *s, AVStream *st)
+{
+ AVRational rate = {st->codec->time_base.den, st->codec->time_base.num};
+ /* if the codec time base makes no sense, try to fallback on stream frame rate */
+ if (av_timecode_check_frame_rate(rate) < 0) {
+ av_log(s, AV_LOG_DEBUG, "timecode: tbc=%d/%d invalid, fallback on %d/%d\n",
+ rate.num, rate.den, st->avg_frame_rate.num, st->avg_frame_rate.den);
+ rate = st->avg_frame_rate;
+ }
+
+ return rate;
+}
+
+static int mov_get_mpeg2_xdcam_codec_tag(AVFormatContext *s, MOVTrack *track)
+{
+ int tag = track->enc->codec_tag;
+ int interlaced = track->enc->field_order > AV_FIELD_PROGRESSIVE;
+ AVStream *st = track->st;
+ int rate = av_q2d(find_fps(s, st));
+
+ if (!tag)
+ tag = MKTAG('m', '2', 'v', '1'); //fallback tag
+
+ if (track->enc->pix_fmt == AV_PIX_FMT_YUV420P) {
+ if (track->enc->width == 1280 && track->enc->height == 720) {
+ if (!interlaced) {
+ if (rate == 24) tag = MKTAG('x','d','v','4');
+ else if (rate == 25) tag = MKTAG('x','d','v','5');
+ else if (rate == 30) tag = MKTAG('x','d','v','1');
+ else if (rate == 50) tag = MKTAG('x','d','v','a');
+ else if (rate == 60) tag = MKTAG('x','d','v','9');
+ }
+ } else if (track->enc->width == 1440 && track->enc->height == 1080) {
+ if (!interlaced) {
+ if (rate == 24) tag = MKTAG('x','d','v','6');
+ else if (rate == 25) tag = MKTAG('x','d','v','7');
+ else if (rate == 30) tag = MKTAG('x','d','v','8');
+ } else {
+ if (rate == 25) tag = MKTAG('x','d','v','3');
+ else if (rate == 30) tag = MKTAG('x','d','v','2');
+ }
+ } else if (track->enc->width == 1920 && track->enc->height == 1080) {
+ if (!interlaced) {
+ if (rate == 24) tag = MKTAG('x','d','v','d');
+ else if (rate == 25) tag = MKTAG('x','d','v','e');
+ else if (rate == 30) tag = MKTAG('x','d','v','f');
+ } else {
+ if (rate == 25) tag = MKTAG('x','d','v','c');
+ else if (rate == 30) tag = MKTAG('x','d','v','b');
+ }
+ }
+ } else if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) {
+ if (track->enc->width == 1280 && track->enc->height == 720) {
+ if (!interlaced) {
+ if (rate == 24) tag = MKTAG('x','d','5','4');
+ else if (rate == 25) tag = MKTAG('x','d','5','5');
+ else if (rate == 30) tag = MKTAG('x','d','5','1');
+ else if (rate == 50) tag = MKTAG('x','d','5','a');
+ else if (rate == 60) tag = MKTAG('x','d','5','9');
+ }
+ } else if (track->enc->width == 1920 && track->enc->height == 1080) {
+ if (!interlaced) {
+ if (rate == 24) tag = MKTAG('x','d','5','d');
+ else if (rate == 25) tag = MKTAG('x','d','5','e');
+ else if (rate == 30) tag = MKTAG('x','d','5','f');
+ } else {
+ if (rate == 25) tag = MKTAG('x','d','5','c');
+ else if (rate == 30) tag = MKTAG('x','d','5','b');
+ }
+ }
+ }
+
+ return tag;
+}
+
+static int mov_get_h264_codec_tag(AVFormatContext *s, MOVTrack *track)
+{
+ int tag = track->enc->codec_tag;
+ int interlaced = track->enc->field_order > AV_FIELD_PROGRESSIVE;
+ AVStream *st = track->st;
+ int rate = av_q2d(find_fps(s, st));
+
+ if (!tag)
+ tag = MKTAG('a', 'v', 'c', 'i'); //fallback tag
+
+ if (track->enc->pix_fmt == AV_PIX_FMT_YUV420P10) {
+ if (track->enc->width == 960 && track->enc->height == 720) {
+ if (!interlaced) {
+ if (rate == 24) tag = MKTAG('a','i','5','p');
+ else if (rate == 25) tag = MKTAG('a','i','5','q');
+ else if (rate == 30) tag = MKTAG('a','i','5','p');
+ else if (rate == 50) tag = MKTAG('a','i','5','q');
+ else if (rate == 60) tag = MKTAG('a','i','5','p');
+ }
+ } else if (track->enc->width == 1440 && track->enc->height == 1080) {
+ if (!interlaced) {
+ if (rate == 24) tag = MKTAG('a','i','5','3');
+ else if (rate == 25) tag = MKTAG('a','i','5','2');
+ else if (rate == 30) tag = MKTAG('a','i','5','3');
+ } else {
+ if (rate == 50) tag = MKTAG('a','i','5','5');
+ else if (rate == 60) tag = MKTAG('a','i','5','6');
+ }
+ }
+ } else if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P10) {
+ if (track->enc->width == 1280 && track->enc->height == 720) {
+ if (!interlaced) {
+ if (rate == 24) tag = MKTAG('a','i','1','p');
+ else if (rate == 25) tag = MKTAG('a','i','1','q');
+ else if (rate == 30) tag = MKTAG('a','i','1','p');
+ else if (rate == 50) tag = MKTAG('a','i','1','q');
+ else if (rate == 60) tag = MKTAG('a','i','1','p');
+ }
+ } else if (track->enc->width == 1920 && track->enc->height == 1080) {
+ if (!interlaced) {
+ if (rate == 24) tag = MKTAG('a','i','1','3');
+ else if (rate == 25) tag = MKTAG('a','i','1','2');
+ else if (rate == 30) tag = MKTAG('a','i','1','3');
+ } else {
+ if (rate == 25) tag = MKTAG('a','i','1','5');
+ else if (rate == 50) tag = MKTAG('a','i','1','5');
+ else if (rate == 60) tag = MKTAG('a','i','1','6');
+ }
+ } else if ( track->enc->width == 4096 && track->enc->height == 2160
+ || track->enc->width == 3840 && track->enc->height == 2160
+ || track->enc->width == 2048 && track->enc->height == 1080) {
+ tag = MKTAG('a','i','v','x');
+ }
+ }
+
+ return tag;
+}
+
static const struct {
enum AVPixelFormat pix_fmt;
uint32_t tag;
unsigned bps;
} mov_pix_fmt_tags[] = {
+ { AV_PIX_FMT_YUYV422, MKTAG('y','u','v','2'), 0 },
{ AV_PIX_FMT_YUYV422, MKTAG('y','u','v','s'), 0 },
{ AV_PIX_FMT_UYVY422, MKTAG('2','v','u','y'), 0 },
{ AV_PIX_FMT_RGB555BE,MKTAG('r','a','w',' '), 16 },
@@ -871,15 +1330,24 @@ static int mov_get_rawvideo_codec_tag(AVFormatContext *s, MOVTrack *track)
{
int tag = track->enc->codec_tag;
int i;
+ enum AVPixelFormat pix_fmt;
for (i = 0; i < FF_ARRAY_ELEMS(mov_pix_fmt_tags); i++) {
if (track->enc->pix_fmt == mov_pix_fmt_tags[i].pix_fmt) {
tag = mov_pix_fmt_tags[i].tag;
track->enc->bits_per_coded_sample = mov_pix_fmt_tags[i].bps;
- break;
+ if (track->enc->codec_tag == mov_pix_fmt_tags[i].tag)
+ break;
}
}
+ pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_mov,
+ track->enc->bits_per_coded_sample);
+ if (tag == MKTAG('r','a','w',' ') &&
+ track->enc->pix_fmt != pix_fmt &&
+ track->enc->pix_fmt != AV_PIX_FMT_NONE)
+ av_log(s, AV_LOG_ERROR, "%s rawvideo cannot be written to mov, output file will be unreadable\n",
+ av_get_pix_fmt_name(track->enc->pix_fmt));
return tag;
}
@@ -891,11 +1359,17 @@ static int mov_get_codec_tag(AVFormatContext *s, MOVTrack *track)
(track->enc->codec_id == AV_CODEC_ID_DVVIDEO ||
track->enc->codec_id == AV_CODEC_ID_RAWVIDEO ||
track->enc->codec_id == AV_CODEC_ID_H263 ||
+ track->enc->codec_id == AV_CODEC_ID_H264 ||
+ track->enc->codec_id == AV_CODEC_ID_MPEG2VIDEO ||
av_get_bits_per_sample(track->enc->codec_id)))) { // pcm audio
if (track->enc->codec_id == AV_CODEC_ID_DVVIDEO)
tag = mov_get_dv_codec_tag(s, track);
else if (track->enc->codec_id == AV_CODEC_ID_RAWVIDEO)
tag = mov_get_rawvideo_codec_tag(s, track);
+ else if (track->enc->codec_id == AV_CODEC_ID_MPEG2VIDEO)
+ tag = mov_get_mpeg2_xdcam_codec_tag(s, track);
+ else if (track->enc->codec_id == AV_CODEC_ID_H264)
+ tag = mov_get_h264_codec_tag(s, track);
else if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO) {
tag = ff_codec_get_tag(ff_codec_movvideo_tags, track->enc->codec_id);
if (!tag) { // if no mac fcc found, try with Microsoft tags
@@ -932,7 +1406,7 @@ static const AVCodecTag codec_3gp_tags[] = {
{ AV_CODEC_ID_NONE, 0 },
};
-static const AVCodecTag codec_f4v_tags[] = {
+static const AVCodecTag codec_f4v_tags[] = { // XXX: add GIF/PNG/JPEG?
{ AV_CODEC_ID_MP3, MKTAG('.','m','p','3') },
{ AV_CODEC_ID_AAC, MKTAG('m','p','4','a') },
{ AV_CODEC_ID_H264, MKTAG('a','v','c','1') },
@@ -1026,9 +1500,96 @@ static int mov_write_pasp_tag(AVIOContext *pb, MOVTrack *track)
return 16;
}
-static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track)
+static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track)
+{
+ // Ref: https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9
+
+ if (track->enc->color_primaries == AVCOL_PRI_UNSPECIFIED &&
+ track->enc->color_trc == AVCOL_TRC_UNSPECIFIED &&
+ track->enc->colorspace == AVCOL_SPC_UNSPECIFIED) {
+ if ((track->enc->width >= 1920 && track->enc->height >= 1080)
+ || (track->enc->width == 1280 && track->enc->height == 720)) {
+ av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming bt709\n");
+ track->enc->color_primaries = AVCOL_PRI_BT709;
+ } else if (track->enc->width == 720 && track->height == 576) {
+ av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming bt470bg\n");
+ track->enc->color_primaries = AVCOL_PRI_BT470BG;
+ } else if (track->enc->width == 720 &&
+ (track->height == 486 || track->height == 480)) {
+ av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming smpte170\n");
+ track->enc->color_primaries = AVCOL_PRI_SMPTE170M;
+ } else {
+ av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, unable to assume anything\n");
+ }
+ switch (track->enc->color_primaries) {
+ case AVCOL_PRI_BT709:
+ track->enc->color_trc = AVCOL_TRC_BT709;
+ track->enc->colorspace = AVCOL_SPC_BT709;
+ break;
+ case AVCOL_PRI_SMPTE170M:
+ case AVCOL_PRI_BT470BG:
+ track->enc->color_trc = AVCOL_TRC_BT709;
+ track->enc->colorspace = AVCOL_SPC_SMPTE170M;
+ break;
+ }
+ }
+
+ avio_wb32(pb, 18);
+ ffio_wfourcc(pb, "colr");
+ ffio_wfourcc(pb, "nclc");
+ switch (track->enc->color_primaries) {
+ case AVCOL_PRI_BT709: avio_wb16(pb, 1); break;
+ case AVCOL_PRI_SMPTE170M:
+ case AVCOL_PRI_SMPTE240M: avio_wb16(pb, 6); break;
+ case AVCOL_PRI_BT470BG: avio_wb16(pb, 5); break;
+ default: avio_wb16(pb, 2);
+ }
+ switch (track->enc->color_trc) {
+ case AVCOL_TRC_BT709: avio_wb16(pb, 1); break;
+ case AVCOL_TRC_SMPTE170M: avio_wb16(pb, 1); break; // remapped
+ case AVCOL_TRC_SMPTE240M: avio_wb16(pb, 7); break;
+ default: avio_wb16(pb, 2);
+ }
+ switch (track->enc->colorspace) {
+ case AVCOL_TRC_BT709: avio_wb16(pb, 1); break;
+ case AVCOL_PRI_SMPTE170M: avio_wb16(pb, 6); break;
+ case AVCOL_PRI_SMPTE240M: avio_wb16(pb, 7); break;
+ default: avio_wb16(pb, 2);
+ }
+
+ return 18;
+}
+
+static void find_compressor(char * compressor_name, int len, MOVTrack *track)
{
AVDictionaryEntry *encoder;
+ int xdcam_res = (track->enc->width == 1280 && track->enc->height == 720)
+ || (track->enc->width == 1440 && track->enc->height == 1080)
+ || (track->enc->width == 1920 && track->enc->height == 1080);
+
+ if (track->mode == MODE_MOV &&
+ (encoder = av_dict_get(track->st->metadata, "encoder", NULL, 0))) {
+ av_strlcpy(compressor_name, encoder->value, 32);
+ } else if (track->enc->codec_id == AV_CODEC_ID_MPEG2VIDEO && xdcam_res) {
+ int interlaced = track->enc->field_order > AV_FIELD_PROGRESSIVE;
+ AVStream *st = track->st;
+ int rate = av_q2d(find_fps(NULL, st));
+ av_strlcatf(compressor_name, len, "XDCAM");
+ if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) {
+ av_strlcatf(compressor_name, len, " HD422");
+ } else if(track->enc->width == 1440) {
+ av_strlcatf(compressor_name, len, " HD");
+ } else
+ av_strlcatf(compressor_name, len, " EX");
+
+ av_strlcatf(compressor_name, len, " %d%c", track->enc->height, interlaced ? 'i' : 'p');
+
+ av_strlcatf(compressor_name, len, "%d", rate * (interlaced + 1));
+ }
+}
+
+static int mov_write_video_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track)
+{
int64_t pos = avio_tell(pb);
char compressor_name[32] = { 0 };
@@ -1062,9 +1623,7 @@ static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track)
avio_wb16(pb, 1); /* Frame count (= 1) */
/* FIXME not sure, ISO 14496-1 draft where it shall be set to 0 */
- if (track->mode == MODE_MOV &&
- (encoder = av_dict_get(track->st->metadata, "encoder", NULL, 0)))
- av_strlcpy(compressor_name, encoder->value, 32);
+ find_compressor(compressor_name, 32, track);
avio_w8(pb, strlen(compressor_name));
avio_write(pb, compressor_name, 31);
@@ -1077,27 +1636,39 @@ static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track)
mov_write_esds_tag(pb, track);
else if (track->enc->codec_id == AV_CODEC_ID_H263)
mov_write_d263_tag(pb);
- else if (track->enc->codec_id == AV_CODEC_ID_SVQ3)
- mov_write_svq3_tag(pb);
- else if (track->enc->codec_id == AV_CODEC_ID_DNXHD)
+ else if (track->enc->codec_id == AV_CODEC_ID_AVUI ||
+ track->enc->codec_id == AV_CODEC_ID_SVQ3) {
+ mov_write_extradata_tag(pb, track);
+ avio_wb32(pb, 0);
+ } else if (track->enc->codec_id == AV_CODEC_ID_DNXHD)
mov_write_avid_tag(pb, track);
else if (track->enc->codec_id == AV_CODEC_ID_HEVC)
mov_write_hvcc_tag(pb, track);
- else if (track->enc->codec_id == AV_CODEC_ID_H264) {
+ else if (track->enc->codec_id == AV_CODEC_ID_H264 && !TAG_IS_AVCI(track->tag)) {
mov_write_avcc_tag(pb, track);
if (track->mode == MODE_IPOD)
mov_write_uuid_tag_ipod(pb);
- } else if (track->enc->field_order != AV_FIELD_UNKNOWN)
- mov_write_fiel_tag(pb, track);
- else if (track->enc->codec_id == AV_CODEC_ID_VC1 && track->vos_len > 0)
+ } else if (track->enc->codec_id == AV_CODEC_ID_VC1 && track->vos_len > 0)
mov_write_dvc1_tag(pb, track);
else if (track->enc->codec_id == AV_CODEC_ID_VP6F ||
track->enc->codec_id == AV_CODEC_ID_VP6A) {
/* Don't write any potential extradata here - the cropping
* is signalled via the normal width/height fields. */
+ } else if (track->enc->codec_id == AV_CODEC_ID_R10K) {
+ if (track->enc->codec_tag == MKTAG('R','1','0','k'))
+ mov_write_dpxe_tag(pb, track);
} else if (track->vos_len > 0)
mov_write_glbl_tag(pb, track);
+ if (track->enc->codec_id != AV_CODEC_ID_H264 &&
+ track->enc->codec_id != AV_CODEC_ID_MPEG4 &&
+ track->enc->codec_id != AV_CODEC_ID_DNXHD)
+ if (track->enc->field_order != AV_FIELD_UNKNOWN)
+ mov_write_fiel_tag(pb, track);
+
+ if (mov->flags & FF_MOV_FLAG_WRITE_COLR)
+ mov_write_colr_tag(pb, track);
+
if (track->enc->sample_aspect_ratio.den && track->enc->sample_aspect_ratio.num &&
track->enc->sample_aspect_ratio.den != track->enc->sample_aspect_ratio.num) {
mov_write_pasp_tag(pb, track);
@@ -1126,9 +1697,57 @@ static int mov_write_rtp_tag(AVIOContext *pb, MOVTrack *track)
return update_size(pb, pos);
}
+static int mov_write_source_reference_tag(AVIOContext *pb, MOVTrack *track, const char *reel_name)
+{
+ uint64_t str_size =strlen(reel_name);
+ int64_t pos = avio_tell(pb);
+
+ if (str_size >= UINT16_MAX){
+ av_log(NULL, AV_LOG_ERROR, "reel_name length %"PRIu64" is too large\n", str_size);
+ avio_wb16(pb, 0);
+ return AVERROR(EINVAL);
+ }
+
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "name"); /* Data format */
+ avio_wb16(pb, str_size); /* string size */
+ avio_wb16(pb, track->language); /* langcode */
+ avio_write(pb, reel_name, str_size); /* reel name */
+ return update_size(pb,pos);
+}
+
static int mov_write_tmcd_tag(AVIOContext *pb, MOVTrack *track)
{
int64_t pos = avio_tell(pb);
+#if 1
+ int frame_duration = av_rescale(track->timescale, track->enc->time_base.num, track->enc->time_base.den);
+ int nb_frames = 1.0/av_q2d(track->enc->time_base) + 0.5;
+ AVDictionaryEntry *t = NULL;
+
+ if (nb_frames > 255) {
+ av_log(NULL, AV_LOG_ERROR, "fps %d is too large\n", nb_frames);
+ return AVERROR(EINVAL);
+ }
+
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "tmcd"); /* Data format */
+ avio_wb32(pb, 0); /* Reserved */
+ avio_wb32(pb, 1); /* Data reference index */
+ avio_wb32(pb, 0); /* Flags */
+ avio_wb32(pb, track->timecode_flags); /* Flags (timecode) */
+ avio_wb32(pb, track->timescale); /* Timescale */
+ avio_wb32(pb, frame_duration); /* Frame duration */
+ avio_w8(pb, nb_frames); /* Number of frames */
+ avio_w8(pb, 0); /* Reserved */
+
+ if (track->st)
+ t = av_dict_get(track->st->metadata, "reel_name", NULL, 0);
+
+ if (t && utf8len(t->value))
+ mov_write_source_reference_tag(pb, track, t->value);
+ else
+ avio_wb16(pb, 0); /* zero size */
+#else
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "tmcd"); /* Data format */
@@ -1136,10 +1755,11 @@ static int mov_write_tmcd_tag(AVIOContext *pb, MOVTrack *track)
avio_wb32(pb, 1); /* Data reference index */
if (track->enc->extradata_size)
avio_write(pb, track->enc->extradata, track->enc->extradata_size);
+#endif
return update_size(pb, pos);
}
-static int mov_write_stsd_tag(AVIOContext *pb, MOVTrack *track)
+static int mov_write_stsd_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track)
{
int64_t pos = avio_tell(pb);
avio_wb32(pb, 0); /* size */
@@ -1147,7 +1767,7 @@ static int mov_write_stsd_tag(AVIOContext *pb, MOVTrack *track)
avio_wb32(pb, 0); /* version & flags */
avio_wb32(pb, 1); /* entry count */
if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO)
- mov_write_video_tag(pb, track);
+ mov_write_video_tag(pb, mov, track);
else if (track->enc->codec_type == AVMEDIA_TYPE_AUDIO)
mov_write_audio_tag(pb, track);
else if (track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE)
@@ -1166,7 +1786,9 @@ static int mov_write_ctts_tag(AVIOContext *pb, MOVTrack *track)
uint32_t atom_size;
int i;
- ctts_entries = av_malloc((track->entry + 1) * sizeof(*ctts_entries)); /* worst case */
+ ctts_entries = av_malloc_array((track->entry + 1), sizeof(*ctts_entries)); /* worst case */
+ if (!ctts_entries)
+ return AVERROR(ENOMEM);
ctts_entries[0].count = 1;
ctts_entries[0].duration = track->cluster[0].cts;
for (i = 1; i < track->entry; i++) {
@@ -1207,7 +1829,7 @@ static int mov_write_stts_tag(AVIOContext *pb, MOVTrack *track)
entries = 1;
} else {
stts_entries = track->entry ?
- av_malloc(track->entry * sizeof(*stts_entries)) : /* worst case */
+ av_malloc_array(track->entry, sizeof(*stts_entries)) : /* worst case */
NULL;
for (i = 0; i < track->entry; i++) {
int duration = get_cluster_duration(track, i);
@@ -1242,18 +1864,21 @@ 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 */
return 28;
}
-static int mov_write_stbl_tag(AVIOContext *pb, MOVTrack *track)
+static int mov_write_stbl_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track)
{
int64_t pos = avio_tell(pb);
+ int ret;
+
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "stbl");
- mov_write_stsd_tag(pb, track);
+ mov_write_stsd_tag(pb, mov, track);
mov_write_stts_tag(pb, track);
if ((track->enc->codec_type == AVMEDIA_TYPE_VIDEO ||
track->enc->codec_tag == MKTAG('r','t','p',' ')) &&
@@ -1262,8 +1887,11 @@ static int mov_write_stbl_tag(AVIOContext *pb, MOVTrack *track)
if (track->mode == MODE_MOV && track->flags & MOV_TRACK_STPS)
mov_write_stss_tag(pb, track, MOV_PARTIAL_SYNC_SAMPLE);
if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO &&
- track->flags & MOV_TRACK_CTTS && track->entry)
- mov_write_ctts_tag(pb, track);
+ track->flags & MOV_TRACK_CTTS && track->entry) {
+
+ if ((ret = mov_write_ctts_tag(pb, track)) < 0)
+ return ret;
+ }
mov_write_stsc_tag(pb, track);
mov_write_stsz_tag(pb, track);
mov_write_stco_tag(pb, track);
@@ -1287,9 +1915,32 @@ static int mov_write_nmhd_tag(AVIOContext *pb)
return 12;
}
-static int mov_write_gmhd_tag(AVIOContext *pb)
+static int mov_write_tcmi_tag(AVIOContext *pb, MOVTrack *track)
+{
+ int64_t pos = avio_tell(pb);
+ const char *font = "Lucida Grande";
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "tcmi"); /* timecode media information atom */
+ avio_wb32(pb, 0); /* version & flags */
+ avio_wb16(pb, 0); /* text font */
+ avio_wb16(pb, 0); /* text face */
+ avio_wb16(pb, 12); /* text size */
+ avio_wb16(pb, 0); /* (unknown, not in the QT specs...) */
+ avio_wb16(pb, 0x0000); /* text color (red) */
+ avio_wb16(pb, 0x0000); /* text color (green) */
+ avio_wb16(pb, 0x0000); /* text color (blue) */
+ avio_wb16(pb, 0xffff); /* background color (red) */
+ avio_wb16(pb, 0xffff); /* background color (green) */
+ avio_wb16(pb, 0xffff); /* background color (blue) */
+ avio_w8(pb, strlen(font)); /* font len (part of the pascal string) */
+ avio_write(pb, font, strlen(font)); /* font name */
+ return update_size(pb, pos);
+}
+
+static int mov_write_gmhd_tag(AVIOContext *pb, MOVTrack *track)
{
- avio_wb32(pb, 0x20); /* size */
+ int64_t pos = avio_tell(pb);
+ avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "gmhd");
avio_wb32(pb, 0x18); /* gmin size */
ffio_wfourcc(pb, "gmin");/* generic media info */
@@ -1300,7 +1951,36 @@ static int mov_write_gmhd_tag(AVIOContext *pb)
avio_wb16(pb, 0x8000); /* opColor (b?) */
avio_wb16(pb, 0); /* balance */
avio_wb16(pb, 0); /* reserved */
- return 0x20;
+
+ /*
+ * This special text atom is required for
+ * Apple Quicktime chapters. The contents
+ * don't appear to be documented, so the
+ * bytes are copied verbatim.
+ */
+ if (track->tag != MKTAG('c','6','0','8')) {
+ avio_wb32(pb, 0x2C); /* size */
+ ffio_wfourcc(pb, "text");
+ avio_wb16(pb, 0x01);
+ avio_wb32(pb, 0x00);
+ avio_wb32(pb, 0x00);
+ avio_wb32(pb, 0x00);
+ avio_wb32(pb, 0x01);
+ avio_wb32(pb, 0x00);
+ avio_wb32(pb, 0x00);
+ avio_wb32(pb, 0x00);
+ avio_wb32(pb, 0x00004000);
+ avio_wb16(pb, 0x0000);
+ }
+
+ if (track->enc->codec_tag == MKTAG('t','m','c','d')) {
+ int64_t tmcd_pos = avio_tell(pb);
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "tmcd");
+ mov_write_tcmi_tag(pb, track);
+ update_size(pb, tmcd_pos);
+ }
+ return update_size(pb, pos);
}
static int mov_write_smhd_tag(AVIOContext *pb)
@@ -1346,16 +2026,19 @@ static int mov_write_hdlr_tag(AVIOContext *pb, MOVTrack *track)
hdlr_type = "soun";
descr = "SoundHandler";
} else if (track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE) {
- if (track->tag == MKTAG('t','x','3','g')) {
- hdlr_type = "sbtl";
- } else if (track->tag == MKTAG('m','p','4','s')) {
- hdlr_type = "subp";
- } else if (is_clcp_track(track)) {
+ if (is_clcp_track(track)) {
hdlr_type = "clcp";
+ descr = "ClosedCaptionHandler";
} else {
- hdlr_type = "text";
- }
+ if (track->tag == MKTAG('t','x','3','g')) {
+ hdlr_type = "sbtl";
+ } else if (track->tag == MKTAG('m','p','4','s')) {
+ hdlr_type = "subp";
+ } else {
+ hdlr_type = "text";
+ }
descr = "SubtitleHandler";
+ }
} else if (track->enc->codec_tag == MKTAG('r','t','p',' ')) {
hdlr_type = "hint";
descr = "HintHandler";
@@ -1413,9 +2096,11 @@ static int mov_write_hmhd_tag(AVIOContext *pb)
return 28;
}
-static int mov_write_minf_tag(AVIOContext *pb, MOVTrack *track)
+static int mov_write_minf_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track)
{
int64_t pos = avio_tell(pb);
+ int ret;
+
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "minf");
if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO)
@@ -1424,19 +2109,20 @@ static int mov_write_minf_tag(AVIOContext *pb, MOVTrack *track)
mov_write_smhd_tag(pb);
else if (track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE) {
if (track->tag == MKTAG('t','e','x','t') || is_clcp_track(track)) {
- mov_write_gmhd_tag(pb);
+ mov_write_gmhd_tag(pb, track);
} else {
mov_write_nmhd_tag(pb);
}
} else if (track->tag == MKTAG('r','t','p',' ')) {
mov_write_hmhd_tag(pb);
} else if (track->tag == MKTAG('t','m','c','d')) {
- mov_write_gmhd_tag(pb);
+ mov_write_gmhd_tag(pb, track);
}
if (track->mode == MODE_MOV) /* FIXME: Why do it for MODE_MOV only ? */
mov_write_hdlr_tag(pb, NULL);
mov_write_dinf_tag(pb);
- mov_write_stbl_tag(pb, track);
+ if ((ret = mov_write_stbl_tag(pb, mov, track)) < 0)
+ return ret;
return update_size(pb, pos);
}
@@ -1483,14 +2169,35 @@ static int mov_write_mdia_tag(AVIOContext *pb, MOVMuxContext *mov,
MOVTrack *track)
{
int64_t pos = avio_tell(pb);
+ int ret;
+
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "mdia");
mov_write_mdhd_tag(pb, mov, track);
mov_write_hdlr_tag(pb, track);
- mov_write_minf_tag(pb, track);
+ if ((ret = mov_write_minf_tag(pb, mov, track)) < 0)
+ return ret;
return update_size(pb, pos);
}
+/* transformation matrix
+ |a b u|
+ |c d v|
+ |tx ty w| */
+static void write_matrix(AVIOContext *pb, int16_t a, int16_t b, int16_t c,
+ int16_t d, int16_t tx, int16_t ty)
+{
+ avio_wb32(pb, a << 16); /* 16.16 format */
+ avio_wb32(pb, b << 16); /* 16.16 format */
+ avio_wb32(pb, 0); /* u in 2.30 format */
+ avio_wb32(pb, c << 16); /* 16.16 format */
+ avio_wb32(pb, d << 16); /* 16.16 format */
+ avio_wb32(pb, 0); /* v in 2.30 format */
+ avio_wb32(pb, tx << 16); /* 16.16 format */
+ avio_wb32(pb, ty << 16); /* 16.16 format */
+ avio_wb32(pb, 1 << 30); /* w in 2.30 format */
+}
+
static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov,
MOVTrack *track, AVStream *st)
{
@@ -1498,6 +2205,7 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov,
track->timescale, AV_ROUND_UP);
int version = duration < INT32_MAX ? 0 : 1;
int flags = MOV_TKHD_FLAG_IN_MOVIE;
+ int rotation = 0;
int group = 0;
@@ -1546,16 +2254,19 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov,
avio_wb16(pb, 0); /* reserved */
/* Matrix structure */
- avio_wb32(pb, 0x00010000); /* reserved */
- avio_wb32(pb, 0x0); /* reserved */
- avio_wb32(pb, 0x0); /* reserved */
- avio_wb32(pb, 0x0); /* reserved */
- avio_wb32(pb, 0x00010000); /* reserved */
- avio_wb32(pb, 0x0); /* reserved */
- avio_wb32(pb, 0x0); /* reserved */
- avio_wb32(pb, 0x0); /* reserved */
- avio_wb32(pb, 0x40000000); /* reserved */
-
+ if (st && st->metadata) {
+ AVDictionaryEntry *rot = av_dict_get(st->metadata, "rotate", NULL, 0);
+ rotation = (rot && rot->value) ? atoi(rot->value) : 0;
+ }
+ if (rotation == 90) {
+ write_matrix(pb, 0, 1, -1, 0, track->enc->height, 0);
+ } else if (rotation == 180) {
+ write_matrix(pb, -1, 0, 0, -1, track->enc->width, track->enc->height);
+ } else if (rotation == 270) {
+ write_matrix(pb, 0, -1, 1, 0, 0, track->enc->width);
+ } else {
+ write_matrix(pb, 1, 0, 0, 1, 0, 0);
+ }
/* Track width and height, for visual only */
if (st && (track->enc->codec_type == AVMEDIA_TYPE_VIDEO ||
track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE)) {
@@ -1616,7 +2327,21 @@ static int mov_write_edts_tag(AVIOContext *pb, MOVMuxContext *mov,
int version = duration < INT32_MAX ? 0 : 1;
int entry_size, entry_count, size;
int64_t delay, start_ct = track->start_cts;
- delay = av_rescale_rnd(track->start_dts + start_ct, MOV_TIMESCALE,
+ int64_t start_dts = track->start_dts;
+
+ if (track->entry) {
+ if (start_dts != track->cluster[0].dts || start_ct != track->cluster[0].cts) {
+
+ av_log(mov->fc, AV_LOG_DEBUG,
+ "EDTS using dts:%"PRId64" cts:%d instead of dts:%"PRId64" cts:%"PRId64" tid:%d\n",
+ track->cluster[0].dts, track->cluster[0].cts,
+ start_dts, start_ct, track->track_id);
+ start_dts = track->cluster[0].dts;
+ start_ct = track->cluster[0].cts;
+ }
+ }
+
+ delay = av_rescale_rnd(start_dts + start_ct, MOV_TIMESCALE,
track->timescale, AV_ROUND_DOWN);
version |= delay < INT32_MAX ? 0 : 1;
@@ -1652,7 +2377,8 @@ static int mov_write_edts_tag(AVIOContext *pb, MOVMuxContext *mov,
* special meaning. Normally start_ct should end up positive or zero
* here, but use FFMIN in case dts is a a small positive integer
* rounded to 0 when represented in MOV_TIMESCALE units. */
- start_ct = -FFMIN(track->start_dts, 0);
+ av_assert0(av_rescale_rnd(start_dts, MOV_TIMESCALE, track->timescale, AV_ROUND_DOWN) <= 0);
+ start_ct = -FFMIN(start_dts, 0);
/* Note, this delay is calculated from the pts of the first sample,
* ensuring that we don't reduce the duration for cases with
* dts<0 pts=0. */
@@ -1774,26 +2500,33 @@ static int mov_write_trak_tag(AVIOContext *pb, MOVMuxContext *mov,
{
int64_t pos = avio_tell(pb);
int entry_backup = track->entry;
+ int chunk_backup = track->chunkCount;
+ int ret;
+
/* If we want to have an empty moov, but some samples already have been
* buffered (delay_moov), pretend that no samples have been written yet. */
if (mov->flags & FF_MOV_FLAG_EMPTY_MOOV)
- track->entry = 0;
+ track->chunkCount = track->entry = 0;
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "trak");
mov_write_tkhd_tag(pb, mov, track, st);
- if (track->start_dts != AV_NOPTS_VALUE &&
- (track->mode == MODE_PSP || track->flags & MOV_TRACK_CTTS ||
- track->start_dts || is_clcp_track(track))) {
+
+ av_assert2(mov->use_editlist >= 0);
+
+ if (track->start_dts != AV_NOPTS_VALUE) {
if (mov->use_editlist)
- mov_write_edts_tag(pb, mov, track); // PSP Movies require edts box
+ mov_write_edts_tag(pb, mov, track); // PSP Movies and several other cases require edts box
else if ((track->entry && track->cluster[0].dts) || track->mode == MODE_PSP || is_clcp_track(track))
av_log(mov->fc, AV_LOG_WARNING,
"Not writing any edit list even though one would have been required\n");
}
+
if (track->tref_tag)
mov_write_tref_tag(pb, track);
- mov_write_mdia_tag(pb, mov, track);
+
+ if ((ret = mov_write_mdia_tag(pb, mov, track)) < 0)
+ return ret;
if (track->mode == MODE_PSP)
mov_write_uuid_tag_psp(pb, track); // PSP Movies require this uuid box
if (track->tag == MKTAG('r','t','p',' '))
@@ -1801,16 +2534,17 @@ static int mov_write_trak_tag(AVIOContext *pb, MOVMuxContext *mov,
if (track->mode == MODE_MOV) {
if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO) {
double sample_aspect_ratio = av_q2d(st->sample_aspect_ratio);
- if ((0.0 != sample_aspect_ratio && 1.0 != sample_aspect_ratio)) {
+ if (st->sample_aspect_ratio.num && 1.0 != sample_aspect_ratio) {
mov_write_tapt_tag(pb, track);
}
}
- if (is_clcp_track(track)) {
+ if (is_clcp_track(track) && st->sample_aspect_ratio.num) {
mov_write_tapt_tag(pb, track);
}
}
mov_write_track_udta_tag(pb, mov, st);
track->entry = entry_backup;
+ track->chunkCount = chunk_backup;
return update_size(pb, pos);
}
@@ -1870,12 +2604,12 @@ static int mov_write_mvex_tag(AVIOContext *pb, MOVMuxContext *mov)
static int mov_write_mvhd_tag(AVIOContext *pb, MOVMuxContext *mov)
{
int max_track_id = 1, i;
- int64_t max_track_len_temp, max_track_len = 0;
+ int64_t max_track_len = 0;
int version;
for (i = 0; i < mov->nb_streams; i++) {
if (mov->tracks[i].entry > 0 && mov->tracks[i].timescale) {
- max_track_len_temp = av_rescale_rnd(mov->tracks[i].track_duration,
+ int64_t max_track_len_temp = av_rescale_rnd(mov->tracks[i].track_duration,
MOV_TIMESCALE,
mov->tracks[i].timescale,
AV_ROUND_UP);
@@ -1893,7 +2627,8 @@ static int mov_write_mvhd_tag(AVIOContext *pb, MOVMuxContext *mov)
}
version = max_track_len < UINT32_MAX ? 0 : 1;
- (version == 1) ? avio_wb32(pb, 120) : avio_wb32(pb, 108); /* size */
+ avio_wb32(pb, version == 1 ? 120 : 108); /* size */
+
ffio_wfourcc(pb, "mvhd");
avio_w8(pb, version);
avio_wb24(pb, 0); /* flags */
@@ -1914,15 +2649,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) */
@@ -2008,28 +2735,79 @@ static int mov_write_string_metadata(AVFormatContext *s, AVIOContext *pb,
return mov_write_string_tag(pb, name, t->value, lang, long_style);
}
-/* iTunes track number */
+/* iTunes bpm number */
+static int mov_write_tmpo_tag(AVIOContext *pb, AVFormatContext *s)
+{
+ AVDictionaryEntry *t = av_dict_get(s->metadata, "tmpo", NULL, 0);
+ int size = 0, tmpo = t ? atoi(t->value) : 0;
+ if (tmpo) {
+ size = 26;
+ avio_wb32(pb, size);
+ ffio_wfourcc(pb, "tmpo");
+ avio_wb32(pb, size-8); /* size */
+ ffio_wfourcc(pb, "data");
+ avio_wb32(pb, 0x15); //type specifier
+ avio_wb32(pb, 0);
+ avio_wb16(pb, tmpo); // data
+ }
+ return size;
+}
+
+/* iTunes track or disc number */
static int mov_write_trkn_tag(AVIOContext *pb, MOVMuxContext *mov,
- AVFormatContext *s)
+ AVFormatContext *s, int disc)
{
- AVDictionaryEntry *t = av_dict_get(s->metadata, "track", NULL, 0);
+ AVDictionaryEntry *t = av_dict_get(s->metadata,
+ disc ? "disc" : "track",
+ NULL, 0);
int size = 0, track = t ? atoi(t->value) : 0;
if (track) {
+ int tracks = 0;
+ char *slash = strchr(t->value, '/');
+ if (slash)
+ tracks = atoi(slash + 1);
avio_wb32(pb, 32); /* size */
- ffio_wfourcc(pb, "trkn");
+ ffio_wfourcc(pb, disc ? "disk" : "trkn");
avio_wb32(pb, 24); /* size */
ffio_wfourcc(pb, "data");
avio_wb32(pb, 0); // 8 bytes empty
avio_wb32(pb, 0);
avio_wb16(pb, 0); // empty
- avio_wb16(pb, track); // track number
- avio_wb16(pb, 0); // total track number
+ avio_wb16(pb, track); // track / disc number
+ avio_wb16(pb, tracks); // total track / disc number
avio_wb16(pb, 0); // empty
size = 32;
}
return size;
}
+static int mov_write_int8_metadata(AVFormatContext *s, AVIOContext *pb,
+ const char *name, const char *tag,
+ int len)
+{
+ AVDictionaryEntry *t = NULL;
+ uint8_t num;
+ int size = 24 + len;
+
+ if (len != 1 && len != 4)
+ return -1;
+
+ if (!(t = av_dict_get(s->metadata, tag, NULL, 0)))
+ return 0;
+ num = atoi(t->value);
+
+ avio_wb32(pb, size);
+ ffio_wfourcc(pb, name);
+ avio_wb32(pb, size - 8);
+ ffio_wfourcc(pb, "data");
+ avio_wb32(pb, 0x15);
+ avio_wb32(pb, 0);
+ if (len==4) avio_wb32(pb, num);
+ else avio_w8 (pb, num);
+
+ return size;
+}
+
/* iTunes meta data list */
static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov,
AVFormatContext *s)
@@ -2043,7 +2821,8 @@ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov,
mov_write_string_metadata(s, pb, "\251wrt", "composer" , 1);
mov_write_string_metadata(s, pb, "\251alb", "album" , 1);
mov_write_string_metadata(s, pb, "\251day", "date" , 1);
- if (!mov_write_string_metadata(s, pb, "\251too", "encoding_tool", 1))
+ if (!mov->exact &&
+ !mov_write_string_metadata(s, pb, "\251too", "encoding_tool", 1))
mov_write_string_tag(pb, "\251too", LIBAVFORMAT_IDENT, 0, 1);
mov_write_string_metadata(s, pb, "\251cmt", "comment" , 1);
mov_write_string_metadata(s, pb, "\251gen", "genre" , 1);
@@ -2055,7 +2834,15 @@ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov,
mov_write_string_metadata(s, pb, "tvsh", "show" , 1);
mov_write_string_metadata(s, pb, "tven", "episode_id",1);
mov_write_string_metadata(s, pb, "tvnn", "network" , 1);
- mov_write_trkn_tag(pb, mov, s);
+ mov_write_int8_metadata (s, pb, "tves", "episode_sort",4);
+ mov_write_int8_metadata (s, pb, "tvsn", "season_number",4);
+ mov_write_int8_metadata (s, pb, "stik", "media_type",1);
+ mov_write_int8_metadata (s, pb, "hdvd", "hd_video", 1);
+ mov_write_int8_metadata (s, pb, "pgap", "gapless_playback",1);
+ mov_write_int8_metadata (s, pb, "cpil", "compilation", 1);
+ mov_write_trkn_tag(pb, mov, s, 0); // track number
+ mov_write_trkn_tag(pb, mov, s, 1); // disc number
+ mov_write_tmpo_tag(pb, s);
return update_size(pb, pos);
}
@@ -2074,6 +2861,26 @@ static int mov_write_meta_tag(AVIOContext *pb, MOVMuxContext *mov,
return size;
}
+static int mov_write_raw_metadata_tag(AVFormatContext *s, AVIOContext *pb,
+ const char *name, const char *key)
+{
+ int len;
+ AVDictionaryEntry *t;
+
+ if (!(t = av_dict_get(s->metadata, key, NULL, 0)))
+ return 0;
+
+ len = strlen(t->value);
+ if (len > 0) {
+ int size = len + 8;
+ avio_wb32(pb, size);
+ ffio_wfourcc(pb, name);
+ avio_write(pb, t->value, len);
+ return size;
+ }
+ return 0;
+}
+
static int ascii_to_wc(AVIOContext *pb, const uint8_t *b)
{
int val;
@@ -2147,9 +2954,6 @@ static int mov_write_udta_tag(AVIOContext *pb, MOVMuxContext *mov,
int ret, size;
uint8_t *buf;
- if (s->flags & AVFMT_FLAG_BITEXACT)
- return 0;
-
ret = avio_open_dyn_buf(&pb_buf);
if (ret < 0)
return ret;
@@ -2170,9 +2974,13 @@ static int mov_write_udta_tag(AVIOContext *pb, MOVMuxContext *mov,
mov_write_string_metadata(s, pb_buf, "\251alb", "album", 0);
mov_write_string_metadata(s, pb_buf, "\251day", "date", 0);
mov_write_string_metadata(s, pb_buf, "\251swr", "encoder", 0);
+ // currently ignored by mov.c
mov_write_string_metadata(s, pb_buf, "\251des", "comment", 0);
+ // add support for libquicktime, this atom is also actually read by mov.c
+ mov_write_string_metadata(s, pb_buf, "\251cmt", "comment", 0);
mov_write_string_metadata(s, pb_buf, "\251gen", "genre", 0);
mov_write_string_metadata(s, pb_buf, "\251cpy", "copyright", 0);
+ mov_write_raw_metadata_tag(s, pb_buf, "XMP_", "xmp");
} else {
/* iTunes meta data */
mov_write_meta_tag(pb_buf, mov, s);
@@ -2206,6 +3014,7 @@ static void mov_write_psp_udta_tag(AVIOContext *pb,
static int mov_write_uuidusmt_tag(AVIOContext *pb, AVFormatContext *s)
{
+ MOVMuxContext *mov = s->priv_data;
AVDictionaryEntry *title = av_dict_get(s->metadata, "title", NULL, 0);
int64_t pos, pos2;
@@ -2230,7 +3039,8 @@ static int mov_write_uuidusmt_tag(AVIOContext *pb, AVFormatContext *s)
avio_wb16(pb, 0x0); /* ? */
avio_wb16(pb, 0x021C); /* data */
- mov_write_psp_udta_tag(pb, LIBAVCODEC_IDENT, "eng", 0x04);
+ if (!mov->exact)
+ mov_write_psp_udta_tag(pb, LIBAVCODEC_IDENT, "eng", 0x04);
mov_write_psp_udta_tag(pb, title->value, "eng", 0x01);
mov_write_psp_udta_tag(pb, "2006/04/01 11:11:11", "und", 0x03);
@@ -2241,6 +3051,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)
{
@@ -2255,6 +3088,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)
@@ -2269,13 +3105,26 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov,
mov->tracks[mov->tracks[i].src_track].track_id;
}
}
+ for (i = 0; i < mov->nb_streams; i++) {
+ if (mov->tracks[i].tag == MKTAG('t','m','c','d')) {
+ int src_trk = mov->tracks[i].src_track;
+ mov->tracks[src_trk].tref_tag = mov->tracks[i].tag;
+ mov->tracks[src_trk].tref_id = mov->tracks[i].track_id;
+ //src_trk may have a different timescale than the tmcd track
+ mov->tracks[i].track_duration = av_rescale(mov->tracks[src_trk].track_duration,
+ mov->tracks[i].timescale,
+ mov->tracks[src_trk].timescale);
+ }
+ }
mov_write_mvhd_tag(pb, mov);
if (mov->mode != MODE_MOV && !mov->iods_skip)
mov_write_iods_tag(pb, mov);
for (i = 0; i < mov->nb_streams; i++) {
if (mov->tracks[i].entry > 0 || mov->flags & FF_MOV_FLAG_FRAGMENT) {
- mov_write_trak_tag(pb, mov, &(mov->tracks[i]), i < s->nb_streams ? s->streams[i] : NULL);
+ int ret = mov_write_trak_tag(pb, mov, &(mov->tracks[i]), i < s->nb_streams ? s->streams[i] : NULL);
+ if (ret < 0)
+ return ret;
}
}
if (mov->flags & FF_MOV_FLAG_FRAGMENT)
@@ -2312,7 +3161,7 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov)
{
int64_t pos = avio_tell(pb);
int i;
- const uint8_t uuid[] = {
+ static const uint8_t uuid[] = {
0xa5, 0xd4, 0x0b, 0x30, 0xe8, 0x14, 0x11, 0xdd,
0xba, 0x2f, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66
};
@@ -2325,7 +3174,8 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov)
avio_printf(pb, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
avio_printf(pb, "<smil xmlns=\"http://www.w3.org/2001/SMIL20/Language\">\n");
avio_printf(pb, "<head>\n");
- avio_printf(pb, "<meta name=\"creator\" content=\"%s\" />\n",
+ if (!mov->exact)
+ avio_printf(pb, "<meta name=\"creator\" content=\"%s\" />\n",
LIBAVFORMAT_IDENT);
avio_printf(pb, "</head>\n");
avio_printf(pb, "<body>\n");
@@ -2518,7 +3368,7 @@ static int mov_write_trun_tag(AVIOContext *pb, MOVMuxContext *mov,
static int mov_write_tfxd_tag(AVIOContext *pb, MOVTrack *track)
{
int64_t pos = avio_tell(pb);
- const uint8_t uuid[] = {
+ static const uint8_t uuid[] = {
0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6,
0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2
};
@@ -2540,7 +3390,7 @@ static int mov_write_tfrf_tag(AVIOContext *pb, MOVMuxContext *mov,
{
int n = track->nb_frag_info - 1 - entry, i;
int size = 8 + 16 + 4 + 1 + 16*n;
- const uint8_t uuid[] = {
+ static const uint8_t uuid[] = {
0xd4, 0x80, 0x7e, 0xf2, 0xca, 0x39, 0x46, 0x95,
0x8e, 0x54, 0x26, 0xcb, 0x9e, 0x46, 0xa7, 0x9f
};
@@ -2564,8 +3414,7 @@ static int mov_write_tfrf_tag(AVIOContext *pb, MOVMuxContext *mov,
int free_size = 16 * (mov->ism_lookahead - n);
avio_wb32(pb, free_size);
ffio_wfourcc(pb, "free");
- for (i = 0; i < free_size - 8; i++)
- avio_w8(pb, 0);
+ ffio_fill(pb, 0, free_size - 8);
}
return 0;
@@ -3006,9 +3855,22 @@ static void mov_write_uuidprof_tag(AVIOContext *pb, AVFormatContext *s)
static int mov_write_identification(AVIOContext *pb, AVFormatContext *s)
{
MOVMuxContext *mov = s->priv_data;
+ int i;
+
mov_write_ftyp_tag(pb,s);
if (mov->mode == MODE_PSP) {
- if (s->nb_streams != 2) {
+ int video_streams_nb = 0, audio_streams_nb = 0, other_streams_nb = 0;
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
+ video_streams_nb++;
+ else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO)
+ audio_streams_nb++;
+ else
+ other_streams_nb++;
+ }
+
+ if (video_streams_nb != 1 || audio_streams_nb != 1 || other_streams_nb) {
av_log(s, AV_LOG_ERROR, "PSP mode need one video and one audio stream\n");
return AVERROR(EINVAL);
}
@@ -3100,16 +3962,15 @@ static int mov_flush_fragment(AVFormatContext *s)
MOVMuxContext *mov = s->priv_data;
int i, first_track = -1;
int64_t mdat_size = 0;
+ int ret;
if (!(mov->flags & FF_MOV_FLAG_FRAGMENT))
return 0;
if (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)
@@ -3118,16 +3979,14 @@ static int mov_flush_fragment(AVFormatContext *s)
if (i < mov->nb_streams)
return 0;
- if ((ret = ffio_open_null_buf(&moov_buf)) < 0)
- return ret;
- mov_write_moov_tag(moov_buf, mov, s);
- buf_size = ffio_close_null_buf(moov_buf);
+ moov_size = get_moov_size(s);
for (i = 0; i < mov->nb_streams; i++)
- mov->tracks[i].data_offset = pos + buf_size + 8;
+ mov->tracks[i].data_offset = pos + moov_size + 8;
if (mov->flags & FF_MOV_FLAG_DELAY_MOOV)
mov_write_identification(s->pb, s);
- mov_write_moov_tag(s->pb, mov, s);
+ if ((ret = mov_write_moov_tag(s->pb, mov, s)) < 0)
+ return ret;
if (mov->flags & FF_MOV_FLAG_DELAY_MOOV) {
if (mov->flags & FF_MOV_FLAG_FASTSTART)
@@ -3242,6 +4101,21 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
int size = pkt->size, ret = 0;
uint8_t *reformatted_data = NULL;
+ if (trk->entry) {
+ int64_t duration = pkt->dts - trk->cluster[trk->entry - 1].dts;
+ if (duration < 0 || duration > INT_MAX) {
+ av_log(s, AV_LOG_ERROR, "Application provided duration: %"PRId64" / timestamp: %"PRId64" is out of range for mov/mp4 format\n",
+ duration, pkt->dts
+ );
+
+ pkt->dts = trk->cluster[trk->entry - 1].dts + 1;
+ pkt->pts = AV_NOPTS_VALUE;
+ }
+ if (pkt->duration < 0) {
+ av_log(s, AV_LOG_ERROR, "Application provided duration: %d is invalid\n", pkt->duration);
+ return AVERROR(EINVAL);
+ }
+ }
if (mov->flags & FF_MOV_FLAG_FRAGMENT) {
int ret;
if (mov->fragments > 0 || mov->flags & FF_MOV_FLAG_EMPTY_MOOV) {
@@ -3273,19 +4147,32 @@ 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
samples_in_chunk = 1;
/* copy extradata if it exists */
- if (trk->vos_len == 0 && enc->extradata_size > 0) {
+ if (trk->vos_len == 0 && enc->extradata_size > 0 && !TAG_IS_AVCI(trk->tag)) {
trk->vos_len = enc->extradata_size;
trk->vos_data = av_malloc(trk->vos_len);
memcpy(trk->vos_data, enc->extradata, trk->vos_len);
}
- if (enc->codec_id == AV_CODEC_ID_H264 && trk->vos_len > 0 && *(uint8_t *)trk->vos_data != 1) {
+ if (enc->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 &&
+ (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) {
+ if (!s->streams[pkt->stream_index]->nb_frames) {
+ av_log(s, AV_LOG_ERROR, "Malformed AAC bitstream detected: "
+ "use the audio bitstream filter 'aac_adtstoasc' to fix it "
+ "('-bsf:a aac_adtstoasc' option with ffmpeg)\n");
+ return -1;
+ }
+ av_log(s, AV_LOG_WARNING, "aac bitstream error\n");
+ }
+ if (enc->codec_id == AV_CODEC_ID_H264 && trk->vos_len > 0 && *(uint8_t *)trk->vos_data != 1 && !TAG_IS_AVCI(trk->tag)) {
/* from x264 or from bytestream h264 */
/* nal reformating needed */
if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) {
@@ -3304,6 +4191,15 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
} else {
size = ff_hevc_annexb2mp4(pb, pkt->data, pkt->size, 0, NULL);
}
+#if CONFIG_AC3_PARSER
+ } else if (enc->codec_id == AV_CODEC_ID_EAC3) {
+ size = handle_eac3(mov, pkt, trk);
+ if (size < 0)
+ return size;
+ else if (!size)
+ goto end;
+ avio_write(pb, pkt->data, size);
+#endif
} else {
avio_write(pb, pkt->data, size);
}
@@ -3332,6 +4228,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
trk->cluster[trk->entry].pos = avio_tell(pb) - size;
trk->cluster[trk->entry].samples_in_chunk = samples_in_chunk;
+ trk->cluster[trk->entry].chunkNum = 0;
trk->cluster[trk->entry].size = size;
trk->cluster[trk->entry].entries = samples_in_chunk;
trk->cluster[trk->entry].dts = pkt->dts;
@@ -3355,6 +4252,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
trk->frag_discont = 0;
}
}
+
if (!trk->entry && trk->start_dts == AV_NOPTS_VALUE && !mov->use_editlist &&
s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_MAKE_ZERO) {
/* Not using edit lists and shifting the first track to start from zero.
@@ -3380,6 +4278,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
pkt->stream_index, 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");
@@ -3414,17 +4313,15 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
ff_mov_add_hinted_packet(s, pkt, trk->hint_track, trk->entry,
reformatted_data, size);
+end:
err:
+
av_free(reformatted_data);
return ret;
}
-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;
@@ -3441,7 +4338,7 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
mov->flags &= ~FF_MOV_FLAG_FRAG_DISCONT;
}
- 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);
@@ -3456,6 +4353,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);
}
}
@@ -3463,12 +4427,12 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
// as samples, and a tref pointing from the other tracks to the chapter one.
static int mov_create_chapter_track(AVFormatContext *s, int tracknum)
{
+ AVIOContext *pb;
+
MOVMuxContext *mov = s->priv_data;
MOVTrack *track = &mov->tracks[tracknum];
AVPacket pkt = { .stream_index = tracknum, .flags = AV_PKT_FLAG_KEY };
int i, len;
- // These properties are required to make QT recognize the chapter track
- uint8_t chapter_properties[43] = { 0, 0, 0, 0, 0, 0, 0, 1, };
track->mode = mov->mode;
track->tag = MKTAG('t','e','x','t');
@@ -3477,11 +4441,57 @@ static int mov_create_chapter_track(AVFormatContext *s, int tracknum)
if (!track->enc)
return AVERROR(ENOMEM);
track->enc->codec_type = AVMEDIA_TYPE_SUBTITLE;
- track->enc->extradata = av_malloc(sizeof(chapter_properties));
- if (!track->enc->extradata)
+#if 0
+ // These properties are required to make QT recognize the chapter track
+ uint8_t chapter_properties[43] = { 0, 0, 0, 0, 0, 0, 0, 1, };
+ if (ff_alloc_extradata(track->enc, sizeof(chapter_properties)))
return AVERROR(ENOMEM);
- track->enc->extradata_size = sizeof(chapter_properties);
memcpy(track->enc->extradata, chapter_properties, sizeof(chapter_properties));
+#else
+ if (avio_open_dyn_buf(&pb) >= 0) {
+ int size;
+ uint8_t *buf;
+
+ /* Stub header (usually for Quicktime chapter track) */
+ // TextSampleEntry
+ avio_wb32(pb, 0x01); // displayFlags
+ avio_w8(pb, 0x00); // horizontal justification
+ avio_w8(pb, 0x00); // vertical justification
+ avio_w8(pb, 0x00); // bgColourRed
+ avio_w8(pb, 0x00); // bgColourGreen
+ avio_w8(pb, 0x00); // bgColourBlue
+ avio_w8(pb, 0x00); // bgColourAlpha
+ // BoxRecord
+ avio_wb16(pb, 0x00); // defTextBoxTop
+ avio_wb16(pb, 0x00); // defTextBoxLeft
+ avio_wb16(pb, 0x00); // defTextBoxBottom
+ avio_wb16(pb, 0x00); // defTextBoxRight
+ // StyleRecord
+ avio_wb16(pb, 0x00); // startChar
+ avio_wb16(pb, 0x00); // endChar
+ avio_wb16(pb, 0x01); // fontID
+ avio_w8(pb, 0x00); // fontStyleFlags
+ avio_w8(pb, 0x00); // fontSize
+ avio_w8(pb, 0x00); // fgColourRed
+ avio_w8(pb, 0x00); // fgColourGreen
+ avio_w8(pb, 0x00); // fgColourBlue
+ avio_w8(pb, 0x00); // fgColourAlpha
+ // FontTableBox
+ avio_wb32(pb, 0x0D); // box size
+ ffio_wfourcc(pb, "ftab"); // box atom name
+ avio_wb16(pb, 0x01); // entry count
+ // FontRecord
+ avio_wb16(pb, 0x01); // font ID
+ avio_w8(pb, 0x00); // font name length
+
+ if ((size = avio_close_dyn_buf(pb, &buf)) > 0) {
+ track->enc->extradata = buf;
+ track->enc->extradata_size = size;
+ } else {
+ av_freep(&buf);
+ }
+ }
+#endif
for (i = 0; i < s->nb_chapters; i++) {
AVChapter *c = s->chapters[i];
@@ -3512,6 +4522,46 @@ static int mov_create_chapter_track(AVFormatContext *s, int tracknum)
return 0;
}
+static int mov_create_timecode_track(AVFormatContext *s, int index, int src_index, const char *tcstr)
+{
+ int ret;
+ MOVMuxContext *mov = s->priv_data;
+ MOVTrack *track = &mov->tracks[index];
+ AVStream *src_st = s->streams[src_index];
+ AVTimecode tc;
+ AVPacket pkt = {.stream_index = index, .flags = AV_PKT_FLAG_KEY, .size = 4};
+ AVRational rate = find_fps(s, src_st);
+
+ /* compute the frame number */
+ ret = av_timecode_init_from_string(&tc, rate, tcstr, s);
+ if (ret < 0)
+ return ret;
+
+ /* tmcd track based on video stream */
+ track->mode = mov->mode;
+ track->tag = MKTAG('t','m','c','d');
+ track->src_track = src_index;
+ track->timescale = mov->tracks[src_index].timescale;
+ if (tc.flags & AV_TIMECODE_FLAG_DROPFRAME)
+ track->timecode_flags |= MOV_TIMECODE_FLAG_DROPFRAME;
+
+ /* set st to src_st for metadata access*/
+ track->st = src_st;
+
+ /* encode context: tmcd data stream */
+ track->enc = avcodec_alloc_context3(NULL);
+ track->enc->codec_type = AVMEDIA_TYPE_DATA;
+ track->enc->codec_tag = track->tag;
+ track->enc->time_base = av_inv_q(rate);
+
+ /* the tmcd track just contains one packet with the frame number */
+ pkt.data = av_malloc(pkt.size);
+ AV_WB32(pkt.data, tc.start);
+ ret = ff_mov_write_packet(s, &pkt);
+ av_free(pkt.data);
+ return ret;
+}
+
/*
* st->disposition controls the "enabled" flag in the tkhd tag.
* QuickTime will not play a track if it is not enabled. So make sure
@@ -3571,18 +4621,20 @@ static void mov_free(AVFormatContext *s)
if (mov->chapter_track) {
if (mov->tracks[mov->chapter_track].enc)
- av_free(mov->tracks[mov->chapter_track].enc->extradata);
+ av_freep(&mov->tracks[mov->chapter_track].enc->extradata);
av_freep(&mov->tracks[mov->chapter_track].enc);
}
for (i = 0; i < mov->nb_streams; i++) {
if (mov->tracks[i].tag == MKTAG('r','t','p',' '))
ff_mov_close_hinting(&mov->tracks[i]);
+ else if (mov->tracks[i].tag == MKTAG('t','m','c','d') && mov->nb_meta_tmcd)
+ av_freep(&mov->tracks[i].enc);
av_freep(&mov->tracks[i].cluster);
av_freep(&mov->tracks[i].frag_info);
if (mov->tracks[i].vos_len)
- av_free(mov->tracks[i].vos_data);
+ av_freep(&mov->tracks[i].vos_data);
}
av_freep(&mov->tracks);
@@ -3657,8 +4709,8 @@ static int mov_write_header(AVFormatContext *s)
{
AVIOContext *pb = s->pb;
MOVMuxContext *mov = s->priv_data;
- AVDictionaryEntry *t;
- int i, ret, hint_track = 0;
+ AVDictionaryEntry *t, *global_tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
+ int i, ret, hint_track = 0, tmcd_track = 0;
mov->fc = s;
@@ -3675,6 +4727,9 @@ static int mov_write_header(AVFormatContext *s)
else if (!strcmp("f4v", s->oformat->name)) mov->mode = MODE_F4V;
}
+ if (s->flags & AVFMT_FLAG_BITEXACT)
+ mov->exact = 1;
+
if (mov->flags & FF_MOV_FLAG_DELAY_MOOV)
mov->flags |= FF_MOV_FLAG_EMPTY_MOOV;
@@ -3694,6 +4749,10 @@ static int mov_write_header(AVFormatContext *s)
mov->flags |= FF_MOV_FLAG_FRAGMENT | FF_MOV_FLAG_EMPTY_MOOV |
FF_MOV_FLAG_DEFAULT_BASE_MOOF;
+ if (mov->flags & FF_MOV_FLAG_FASTSTART) {
+ mov->reserved_moov_size = -1;
+ }
+
if (mov->use_editlist < 0) {
mov->use_editlist = 1;
if (mov->flags & FF_MOV_FLAG_FRAGMENT &&
@@ -3721,7 +4780,6 @@ static int mov_write_header(AVFormatContext *s)
return AVERROR(EINVAL);
}
-
if (!(mov->flags & FF_MOV_FLAG_DELAY_MOOV)) {
if ((ret = mov_write_identification(pb, s)) < 0)
return ret;
@@ -3743,9 +4801,35 @@ static int mov_write_header(AVFormatContext *s)
}
}
+ if (mov->mode == MODE_MOV) {
+ tmcd_track = mov->nb_streams;
+
+ /* +1 tmcd track for each video stream with a timecode */
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
+ (global_tcr || av_dict_get(st->metadata, "timecode", NULL, 0)))
+ mov->nb_meta_tmcd++;
+ }
+
+ /* check if there is already a tmcd track to remux */
+ if (mov->nb_meta_tmcd) {
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ if (st->codec->codec_tag == MKTAG('t','m','c','d')) {
+ av_log(s, AV_LOG_WARNING, "You requested a copy of the original timecode track "
+ "so timecode metadata are now ignored\n");
+ mov->nb_meta_tmcd = 0;
+ }
+ }
+ }
+
+ mov->nb_streams += mov->nb_meta_tmcd;
+ }
+
// Reserve an extra stream for chapters for the case where chapters
// are written in the trailer
- mov->tracks = av_mallocz((mov->nb_streams + 1) * sizeof(*mov->tracks));
+ mov->tracks = av_mallocz_array((mov->nb_streams + 1), sizeof(*mov->tracks));
if (!mov->tracks)
return AVERROR(ENOMEM);
@@ -3762,8 +4846,10 @@ static int mov_write_header(AVFormatContext *s)
track->mode = mov->mode;
track->tag = mov_find_codec_tag(s, track);
if (!track->tag) {
- av_log(s, AV_LOG_ERROR, "track %d: could not find tag, "
- "codec not currently supported in container\n", i);
+ av_log(s, AV_LOG_ERROR, "Could not find tag for codec %s in stream #%d, "
+ "codec not currently supported in container\n",
+ avcodec_get_name(st->codec->codec_id), i);
+ ret = AVERROR(EINVAL);
goto error;
}
/* If hinting of this track is enabled by a later hint track,
@@ -3777,11 +4863,23 @@ static int mov_write_header(AVFormatContext *s)
track->tag == MKTAG('m','x','5','p') || track->tag == MKTAG('m','x','5','n')) {
if (st->codec->width != 720 || (st->codec->height != 608 && st->codec->height != 512)) {
av_log(s, AV_LOG_ERROR, "D-10/IMX must use 720x608 or 720x512 video resolution\n");
+ ret = AVERROR(EINVAL);
goto error;
}
track->height = track->tag >> 24 == 'n' ? 486 : 576;
}
- track->timescale = st->time_base.den;
+ if (mov->video_track_timescale) {
+ track->timescale = mov->video_track_timescale;
+ } else {
+ track->timescale = st->time_base.den;
+ while(track->timescale < 10000)
+ track->timescale *= 2;
+ }
+ if (st->codec->width > 65535 || st->codec->height > 65535) {
+ av_log(s, AV_LOG_ERROR, "Resolution %dx%d too large for mov/mp4\n", st->codec->width, st->codec->height);
+ ret = AVERROR(EINVAL);
+ goto error;
+ }
if (track->mode == MODE_MOV && track->timescale > 100000)
av_log(s, AV_LOG_WARNING,
"WARNING codec timebase is very high. If duration is too long,\n"
@@ -3789,29 +4887,40 @@ static int mov_write_header(AVFormatContext *s)
"or choose different container.\n");
} else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
track->timescale = st->codec->sample_rate;
- /* set sample_size for PCM and ADPCM */
- if (av_get_bits_per_sample(st->codec->codec_id) ||
- st->codec->codec_id == AV_CODEC_ID_ILBC) {
+ if (!st->codec->frame_size && !av_get_bits_per_sample(st->codec->codec_id)) {
+ av_log(s, AV_LOG_WARNING, "track %d: codec frame size is not set\n", i);
+ track->audio_vbr = 1;
+ }else if (st->codec->codec_id == AV_CODEC_ID_ADPCM_MS ||
+ st->codec->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV ||
+ st->codec->codec_id == AV_CODEC_ID_ILBC){
if (!st->codec->block_align) {
- av_log(s, AV_LOG_ERROR, "track %d: codec block align is not set\n", i);
+ av_log(s, AV_LOG_ERROR, "track %d: codec block align is not set for adpcm\n", i);
+ ret = AVERROR(EINVAL);
goto error;
}
track->sample_size = st->codec->block_align;
+ }else if (st->codec->frame_size > 1){ /* assume compressed audio */
+ track->audio_vbr = 1;
+ }else{
+ track->sample_size = (av_get_bits_per_sample(st->codec->codec_id) >> 3) * st->codec->channels;
}
- /* set audio_vbr for compressed audio */
- if (av_get_bits_per_sample(st->codec->codec_id) < 8) {
+ if (st->codec->codec_id == AV_CODEC_ID_ILBC ||
+ st->codec->codec_id == AV_CODEC_ID_ADPCM_IMA_QT) {
track->audio_vbr = 1;
}
if (track->mode != MODE_MOV &&
track->enc->codec_id == AV_CODEC_ID_MP3 && track->timescale < 16000) {
av_log(s, AV_LOG_ERROR, "track %d: muxing mp3 at %dhz is not supported\n",
i, track->enc->sample_rate);
+ ret = AVERROR(EINVAL);
goto error;
}
} else if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
track->timescale = st->time_base.den;
} else if (st->codec->codec_type == AVMEDIA_TYPE_DATA) {
track->timescale = st->time_base.den;
+ } else {
+ track->timescale = MOV_TIMESCALE;
}
if (!track->height)
track->height = st->codec->height;
@@ -3826,7 +4935,7 @@ static int mov_write_header(AVFormatContext *s)
if (st->codec->extradata_size) {
if (st->codec->codec_id == AV_CODEC_ID_DVD_SUBTITLE)
mov_create_dvd_sub_decoder_specific_info(track, st);
- else {
+ else if (!TAG_IS_AVCI(track->tag)){
track->vos_len = st->codec->extradata_size;
track->vos_data = av_malloc(track->vos_len);
memcpy(track->vos_data, st->codec->extradata, track->vos_len);
@@ -3834,8 +4943,40 @@ static int mov_write_header(AVFormatContext *s)
}
}
+ for (i = 0; i < s->nb_streams; i++) {
+ int j;
+ AVStream *st= s->streams[i];
+ MOVTrack *track= &mov->tracks[i];
+
+ if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO ||
+ track->enc->channel_layout != AV_CH_LAYOUT_MONO)
+ continue;
+
+ for (j = 0; j < s->nb_streams; j++) {
+ AVStream *stj= s->streams[j];
+ MOVTrack *trackj= &mov->tracks[j];
+ if (j == i)
+ continue;
+
+ if (stj->codec->codec_type != AVMEDIA_TYPE_AUDIO ||
+ trackj->enc->channel_layout != AV_CH_LAYOUT_MONO ||
+ trackj->language != track->language ||
+ trackj->tag != track->tag
+ )
+ continue;
+ track->multichannel_as_mono++;
+ }
+ }
+
enable_tracks(s);
+
+ if (mov->reserved_moov_size){
+ mov->reserved_moov_pos= avio_tell(pb);
+ if (mov->reserved_moov_size > 0)
+ avio_skip(pb, mov->reserved_moov_size);
+ }
+
if (mov->flags & FF_MOV_FLAG_FRAGMENT) {
/* If no fragmentation options have been set, set a default. */
if (!(mov->flags & (FF_MOV_FLAG_FRAG_KEYFRAME |
@@ -3854,7 +4995,7 @@ static int mov_write_header(AVFormatContext *s)
mov->time += 0x7C25B080; // 1970 based -> 1904 based
if (mov->chapter_track)
- if (mov_create_chapter_track(s, mov->chapter_track) < 0)
+ if ((ret = mov_create_chapter_track(s, mov->chapter_track)) < 0)
goto error;
if (mov->flags & FF_MOV_FLAG_RTP_HINT) {
@@ -3863,12 +5004,31 @@ static int mov_write_header(AVFormatContext *s)
AVStream *st = s->streams[i];
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
- ff_mov_init_hinting(s, hint_track, i);
+ if ((ret = ff_mov_init_hinting(s, hint_track, i)) < 0)
+ goto error;
hint_track++;
}
}
}
+ if (mov->nb_meta_tmcd) {
+ /* Initialize the tmcd tracks */
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ t = global_tcr;
+
+ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (!t)
+ t = av_dict_get(st->metadata, "timecode", NULL, 0);
+ if (!t)
+ continue;
+ if ((ret = mov_create_timecode_track(s, tmcd_track, i, t->value)) < 0)
+ goto error;
+ tmcd_track++;
+ }
+ }
+ }
+
avio_flush(pb);
if (mov->flags & FF_MOV_FLAG_ISML)
@@ -3876,7 +5036,8 @@ static int mov_write_header(AVFormatContext *s)
if (mov->flags & FF_MOV_FLAG_EMPTY_MOOV &&
!(mov->flags & FF_MOV_FLAG_DELAY_MOOV)) {
- mov_write_moov_tag(pb, mov, s);
+ if ((ret = mov_write_moov_tag(pb, mov, s)) < 0)
+ return ret;
mov->fragments++;
if (mov->flags & FF_MOV_FLAG_FASTSTART)
mov->reserved_moov_pos = avio_tell(pb);
@@ -3885,7 +5046,7 @@ static int mov_write_header(AVFormatContext *s)
return 0;
error:
mov_free(s);
- return -1;
+ return ret;
}
static int get_moov_size(AVFormatContext *s)
@@ -3896,7 +5057,8 @@ static int get_moov_size(AVFormatContext *s)
if ((ret = ffio_open_null_buf(&moov_buf)) < 0)
return ret;
- mov_write_moov_tag(moov_buf, mov, s);
+ if ((ret = mov_write_moov_tag(moov_buf, mov, s)) < 0)
+ return ret;
return ffio_close_null_buf(moov_buf);
}
@@ -4032,6 +5194,19 @@ static int mov_write_trailer(AVFormatContext *s)
int i;
int64_t moov_pos;
+ /*
+ * Before actually writing the trailer, make sure that there are no
+ * dangling subtitles, that need a terminating sample.
+ */
+ for (i = 0; i < mov->nb_streams; i++) {
+ MOVTrack *trk = &mov->tracks[i];
+ if (trk->enc->codec_id == AV_CODEC_ID_MOV_TEXT &&
+ !trk->last_sample_is_subtitle_end) {
+ mov_write_subtitle_end_packet(s, i, trk->track_duration);
+ trk->last_sample_is_subtitle_end = 1;
+ }
+ }
+
// If there were no chapters when the header was written, but there
// are chapters now, write them in the trailer. This only works
// when we are not doing fragments.
@@ -4059,18 +5234,35 @@ static int mov_write_trailer(AVFormatContext *s)
ffio_wfourcc(pb, "mdat");
avio_wb64(pb, mov->mdat_size + 16);
}
- avio_seek(pb, moov_pos, SEEK_SET);
+ avio_seek(pb, mov->reserved_moov_size > 0 ? mov->reserved_moov_pos : moov_pos, SEEK_SET);
if (mov->flags & FF_MOV_FLAG_FASTSTART) {
av_log(s, AV_LOG_INFO, "Starting second pass: moving the moov atom to the beginning of the file\n");
res = shift_data(s);
if (res == 0) {
avio_seek(pb, mov->reserved_moov_pos, SEEK_SET);
- mov_write_moov_tag(pb, mov, s);
+ if ((res = mov_write_moov_tag(pb, mov, s)) < 0)
+ goto error;
}
+ } else if (mov->reserved_moov_size > 0) {
+ int64_t size;
+ if ((res = mov_write_moov_tag(pb, mov, s)) < 0)
+ goto error;
+ 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);
+ res = AVERROR(EINVAL);
+ goto error;
+ }
+ avio_wb32(pb, size);
+ ffio_wfourcc(pb, "free");
+ ffio_fill(pb, 0, size - 8);
+ avio_seek(pb, moov_pos, SEEK_SET);
} else {
- mov_write_moov_tag(pb, mov, s);
+ if ((res = mov_write_moov_tag(pb, mov, s)) < 0)
+ goto error;
}
+ res = 0;
} else {
mov_auto_flush_fragment(s);
for (i = 0; i < mov->nb_streams; i++)
@@ -4236,7 +5428,7 @@ AVOutputFormat ff_f4v_muxer = {
.write_header = mov_write_header,
.write_packet = mov_write_packet,
.write_trailer = mov_write_trailer,
- .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
+ .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH,
.codec_tag = (const AVCodecTag* const []){ codec_f4v_tags, 0 },
.priv_class = &f4v_muxer_class,
};
diff --git a/libavformat/movenc.h b/libavformat/movenc.h
index 682820ed0e..3a729374da 100644
--- a/libavformat/movenc.h
+++ b/libavformat/movenc.h
@@ -4,20 +4,20 @@
* Copyright (c) 2004 Gildas Bazin <gbazin at videolan dot org>
* Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -36,7 +36,7 @@
#define MODE_MOV 0x02
#define MODE_3GP 0x04
#define MODE_PSP 0x08 // example working PSP command line:
-// avconv -i testinput.avi -f psp -r 14.985 -s 320x240 -b 768 -ar 24000 -ab 32 M4V00001.MP4
+// ffmpeg -i testinput.avi -f psp -r 14.985 -s 320x240 -b 768 -ar 24000 -ab 32 M4V00001.MP4
#define MODE_3G2 0x10
#define MODE_IPOD 0x20
#define MODE_ISM 0x40
@@ -47,6 +47,7 @@ typedef struct MOVIentry {
int64_t dts;
unsigned int size;
unsigned int samples_in_chunk;
+ unsigned int chunkNum; ///< Chunk number if the current entry is a chunk start otherwise 0
unsigned int entries;
int cts;
#define MOV_SYNC_SAMPLE 0x0001
@@ -82,18 +83,25 @@ typedef struct MOVTrack {
unsigned timescale;
uint64_t time;
int64_t track_duration;
+ int last_sample_is_subtitle_end;
long sample_count;
long sample_size;
+ long chunkCount;
int has_keyframes;
#define MOV_TRACK_CTTS 0x0001
#define MOV_TRACK_STPS 0x0002
#define MOV_TRACK_ENABLED 0x0004
uint32_t flags;
+#define MOV_TIMECODE_FLAG_DROPFRAME 0x0001
+#define MOV_TIMECODE_FLAG_24HOURSMAX 0x0002
+#define MOV_TIMECODE_FLAG_ALLOWNEGATIVE 0x0004
+ uint32_t timecode_flags;
int language;
int track_id;
int tag; ///< stsd fourcc
AVStream *st;
AVCodecContext *enc;
+ int multichannel_as_mono;
int vos_len;
uint8_t *vos_data;
@@ -107,7 +115,7 @@ typedef struct MOVTrack {
int64_t start_cts;
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;
@@ -135,6 +143,8 @@ typedef struct MOVTrack {
int packet_entry;
int slices;
} vc1_info;
+
+ void *eac3_priv;
} MOVTrack;
typedef struct MOVMuxContext {
@@ -142,6 +152,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;
@@ -149,6 +160,8 @@ typedef struct MOVMuxContext {
int flags;
int rtp_flags;
+ int exact;
+
int iods_skip;
int iods_video_profile;
int iods_audio_profile;
@@ -161,6 +174,9 @@ typedef struct MOVMuxContext {
AVIOContext *mdat_buf;
int first_trun;
+ int video_track_timescale;
+
+ int reserved_moov_size; ///< 0 for disabled, -1 for automatic, size otherwise
int64_t reserved_moov_pos;
char *major_brand;
@@ -185,6 +201,7 @@ typedef struct MOVMuxContext {
#define FF_MOV_FLAG_DASH (1 << 11)
#define FF_MOV_FLAG_FRAG_DISCONT (1 << 12)
#define FF_MOV_FLAG_DELAY_MOOV (1 << 13)
+#define FF_MOV_FLAG_WRITE_COLR (1 << 14)
int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt);
diff --git a/libavformat/movenchint.c b/libavformat/movenchint.c
index a8f5f34f9c..006aa09f62 100644
--- a/libavformat/movenchint.c
+++ b/libavformat/movenchint.c
@@ -2,20 +2,20 @@
* MOV, 3GP, MP4 muxer RTP hinting
* Copyright (c) 2010 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -72,7 +72,7 @@ static void sample_queue_pop(HintSampleQueue *queue)
if (queue->len <= 0)
return;
if (queue->samples[0].own_data)
- av_free(queue->samples[0].data);
+ av_freep(&queue->samples[0].data);
queue->len--;
memmove(queue->samples, queue->samples + 1, sizeof(HintSample)*queue->len);
}
@@ -85,7 +85,7 @@ static void sample_queue_free(HintSampleQueue *queue)
int i;
for (i = 0; i < queue->len; i++)
if (queue->samples[i].own_data)
- av_free(queue->samples[i].data);
+ av_freep(&queue->samples[i].data);
av_freep(&queue->samples);
queue->len = 0;
queue->size = 0;
@@ -104,11 +104,12 @@ static void sample_queue_push(HintSampleQueue *queue, uint8_t *data, int size,
if (size <= 14)
return;
if (!queue->samples || queue->len >= queue->size) {
- queue->size += 10;
- if (av_reallocp(&queue->samples, sizeof(*queue->samples) * queue->size) < 0) {
- queue->len = queue->size = 0;
+ HintSample *samples;
+ samples = av_realloc_array(queue->samples, queue->size + 10, sizeof(HintSample));
+ if (!samples)
return;
- }
+ queue->size += 10;
+ queue->samples = samples;
}
queue->samples[queue->len].data = data;
queue->samples[queue->len].size = size;
@@ -421,7 +422,7 @@ int ff_mov_add_hinted_packet(AVFormatContext *s, AVPacket *pkt,
sample_queue_push(&trk->sample_queue, pkt->data, pkt->size, sample);
/* Feed the packet to the RTP muxer */
- ff_write_chained(rtp_ctx, 0, pkt, s);
+ ff_write_chained(rtp_ctx, 0, pkt, s, 0);
/* Fetch the output from the RTP muxer, open a new output buffer
* for next time. */
diff --git a/libavformat/mp3dec.c b/libavformat/mp3dec.c
index bf12fdb2c5..480cffedad 100644
--- a/libavformat/mp3dec.c
+++ b/libavformat/mp3dec.c
@@ -2,23 +2,24 @@
* MP3 demuxer
* Copyright (c) 2003 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/opt.h"
#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/crc.h"
@@ -40,10 +41,15 @@
#define XING_TOC_COUNT 100
-typedef struct MP3DecContext {
+typedef struct {
+ AVClass *class;
+ int64_t filesize;
int xing_toc;
+ int start_pad;
+ int end_pad;
+ int usetoc;
unsigned frames; /* Total number of frames in file */
- unsigned size; /* Total number of bytes in the stream */
+ unsigned header_filesize; /* Total number of bytes in the stream */
int is_cbr;
} MP3DecContext;
@@ -52,9 +58,9 @@ typedef struct MP3DecContext {
static int mp3_read_probe(AVProbeData *p)
{
int max_frames, first_frames = 0;
- int fsize, frames, sample_rate;
+ int fsize, frames;
uint32_t header;
- uint8_t *buf, *buf0, *buf2, *end;
+ const uint8_t *buf, *buf0, *buf2, *end;
AVCodecContext avctx;
buf0 = p->buf;
@@ -67,10 +73,13 @@ static int mp3_read_probe(AVProbeData *p)
for(; buf < end; buf= buf2+1) {
buf2 = buf;
+ if(ff_mpa_check_header(AV_RB32(buf2)))
+ continue;
for(frames = 0; buf2 < end; frames++) {
+ int dummy;
header = AV_RB32(buf2);
- fsize = avpriv_mpa_decode_header(&avctx, header, &sample_rate, &sample_rate, &sample_rate, &sample_rate);
+ fsize = avpriv_mpa_decode_header(&avctx, header, &dummy, &dummy, &dummy, &dummy);
if(fsize < 0)
break;
buf2 += fsize;
@@ -81,34 +90,13 @@ static int mp3_read_probe(AVProbeData *p)
}
// keep this in sync with ac3 probe, both need to avoid
// issues with MPEG-files!
- if (first_frames >= 10)
- return AVPROBE_SCORE_EXTENSION + 5;
- if (first_frames >= 4)
- return AVPROBE_SCORE_EXTENSION + 1;
-
- if (max_frames) {
- int pes = 0, i;
- unsigned int code = -1;
-
-#define VIDEO_ID 0x000001e0
-#define AUDIO_ID 0x000001c0
- /* do a search for mpegps headers to be able to properly bias
- * towards mpegps if we detect this stream as both. */
- for (i = 0; i<p->buf_size; i++) {
- code = (code << 8) + p->buf[i];
- if ((code & 0xffffff00) == 0x100) {
- if ((code & 0x1f0) == VIDEO_ID) pes++;
- else if((code & 0x1e0) == AUDIO_ID) pes++;
- }
- }
-
- if (pes)
- max_frames = (max_frames + pes - 1) / pes;
- }
- if (max_frames > 500) return AVPROBE_SCORE_EXTENSION;
- else if (max_frames >= 4) return AVPROBE_SCORE_EXTENSION / 2;
- else if (max_frames >= 1) return 1;
- else return 0;
+ if (first_frames>=4) return AVPROBE_SCORE_EXTENSION + 1;
+ else if(max_frames>200)return AVPROBE_SCORE_EXTENSION;
+ else if(max_frames>=4 && max_frames >= p->buf_size/10000) return AVPROBE_SCORE_EXTENSION / 2;
+ else if(ff_id3v2_match(buf0, ID3v2_DEFAULT_MAGIC) && 2*ff_id3v2_tag_len(buf0) >= p->buf_size)
+ return p->buf_size < PROBE_BUF_MAX ? AVPROBE_SCORE_EXTENSION / 4 : AVPROBE_SCORE_EXTENSION - 2;
+ else if(max_frames>=1 && max_frames >= p->buf_size/10000) return 1;
+ else return 0;
//mpegps_mp3_unrecognized_format.mpg has max_frames=3
}
@@ -116,22 +104,24 @@ static void read_xing_toc(AVFormatContext *s, int64_t filesize, int64_t duration
{
int i;
MP3DecContext *mp3 = s->priv_data;
+ int fill_index = mp3->usetoc && duration > 0;
if (!filesize &&
!(filesize = avio_size(s->pb))) {
av_log(s, AV_LOG_WARNING, "Cannot determine file size, skipping TOC table.\n");
- return;
+ fill_index = 0;
}
for (i = 0; i < XING_TOC_COUNT; i++) {
uint8_t b = avio_r8(s->pb);
-
- av_add_index_entry(s->streams[0],
+ if (fill_index)
+ av_add_index_entry(s->streams[0],
av_rescale(b, filesize, 256),
av_rescale(i, duration, XING_TOC_COUNT),
0, 0, AVINDEX_KEYFRAME);
}
- mp3->xing_toc = 1;
+ if (fill_index)
+ mp3->xing_toc = 1;
}
static void mp3_parse_info_tag(AVFormatContext *s, AVStream *st,
@@ -149,7 +139,8 @@ static void mp3_parse_info_tag(AVFormatContext *s, AVStream *st,
int32_t r_gain = INT32_MIN, a_gain = INT32_MIN;
MP3DecContext *mp3 = s->priv_data;
- const int64_t xing_offtbl[2][2] = {{32, 17}, {17,9}};
+ static const int64_t xing_offtbl[2][2] = {{32, 17}, {17,9}};
+ uint64_t fsize = avio_size(s->pb);
/* Check for Xing / Info tag */
avio_skip(s->pb, xing_offtbl[c->lsf == 1][c->nb_channels == 1]);
@@ -162,12 +153,22 @@ static void mp3_parse_info_tag(AVFormatContext *s, AVStream *st,
if (v & XING_FLAG_FRAMES)
mp3->frames = avio_rb32(s->pb);
if (v & XING_FLAG_SIZE)
- mp3->size = avio_rb32(s->pb);
- if (v & XING_FLAG_TOC && mp3->frames)
- read_xing_toc(s, mp3->size, av_rescale_q(mp3->frames,
+ mp3->header_filesize = avio_rb32(s->pb);
+ if (fsize && mp3->header_filesize) {
+ uint64_t min, delta;
+ min = FFMIN(fsize, mp3->header_filesize);
+ delta = FFMAX(fsize, mp3->header_filesize) - min;
+ if (fsize > mp3->header_filesize && delta > min >> 4) {
+ mp3->frames = 0;
+ } else if (delta > min >> 4) {
+ av_log(s, AV_LOG_WARNING,
+ "filesize and duration do not match (growing file?)\n");
+ }
+ }
+ if (v & XING_FLAG_TOC)
+ read_xing_toc(s, mp3->header_filesize, av_rescale_q(mp3->frames,
(AVRational){spf, c->sample_rate},
st->time_base));
-
/* VBR quality */
if (v & XING_FLAC_QSCALE)
avio_rb32(s->pb);
@@ -213,7 +214,25 @@ static void mp3_parse_info_tag(AVFormatContext *s, AVStream *st,
avio_r8(s->pb);
/* Encoder delays */
- avio_rb24(s->pb);
+ v= avio_rb24(s->pb);
+ if(AV_RB32(version) == MKBETAG('L', 'A', 'M', 'E')
+ || AV_RB32(version) == MKBETAG('L', 'a', 'v', 'f')
+ || AV_RB32(version) == MKBETAG('L', 'a', 'v', 'c')
+ ) {
+
+ mp3->start_pad = v>>12;
+ mp3-> end_pad = v&4095;
+ st->skip_samples = mp3->start_pad + 528 + 1;
+ if (mp3->frames) {
+ st->first_discard_sample = -mp3->end_pad + 528 + 1 + mp3->frames * (int64_t)spf;
+ st->last_discard_sample = mp3->frames * (int64_t)spf;
+ }
+ if (!st->start_time)
+ st->start_time = av_rescale_q(st->skip_samples,
+ (AVRational){1, c->sample_rate},
+ st->time_base);
+ av_log(s, AV_LOG_DEBUG, "pad %d %d\n", mp3->start_pad, mp3-> end_pad);
+ }
/* Misc */
avio_r8(s->pb);
@@ -253,7 +272,7 @@ static void mp3_parse_vbri_tag(AVFormatContext *s, AVStream *st, int64_t base)
if (avio_rb16(s->pb) == 1) {
/* skip delay and quality */
avio_skip(s->pb, 4);
- mp3->size = avio_rb32(s->pb);
+ mp3->header_filesize = avio_rb32(s->pb);
mp3->frames = avio_rb32(s->pb);
}
}
@@ -283,12 +302,12 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base)
spf = c.lsf ? 576 : 1152; /* Samples per frame, layer 3 */
mp3->frames = 0;
- mp3->size = 0;
+ mp3->header_filesize = 0;
mp3_parse_info_tag(s, st, &c, spf);
mp3_parse_vbri_tag(s, st, base);
- if (!mp3->frames && !mp3->size)
+ if (!mp3->frames && !mp3->header_filesize)
return -1;
/* Skip the vbr tag frame */
@@ -297,14 +316,15 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base)
if (mp3->frames)
st->duration = av_rescale_q(mp3->frames, (AVRational){spf, c.sample_rate},
st->time_base);
- if (mp3->size && mp3->frames && !mp3->is_cbr)
- st->codec->bit_rate = av_rescale(mp3->size, 8 * c.sample_rate, mp3->frames * (int64_t)spf);
+ if (mp3->header_filesize && mp3->frames && !mp3->is_cbr)
+ st->codec->bit_rate = av_rescale(mp3->header_filesize, 8 * c.sample_rate, mp3->frames * (int64_t)spf);
return 0;
}
static int mp3_read_header(AVFormatContext *s)
{
+ MP3DecContext *mp3 = s->priv_data;
AVStream *st;
int64_t off;
int ret;
@@ -315,17 +335,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);
@@ -341,15 +365,26 @@ static int mp3_read_header(AVFormatContext *s)
static int mp3_read_packet(AVFormatContext *s, AVPacket *pkt)
{
- int ret;
-
- ret = av_get_packet(s->pb, pkt, MP3_PACKET_SIZE);
- if (ret < 0)
- return ret;
+ MP3DecContext *mp3 = s->priv_data;
+ int ret, size;
+ int64_t pos;
+
+ size= MP3_PACKET_SIZE;
+ pos = avio_tell(s->pb);
+ if(mp3->filesize > ID3v1_TAG_SIZE && pos < mp3->filesize)
+ size= FFMIN(size, mp3->filesize - pos);
+
+ ret= av_get_packet(s->pb, pkt, size);
+ if (ret <= 0) {
+ if(ret<0)
+ return ret;
+ return AVERROR_EOF;
+ }
+ pkt->flags &= ~AV_PKT_FLAG_CORRUPT;
pkt->stream_index = 0;
- if (ret > ID3v1_TAG_SIZE &&
+ if (ret >= ID3v1_TAG_SIZE &&
memcmp(&pkt->data[ret - ID3v1_TAG_SIZE], "TAG", 3) == 0)
ret -= ID3v1_TAG_SIZE;
@@ -359,38 +394,113 @@ static int mp3_read_packet(AVFormatContext *s, AVPacket *pkt)
return ret;
}
+static int check(AVFormatContext *s, int64_t pos)
+{
+ int64_t ret = avio_seek(s->pb, pos, SEEK_SET);
+ unsigned header;
+ MPADecodeHeader sd;
+ if (ret < 0)
+ return ret;
+ header = avio_rb32(s->pb);
+ if (ff_mpa_check_header(header) < 0)
+ return -1;
+ if (avpriv_mpegaudio_decode_header(&sd, header) == 1)
+ return -1;
+ return sd.frame_size;
+}
+
static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp,
int flags)
{
MP3DecContext *mp3 = s->priv_data;
- AVIndexEntry *ie;
+ AVIndexEntry *ie, ie1;
AVStream *st = s->streams[0];
int64_t ret = av_index_search_timestamp(st, timestamp, flags);
- uint32_t header = 0;
-
- if (!mp3->xing_toc)
- return AVERROR(ENOSYS);
+ int i, j;
+ int dir = (flags&AVSEEK_FLAG_BACKWARD) ? -1 : 1;
+ int64_t best_pos;
+ int best_score;
+
+ if ( mp3->is_cbr
+ && st->duration > 0
+ && mp3->header_filesize > s->internal->data_offset
+ && mp3->frames) {
+ int64_t filesize = avio_size(s->pb);
+ int64_t duration;
+ if (filesize <= s->internal->data_offset)
+ filesize = mp3->header_filesize;
+ filesize -= s->internal->data_offset;
+ duration = av_rescale(st->duration, filesize, mp3->header_filesize - s->internal->data_offset);
+ ie = &ie1;
+ timestamp = av_clip64(timestamp, 0, duration);
+ ie->timestamp = timestamp;
+ ie->pos = av_rescale(timestamp, filesize, duration) + s->internal->data_offset;
+ } else if (mp3->xing_toc) {
+ if (ret < 0)
+ return ret;
+
+ ie = &st->index_entries[ret];
+ } else {
+ st->skip_samples = timestamp <= 0 ? mp3->start_pad + 528 + 1 : 0;
- if (ret < 0)
- return ret;
+ return -1;
+ }
- ie = &st->index_entries[ret];
+ avio_seek(s->pb, FFMAX(ie->pos - 4096, 0), SEEK_SET);
ret = avio_seek(s->pb, ie->pos, SEEK_SET);
if (ret < 0)
return ret;
- while (!s->pb->eof_reached) {
- header = (header << 8) + avio_r8(s->pb);
- if (ff_mpa_check_header(header) >= 0) {
- ff_update_cur_dts(s, st, ie->timestamp);
- ret = avio_seek(s->pb, -4, SEEK_CUR);
- return (ret >= 0) ? 0 : ret;
+#define MIN_VALID 3
+ best_pos = ie->pos;
+ best_score = 999;
+ for(i=0; i<4096; i++) {
+ int64_t pos = ie->pos + (dir > 0 ? i - 1024 : -i);
+ int64_t candidate = -1;
+ int score = 999;
+
+ if (pos < 0)
+ continue;
+
+ for(j=0; j<MIN_VALID; j++) {
+ ret = check(s, pos);
+ if(ret < 0)
+ break;
+ if ((ie->pos - pos)*dir <= 0 && abs(MIN_VALID/2-j) < score) {
+ candidate = pos;
+ score = abs(MIN_VALID/2-j);
+ }
+ pos += ret;
+ }
+ if (best_score > score && j == MIN_VALID) {
+ best_pos = candidate;
+ best_score = score;
+ if(score == 0)
+ break;
}
}
- return AVERROR_EOF;
+ ret = avio_seek(s->pb, best_pos, SEEK_SET);
+ if (ret < 0)
+ return ret;
+ ff_update_cur_dts(s, st, ie->timestamp);
+ st->skip_samples = ie->timestamp <= 0 ? mp3->start_pad + 528 + 1 : 0;
+ return 0;
}
+static const AVOption options[] = {
+ { "usetoc", "use table of contents", offsetof(MP3DecContext, usetoc), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, AV_OPT_FLAG_DECODING_PARAM},
+ { NULL },
+};
+
+static const AVClass demuxer_class = {
+ .class_name = "mp3",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+ .category = AV_CLASS_CATEGORY_DEMUXER,
+};
+
AVInputFormat ff_mp3_demuxer = {
.name = "mp3",
.long_name = NULL_IF_CONFIG_SMALL("MP2/3 (MPEG audio layer 2/3)"),
@@ -401,4 +511,5 @@ AVInputFormat ff_mp3_demuxer = {
.priv_data_size = sizeof(MP3DecContext),
.flags = AVFMT_GENERIC_INDEX,
.extensions = "mp2,mp3,m2a,mpa", /* XXX: use probe */
+ .priv_class = &demuxer_class,
};
diff --git a/libavformat/mp3enc.c b/libavformat/mp3enc.c
index 9cff9d33e7..d4b6af0204 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
*/
@@ -54,11 +54,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, "TDRC", 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);
@@ -125,7 +126,7 @@ 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;
@@ -135,14 +136,14 @@ static void mp3_write_xing(AVFormatContext *s)
MPADecodeHeader mpah;
int srate_idx, i, channels;
int bitrate_idx;
- int best_bitrate_idx;
+ int best_bitrate_idx = -1;
int best_bitrate_error = INT_MAX;
int ret;
int ver = 0;
- int lsf, bytes_needed;
+ int bytes_needed;
if (!s->pb->seekable || !mp3->write_xing)
- return;
+ return 0;
for (i = 0; i < FF_ARRAY_ELEMS(avpriv_mpa_freq_tab); i++) {
const uint16_t base_freq = avpriv_mpa_freq_tab[i];
@@ -156,9 +157,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) {
@@ -166,35 +166,35 @@ static void mp3_write_xing(AVFormatContext *s)
case 2: channels = MPA_STEREO; break;
default: av_log(s, AV_LOG_WARNING, "Unsupported number of channels, "
"not writing Xing header.\n");
- return;
+ return -1;
}
/* dummy MPEG audio header */
- header = 0xff << 24; // sync
+ header = 0xffU << 24; // sync
header |= (0x7 << 5 | ver << 3 | 0x1 << 1 | 0x1) << 16; // sync/audio-version/layer 3/no crc*/
header |= (srate_idx << 2) << 8;
header |= channels << 6;
- lsf = !((header & (1 << 20) && header & (1 << 19)));
-
- mp3->xing_offset = xing_offtbl[ver != 3][channels == 1] + 4;
- bytes_needed = mp3->xing_offset + XING_SIZE;
-
for (bitrate_idx = 1; bitrate_idx < 15; bitrate_idx++) {
- int bit_rate = 1000 * avpriv_mpa_bitrate_tab[lsf][3 - 1][bitrate_idx];
+ int bit_rate = 1000 * avpriv_mpa_bitrate_tab[ver != 3][3 - 1][bitrate_idx];
int error = FFABS(bit_rate - codec->bit_rate);
- if (error < best_bitrate_error){
+ if (error < best_bitrate_error) {
best_bitrate_error = error;
best_bitrate_idx = bitrate_idx;
}
}
+ av_assert0(best_bitrate_idx >= 0);
- for (bitrate_idx = best_bitrate_idx; bitrate_idx < 15; bitrate_idx++) {
+ for (bitrate_idx = best_bitrate_idx; ; bitrate_idx++) {
int32_t mask = bitrate_idx << (4 + 8);
+ if (15 == bitrate_idx)
+ return -1;
header |= mask;
avpriv_mpegaudio_decode_header(&mpah, header);
+ mp3->xing_offset = xing_offtbl[mpah.lsf == 1][mpah.nb_channels == 1] + 4;
+ bytes_needed = mp3->xing_offset + XING_SIZE;
if (bytes_needed <= mpah.frame_size)
break;
@@ -204,27 +204,25 @@ static void mp3_write_xing(AVFormatContext *s)
ret = avio_open_dyn_buf(&dyn_ctx);
if (ret < 0)
- return;
+ return ret;
avio_wb32(dyn_ctx, header);
- avpriv_mpegaudio_decode_header(&mpah, header);
-
- av_assert0(mpah.frame_size >= bytes_needed);
-
ffio_fill(dyn_ctx, 0, mp3->xing_offset - 4);
ffio_wfourcc(dyn_ctx, "Xing");
avio_wb32(dyn_ctx, 0x01 | 0x02 | 0x04 | 0x08); // frames / size / TOC / vbr scale
mp3->size = mpah.frame_size;
- mp3->want = 1;
+ mp3->want=1;
+ mp3->seen=0;
+ mp3->pos=0;
avio_wb32(dyn_ctx, 0); // frames
avio_wb32(dyn_ctx, 0); // size
// TOC
for (i = 0; i < XING_TOC_SIZE; i++)
- avio_w8(dyn_ctx, 255 * i / XING_TOC_SIZE);
+ avio_w8(dyn_ctx, (uint8_t)(255 * i / XING_TOC_SIZE));
// vbr quality
// we write it, because some (broken) tools always expect it to be present
@@ -233,10 +231,15 @@ static void mp3_write_xing(AVFormatContext *s)
// encoder short version string
if (enc) {
uint8_t encoder_str[9] = { 0 };
- memcpy(encoder_str, enc->value, FFMIN(strlen(enc->value), sizeof(encoder_str)));
+ if ( strlen(enc->value) > sizeof(encoder_str)
+ && !strcmp("Lavc libmp3lame", enc->value)) {
+ memcpy(encoder_str, "Lavf lame", 9);
+ } else
+ memcpy(encoder_str, enc->value, FFMIN(strlen(enc->value), sizeof(encoder_str)));
+
avio_write(dyn_ctx, encoder_str, sizeof(encoder_str));
} else
- ffio_fill(dyn_ctx, 0, 9);
+ avio_write(dyn_ctx, "Lavf\0\0\0\0\0", 9);
avio_w8(dyn_ctx, 0); // tag revision 0 / unknown vbr method
avio_w8(dyn_ctx, 0); // unknown lowpass filter value
@@ -245,12 +248,10 @@ static void mp3_write_xing(AVFormatContext *s)
avio_w8(dyn_ctx, 0); // unknown abr/minimal bitrate
// encoder delay
- if (codec->initial_padding >= 1 << 12) {
+ if (codec->initial_padding - 528 - 1 >= 1 << 12) {
av_log(s, AV_LOG_WARNING, "Too many samples of initial padding.\n");
- avio_wb24(dyn_ctx, 0);
- } else {
- avio_wb24(dyn_ctx, codec->initial_padding << 12);
}
+ avio_wb24(dyn_ctx, FFMAX(codec->initial_padding - 528 - 1, 0)<<12);
avio_w8(dyn_ctx, 0); // misc
avio_w8(dyn_ctx, 0); // mp3gain
@@ -268,6 +269,8 @@ static void mp3_write_xing(AVFormatContext *s)
avio_write(s->pb, mp3->xing_frame, mp3->xing_frame_size);
mp3->audio_size = mp3->xing_frame_size;
+
+ return 0;
}
/*
@@ -288,7 +291,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;
@@ -304,22 +307,43 @@ static int mp3_write_audio_packet(AVFormatContext *s, AVPacket *pkt)
{
MP3Context *mp3 = s->priv_data;
- if (mp3->xing_offset && pkt->size >= 4) {
- MPADecodeHeader c;
+ if (pkt->data && pkt->size >= 4) {
+ MPADecodeHeader mpah;
+ int av_unused base;
uint32_t h;
h = AV_RB32(pkt->data);
if (ff_mpa_check_header(h) == 0) {
- avpriv_mpegaudio_decode_header(&c, h);
+ avpriv_mpegaudio_decode_header(&mpah, h);
if (!mp3->initial_bitrate)
- mp3->initial_bitrate = c.bit_rate;
- if ((c.bit_rate == 0) || (mp3->initial_bitrate != c.bit_rate))
+ mp3->initial_bitrate = mpah.bit_rate;
+ if ((mpah.bit_rate == 0) || (mp3->initial_bitrate != mpah.bit_rate))
mp3->has_variable_bitrate = 1;
+ } else {
+ av_log(s, AV_LOG_WARNING, "Audio packet of size %d (starting with %08X...) "
+ "is invalid, writing it anyway.\n", pkt->size, h);
}
- mp3_xing_add_frame(mp3, pkt);
+#ifdef FILTER_VBR_HEADERS
+ /* filter out XING and INFO headers. */
+ base = 4 + xing_offtbl[mpah.lsf == 1][mpah.nb_channels == 1];
+
+ if (base + 4 <= pkt->size) {
+ uint32_t v = AV_RB32(pkt->data + base);
+
+ if (MKBETAG('X','i','n','g') == v || MKBETAG('I','n','f','o') == v)
+ return 0;
+ }
+
+ /* 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);
mp3->audio_size += pkt->size;
mp3->audio_crc = av_crc(av_crc_get_table(AV_CRC_16_ANSI_LE),
mp3->audio_crc, pkt->data, pkt->size);
@@ -335,7 +359,7 @@ static int mp3_queue_flush(AVFormatContext *s)
AVPacketList *pktl;
int ret = 0, write = 1;
- ff_id3v2_finish(&mp3->id3, s->pb);
+ ff_id3v2_finish(&mp3->id3, s->pb, s->metadata_header_padding);
mp3_write_xing(s);
while ((pktl = mp3->queue)) {
@@ -431,6 +455,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",
@@ -471,14 +506,18 @@ static int mp3_write_packet(AVFormatContext *s, AVPacket *pkt)
if (mp3->pics_to_write) {
/* buffer audio packets until we get all the pictures */
AVPacketList *pktl = av_mallocz(sizeof(*pktl));
- if (!pktl)
- return AVERROR(ENOMEM);
+ int ret;
+ if (!pktl) {
+ av_log(s, AV_LOG_WARNING, "Not enough memory to buffer audio. Skipping picture streams\n");
+ mp3->pics_to_write = 0;
+ mp3_queue_flush(s);
+ return mp3_write_audio_packet(s, pkt);
+ }
- pktl->pkt = *pkt;
- pktl->pkt.buf = av_buffer_ref(pkt->buf);
- if (!pktl->pkt.buf) {
+ ret = av_copy_packet(&pktl->pkt, pkt);
+ if (ret < 0) {
av_freep(&pktl);
- return AVERROR(ENOMEM);
+ return ret;
}
if (mp3->queue_end)
@@ -567,7 +606,7 @@ static int mp3_write_header(struct AVFormatContext *s)
if (!mp3->pics_to_write) {
if (mp3->id3v2_version)
- ff_id3v2_finish(&mp3->id3, s->pb);
+ ff_id3v2_finish(&mp3->id3, s->pb, s->metadata_header_padding);
mp3_write_xing(s);
}
@@ -585,6 +624,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 1c8a295863..a62a3f2723 100644
--- a/libavformat/mpc.c
+++ b/libavformat/mpc.c
@@ -2,20 +2,20 @@
* Musepack demuxer
* Copyright (c) 2006 Konstantin Shishkov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -95,9 +95,8 @@ static int mpc_read_header(AVFormatContext *s)
st->codec->channel_layout = AV_CH_LAYOUT_STEREO;
st->codec->bits_per_coded_sample = 16;
- st->codec->extradata_size = 16;
- st->codec->extradata = av_mallocz(st->codec->extradata_size+FF_INPUT_BUFFER_PADDING_SIZE);
- avio_read(s->pb, st->codec->extradata, 16);
+ if (ff_get_extradata(st->codec, s->pb, 16) < 0)
+ return AVERROR(ENOMEM);
st->codec->sample_rate = mpc_rate[st->codec->extradata[2] & 3];
avpriv_set_pts_info(st, 32, MPC_FRAMESIZE, st->codec->sample_rate);
/* scan for seekpoints */
@@ -153,7 +152,7 @@ static int mpc_read_packet(AVFormatContext *s, AVPacket *pkt)
}
c->curbits = (curbits + size2) & 0x1F;
- if ((ret = av_new_packet(pkt, size)) < 0)
+ if ((ret = av_new_packet(pkt, size + 4)) < 0)
return ret;
pkt->data[0] = curbits;
@@ -196,11 +195,11 @@ static int mpc_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp
MPCContext *c = s->priv_data;
AVPacket pkt1, *pkt = &pkt1;
int ret;
- int index = av_index_search_timestamp(st, timestamp - DELAY_FRAMES, flags);
+ int index = av_index_search_timestamp(st, FFMAX(timestamp - DELAY_FRAMES, 0), flags);
uint32_t lastframe;
/* if found, seek there */
- if (index >= 0){
+ if (index >= 0 && st->index_entries[st->nb_index_entries-1].timestamp >= timestamp - DELAY_FRAMES){
c->curframe = st->index_entries[index].pos;
return 0;
}
diff --git a/libavformat/mpc8.c b/libavformat/mpc8.c
index 07f12d291c..0015fe4942 100644
--- a/libavformat/mpc8.c
+++ b/libavformat/mpc8.c
@@ -2,20 +2,20 @@
* Musepack SV8 demuxer
* Copyright (c) 2007 Konstantin Shishkov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -55,9 +55,9 @@ typedef struct MPCContext {
int64_t apetag_start;
} MPCContext;
-static inline int64_t bs_get_v(uint8_t **bs)
+static inline int64_t bs_get_v(const uint8_t **bs)
{
- int64_t v = 0;
+ uint64_t v = 0;
int br = 0;
int c;
@@ -75,8 +75,8 @@ static inline int64_t bs_get_v(uint8_t **bs)
static int mpc8_probe(AVProbeData *p)
{
- uint8_t *bs = p->buf + 4;
- uint8_t *bs_end = bs + p->buf_size;
+ const uint8_t *bs = p->buf + 4;
+ const uint8_t *bs_end = bs + p->buf_size;
int64_t size;
if (p->buf_size < 16)
@@ -91,7 +91,7 @@ static int mpc8_probe(AVProbeData *p)
size = bs_get_v(&bs);
if (size < 2)
return 0;
- if (bs + size - 2 >= bs_end)
+ if (size >= bs_end - bs + 2)
return AVPROBE_SCORE_EXTENSION - 1; // seems to be valid MPC but no header yet
if (header_found) {
if (size < 11 || size > 28)
@@ -108,7 +108,7 @@ static int mpc8_probe(AVProbeData *p)
static inline int64_t gb_get_v(GetBitContext *gb)
{
- int64_t v = 0;
+ uint64_t v = 0;
int bits = 0;
while(get_bits1(gb) && bits < 64-7){
v <<= 7;
@@ -136,7 +136,7 @@ static void mpc8_parse_seektable(AVFormatContext *s, int64_t off)
int tag;
int64_t size, pos, ppos[2];
uint8_t *buf;
- int i, t, seekd;
+ int i, t, seekd, ret;
GetBitContext gb;
if (s->nb_streams == 0) {
@@ -150,13 +150,20 @@ static void mpc8_parse_seektable(AVFormatContext *s, int64_t off)
av_log(s, AV_LOG_ERROR, "No seek table at given position\n");
return;
}
- if (size < 0 || size >= INT_MAX / 2) {
+ if (size > INT_MAX/10 || size<=0) {
av_log(s, AV_LOG_ERROR, "Bad seek table size\n");
return;
}
if(!(buf = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE)))
return;
- avio_read(s->pb, buf, size);
+ ret = avio_read(s->pb, buf, size);
+ if (ret != size) {
+ av_log(s, AV_LOG_ERROR, "seek table truncated\n");
+ av_free(buf);
+ return;
+ }
+ memset(buf+size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+
init_get_bits(&gb, buf, size * 8);
size = gb_get_v(&gb);
if(size > UINT_MAX/4 || size > c->samples/1152){
@@ -213,9 +220,13 @@ static int mpc8_read_header(AVFormatContext *s)
return AVERROR_INVALIDDATA;
}
- while(!pb->eof_reached){
+ while(!avio_feof(pb)){
pos = avio_tell(pb);
mpc8_get_chunk_header(pb, &tag, &size);
+ if (size < 0) {
+ av_log(s, AV_LOG_ERROR, "Invalid chunk length\n");
+ return AVERROR_INVALIDDATA;
+ }
if(tag == TAG_STREAMHDR)
break;
mpc8_handle_chunk(s, tag, pos, size);
@@ -241,9 +252,8 @@ static int mpc8_read_header(AVFormatContext *s)
st->codec->codec_id = AV_CODEC_ID_MUSEPACK8;
st->codec->bits_per_coded_sample = 16;
- st->codec->extradata_size = 2;
- st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
- avio_read(pb, st->codec->extradata, st->codec->extradata_size);
+ if (ff_get_extradata(st->codec, pb, 2) < 0)
+ return AVERROR(ENOMEM);
st->codec->channels = (st->codec->extradata[1] >> 4) + 1;
st->codec->sample_rate = mpc8_rate[st->codec->extradata[0] >> 5];
@@ -251,6 +261,8 @@ static int mpc8_read_header(AVFormatContext *s)
st->start_time = 0;
st->duration = c->samples / (1152 << (st->codec->extradata[1]&3)*2);
size -= avio_tell(pb) - pos;
+ if (size > 0)
+ avio_skip(pb, size);
if (pb->seekable) {
int64_t pos = avio_tell(s->pb);
@@ -267,7 +279,7 @@ static int mpc8_read_packet(AVFormatContext *s, AVPacket *pkt)
int tag;
int64_t pos, size;
- while(!s->pb->eof_reached){
+ while(!avio_feof(s->pb)){
pos = avio_tell(s->pb);
/* don't return bogus packets with the ape tag data */
diff --git a/libavformat/mpeg.c b/libavformat/mpeg.c
index 7c87517a2f..a0b5738790 100644
--- a/libavformat/mpeg.c
+++ b/libavformat/mpeg.c
@@ -2,20 +2,20 @@
* MPEG1/2 demuxer
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -23,15 +23,19 @@
#include "internal.h"
#include "mpeg.h"
-#undef NDEBUG
-#include <assert.h>
+#if CONFIG_VOBSUB_DEMUXER
+# include "subtitles.h"
+# include "libavutil/bprint.h"
+#endif
+
+#include "libavutil/avassert.h"
/*********************************************/
/* demux code */
#define MAX_SYNC_SIZE 100000
-static int check_pes(uint8_t *p, uint8_t *end)
+static int check_pes(const uint8_t *p, const uint8_t *end)
{
int pes1;
int pes2 = (p[3] & 0xC0) == 0x80 &&
@@ -64,53 +68,55 @@ static int mpegps_probe(AVProbeData *p)
int i;
int sys = 0, pspack = 0, priv1 = 0, vid = 0;
int audio = 0, invalid = 0, score = 0;
+ int endpes = 0;
for (i = 0; i < p->buf_size; i++) {
code = (code << 8) + p->buf[i];
if ((code & 0xffffff00) == 0x100) {
int len = p->buf[i + 1] << 8 | p->buf[i + 2];
- int pes = check_pes(p->buf + i, p->buf + p->buf_size);
+ int pes = endpes <= i && check_pes(p->buf + i, p->buf + p->buf_size);
int pack = check_pack_header(p->buf + i);
if (code == SYSTEM_HEADER_START_CODE)
sys++;
else if (code == PACK_START_CODE && pack)
pspack++;
- else if ((code & 0xf0) == VIDEO_ID && pes)
+ else if ((code & 0xf0) == VIDEO_ID && pes) {
+ endpes = i + len;
vid++;
+ }
// skip pes payload to avoid start code emulation for private
// and audio streams
- else if ((code & 0xe0) == AUDIO_ID && pes) {
- audio++;
- i += len;
- } else if (code == PRIVATE_STREAM_1 && pes) {
- priv1++;
- i += len;
- } else if ((code & 0xf0) == VIDEO_ID && !pes)
- invalid++;
- else if ((code & 0xe0) == AUDIO_ID && !pes)
- invalid++;
- else if (code == PRIVATE_STREAM_1 && !pes)
- invalid++;
+ else if ((code & 0xe0) == AUDIO_ID && pes) {audio++; i+=len;}
+ else if (code == PRIVATE_STREAM_1 && pes) {priv1++; i+=len;}
+ else if (code == 0x1fd && pes) vid++; //VC1
+
+ else if ((code & 0xf0) == VIDEO_ID && !pes) invalid++;
+ else if ((code & 0xe0) == AUDIO_ID && !pes) invalid++;
+ else if (code == PRIVATE_STREAM_1 && !pes) invalid++;
}
}
- if (vid + audio > invalid) /* invalid VDR files nd short PES streams */
+ if (vid + audio > invalid + 1) /* invalid VDR files nd short PES streams */
score = AVPROBE_SCORE_EXTENSION / 2;
+// av_log(NULL, AV_LOG_ERROR, "vid:%d aud:%d sys:%d pspack:%d invalid:%d size:%d \n",
+// vid, audio, sys, pspack, invalid, p->buf_size);
+
if (sys > invalid && sys * 9 <= pspack * 10)
- return pspack > 2 ? AVPROBE_SCORE_EXTENSION + 2
- : AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg
+ return (audio > 12 || vid > 3 || pspack > 2) ? AVPROBE_SCORE_EXTENSION + 2
+ : AVPROBE_SCORE_EXTENSION / 2 + 1; // 1 more than mp3
if (pspack > invalid && (priv1 + vid + audio) * 10 >= pspack * 9)
return pspack > 2 ? AVPROBE_SCORE_EXTENSION + 2
: AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg
if ((!!vid ^ !!audio) && (audio > 4 || vid > 1) && !sys &&
!pspack && p->buf_size > 2048 && vid + audio > invalid) /* PES stream */
- return (audio > 12 || vid > 3) ? AVPROBE_SCORE_EXTENSION + 2
- : AVPROBE_SCORE_EXTENSION / 2;
+ return (audio > 12 || vid > 3 + 2 * invalid) ? AVPROBE_SCORE_EXTENSION + 2
+ : AVPROBE_SCORE_EXTENSION / 2;
// 02-Penguin.flac has sys:0 priv1:0 pspack:0 vid:0 audio:1
// mp3_misidentified_2.mp3 has sys:0 priv1:0 pspack:0 vid:0 audio:6
+ // Have\ Yourself\ a\ Merry\ Little\ Christmas.mp3 0 0 0 5 0 1 len:21618
return score;
}
@@ -118,25 +124,30 @@ typedef struct MpegDemuxContext {
int32_t header_state;
unsigned char psm_es_type[256];
int sofdec;
+ int dvd;
+ int imkh_cctv;
+#if CONFIG_VOBSUB_DEMUXER
+ AVFormatContext *sub_ctx;
+ FFDemuxSubtitlesQueue q[32];
+#endif
} MpegDemuxContext;
static int mpegps_read_header(AVFormatContext *s)
{
MpegDemuxContext *m = s->priv_data;
- const char *sofdec = "Sofdec";
- int v, i = 0;
+ char buffer[7];
+ int64_t last_pos = avio_tell(s->pb);
m->header_state = 0xff;
s->ctx_flags |= AVFMTCTX_NOHEADER;
- m->sofdec = -1;
- do {
- v = avio_r8(s->pb);
- m->header_state = m->header_state << 8 | v;
- m->sofdec++;
- } while (v == sofdec[i] && i++ < 6);
-
- m->sofdec = (m->sofdec == 6) ? 1 : 0;
+ avio_get_str(s->pb, 6, buffer, sizeof(buffer));
+ if (!memcmp("IMKH", buffer, 4)) {
+ m->imkh_cctv = 1;
+ } else if (!memcmp("Sofdec", buffer, 6)) {
+ m->sofdec = 1;
+ } else
+ avio_seek(s->pb, last_pos, SEEK_SET);
/* no need to do more */
return 0;
@@ -161,7 +172,7 @@ static int find_next_start_code(AVIOContext *pb, int *size_ptr,
state = *header_state;
n = *size_ptr;
while (n > 0) {
- if (pb->eof_reached)
+ if (avio_feof(pb))
break;
v = avio_r8(pb);
n--;
@@ -197,7 +208,9 @@ static long mpegps_psm_parse(MpegDemuxContext *m, AVIOContext *pb)
/* skip program_stream_info */
avio_skip(pb, ps_info_length);
- es_map_length = avio_rb16(pb);
+ /*es_map_length = */avio_rb16(pb);
+ /* Ignore es_map_length, trust psm_length */
+ es_map_length = psm_length - ps_info_length - 10;
/* at least one es available? */
while (es_map_length >= 4) {
@@ -237,7 +250,7 @@ redo:
startcode = find_next_start_code(s->pb, &size, &m->header_state);
last_sync = avio_tell(s->pb);
if (startcode < 0) {
- if (s->pb->eof_reached)
+ if (avio_feof(s->pb))
return AVERROR_EOF;
// FIXME we should remember header_state
return AVERROR(EAGAIN);
@@ -252,21 +265,82 @@ redo:
goto redo;
}
if (startcode == PRIVATE_STREAM_2) {
- len = avio_rb16(s->pb);
if (!m->sofdec) {
- while (len-- >= 6) {
- if (avio_r8(s->pb) == 'S') {
- uint8_t buf[5];
- avio_read(s->pb, buf, sizeof(buf));
- m->sofdec = !memcmp(buf, "ofdec", 5);
- len -= sizeof(buf);
- break;
+ /* Need to detect whether this from a DVD or a 'Sofdec' stream */
+ int len = avio_rb16(s->pb);
+ int bytesread = 0;
+ uint8_t *ps2buf = av_malloc(len);
+
+ if (ps2buf) {
+ bytesread = avio_read(s->pb, ps2buf, len);
+
+ if (bytesread != len) {
+ avio_skip(s->pb, len - bytesread);
+ } else {
+ uint8_t *p = 0;
+ if (len >= 6)
+ p = memchr(ps2buf, 'S', len - 5);
+
+ if (p)
+ m->sofdec = !memcmp(p+1, "ofdec", 5);
+
+ m->sofdec -= !m->sofdec;
+
+ if (m->sofdec < 0) {
+ if (len == 980 && ps2buf[0] == 0) {
+ /* PCI structure? */
+ uint32_t startpts = AV_RB32(ps2buf + 0x0d);
+ uint32_t endpts = AV_RB32(ps2buf + 0x11);
+ uint8_t hours = ((ps2buf[0x19] >> 4) * 10) + (ps2buf[0x19] & 0x0f);
+ uint8_t mins = ((ps2buf[0x1a] >> 4) * 10) + (ps2buf[0x1a] & 0x0f);
+ uint8_t secs = ((ps2buf[0x1b] >> 4) * 10) + (ps2buf[0x1b] & 0x0f);
+
+ m->dvd = (hours <= 23 &&
+ mins <= 59 &&
+ secs <= 59 &&
+ (ps2buf[0x19] & 0x0f) < 10 &&
+ (ps2buf[0x1a] & 0x0f) < 10 &&
+ (ps2buf[0x1b] & 0x0f) < 10 &&
+ endpts >= startpts);
+ } else if (len == 1018 && ps2buf[0] == 1) {
+ /* DSI structure? */
+ uint8_t hours = ((ps2buf[0x1d] >> 4) * 10) + (ps2buf[0x1d] & 0x0f);
+ uint8_t mins = ((ps2buf[0x1e] >> 4) * 10) + (ps2buf[0x1e] & 0x0f);
+ uint8_t secs = ((ps2buf[0x1f] >> 4) * 10) + (ps2buf[0x1f] & 0x0f);
+
+ m->dvd = (hours <= 23 &&
+ mins <= 59 &&
+ secs <= 59 &&
+ (ps2buf[0x1d] & 0x0f) < 10 &&
+ (ps2buf[0x1e] & 0x0f) < 10 &&
+ (ps2buf[0x1f] & 0x0f) < 10);
+ }
+ }
}
+
+ av_free(ps2buf);
+
+ /* If this isn't a DVD packet or no memory
+ * could be allocated, just ignore it.
+ * If we did, move back to the start of the
+ * packet (plus 'length' field) */
+ if (!m->dvd || avio_skip(s->pb, -(len + 2)) < 0) {
+ /* Skip back failed.
+ * This packet will be lost but that can't be helped
+ * if we can't skip back
+ */
+ goto redo;
+ }
+ } else {
+ /* No memory */
+ avio_skip(s->pb, len);
+ goto redo;
}
- m->sofdec -= !m->sofdec;
+ } else if (!m->dvd) {
+ int len = avio_rb16(s->pb);
+ avio_skip(s->pb, len);
+ goto redo;
}
- avio_skip(s->pb, len);
- goto redo;
}
if (startcode == PROGRAM_STREAM_MAP) {
mpegps_psm_parse(m, s->pb);
@@ -276,7 +350,9 @@ redo:
/* find matching stream */
if (!((startcode >= 0x1c0 && startcode <= 0x1df) ||
(startcode >= 0x1e0 && startcode <= 0x1ef) ||
- (startcode == 0x1bd) || (startcode == 0x1fd)))
+ (startcode == 0x1bd) ||
+ (startcode == PRIVATE_STREAM_2) ||
+ (startcode == 0x1fd)))
goto redo;
if (ppos) {
*ppos = avio_tell(s->pb) - 4;
@@ -284,6 +360,8 @@ redo:
len = avio_rb16(s->pb);
pts =
dts = AV_NOPTS_VALUE;
+ if (startcode != PRIVATE_STREAM_2)
+ {
/* stuffing */
for (;;) {
if (len < 1)
@@ -358,22 +436,11 @@ redo:
avio_skip(s->pb, header_len);
} else if (c != 0xf)
goto redo;
+ }
- if (startcode == PRIVATE_STREAM_1 && !m->psm_es_type[startcode & 0xff]) {
+ if (startcode == PRIVATE_STREAM_1) {
startcode = avio_r8(s->pb);
len--;
- if (startcode >= 0x80 && startcode <= 0xcf) {
- /* audio: skip header */
- avio_r8(s->pb);
- avio_r8(s->pb);
- avio_r8(s->pb);
- len -= 3;
- if (startcode >= 0xb0 && startcode <= 0xbf) {
- /* MLP/TrueHD audio has a 4-byte header */
- avio_r8(s->pb);
- len--;
- }
- }
}
if (len < 0)
goto error_redo;
@@ -401,20 +468,30 @@ static int mpegps_read_packet(AVFormatContext *s,
MpegDemuxContext *m = s->priv_data;
AVStream *st;
int len, startcode, i, es_type, ret;
+ int lpcm_header_len = -1; //Init to suppress warning
+ int request_probe= 0;
enum AVCodecID codec_id = AV_CODEC_ID_NONE;
enum AVMediaType type;
int64_t pts, dts, dummy_pos; // dummy_pos is needed for the index building to work
- uint8_t av_uninit(dvdaudio_substream_type);
redo:
len = mpegps_read_pes_header(s, &dummy_pos, &startcode, &pts, &dts);
if (len < 0)
return len;
- if (startcode == 0x1bd) {
- dvdaudio_substream_type = avio_r8(s->pb);
- avio_skip(s->pb, 3);
- len -= 4;
+ if (startcode >= 0x80 && startcode <= 0xcf) {
+ if (len < 4)
+ goto skip;
+
+ /* audio: skip header */
+ avio_r8(s->pb);
+ lpcm_header_len = avio_rb16(s->pb);
+ len -= 3;
+ if (startcode >= 0xb0 && startcode <= 0xbf) {
+ /* MLP/TrueHD audio has a 4-byte header */
+ avio_r8(s->pb);
+ len--;
+ }
}
/* now find stream */
@@ -425,7 +502,6 @@ redo:
}
es_type = m->psm_es_type[startcode & 0xff];
- if (es_type > 0 && es_type != STREAM_TYPE_PRIVATE_DATA) {
if (es_type == STREAM_TYPE_VIDEO_MPEG1) {
codec_id = AV_CODEC_ID_MPEG2VIDEO;
type = AVMEDIA_TYPE_VIDEO;
@@ -448,9 +524,9 @@ redo:
} else if (es_type == STREAM_TYPE_AUDIO_AC3) {
codec_id = AV_CODEC_ID_AC3;
type = AVMEDIA_TYPE_AUDIO;
- } else {
- goto skip;
- }
+ } else if (m->imkh_cctv && es_type == 0x91) {
+ codec_id = AV_CODEC_ID_PCM_MULAW;
+ type = AVMEDIA_TYPE_AUDIO;
} else if (startcode >= 0x1e0 && startcode <= 0x1ef) {
static const unsigned char avs_seqh[4] = { 0, 0, 1, 0xb0 };
unsigned char buf[8];
@@ -460,11 +536,20 @@ redo:
if (!memcmp(buf, avs_seqh, 4) && (buf[6] != 0 || buf[7] != 1))
codec_id = AV_CODEC_ID_CAVS;
else
- codec_id = AV_CODEC_ID_PROBE;
+ request_probe= 1;
type = AVMEDIA_TYPE_VIDEO;
+ } else if (startcode == PRIVATE_STREAM_2) {
+ type = AVMEDIA_TYPE_DATA;
+ codec_id = AV_CODEC_ID_DVD_NAV;
} else if (startcode >= 0x1c0 && startcode <= 0x1df) {
type = AVMEDIA_TYPE_AUDIO;
- codec_id = m->sofdec > 0 ? AV_CODEC_ID_ADPCM_ADX : AV_CODEC_ID_MP2;
+ if (m->sofdec > 0) {
+ codec_id = AV_CODEC_ID_ADPCM_ADX;
+ // Auto-detect AC-3
+ request_probe = 50;
+ } else {
+ codec_id = AV_CODEC_ID_MP2;
+ }
} else if (startcode >= 0x80 && startcode <= 0x87) {
type = AVMEDIA_TYPE_AUDIO;
codec_id = AV_CODEC_ID_AC3;
@@ -475,7 +560,11 @@ redo:
codec_id = AV_CODEC_ID_DTS;
} else if (startcode >= 0xa0 && startcode <= 0xaf) {
type = AVMEDIA_TYPE_AUDIO;
- codec_id = AV_CODEC_ID_PCM_DVD;
+ if (lpcm_header_len == 6) {
+ codec_id = AV_CODEC_ID_MLP;
+ } else {
+ codec_id = AV_CODEC_ID_PCM_DVD;
+ }
} else if (startcode >= 0xb0 && startcode <= 0xbf) {
type = AVMEDIA_TYPE_AUDIO;
codec_id = AV_CODEC_ID_TRUEHD;
@@ -489,23 +578,6 @@ redo:
} else if (startcode >= 0xfd55 && startcode <= 0xfd5f) {
type = AVMEDIA_TYPE_VIDEO;
codec_id = AV_CODEC_ID_VC1;
- } else if (startcode == 0x1bd) {
- // check dvd audio substream type
- type = AVMEDIA_TYPE_AUDIO;
- switch (dvdaudio_substream_type & 0xe0) {
- case 0xa0:
- codec_id = AV_CODEC_ID_PCM_DVD;
- break;
- case 0x80:
- if ((dvdaudio_substream_type & 0xf8) == 0x88)
- codec_id = AV_CODEC_ID_DTS;
- else
- codec_id = AV_CODEC_ID_AC3;
- break;
- default:
- av_log(s, AV_LOG_ERROR, "Unknown 0x1bd sub-stream\n");
- goto skip;
- }
} else {
skip:
/* skip packet */
@@ -519,11 +591,25 @@ skip:
st->id = startcode;
st->codec->codec_type = type;
st->codec->codec_id = codec_id;
+ if (st->codec->codec_id == AV_CODEC_ID_PCM_MULAW) {
+ st->codec->channels = 1;
+ st->codec->channel_layout = AV_CH_LAYOUT_MONO;
+ st->codec->sample_rate = 8000;
+ }
+ st->request_probe = request_probe;
st->need_parsing = AVSTREAM_PARSE_FULL;
found:
if (st->discard >= AVDISCARD_ALL)
goto skip;
+ if (startcode >= 0xa0 && startcode <= 0xaf) {
+ if (lpcm_header_len == 6 && st->codec->codec_id == AV_CODEC_ID_MLP) {
+ if (len < 6)
+ goto skip;
+ avio_skip(s->pb, 6);
+ len -=6;
+ }
+ }
ret = av_get_packet(s->pb, pkt, len);
pkt->pts = pts;
@@ -575,3 +661,345 @@ AVInputFormat ff_mpegps_demuxer = {
.read_timestamp = mpegps_read_dts,
.flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT,
};
+
+#if CONFIG_VOBSUB_DEMUXER
+
+#define REF_STRING "# VobSub index file,"
+#define MAX_LINE_SIZE 2048
+
+static int vobsub_probe(AVProbeData *p)
+{
+ if (!strncmp(p->buf, REF_STRING, sizeof(REF_STRING) - 1))
+ return AVPROBE_SCORE_MAX;
+ return 0;
+}
+
+static int vobsub_read_header(AVFormatContext *s)
+{
+ int i, ret = 0, header_parsed = 0, langidx = 0;
+ MpegDemuxContext *vobsub = s->priv_data;
+ char *sub_name = NULL;
+ size_t fname_len;
+ char *ext, *header_str;
+ AVBPrint header;
+ int64_t delay = 0;
+ AVStream *st = NULL;
+ int stream_id = -1;
+ char id[64] = {0};
+ char alt[MAX_LINE_SIZE] = {0};
+ AVInputFormat *iformat;
+
+ sub_name = av_strdup(s->filename);
+ fname_len = strlen(sub_name);
+ ext = sub_name - 3 + fname_len;
+ if (fname_len < 4 || *(ext - 1) != '.') {
+ av_log(s, AV_LOG_ERROR, "The input index filename is too short "
+ "to guess the associated .SUB file\n");
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+ memcpy(ext, !strncmp(ext, "IDX", 3) ? "SUB" : "sub", 3);
+ av_log(s, AV_LOG_VERBOSE, "IDX/SUB: %s -> %s\n", s->filename, sub_name);
+
+ if (!(iformat = av_find_input_format("mpeg"))) {
+ ret = AVERROR_DEMUXER_NOT_FOUND;
+ goto end;
+ }
+
+ vobsub->sub_ctx = avformat_alloc_context();
+ if (!vobsub->sub_ctx) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+
+ if ((ret = ff_copy_whitelists(vobsub->sub_ctx, s)) < 0)
+ goto end;
+
+ ret = avformat_open_input(&vobsub->sub_ctx, sub_name, iformat, NULL);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "Unable to open %s as MPEG subtitles\n", sub_name);
+ goto end;
+ }
+
+ av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED);
+ while (!avio_feof(s->pb)) {
+ char line[MAX_LINE_SIZE];
+ int len = ff_get_line(s->pb, line, sizeof(line));
+
+ if (!len)
+ break;
+
+ line[strcspn(line, "\r\n")] = 0;
+
+ if (!strncmp(line, "id:", 3)) {
+ if (sscanf(line, "id: %63[^,], index: %u", id, &stream_id) != 2) {
+ av_log(s, AV_LOG_WARNING, "Unable to parse index line '%s', "
+ "assuming 'id: und, index: 0'\n", line);
+ strcpy(id, "und");
+ stream_id = 0;
+ }
+
+ if (stream_id >= FF_ARRAY_ELEMS(vobsub->q)) {
+ av_log(s, AV_LOG_ERROR, "Maximum number of subtitles streams reached\n");
+ ret = AVERROR(EINVAL);
+ goto end;
+ }
+
+ header_parsed = 1;
+ alt[0] = '\0';
+ /* We do not create the stream immediately to avoid adding empty
+ * streams. See the following timestamp entry. */
+
+ av_log(s, AV_LOG_DEBUG, "IDX stream[%d] id=%s\n", stream_id, id);
+
+ } else if (!strncmp(line, "timestamp:", 10)) {
+ AVPacket *sub;
+ int hh, mm, ss, ms;
+ int64_t pos, timestamp;
+ const char *p = line + 10;
+
+ if (stream_id == -1) {
+ av_log(s, AV_LOG_ERROR, "Timestamp declared before any stream\n");
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+
+ if (!st || st->id != stream_id) {
+ st = avformat_new_stream(s, NULL);
+ if (!st) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ st->id = stream_id;
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_DVD_SUBTITLE;
+ avpriv_set_pts_info(st, 64, 1, 1000);
+ av_dict_set(&st->metadata, "language", id, 0);
+ if (alt[0])
+ av_dict_set(&st->metadata, "title", alt, 0);
+ }
+
+ if (sscanf(p, "%02d:%02d:%02d:%03d, filepos: %"SCNx64,
+ &hh, &mm, &ss, &ms, &pos) != 5) {
+ av_log(s, AV_LOG_ERROR, "Unable to parse timestamp line '%s', "
+ "abort parsing\n", line);
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+ timestamp = (hh*3600LL + mm*60LL + ss) * 1000LL + ms + delay;
+ timestamp = av_rescale_q(timestamp, av_make_q(1, 1000), st->time_base);
+
+ sub = ff_subtitles_queue_insert(&vobsub->q[s->nb_streams - 1], "", 0, 0);
+ if (!sub) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ sub->pos = pos;
+ sub->pts = timestamp;
+ sub->stream_index = s->nb_streams - 1;
+
+ } else if (!strncmp(line, "alt:", 4)) {
+ const char *p = line + 4;
+
+ while (*p == ' ')
+ p++;
+ av_log(s, AV_LOG_DEBUG, "IDX stream[%d] name=%s\n", stream_id, p);
+ av_strlcpy(alt, p, sizeof(alt));
+ header_parsed = 1;
+
+ } else if (!strncmp(line, "delay:", 6)) {
+ int sign = 1, hh = 0, mm = 0, ss = 0, ms = 0;
+ const char *p = line + 6;
+
+ while (*p == ' ')
+ p++;
+ if (*p == '-' || *p == '+') {
+ sign = *p == '-' ? -1 : 1;
+ p++;
+ }
+ sscanf(p, "%d:%d:%d:%d", &hh, &mm, &ss, &ms);
+ delay = ((hh*3600LL + mm*60LL + ss) * 1000LL + ms) * sign;
+
+ } else if (!strncmp(line, "langidx:", 8)) {
+ const char *p = line + 8;
+
+ if (sscanf(p, "%d", &langidx) != 1)
+ av_log(s, AV_LOG_ERROR, "Invalid langidx specified\n");
+
+ } else if (!header_parsed) {
+ if (line[0] && line[0] != '#')
+ av_bprintf(&header, "%s\n", line);
+ }
+ }
+
+ if (langidx < s->nb_streams)
+ s->streams[langidx]->disposition |= AV_DISPOSITION_DEFAULT;
+
+ for (i = 0; i < s->nb_streams; i++) {
+ vobsub->q[i].sort = SUB_SORT_POS_TS;
+ ff_subtitles_queue_finalize(&vobsub->q[i]);
+ }
+
+ if (!av_bprint_is_complete(&header)) {
+ av_bprint_finalize(&header, NULL);
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ av_bprint_finalize(&header, &header_str);
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *sub_st = s->streams[i];
+ sub_st->codec->extradata = av_strdup(header_str);
+ sub_st->codec->extradata_size = header.len;
+ }
+ av_free(header_str);
+
+end:
+ av_free(sub_name);
+ return ret;
+}
+
+static int vobsub_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ MpegDemuxContext *vobsub = s->priv_data;
+ FFDemuxSubtitlesQueue *q;
+ AVIOContext *pb = vobsub->sub_ctx->pb;
+ int ret, psize, total_read = 0, i;
+ AVPacket idx_pkt;
+
+ int64_t min_ts = INT64_MAX;
+ int sid = 0;
+ for (i = 0; i < s->nb_streams; i++) {
+ FFDemuxSubtitlesQueue *tmpq = &vobsub->q[i];
+ int64_t ts;
+ av_assert0(tmpq->nb_subs);
+ ts = tmpq->subs[tmpq->current_sub_idx].pts;
+ if (ts < min_ts) {
+ min_ts = ts;
+ sid = i;
+ }
+ }
+ q = &vobsub->q[sid];
+ ret = ff_subtitles_queue_read_packet(q, &idx_pkt);
+ if (ret < 0)
+ return ret;
+
+ /* compute maximum packet size using the next packet position. This is
+ * useful when the len in the header is non-sense */
+ if (q->current_sub_idx < q->nb_subs) {
+ psize = q->subs[q->current_sub_idx].pos - idx_pkt.pos;
+ } else {
+ int64_t fsize = avio_size(pb);
+ psize = fsize < 0 ? 0xffff : fsize - idx_pkt.pos;
+ }
+
+ avio_seek(pb, idx_pkt.pos, SEEK_SET);
+
+ av_init_packet(pkt);
+ pkt->size = 0;
+ pkt->data = NULL;
+
+ do {
+ int n, to_read, startcode;
+ int64_t pts, dts;
+ int64_t old_pos = avio_tell(pb), new_pos;
+ int pkt_size;
+
+ ret = mpegps_read_pes_header(vobsub->sub_ctx, NULL, &startcode, &pts, &dts);
+ if (ret < 0) {
+ if (pkt->size) // raise packet even if incomplete
+ break;
+ goto fail;
+ }
+ to_read = ret & 0xffff;
+ new_pos = avio_tell(pb);
+ pkt_size = ret + (new_pos - old_pos);
+
+ /* this prevents reads above the current packet */
+ if (total_read + pkt_size > psize)
+ break;
+ total_read += pkt_size;
+
+ /* the current chunk doesn't match the stream index (unlikely) */
+ if ((startcode & 0x1f) != idx_pkt.stream_index)
+ break;
+
+ ret = av_grow_packet(pkt, to_read);
+ if (ret < 0)
+ goto fail;
+
+ n = avio_read(pb, pkt->data + (pkt->size - to_read), to_read);
+ if (n < to_read)
+ pkt->size -= to_read - n;
+ } while (total_read < psize);
+
+ pkt->pts = pkt->dts = idx_pkt.pts;
+ pkt->pos = idx_pkt.pos;
+ pkt->stream_index = idx_pkt.stream_index;
+
+ av_free_packet(&idx_pkt);
+ return 0;
+
+fail:
+ av_free_packet(pkt);
+ av_free_packet(&idx_pkt);
+ return ret;
+}
+
+static int vobsub_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ MpegDemuxContext *vobsub = s->priv_data;
+
+ /* Rescale requested timestamps based on the first stream (timebase is the
+ * same for all subtitles stream within a .idx/.sub). Rescaling is done just
+ * like in avformat_seek_file(). */
+ if (stream_index == -1 && s->nb_streams != 1) {
+ int i, ret = 0;
+ AVRational time_base = s->streams[0]->time_base;
+ ts = av_rescale_q(ts, AV_TIME_BASE_Q, time_base);
+ min_ts = av_rescale_rnd(min_ts, time_base.den,
+ time_base.num * (int64_t)AV_TIME_BASE,
+ AV_ROUND_UP | AV_ROUND_PASS_MINMAX);
+ max_ts = av_rescale_rnd(max_ts, time_base.den,
+ time_base.num * (int64_t)AV_TIME_BASE,
+ AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX);
+ for (i = 0; i < s->nb_streams; i++) {
+ int r = ff_subtitles_queue_seek(&vobsub->q[i], s, stream_index,
+ min_ts, ts, max_ts, flags);
+ if (r < 0)
+ ret = r;
+ }
+ return ret;
+ }
+
+ if (stream_index == -1) // only 1 stream
+ stream_index = 0;
+ return ff_subtitles_queue_seek(&vobsub->q[stream_index], s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int vobsub_read_close(AVFormatContext *s)
+{
+ int i;
+ MpegDemuxContext *vobsub = s->priv_data;
+
+ for (i = 0; i < s->nb_streams; i++)
+ ff_subtitles_queue_clean(&vobsub->q[i]);
+ if (vobsub->sub_ctx)
+ avformat_close_input(&vobsub->sub_ctx);
+ return 0;
+}
+
+AVInputFormat ff_vobsub_demuxer = {
+ .name = "vobsub",
+ .long_name = NULL_IF_CONFIG_SMALL("VobSub subtitle format"),
+ .priv_data_size = sizeof(MpegDemuxContext),
+ .read_probe = vobsub_probe,
+ .read_header = vobsub_read_header,
+ .read_packet = vobsub_read_packet,
+ .read_seek2 = vobsub_read_seek,
+ .read_close = vobsub_read_close,
+ .flags = AVFMT_SHOW_IDS,
+ .extensions = "idx",
+};
+#endif
diff --git a/libavformat/mpeg.h b/libavformat/mpeg.h
index d0b36ee153..b43517c30a 100644
--- a/libavformat/mpeg.h
+++ b/libavformat/mpeg.h
@@ -2,20 +2,20 @@
* MPEG1/2 muxer and demuxer common defines
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -42,7 +42,7 @@
#define VIDEO_ID 0xe0
#define H264_ID 0xe2
#define AC3_ID 0x80
-#define DTS_ID 0x8a
+#define DTS_ID 0x88
#define LPCM_ID 0xa0
#define SUB_ID 0x20
@@ -58,7 +58,6 @@
#define STREAM_TYPE_VIDEO_CAVS 0x42
#define STREAM_TYPE_AUDIO_AC3 0x81
-#define STREAM_TYPE_AUDIO_DTS 0x8a
static const int lpcm_freq_tab[4] = { 48000, 96000, 44100, 32000 };
diff --git a/libavformat/mpegenc.c b/libavformat/mpegenc.c
index fd3bbec413..9cee9674a0 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
*/
@@ -35,9 +35,6 @@
#define MAX_PAYLOAD_SIZE 4096
-#undef NDEBUG
-#include <assert.h>
-
typedef struct PacketDesc {
int64_t pts;
int64_t dts;
@@ -69,6 +66,7 @@ typedef struct MpegMuxContext {
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;
@@ -268,8 +266,7 @@ static int put_system_header(AVFormatContext *ctx, uint8_t *buf,
flush_put_bits(&pb);
size = put_bits_ptr(&pb) - pb.buf;
/* patch packet size */
- buf[4] = (size - 6) >> 8;
- buf[5] = (size - 6) & 0xff;
+ AV_WB16(buf + 4, size - 6);
return size;
}
@@ -324,7 +321,7 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx)
} else
s->packet_size = 2048;
if (ctx->max_delay < 0) /* Not set by the caller */
- ctx->max_delay = 0;
+ ctx->max_delay = 0.7*AV_TIME_BASE;
s->vcd_padding_bytes_written = 0;
s->vcd_padding_bitrate = 0;
@@ -351,6 +348,15 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx)
switch (st->codec->codec_type) {
case AVMEDIA_TYPE_AUDIO:
+ if (!s->is_mpeg2 &&
+ (st->codec->codec_id == AV_CODEC_ID_AC3 ||
+ st->codec->codec_id == AV_CODEC_ID_DTS ||
+ st->codec->codec_id == AV_CODEC_ID_PCM_S16BE))
+ av_log(ctx, AV_LOG_WARNING,
+ "%s in MPEG-1 system streams is not widely supported, "
+ "consider using the vob or the dvd muxer "
+ "to force a MPEG-2 program stream.\n",
+ avcodec_get_name(st->codec->codec_id));
if (st->codec->codec_id == AV_CODEC_ID_AC3) {
stream->id = ac3_id++;
} else if (st->codec->codec_id == AV_CODEC_ID_DTS) {
@@ -387,10 +393,16 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx)
stream->max_buffer_size = 6 * 1024 + st->codec->rc_buffer_size / 8;
else {
av_log(ctx, AV_LOG_WARNING,
- "VBV buffer size not set, muxing may fail\n");
+ "VBV buffer size not set, using default size of 130KB\n"
+ "If you want the mpeg file to be compliant to some specification\n"
+ "Like DVD, VCD or others, make sure you set the correct buffer size\n");
// FIXME: this is probably too small as default
stream->max_buffer_size = 230 * 1024;
}
+ if (stream->max_buffer_size > 1024 * 8191) {
+ av_log(ctx, AV_LOG_WARNING, "buffer size %d, too large\n", stream->max_buffer_size);
+ stream->max_buffer_size = 1024 * 8191;
+ }
s->video_bound++;
break;
case AVMEDIA_TYPE_SUBTITLE:
@@ -429,12 +441,18 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx)
video_bitrate += codec_rate;
}
- if (!s->mux_rate) {
+ if (s->user_mux_rate) {
+ s->mux_rate = (s->user_mux_rate + (8 * 50) - 1) / (8 * 50);
+ } else {
/* we increase slightly the bitrate to take into account the
* headers. XXX: compute it exactly */
bitrate += bitrate / 20;
bitrate += 10000;
s->mux_rate = (bitrate + (8 * 50) - 1) / (8 * 50);
+ if (s->mux_rate >= (1<<22)) {
+ av_log(ctx, AV_LOG_WARNING, "mux rate %d is too large\n", s->mux_rate);
+ s->mux_rate = (1<<22) - 1;
+ }
}
if (s->is_vcd) {
@@ -493,12 +511,12 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx)
stream->packet_number = 0;
}
s->system_header_size = get_system_header_size(ctx);
- s->last_scr = 0;
+ s->last_scr = AV_NOPTS_VALUE;
return 0;
fail:
for (i = 0; i < ctx->nb_streams; i++)
- av_free(ctx->streams[i]->priv_data);
+ av_freep(&ctx->streams[i]->priv_data);
return AVERROR(ENOMEM);
}
@@ -852,7 +870,7 @@ static int flush_packet(AVFormatContext *ctx, int stream_index,
}
/* output data */
- assert(payload_size - stuffing_size <= av_fifo_size(stream->fifo));
+ av_assert0(payload_size - stuffing_size <= av_fifo_size(stream->fifo));
av_fifo_generic_read(stream->fifo, ctx->pb,
payload_size - stuffing_size,
(void (*)(void*, void*, int))avio_write);
@@ -920,7 +938,7 @@ static int remove_decoded_packets(AVFormatContext *ctx, int64_t scr)
if (stream->buffer_index < pkt_desc->size ||
stream->predecode_packet == stream->premux_packet) {
av_log(ctx, AV_LOG_ERROR,
- "buffer underflow i=%d bufi=%d size=%d\n",
+ "buffer underflow st=%d bufi=%d size=%d\n",
i, stream->buffer_index, pkt_desc->size);
break;
}
@@ -952,7 +970,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,
@@ -962,14 +980,16 @@ retry:
return 0;
if (avail_data == 0)
continue;
- assert(avail_data > 0);
+ av_assert0(avail_data > 0);
if (space < s->packet_size && !ignore_constraints)
continue;
if (next_pkt && next_pkt->dts - scr > max_delay)
continue;
-
+ if ( stream->predecode_packet
+ && stream->predecode_packet->size > stream->buffer_index)
+ rel_space += 1<<28;
if (rel_space > best_score) {
best_score = rel_space;
best_i = i;
@@ -1004,14 +1024,14 @@ retry:
goto retry;
}
- assert(best_i >= 0);
+ av_assert0(best_i >= 0);
st = ctx->streams[best_i];
stream = st->priv_data;
- assert(av_fifo_size(stream->fifo) > 0);
+ av_assert0(av_fifo_size(stream->fifo) > 0);
- assert(avail_space >= s->packet_size || ignore_constraints);
+ av_assert0(avail_space >= s->packet_size || ignore_constraints);
timestamp_packet = stream->premux_packet;
if (timestamp_packet->unwritten_size == timestamp_packet->size) {
@@ -1029,7 +1049,7 @@ retry:
es_size = flush_packet(ctx, best_i, timestamp_packet->pts,
timestamp_packet->dts, scr, trailer_size);
} else {
- assert(av_fifo_size(stream->fifo) == trailer_size);
+ av_assert0(av_fifo_size(stream->fifo) == trailer_size);
es_size = flush_packet(ctx, best_i, AV_NOPTS_VALUE, AV_NOPTS_VALUE, scr,
trailer_size);
}
@@ -1056,8 +1076,10 @@ retry:
es_size -= stream->premux_packet->unwritten_size;
stream->premux_packet = stream->premux_packet->next;
}
- if (stream->premux_packet && es_size)
+ if (es_size) {
+ av_assert0(stream->premux_packet);
stream->premux_packet->unwritten_size -= es_size;
+ }
if (remove_decoded_packets(ctx, s->last_scr) < 0)
return -1;
@@ -1084,14 +1106,22 @@ static int mpeg_mux_write_packet(AVFormatContext *ctx, AVPacket *pkt)
pts = pkt->pts;
dts = pkt->dts;
- if (pts != AV_NOPTS_VALUE)
- pts += 2 * preload;
- if (dts != AV_NOPTS_VALUE) {
- if (!s->last_scr)
- s->last_scr = dts + preload;
- dts += 2 * preload;
+ if (s->last_scr == AV_NOPTS_VALUE) {
+ if (dts == AV_NOPTS_VALUE || (dts < preload && ctx->avoid_negative_ts) || s->is_dvd) {
+ if (dts != AV_NOPTS_VALUE)
+ s->preload += av_rescale(-dts, AV_TIME_BASE, 90000);
+ s->last_scr = 0;
+ } else {
+ s->last_scr = dts - preload;
+ s->preload = 0;
+ }
+ preload = av_rescale(s->preload, 90000, AV_TIME_BASE);
+ av_log(ctx, AV_LOG_DEBUG, "First SCR: %"PRId64" First DTS: %"PRId64"\n", s->last_scr, dts + preload);
}
+ if (dts != AV_NOPTS_VALUE) dts += preload;
+ if (pts != AV_NOPTS_VALUE) pts += preload;
+
av_dlog(ctx, "dts:%f pts:%f flags:%d stream:%d nopts:%d\n",
dts / 90000.0, pts / 90000.0, pkt->flags,
pkt->stream_index, pts != AV_NOPTS_VALUE);
@@ -1152,8 +1182,8 @@ static int mpeg_mux_end(AVFormatContext *ctx)
for (i = 0; i < ctx->nb_streams; i++) {
stream = ctx->streams[i]->priv_data;
- assert(av_fifo_size(stream->fifo) == 0);
- av_fifo_free(stream->fifo);
+ av_assert0(av_fifo_size(stream->fifo) == 0);
+ av_fifo_freep(&stream->fifo);
}
return 0;
}
@@ -1161,7 +1191,7 @@ static int mpeg_mux_end(AVFormatContext *ctx)
#define OFFSET(x) offsetof(MpegMuxContext, x)
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
- { "muxrate", NULL, OFFSET(mux_rate), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, (1 << 22) - 1, E },
+ { "muxrate", NULL, OFFSET(user_mux_rate), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, ((1<<22) - 1) * (8 * 50), E },
{ "preload", "Initial demux-decode delay in microseconds.", OFFSET(preload), AV_OPT_TYPE_INT, { .i64 = 500000 }, 0, INT_MAX, E },
{ NULL },
};
diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
index 39eb74a766..9e9ad4765e 100644
--- a/libavformat/mpegts.c
+++ b/libavformat/mpegts.c
@@ -2,20 +2,20 @@
* MPEG2 transport stream (aka DVB) demuxer
* Copyright (c) 2002-2003 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -26,6 +26,7 @@
#include "libavutil/dict.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
+#include "libavutil/avassert.h"
#include "libavcodec/bytestream.h"
#include "libavcodec/get_bits.h"
#include "libavcodec/opus.h"
@@ -54,6 +55,7 @@
enum MpegTSFilterType {
MPEGTS_PES,
MPEGTS_SECTION,
+ MPEGTS_PCR,
};
typedef struct MpegTSFilter MpegTSFilter;
@@ -84,6 +86,7 @@ struct MpegTSFilter {
int pid;
int es_id;
int last_cc; /* last cc code (-1 if first packet) */
+ int64_t last_pcr;
enum MpegTSFilterType type;
union {
MpegTSPESFilter pes_filter;
@@ -96,6 +99,9 @@ struct Program {
unsigned int id; // program id/service id
unsigned int nb_pids;
unsigned int pids[MAX_PIDS_PER_PROGRAM];
+
+ /** have we found pmt for this program */
+ int pmt_found;
};
struct MpegTSContext {
@@ -105,9 +111,11 @@ struct MpegTSContext {
/** raw packet size, including FEC if present */
int raw_packet_size;
- int pos47;
- /** position corresponding to pos47, or 0 if pos47 invalid */
- int64_t pos;
+ int size_stat[3];
+ int size_stat_count;
+#define SIZE_STAT_THRESHOLD 10
+
+ int64_t pos47_full;
/** if true, all pids are analyzed to find streams */
int auto_guess;
@@ -115,6 +123,9 @@ struct MpegTSContext {
/** compute exact PCR for each transport stream packet */
int mpeg2ts_compute_pcr;
+ /** fix dvb teletext pts */
+ int fix_teletext_pts;
+
int64_t cur_pcr; /**< used to estimate the exact PCR */
int pcr_incr; /**< used to estimate the exact PCR */
@@ -126,6 +137,11 @@ struct MpegTSContext {
/** to detect seek */
int64_t last_pos;
+ int skip_changes;
+ int skip_clear;
+
+ int scan_all_pmts;
+
int resync_size;
/******************************************/
@@ -135,15 +151,27 @@ struct MpegTSContext {
unsigned int nb_prg;
struct Program *prg;
+ int8_t crc_validity[NB_PID_MAX];
/** filters for various streams specified by PMT + for the PAT and PMT */
MpegTSFilter *pids[NB_PID_MAX];
+ int current_pid;
};
#define MPEGTS_OPTIONS \
- { "resync_size", "Size limit for looking up a new syncronization.", offsetof(MpegTSContext, resync_size), AV_OPT_TYPE_INT, { .i64 = MAX_RESYNC_SIZE}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }
+ { "resync_size", "Size limit for looking up a new synchronization.", offsetof(MpegTSContext, resync_size), AV_OPT_TYPE_INT, { .i64 = MAX_RESYNC_SIZE}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }
static const AVOption options[] = {
MPEGTS_OPTIONS,
+ {"fix_teletext_pts", "Try to fix pts values of dvb teletext streams.", offsetof(MpegTSContext, fix_teletext_pts), AV_OPT_TYPE_INT,
+ {.i64 = 1}, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
+ {"ts_packetsize", "Output option carrying the raw packet size.", offsetof(MpegTSContext, raw_packet_size), AV_OPT_TYPE_INT,
+ {.i64 = 0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY },
+ {"scan_all_pmts", "Scan and combine all PMTs", offsetof(MpegTSContext, scan_all_pmts), AV_OPT_TYPE_INT,
+ { .i64 = -1}, -1, 1, AV_OPT_FLAG_DECODING_PARAM },
+ {"skip_changes", "Skip changing / adding streams / programs.", offsetof(MpegTSContext, skip_changes), AV_OPT_TYPE_INT,
+ {.i64 = 0}, 0, 1, 0 },
+ {"skip_clear", "Skip clearing programs.", offsetof(MpegTSContext, skip_clear), AV_OPT_TYPE_INT,
+ {.i64 = 0}, 0, 1, 0 },
{ NULL },
};
@@ -212,13 +240,42 @@ typedef struct PESContext {
extern AVInputFormat ff_mpegts_demuxer;
+static struct Program * get_program(MpegTSContext *ts, unsigned int programid)
+{
+ int i;
+ for (i = 0; i < ts->nb_prg; i++) {
+ if (ts->prg[i].id == programid) {
+ return &ts->prg[i];
+ }
+ }
+ return NULL;
+}
+
+static void clear_avprogram(MpegTSContext *ts, unsigned int programid)
+{
+ AVProgram *prg = NULL;
+ int i;
+
+ for (i = 0; i < ts->stream->nb_programs; i++)
+ if (ts->stream->programs[i]->id == programid) {
+ prg = ts->stream->programs[i];
+ break;
+ }
+ if (!prg)
+ return;
+ prg->nb_stream_indexes = 0;
+}
+
static void clear_program(MpegTSContext *ts, unsigned int programid)
{
int i;
+ clear_avprogram(ts, programid);
for (i = 0; i < ts->nb_prg; i++)
- if (ts->prg[i].id == programid)
+ if (ts->prg[i].id == programid) {
ts->prg[i].nb_pids = 0;
+ ts->prg[i].pmt_found = 0;
+ }
}
static void clear_programs(MpegTSContext *ts)
@@ -237,28 +294,48 @@ static void add_pat_entry(MpegTSContext *ts, unsigned int programid)
p = &ts->prg[ts->nb_prg];
p->id = programid;
p->nb_pids = 0;
+ p->pmt_found = 0;
ts->nb_prg++;
}
static void add_pid_to_pmt(MpegTSContext *ts, unsigned int programid,
unsigned int pid)
{
+ struct Program *p = get_program(ts, programid);
int i;
- struct Program *p = NULL;
- for (i = 0; i < ts->nb_prg; i++) {
- if (ts->prg[i].id == programid) {
- p = &ts->prg[i];
- break;
- }
- }
if (!p)
return;
if (p->nb_pids >= MAX_PIDS_PER_PROGRAM)
return;
+
+ for (i = 0; i < p->nb_pids; i++)
+ if (p->pids[i] == pid)
+ return;
+
p->pids[p->nb_pids++] = pid;
}
+static void set_pmt_found(MpegTSContext *ts, unsigned int programid)
+{
+ struct Program *p = get_program(ts, programid);
+ if (!p)
+ return;
+
+ p->pmt_found = 1;
+}
+
+static void set_pcr_pid(AVFormatContext *s, unsigned int programid, unsigned int pid)
+{
+ int i;
+ for (i = 0; i < s->nb_programs; i++) {
+ if (s->programs[i]->id == programid) {
+ s->programs[i]->pcr_pid = pid;
+ break;
+ }
+ }
+}
+
/**
* @brief discard_pid() decides if the pid is to be discarded according
* to caller's programs selection
@@ -336,22 +413,27 @@ static void write_section_data(MpegTSContext *ts, MpegTSFilter *tss1,
if (tss->section_h_size != -1 &&
tss->section_index >= tss->section_h_size) {
+ int crc_valid = 1;
tss->end_of_section_reached = 1;
- if (!tss->check_crc ||
- av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1,
- tss->section_buf, tss->section_h_size) == 0)
+
+ if (tss->check_crc) {
+ crc_valid = !av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, tss->section_buf, tss->section_h_size);
+ if (crc_valid) {
+ ts->crc_validity[ tss1->pid ] = 100;
+ }else if (ts->crc_validity[ tss1->pid ] > -10) {
+ ts->crc_validity[ tss1->pid ]--;
+ }else
+ crc_valid = 2;
+ }
+ if (crc_valid)
tss->section_cb(tss1, tss->section_buf, tss->section_h_size);
}
}
-static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts,
- unsigned int pid,
- SectionCallback *section_cb,
- void *opaque,
- int check_crc)
+static MpegTSFilter *mpegts_open_filter(MpegTSContext *ts, unsigned int pid,
+ enum MpegTSFilterType type)
{
MpegTSFilter *filter;
- MpegTSSectionFilter *sec;
av_dlog(ts->stream, "Filter: pid=0x%x\n", pid);
@@ -362,11 +444,26 @@ static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts,
return NULL;
ts->pids[pid] = filter;
- filter->type = MPEGTS_SECTION;
+ filter->type = type;
filter->pid = pid;
filter->es_id = -1;
filter->last_cc = -1;
+ filter->last_pcr= -1;
+
+ return filter;
+}
+
+static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts,
+ unsigned int pid,
+ SectionCallback *section_cb,
+ void *opaque,
+ int check_crc)
+{
+ MpegTSFilter *filter;
+ MpegTSSectionFilter *sec;
+ if (!(filter = mpegts_open_filter(ts, pid, MPEGTS_SECTION)))
+ return NULL;
sec = &filter->u.section_filter;
sec->section_cb = section_cb;
sec->opaque = opaque;
@@ -386,24 +483,20 @@ static MpegTSFilter *mpegts_open_pes_filter(MpegTSContext *ts, unsigned int pid,
MpegTSFilter *filter;
MpegTSPESFilter *pes;
- if (pid >= NB_PID_MAX || ts->pids[pid])
- return NULL;
- filter = av_mallocz(sizeof(MpegTSFilter));
- if (!filter)
+ if (!(filter = mpegts_open_filter(ts, pid, MPEGTS_PES)))
return NULL;
- ts->pids[pid] = filter;
- filter->type = MPEGTS_PES;
- filter->pid = pid;
- filter->es_id = -1;
- filter->last_cc = -1;
-
pes = &filter->u.pes_filter;
pes->pes_cb = pes_cb;
pes->opaque = opaque;
return filter;
}
+static MpegTSFilter *mpegts_open_pcr_filter(MpegTSContext *ts, unsigned int pid)
+{
+ return mpegts_open_filter(ts, pid, MPEGTS_PCR);
+}
+
static void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter)
{
int pid;
@@ -429,29 +522,27 @@ static int analyze(const uint8_t *buf, int size, int packet_size, int *index,
int probe)
{
int stat[TS_MAX_PACKET_SIZE];
+ int stat_all = 0;
int i;
- int x = 0;
int best_score = 0;
- memset(stat, 0, packet_size * sizeof(int));
+ memset(stat, 0, packet_size * sizeof(*stat));
- for (x = i = 0; i < size - 3; i++) {
+ for (i = 0; i < size - 3; i++) {
if (buf[i] == 0x47 &&
- (!probe || (!(buf[i + 1] & 0x80) && (buf[i + 3] & 0x30)))) {
+ (!probe || (!(buf[i + 1] & 0x80) && buf[i + 3] != 0x47))) {
+ int x = i % packet_size;
stat[x]++;
+ stat_all++;
if (stat[x] > best_score) {
best_score = stat[x];
if (index)
*index = x;
}
}
-
- x++;
- if (x == packet_size)
- x = 0;
}
- return best_score;
+ return best_score - FFMAX(stat_all - 10*best_score, 0)/10;
}
/* autodetect fec presence. Must have at least 1024 bytes */
@@ -578,8 +669,13 @@ static const StreamType ISO_types[] = {
{ 0x04, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3 },
{ 0x0f, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
{ 0x10, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
+ /* Makito encoder sets stream type 0x11 for AAC,
+ * so auto-detect LOAS/LATM instead of hardcoding it. */
+#if !CONFIG_LOAS_DEMUXER
{ 0x11, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC_LATM }, /* LATM syntax */
+#endif
{ 0x1b, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
+ { 0x20, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
{ 0x24, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC },
{ 0x42, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_CAVS },
{ 0xd1, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC },
@@ -595,6 +691,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 },
};
@@ -614,11 +712,18 @@ static const StreamType REGD_types[] = {
{ MKTAG('D', 'T', 'S', '2'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS },
{ MKTAG('D', 'T', 'S', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS },
{ MKTAG('H', 'E', 'V', 'C'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC },
+ { MKTAG('K', 'L', 'V', 'A'), AVMEDIA_TYPE_DATA, AV_CODEC_ID_SMPTE_KLV },
{ MKTAG('V', 'C', '-', '1'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 },
{ MKTAG('O', 'p', 'u', 's'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_OPUS },
{ 0 },
};
+static const StreamType METADATA_types[] = {
+ { MKTAG('K','L','V','A'), AVMEDIA_TYPE_DATA, AV_CODEC_ID_SMPTE_KLV },
+ { MKTAG('I','D','3',' '), AVMEDIA_TYPE_DATA, AV_CODEC_ID_TIMED_ID3 },
+ { 0 },
+};
+
/* descriptor present */
static const StreamType DESC_types[] = {
{ 0x6a, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 }, /* AC-3 descriptor */
@@ -633,10 +738,16 @@ static void mpegts_find_stream_type(AVStream *st,
uint32_t stream_type,
const StreamType *types)
{
+ if (avcodec_is_open(st->codec)) {
+ av_log(NULL, AV_LOG_DEBUG, "cannot set stream info, codec is open\n");
+ return;
+ }
+
for (; types->stream_type; types++)
if (stream_type == types->stream_type) {
st->codec->codec_type = types->codec_type;
st->codec->codec_id = types->codec_id;
+ st->request_probe = 0;
return;
}
}
@@ -644,6 +755,14 @@ static void mpegts_find_stream_type(AVStream *st,
static int mpegts_set_stream_info(AVStream *st, PESContext *pes,
uint32_t stream_type, uint32_t prog_reg_desc)
{
+ int old_codec_type = st->codec->codec_type;
+ int old_codec_id = st->codec->codec_id;
+
+ if (avcodec_is_open(st->codec)) {
+ av_log(pes->stream, AV_LOG_DEBUG, "cannot set stream info, codec is open\n");
+ return 0;
+ }
+
avpriv_set_pts_info(st, 33, 1, 90000);
st->priv_data = pes;
st->codec->codec_type = AVMEDIA_TYPE_DATA;
@@ -659,7 +778,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) {
@@ -689,10 +809,23 @@ static int mpegts_set_stream_info(AVStream *st, PESContext *pes,
}
if (st->codec->codec_id == AV_CODEC_ID_NONE)
mpegts_find_stream_type(st, pes->stream_type, MISC_types);
+ if (st->codec->codec_id == AV_CODEC_ID_NONE) {
+ st->codec->codec_id = old_codec_id;
+ st->codec->codec_type = old_codec_type;
+ }
return 0;
}
+static void reset_pes_packet_state(PESContext *pes)
+{
+ pes->pts = AV_NOPTS_VALUE;
+ pes->dts = AV_NOPTS_VALUE;
+ pes->data_index = 0;
+ pes->flags = 0;
+ av_buffer_unref(&pes->buffer);
+}
+
static void new_pes_packet(PESContext *pes, AVPacket *pkt)
{
av_init_packet(pkt);
@@ -720,12 +853,15 @@ static void new_pes_packet(PESContext *pes, AVPacket *pkt)
pkt->pos = pes->ts_packet_pos;
pkt->flags = pes->flags;
- /* reset pts values */
- pes->pts = AV_NOPTS_VALUE;
- pes->dts = AV_NOPTS_VALUE;
- pes->buffer = NULL;
- pes->data_index = 0;
- pes->flags = 0;
+ pes->buffer = NULL;
+ reset_pes_packet_state(pes);
+}
+
+static uint64_t get_ts64(GetBitContext *gb, int bits)
+{
+ if (get_bits_left(gb) < bits)
+ return AV_NOPTS_VALUE;
+ return get_bits64(gb, bits);
}
static int read_sl_header(PESContext *pes, SLConfigDescr *sl,
@@ -736,7 +872,12 @@ static int read_sl_header(PESContext *pes, SLConfigDescr *sl,
int padding_flag = 0, padding_bits = 0, inst_bitrate_flag = 0;
int dts_flag = -1, cts_flag = -1;
int64_t dts = AV_NOPTS_VALUE, cts = AV_NOPTS_VALUE;
- init_get_bits(&gb, buf, buf_size * 8);
+ uint8_t buf_padded[128 + FF_INPUT_BUFFER_PADDING_SIZE];
+ int buf_padded_size = FFMIN(buf_size, sizeof(buf_padded) - FF_INPUT_BUFFER_PADDING_SIZE);
+
+ memcpy(buf_padded, buf, buf_padded_size);
+
+ init_get_bits(&gb, buf_padded, buf_padded_size * 8);
if (sl->use_au_start)
au_start_flag = get_bits1(&gb);
@@ -774,9 +915,9 @@ static int read_sl_header(PESContext *pes, SLConfigDescr *sl,
if (sl->inst_bitrate_len)
inst_bitrate_flag = get_bits1(&gb);
if (dts_flag == 1)
- dts = get_bits64(&gb, sl->timestamp_len);
+ dts = get_ts64(&gb, sl->timestamp_len);
if (cts_flag == 1)
- cts = get_bits64(&gb, sl->timestamp_len);
+ cts = get_ts64(&gb, sl->timestamp_len);
if (sl->au_len > 0)
skip_bits_long(&gb, sl->au_len);
if (inst_bitrate_flag)
@@ -811,9 +952,10 @@ static int mpegts_push_data(MpegTSFilter *filter,
if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) {
new_pes_packet(pes, ts->pkt);
ts->stop_parse = 1;
+ } else {
+ reset_pes_packet_state(pes);
}
pes->state = MPEGTS_HEADER;
- pes->data_index = 0;
pes->ts_packet_pos = pos;
}
p = buf;
@@ -845,6 +987,9 @@ static int mpegts_push_data(MpegTSFilter *filter,
/* stream not present in PMT */
if (!pes->st) {
+ if (ts->skip_changes)
+ goto skip;
+
pes->st = avformat_new_stream(ts->stream, NULL);
if (!pes->st)
return AVERROR(ENOMEM);
@@ -869,12 +1014,12 @@ static int mpegts_push_data(MpegTSFilter *filter,
code != 0x1ff && code != 0x1f2 && /* program_stream_directory, DSMCC_stream */
code != 0x1f8) { /* ITU-T Rec. H.222.1 type E stream */
pes->state = MPEGTS_PESHEADER;
- if (pes->st->codec->codec_id == AV_CODEC_ID_NONE) {
+ if (pes->st->codec->codec_id == AV_CODEC_ID_NONE && !pes->st->request_probe) {
av_dlog(pes->stream,
"pid=%x stream_type=%x probing\n",
pes->pid,
pes->stream_type);
- pes->st->codec->codec_id = AV_CODEC_ID_PROBE;
+ pes->st->request_probe = 1;
}
} else {
pes->state = MPEGTS_PAYLOAD;
@@ -958,10 +1103,54 @@ skip:
p += sl_header_bytes;
buf_size -= sl_header_bytes;
}
+ if (pes->stream_type == 0x15 && buf_size >= 5) {
+ /* skip metadata access unit header */
+ pes->pes_header_size += 5;
+ p += 5;
+ buf_size -= 5;
+ }
+ if (pes->ts->fix_teletext_pts && pes->st->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) {
+ AVProgram *p = NULL;
+ while ((p = av_find_program_from_stream(pes->stream, p, pes->st->index))) {
+ if (p->pcr_pid != -1 && p->discard != AVDISCARD_ALL) {
+ MpegTSFilter *f = pes->ts->pids[p->pcr_pid];
+ if (f) {
+ AVStream *st = NULL;
+ if (f->type == MPEGTS_PES) {
+ PESContext *pcrpes = f->u.pes_filter.opaque;
+ if (pcrpes)
+ st = pcrpes->st;
+ } else if (f->type == MPEGTS_PCR) {
+ int i;
+ for (i = 0; i < p->nb_stream_indexes; i++) {
+ AVStream *pst = pes->stream->streams[p->stream_index[i]];
+ if (pst->codec->codec_type == AVMEDIA_TYPE_VIDEO)
+ st = pst;
+ }
+ }
+ if (f->last_pcr != -1 && st && st->discard != AVDISCARD_ALL) {
+ // teletext packets do not always have correct timestamps,
+ // the standard says they should be handled after 40.6 ms at most,
+ // and the pcr error to this packet should be no more than 100 ms.
+ // TODO: we should interpolate the PCR, not just use the last one
+ int64_t pcr = f->last_pcr / 300;
+ pes->st->pts_wrap_reference = st->pts_wrap_reference;
+ pes->st->pts_wrap_behavior = st->pts_wrap_behavior;
+ if (pes->dts == AV_NOPTS_VALUE || pes->dts < pcr) {
+ pes->pts = pes->dts = pcr;
+ } else if (pes->dts > pcr + 3654 + 9000) {
+ pes->pts = pes->dts = pcr + 3654 + 9000;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
}
break;
case MPEGTS_PAYLOAD:
- if (buf_size > 0 && pes->buffer) {
+ if (pes->buffer) {
if (pes->data_index > 0 &&
pes->data_index + buf_size > pes->total_size) {
new_pes_packet(pes, ts->pkt);
@@ -979,18 +1168,18 @@ skip:
}
memcpy(pes->buffer->data + pes->data_index, p, buf_size);
pes->data_index += buf_size;
+ /* emit complete packets with known packet size
+ * decreases demuxer delay for infrequent packets like subtitles from
+ * a couple of seconds to milliseconds for properly muxed files.
+ * total_size is the number of bytes following pes_packet_length
+ * in the pes header, i.e. not counting the first PES_START_SIZE bytes */
+ if (!ts->stop_parse && pes->total_size < MAX_PES_PAYLOAD &&
+ pes->pes_header_size + pes->data_index == pes->total_size + PES_START_SIZE) {
+ ts->stop_parse = 1;
+ new_pes_packet(pes, ts->pkt);
+ }
}
buf_size = 0;
- /* emit complete packets with known packet size
- * decreases demuxer delay for infrequent packets like subtitles from
- * a couple of seconds to milliseconds for properly muxed files.
- * total_size is the number of bytes following pes_packet_length
- * in the pes header, i.e. not counting the first PES_START_SIZE bytes */
- if (!ts->stop_parse && pes->total_size < MAX_PES_PAYLOAD &&
- pes->pes_header_size + pes->data_index == pes->total_size + PES_START_SIZE) {
- ts->stop_parse = 1;
- new_pes_packet(pes, ts->pkt);
- }
break;
case MPEGTS_SKIP:
buf_size = 0;
@@ -1034,6 +1223,7 @@ typedef struct MP4DescrParseContext {
int descr_count;
int max_descr_count;
int level;
+ int predefined_SLConfigDescriptor_seen;
} MP4DescrParseContext;
static int init_MP4DescrParseContext(MP4DescrParseContext *d, AVFormatContext *s,
@@ -1157,6 +1347,11 @@ static int parse_MP4SLDescrTag(MP4DescrParseContext *d, int64_t off, int len)
descr->sl.timestamp_res = avio_rb32(&d->pb);
avio_rb32(&d->pb);
descr->sl.timestamp_len = avio_r8(&d->pb);
+ if (descr->sl.timestamp_len > 64) {
+ avpriv_request_sample(NULL, "timestamp_len > 64");
+ descr->sl.timestamp_len = 64;
+ return AVERROR_PATCHWELCOME;
+ }
descr->sl.ocr_len = avio_r8(&d->pb);
descr->sl.au_len = avio_r8(&d->pb);
descr->sl.inst_bitrate_len = avio_r8(&d->pb);
@@ -1164,8 +1359,9 @@ static int parse_MP4SLDescrTag(MP4DescrParseContext *d, int64_t off, int len)
descr->sl.degr_prior_len = lengths >> 12;
descr->sl.au_seq_num_len = (lengths >> 7) & 0x1f;
descr->sl.packet_seq_num_len = (lengths >> 2) & 0x1f;
- } else {
+ } else if (!d->predefined_SLConfigDescriptor_seen){
avpriv_report_missing_feature(d->s, "Predefined SLConfigDescriptor");
+ d->predefined_SLConfigDescriptor_seen = 1;
}
return 0;
}
@@ -1281,7 +1477,7 @@ static void m4sl_cb(MpegTSFilter *filter, const uint8_t *section,
AVStream *st;
if (ts->pids[pid]->es_id != mp4_descr[i].es_id)
continue;
- if (!(ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES)) {
+ if (ts->pids[pid]->type != MPEGTS_PES) {
av_log(s, AV_LOG_ERROR, "pid %x is not PES\n", pid);
continue;
}
@@ -1358,7 +1554,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
av_dlog(fc, "tag: 0x%02x len=%d\n", desc_tag, desc_len);
- if (st->codec->codec_id == AV_CODEC_ID_NONE &&
+ if ((st->codec->codec_id == AV_CODEC_ID_NONE || st->request_probe > 0) &&
stream_type == STREAM_TYPE_PRIVATE_DATA)
mpegts_find_stream_type(st, desc_tag, DESC_types);
@@ -1388,7 +1584,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
if (get16(pp, desc_end) < 0)
break;
if (mp4_descr_count > 0 &&
- st->codec->codec_id == AV_CODEC_ID_AAC_LATM &&
+ (st->codec->codec_id == AV_CODEC_ID_AAC_LATM || st->request_probe > 0) &&
mp4_descr->dec_config_descr_len && mp4_descr->es_id == pid) {
AVIOContext pb;
ffio_init_context(&pb, mp4_descr->dec_config_descr,
@@ -1396,46 +1592,118 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
NULL, NULL, NULL, NULL);
ff_mp4_read_dec_config_descr(fc, st, &pb);
if (st->codec->codec_id == AV_CODEC_ID_AAC &&
- st->codec->extradata_size > 0)
- st->need_parsing = 0;
+ st->codec->extradata_size > 0) {
+ st->request_probe = st->need_parsing = 0;
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ }
}
break;
case 0x56: /* DVB teletext descriptor */
- language[0] = get8(pp, desc_end);
- language[1] = get8(pp, desc_end);
- language[2] = get8(pp, desc_end);
- language[3] = 0;
- av_dict_set(&st->metadata, "language", language, 0);
+ {
+ uint8_t *extradata = NULL;
+ int language_count = desc_len / 5;
+
+ if (desc_len > 0 && desc_len % 5 != 0)
+ return AVERROR_INVALIDDATA;
+
+ if (language_count > 0) {
+ /* 4 bytes per language code (3 bytes) with comma or NUL byte should fit language buffer */
+ if (language_count > sizeof(language) / 4) {
+ language_count = sizeof(language) / 4;
+ }
+
+ if (st->codec->extradata == NULL) {
+ if (ff_alloc_extradata(st->codec, language_count * 2)) {
+ return AVERROR(ENOMEM);
+ }
+ }
+
+ if (st->codec->extradata_size < language_count * 2)
+ return AVERROR_INVALIDDATA;
+
+ extradata = st->codec->extradata;
+
+ for (i = 0; i < language_count; i++) {
+ language[i * 4 + 0] = get8(pp, desc_end);
+ language[i * 4 + 1] = get8(pp, desc_end);
+ language[i * 4 + 2] = get8(pp, desc_end);
+ language[i * 4 + 3] = ',';
+
+ memcpy(extradata, *pp, 2);
+ extradata += 2;
+
+ *pp += 2;
+ }
+
+ language[i * 4 - 1] = 0;
+ av_dict_set(&st->metadata, "language", language, 0);
+ }
+ }
break;
case 0x59: /* subtitling descriptor */
- language[0] = get8(pp, desc_end);
- language[1] = get8(pp, desc_end);
- language[2] = get8(pp, desc_end);
- language[3] = 0;
- /* hearing impaired subtitles detection */
- switch (get8(pp, desc_end)) {
- case 0x20: /* DVB subtitles (for the hard of hearing) with no monitor aspect ratio criticality */
- case 0x21: /* DVB subtitles (for the hard of hearing) for display on 4:3 aspect ratio monitor */
- case 0x22: /* DVB subtitles (for the hard of hearing) for display on 16:9 aspect ratio monitor */
- case 0x23: /* DVB subtitles (for the hard of hearing) for display on 2.21:1 aspect ratio monitor */
- case 0x24: /* DVB subtitles (for the hard of hearing) for display on a high definition monitor */
- case 0x25: /* DVB subtitles (for the hard of hearing) with plano-stereoscopic disparity for display on a high definition monitor */
- st->disposition |= AV_DISPOSITION_HEARING_IMPAIRED;
- break;
- }
- if (st->codec->extradata) {
- if (st->codec->extradata_size == 4 &&
- memcmp(st->codec->extradata, *pp, 4))
- avpriv_request_sample(fc, "DVB sub with multiple IDs");
- } else {
- st->codec->extradata = av_malloc(4 + FF_INPUT_BUFFER_PADDING_SIZE);
- if (st->codec->extradata) {
- st->codec->extradata_size = 4;
- memcpy(st->codec->extradata, *pp, 4);
+ {
+ /* 8 bytes per DVB subtitle substream data:
+ * ISO_639_language_code (3 bytes),
+ * subtitling_type (1 byte),
+ * composition_page_id (2 bytes),
+ * ancillary_page_id (2 bytes) */
+ int language_count = desc_len / 8;
+
+ if (desc_len > 0 && desc_len % 8 != 0)
+ return AVERROR_INVALIDDATA;
+
+ if (language_count > 1) {
+ avpriv_request_sample(fc, "DVB subtitles with multiple languages");
+ }
+
+ if (language_count > 0) {
+ uint8_t *extradata;
+
+ /* 4 bytes per language code (3 bytes) with comma or NUL byte should fit language buffer */
+ if (language_count > sizeof(language) / 4) {
+ language_count = sizeof(language) / 4;
+ }
+
+ if (st->codec->extradata == NULL) {
+ if (ff_alloc_extradata(st->codec, language_count * 5)) {
+ return AVERROR(ENOMEM);
+ }
+ }
+
+ if (st->codec->extradata_size < language_count * 5)
+ return AVERROR_INVALIDDATA;
+
+ extradata = st->codec->extradata;
+
+ for (i = 0; i < language_count; i++) {
+ language[i * 4 + 0] = get8(pp, desc_end);
+ language[i * 4 + 1] = get8(pp, desc_end);
+ language[i * 4 + 2] = get8(pp, desc_end);
+ language[i * 4 + 3] = ',';
+
+ /* hearing impaired subtitles detection using subtitling_type */
+ switch (*pp[0]) {
+ case 0x20: /* DVB subtitles (for the hard of hearing) with no monitor aspect ratio criticality */
+ case 0x21: /* DVB subtitles (for the hard of hearing) for display on 4:3 aspect ratio monitor */
+ case 0x22: /* DVB subtitles (for the hard of hearing) for display on 16:9 aspect ratio monitor */
+ case 0x23: /* DVB subtitles (for the hard of hearing) for display on 2.21:1 aspect ratio monitor */
+ case 0x24: /* DVB subtitles (for the hard of hearing) for display on a high definition monitor */
+ case 0x25: /* DVB subtitles (for the hard of hearing) with plano-stereoscopic disparity for display on a high definition monitor */
+ st->disposition |= AV_DISPOSITION_HEARING_IMPAIRED;
+ break;
+ }
+
+ extradata[4] = get8(pp, desc_end); /* subtitling_type */
+ memcpy(extradata, *pp, 4); /* composition_page_id and ancillary_page_id */
+ extradata += 5;
+
+ *pp += 4;
+ }
+
+ language[i * 4 - 1] = 0;
+ av_dict_set(&st->metadata, "language", language, 0);
}
}
- *pp += 4;
- av_dict_set(&st->metadata, "language", language, 0);
break;
case 0x0a: /* ISO 639 language descriptor */
for (i = 0; i + 4 <= desc_len; i += 4) {
@@ -1466,6 +1734,18 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
if (st->codec->codec_id == AV_CODEC_ID_NONE)
mpegts_find_stream_type(st, st->codec->codec_tag, REGD_types);
break;
+ case 0x52: /* stream identifier descriptor */
+ st->stream_identifier = 1 + get8(pp, desc_end);
+ break;
+ case 0x26: /* metadata descriptor */
+ if (get16(pp, desc_end) == 0xFFFF)
+ *pp += 4;
+ if (get8(pp, desc_end) == 0xFF) {
+ st->codec->codec_tag = bytestream_get_le32(pp);
+ if (st->codec->codec_id == AV_CODEC_ID_NONE)
+ mpegts_find_stream_type(st, st->codec->codec_tag, METADATA_types);
+ }
+ break;
case 0x7f: /* DVB extension descriptor */
ext_desc_tag = get8(pp, desc_end);
if (ext_desc_tag < 0)
@@ -1486,7 +1766,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
return AVERROR_INVALIDDATA;
if (channel_config_code <= 0x8) {
st->codec->extradata[9] = channels = channel_config_code ? channel_config_code : 2;
- st->codec->extradata[18] = channel_config_code ? (channels > 2) : 255;
+ st->codec->extradata[18] = channel_config_code ? (channels > 2) : /* Dual Mono */ 255;
st->codec->extradata[19] = opus_stream_cnt[channel_config_code];
st->codec->extradata[20] = opus_coupled_stream_cnt[channel_config_code];
memcpy(&st->codec->extradata[21], opus_channel_map[channels - 1], channels);
@@ -1527,18 +1807,23 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
if (parse_section_header(h, &p, p_end) < 0)
return;
- av_dlog(ts->stream, "sid=0x%x sec_num=%d/%d\n",
- h->id, h->sec_num, h->last_sec_num);
+ av_dlog(ts->stream, "sid=0x%x sec_num=%d/%d version=%d\n",
+ h->id, h->sec_num, h->last_sec_num, h->version);
if (h->tid != PMT_TID)
return;
+ if (!ts->scan_all_pmts && ts->skip_changes)
+ return;
+
+ if (!ts->skip_clear)
+ clear_program(ts, h->id);
- clear_program(ts, h->id);
pcr_pid = get16(&p, p_end);
if (pcr_pid < 0)
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);
@@ -1575,7 +1860,9 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
// stop parsing after pmt, we found header
if (!ts->stream->nb_streams)
- ts->stop_parse = 1;
+ ts->stop_parse = 2;
+
+ set_pmt_found(ts, h->id);
for (;;) {
@@ -1586,14 +1873,18 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
break;
pid = get16(&p, p_end);
if (pid < 0)
- break;
+ goto out;
pid &= 0x1fff;
+ if (pid == ts->current_pid)
+ goto out;
/* now create stream */
if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) {
pes = ts->pids[pid]->u.pes_filter.opaque;
if (!pes->st) {
pes->st = avformat_new_stream(pes->stream, NULL);
+ if (!pes->st)
+ goto out;
pes->st->id = pes->pid;
}
st = pes->st;
@@ -1603,6 +1894,8 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
pes = add_pes_stream(ts, pid, pcr_pid);
if (pes) {
st = avformat_new_stream(pes->stream, NULL);
+ if (!st)
+ goto out;
st->id = pes->pid;
}
} else {
@@ -1611,6 +1904,8 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
st = ts->stream->streams[idx];
} else {
st = avformat_new_stream(ts->stream, NULL);
+ if (!st)
+ goto out;
st->id = pid;
st->codec->codec_type = AVMEDIA_TYPE_DATA;
}
@@ -1628,11 +1923,11 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
desc_list_len = get16(&p, p_end);
if (desc_list_len < 0)
- break;
+ goto out;
desc_list_len &= 0xfff;
desc_list_end = p + desc_list_len;
if (desc_list_end > p_end)
- break;
+ goto out;
for (;;) {
if (ff_parse_mpeg2_descriptor(ts->stream, st, stream_type, &p,
desc_list_end, mp4_descr,
@@ -1649,6 +1944,9 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
p = desc_list_end;
}
+ if (!ts->pids[pcr_pid])
+ mpegts_open_pcr_filter(ts, pcr_pid);
+
out:
for (i = 0; i < mp4_descr_count; i++)
av_free(mp4_descr[i].dec_config_descr);
@@ -1660,6 +1958,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);
@@ -1670,6 +1969,10 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
return;
if (h->tid != PAT_TID)
return;
+ if (ts->skip_changes)
+ return;
+
+ ts->stream->ts_id = h->id;
clear_programs(ts);
for (;;) {
@@ -1681,20 +1984,44 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
break;
pmt_pid &= 0x1fff;
+ if (pmt_pid == ts->current_pid)
+ break;
+
av_dlog(ts->stream, "sid=0x%x pid=0x%x\n", sid, pmt_pid);
if (sid == 0x0000) {
/* NIT info */
} else {
- av_new_program(ts->stream, sid);
- if (ts->pids[pmt_pid])
- mpegts_close_filter(ts, ts->pids[pmt_pid]);
- mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1);
+ MpegTSFilter *fil = ts->pids[pmt_pid];
+ program = av_new_program(ts->stream, sid);
+ if (program) {
+ program->program_num = sid;
+ program->pmt_pid = pmt_pid;
+ }
+ if (fil)
+ if ( fil->type != MPEGTS_SECTION
+ || fil->pid != pmt_pid
+ || fil->u.section_filter.section_cb != pmt_cb)
+ mpegts_close_filter(ts, ts->pids[pmt_pid]);
+
+ if (!ts->pids[pmt_pid])
+ mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1);
add_pat_entry(ts, sid);
add_pid_to_pmt(ts, sid, 0); // add pat pid to program
add_pid_to_pmt(ts, sid, pmt_pid);
}
}
+
+ if (sid < 0) {
+ int i,j;
+ for (j=0; j<ts->stream->nb_programs; j++) {
+ for (i = 0; i < ts->nb_prg; i++)
+ if (ts->prg[i].id == ts->stream->programs[j]->id)
+ break;
+ if (i==ts->nb_prg && !ts->skip_clear)
+ clear_avprogram(ts, ts->stream->programs[j]->id);
+ }
+ }
}
static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
@@ -1714,6 +2041,8 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
return;
if (h->tid != SDT_TID)
return;
+ if (ts->skip_changes)
+ return;
onid = get16(&p, p_end);
if (onid < 0)
return;
@@ -1740,7 +2069,7 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
break;
desc_len = get8(&p, desc_list_end);
desc_end = p + desc_len;
- if (desc_end > desc_list_end)
+ if (desc_len < 0 || desc_end > desc_list_end)
break;
av_dlog(ts->stream, "tag: 0x%02x len=%d\n",
@@ -1775,6 +2104,9 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
}
}
+static int parse_pcr(int64_t *ppcr_high, int *ppcr_low,
+ const uint8_t *packet);
+
/* handle one TS packet */
static int handle_packet(MpegTSContext *ts, const uint8_t *packet)
{
@@ -1795,6 +2127,7 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet)
}
if (!tss)
return 0;
+ ts->current_pid = pid;
afc = (packet[3] >> 4) & 3;
if (afc == 0) /* reserved value */
@@ -1815,7 +2148,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) {
@@ -1824,20 +2157,25 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet)
}
}
- if (!has_payload)
- return 0;
p = packet + 4;
if (has_adaptation) {
+ int64_t pcr_h;
+ int pcr_l;
+ if (parse_pcr(&pcr_h, &pcr_l, packet) == 0)
+ tss->last_pcr = pcr_h * 300 + pcr_l;
/* skip adaptation field */
p += p[0] + 1;
}
/* if past the end of packet, ignore */
p_end = packet + TS_PACKET_SIZE;
- if (p >= p_end)
+ if (p >= p_end || !has_payload)
return 0;
pos = avio_tell(ts->stream->pb);
- MOD_UNLIKELY(ts->pos47, pos, ts->raw_packet_size, ts->pos);
+ if (pos >= 0) {
+ av_assert0(pos >= TS_PACKET_SIZE);
+ ts->pos47_full = pos - TS_PACKET_SIZE;
+ }
if (tss->type == MPEGTS_SECTION) {
if (is_start) {
@@ -1864,17 +2202,74 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet)
p, p_end - p, 0);
}
}
+
+ // stop find_stream_info from waiting for more streams
+ // when all programs have received a PMT
+ if (ts->stream->ctx_flags & AVFMTCTX_NOHEADER && ts->scan_all_pmts <= 0) {
+ int i;
+ for (i = 0; i < ts->nb_prg; i++) {
+ if (!ts->prg[i].pmt_found)
+ break;
+ }
+ if (i == ts->nb_prg && ts->nb_prg > 0) {
+ int types = 0;
+ for (i = 0; i < ts->stream->nb_streams; i++) {
+ AVStream *st = ts->stream->streams[i];
+ types |= 1<<st->codec->codec_type;
+ }
+ if ((types & (1<<AVMEDIA_TYPE_AUDIO) && types & (1<<AVMEDIA_TYPE_VIDEO)) || pos > 100000) {
+ av_log(ts->stream, AV_LOG_DEBUG, "All programs have pmt, headers found\n");
+ ts->stream->ctx_flags &= ~AVFMTCTX_NOHEADER;
+ }
+ }
+ }
+
} else {
int ret;
// Note: The position here points actually behind the current packet.
- if ((ret = tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start,
- pos - ts->raw_packet_size)) < 0)
- return ret;
+ if (tss->type == MPEGTS_PES) {
+ if ((ret = tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start,
+ pos - ts->raw_packet_size)) < 0)
+ return ret;
+ }
}
return 0;
}
+static void reanalyze(MpegTSContext *ts) {
+ AVIOContext *pb = ts->stream->pb;
+ int64_t pos = avio_tell(pb);
+ if (pos < 0)
+ return;
+ pos -= ts->pos47_full;
+ if (pos == TS_PACKET_SIZE) {
+ ts->size_stat[0] ++;
+ } else if (pos == TS_DVHS_PACKET_SIZE) {
+ ts->size_stat[1] ++;
+ } else if (pos == TS_FEC_PACKET_SIZE) {
+ ts->size_stat[2] ++;
+ }
+
+ ts->size_stat_count ++;
+ if (ts->size_stat_count > SIZE_STAT_THRESHOLD) {
+ int newsize = 0;
+ if (ts->size_stat[0] > SIZE_STAT_THRESHOLD) {
+ newsize = TS_PACKET_SIZE;
+ } else if (ts->size_stat[1] > SIZE_STAT_THRESHOLD) {
+ newsize = TS_DVHS_PACKET_SIZE;
+ } else if (ts->size_stat[2] > SIZE_STAT_THRESHOLD) {
+ newsize = TS_FEC_PACKET_SIZE;
+ }
+ if (newsize && newsize != ts->raw_packet_size) {
+ av_log(ts->stream, AV_LOG_WARNING, "changing packet size to %d\n", newsize);
+ ts->raw_packet_size = newsize;
+ }
+ ts->size_stat_count = 0;
+ memset(ts->size_stat, 0, sizeof(ts->size_stat));
+ }
+}
+
/* XXX: try to find a better synchro over several packets (use
* get_packet_size() ?) */
static int mpegts_resync(AVFormatContext *s)
@@ -1885,10 +2280,11 @@ static int mpegts_resync(AVFormatContext *s)
for (i = 0; i < ts->resync_size; i++) {
c = avio_r8(pb);
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR_EOF;
if (c == 0x47) {
avio_seek(pb, -1, SEEK_CUR);
+ reanalyze(s->priv_data);
return 0;
}
}
@@ -1912,7 +2308,9 @@ static int read_packet(AVFormatContext *s, uint8_t *buf, int raw_packet_size,
/* check packet sync byte */
if ((*data)[0] != 0x47) {
/* find a new packet start */
- avio_seek(pb, -TS_PACKET_SIZE, SEEK_CUR);
+ uint64_t pos = avio_tell(pb);
+ avio_seek(pb, -FFMIN(raw_packet_size, pos), SEEK_CUR);
+
if (mpegts_resync(s) < 0)
return AVERROR(EAGAIN);
else
@@ -1932,12 +2330,13 @@ static void finished_reading_packet(AVFormatContext *s, int raw_packet_size)
avio_skip(pb, skip);
}
-static int handle_packets(MpegTSContext *ts, int nb_packets)
+static int handle_packets(MpegTSContext *ts, int64_t nb_packets)
{
AVFormatContext *s = ts->stream;
uint8_t packet[TS_PACKET_SIZE + FF_INPUT_BUFFER_PADDING_SIZE];
const uint8_t *data;
- int packet_num, ret = 0;
+ int64_t packet_num;
+ int ret = 0;
if (avio_tell(s->pb) != ts->last_pos) {
int i;
@@ -1952,6 +2351,7 @@ static int handle_packets(MpegTSContext *ts, int nb_packets)
pes->state = MPEGTS_SKIP; /* skip until pes header */
}
ts->pids[i]->last_cc = -1;
+ ts->pids[i]->last_pcr = -1;
}
}
}
@@ -1960,11 +2360,15 @@ static int handle_packets(MpegTSContext *ts, int nb_packets)
packet_num = 0;
memset(packet + TS_PACKET_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE);
for (;;) {
- if (ts->stop_parse > 0)
- break;
packet_num++;
- if (nb_packets != 0 && packet_num >= nb_packets)
+ if (nb_packets != 0 && packet_num >= nb_packets ||
+ ts->stop_parse > 1) {
+ ret = AVERROR(EAGAIN);
+ break;
+ }
+ if (ts->stop_parse > 0)
break;
+
ret = read_packet(s, packet, ts->raw_packet_size, &data);
if (ret != 0)
break;
@@ -1980,30 +2384,33 @@ static int handle_packets(MpegTSContext *ts, int nb_packets)
static int mpegts_probe(AVProbeData *p)
{
const int size = p->buf_size;
- int score, fec_score, dvhs_score;
+ int maxscore = 0;
+ int sumscore = 0;
+ int i;
int check_count = size / TS_FEC_PACKET_SIZE;
#define CHECK_COUNT 10
+#define CHECK_BLOCK 100
if (check_count < CHECK_COUNT)
return AVERROR_INVALIDDATA;
- score = analyze(p->buf, TS_PACKET_SIZE * check_count,
- TS_PACKET_SIZE, NULL, 1) * CHECK_COUNT / check_count;
- dvhs_score = analyze(p->buf, TS_DVHS_PACKET_SIZE * check_count,
- TS_DVHS_PACKET_SIZE, NULL, 1) * CHECK_COUNT / check_count;
- fec_score = analyze(p->buf, TS_FEC_PACKET_SIZE * check_count,
- TS_FEC_PACKET_SIZE, NULL, 1) * CHECK_COUNT / check_count;
- av_dlog(NULL, "score: %d, dvhs_score: %d, fec_score: %d \n",
- score, dvhs_score, fec_score);
+ for (i = 0; i<check_count; i+=CHECK_BLOCK) {
+ int left = FFMIN(check_count - i, CHECK_BLOCK);
+ int score = analyze(p->buf + TS_PACKET_SIZE *i, TS_PACKET_SIZE *left, TS_PACKET_SIZE , NULL, 1);
+ int dvhs_score = analyze(p->buf + TS_DVHS_PACKET_SIZE*i, TS_DVHS_PACKET_SIZE*left, TS_DVHS_PACKET_SIZE, NULL, 1);
+ int fec_score = analyze(p->buf + TS_FEC_PACKET_SIZE *i, TS_FEC_PACKET_SIZE *left, TS_FEC_PACKET_SIZE , NULL, 1);
+ score = FFMAX3(score, dvhs_score, fec_score);
+ sumscore += score;
+ maxscore = FFMAX(maxscore, score);
+ }
+
+ sumscore = sumscore * CHECK_COUNT / check_count;
+ maxscore = maxscore * CHECK_COUNT / CHECK_BLOCK;
- /* we need a clear definition for the returned score otherwise
- * things will become messy sooner or later */
- if (score > fec_score && score > dvhs_score && score > 6)
- return AVPROBE_SCORE_MAX + score - CHECK_COUNT;
- else if (dvhs_score > score && dvhs_score > fec_score && dvhs_score > 6)
- return AVPROBE_SCORE_MAX + dvhs_score - CHECK_COUNT;
- else if (fec_score > 6)
- return AVPROBE_SCORE_MAX + fec_score - CHECK_COUNT;
+ av_dlog(0, "TS score: %d %d\n", sumscore, maxscore);
+
+ if (sumscore > 6) return AVPROBE_SCORE_MAX + sumscore - CHECK_COUNT;
+ else if (maxscore > 6) return AVPROBE_SCORE_MAX/2 + sumscore - CHECK_COUNT;
else
return AVERROR_INVALIDDATA;
}
@@ -2036,24 +2443,34 @@ static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, const uint8_t *packet)
return 0;
}
+static void seek_back(AVFormatContext *s, AVIOContext *pb, int64_t pos) {
+
+ /* NOTE: We attempt to seek on non-seekable files as well, as the
+ * probe buffer usually is big enough. Only warn if the seek failed
+ * on files where the seek should work. */
+ if (avio_seek(pb, pos, SEEK_SET) < 0)
+ av_log(s, pb->seekable ? AV_LOG_ERROR : AV_LOG_INFO, "Unable to seek back to the start\n");
+}
+
static int mpegts_read_header(AVFormatContext *s)
{
MpegTSContext *ts = s->priv_data;
AVIOContext *pb = s->pb;
- uint8_t buf[5 * 1024];
+ uint8_t buf[8 * 1024] = {0};
int len;
- int64_t pos;
+ int64_t pos, probesize = s->probesize ? s->probesize : s->probesize2;
+
+ if (ffio_ensure_seekback(pb, probesize) < 0)
+ av_log(s, AV_LOG_WARNING, "Failed to allocate buffers for seekback\n");
- /* read the first 1024 bytes to get packet size */
+ /* read the first 8192 bytes to get packet size */
pos = avio_tell(pb);
len = avio_read(pb, buf, sizeof(buf));
- if (len < 0)
- return len;
- if (len != sizeof(buf))
- return AVERROR_BUG;
- ts->raw_packet_size = get_packet_size(buf, sizeof(buf));
- if (ts->raw_packet_size <= 0)
- return AVERROR_INVALIDDATA;
+ ts->raw_packet_size = get_packet_size(buf, len);
+ if (ts->raw_packet_size <= 0) {
+ av_log(s, AV_LOG_WARNING, "Could not detect TS packet size, defaulting to non-FEC/DVHS\n");
+ ts->raw_packet_size = TS_PACKET_SIZE;
+ }
ts->stream = s;
ts->auto_guess = 0;
@@ -2061,14 +2478,13 @@ static int mpegts_read_header(AVFormatContext *s)
/* normal demux */
/* first do a scan to get all the services */
- if (avio_seek(pb, pos, SEEK_SET) < 0 && pb->seekable)
- av_log(s, AV_LOG_ERROR, "Unable to seek back to the start\n");
+ seek_back(s, pb, pos);
mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1);
mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1);
- handle_packets(ts, s->probesize / ts->raw_packet_size);
+ handle_packets(ts, probesize / ts->raw_packet_size);
/* if could not find service, enable auto_guess */
ts->auto_guess = 1;
@@ -2128,7 +2544,7 @@ static int mpegts_read_header(AVFormatContext *s)
st->start_time / 1000000.0, pcrs[0] / 27e6, ts->pcr_incr);
}
- avio_seek(pb, pos, SEEK_SET);
+ seek_back(s, pb, pos);
return 0;
}
@@ -2191,6 +2607,7 @@ static int mpegts_read_packet(AVFormatContext *s, AVPacket *pkt)
ts->pkt = pkt;
ret = handle_packets(ts, 0);
if (ret < 0) {
+ av_free_packet(ts->pkt);
/* flush pes data left */
for (i = 0; i < NB_PID_MAX; i++)
if (ts->pids[i] && ts->pids[i]->type == MPEGTS_PES) {
@@ -2227,7 +2644,7 @@ static int mpegts_read_close(AVFormatContext *s)
return 0;
}
-static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index,
+static av_unused int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index,
int64_t *ppos, int64_t pos_limit)
{
MpegTSContext *ts = s->priv_data;
@@ -2235,74 +2652,69 @@ static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index,
uint8_t buf[TS_PACKET_SIZE];
int pcr_l, pcr_pid =
((PESContext *)s->streams[stream_index]->priv_data)->pcr_pid;
- const int find_next = 1;
+ int pos47 = ts->pos47_full % ts->raw_packet_size;
pos =
- ((*ppos + ts->raw_packet_size - 1 - ts->pos47) / ts->raw_packet_size) *
- ts->raw_packet_size + ts->pos47;
- if (find_next) {
- for (;;) {
- avio_seek(s->pb, pos, SEEK_SET);
- if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE)
+ ((*ppos + ts->raw_packet_size - 1 - pos47) / ts->raw_packet_size) *
+ ts->raw_packet_size + pos47;
+ while(pos < pos_limit) {
+ if (avio_seek(s->pb, pos, SEEK_SET) < 0)
+ return AV_NOPTS_VALUE;
+ if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE)
+ return AV_NOPTS_VALUE;
+ if (buf[0] != 0x47) {
+ avio_seek(s->pb, -TS_PACKET_SIZE, SEEK_CUR);
+ if (mpegts_resync(s) < 0)
return AV_NOPTS_VALUE;
- if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) &&
- parse_pcr(&timestamp, &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(&timestamp, &pcr_l, buf) == 0) {
- break;
- }
+ if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) &&
+ parse_pcr(&timestamp, &pcr_l, buf) == 0) {
+ *ppos = pos;
+ return timestamp;
}
+ pos += ts->raw_packet_size;
}
- *ppos = pos;
- return timestamp;
+ return AV_NOPTS_VALUE;
}
-static int read_seek(AVFormatContext *s, int stream_index, int64_t target_ts, int flags)
+static int64_t mpegts_get_dts(AVFormatContext *s, int stream_index,
+ int64_t *ppos, int64_t pos_limit)
{
MpegTSContext *ts = s->priv_data;
- uint8_t buf[TS_PACKET_SIZE];
int64_t pos;
- int ret;
-
- ret = ff_seek_frame_binary(s, stream_index, target_ts, flags);
- if (ret < 0)
- return ret;
-
- pos = avio_tell(s->pb);
-
- for (;;) {
- avio_seek(s->pb, pos, SEEK_SET);
- ret = avio_read(s->pb, buf, TS_PACKET_SIZE);
+ int pos47 = ts->pos47_full % ts->raw_packet_size;
+ pos = ((*ppos + ts->raw_packet_size - 1 - pos47) / ts->raw_packet_size) * ts->raw_packet_size + pos47;
+ ff_read_frame_flush(s);
+ if (avio_seek(s->pb, pos, SEEK_SET) < 0)
+ return AV_NOPTS_VALUE;
+ while(pos < pos_limit) {
+ int ret;
+ AVPacket pkt;
+ av_init_packet(&pkt);
+ ret = av_read_frame(s, &pkt);
if (ret < 0)
- return ret;
- if (ret != TS_PACKET_SIZE)
- return AVERROR_EOF;
- // pid = AV_RB16(buf + 1) & 0x1fff;
- if (buf[1] & 0x40)
- break;
- pos += ts->raw_packet_size;
+ return AV_NOPTS_VALUE;
+ av_free_packet(&pkt);
+ if (pkt.dts != AV_NOPTS_VALUE && pkt.pos >= 0) {
+ ff_reduce_index(s, pkt.stream_index);
+ av_add_index_entry(s->streams[pkt.stream_index], pkt.pos, pkt.dts, 0, 0, AVINDEX_KEYFRAME /* FIXME keyframe? */);
+ if (pkt.stream_index == stream_index && pkt.pos >= *ppos) {
+ *ppos = pkt.pos;
+ return pkt.dts;
+ }
+ }
+ pos = pkt.pos;
}
- avio_seek(s->pb, pos, SEEK_SET);
- return 0;
+ return AV_NOPTS_VALUE;
}
/**************************************************************/
/* parsing functions - called from other demuxers such as RTP */
-MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s)
+MpegTSContext *avpriv_mpegts_parse_open(AVFormatContext *s)
{
MpegTSContext *ts;
@@ -2313,22 +2725,23 @@ MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s)
ts->raw_packet_size = TS_PACKET_SIZE;
ts->stream = s;
ts->auto_guess = 1;
+ mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1);
+ mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1);
+
return ts;
}
/* return the consumed length if a packet was output, or -1 if no
* packet is output */
-int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt,
- const uint8_t *buf, int len)
+int avpriv_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt,
+ const uint8_t *buf, int len)
{
int len1;
len1 = len;
ts->pkt = pkt;
- ts->stop_parse = 0;
for (;;) {
- if (ts->stop_parse > 0)
- break;
+ ts->stop_parse = 0;
if (len < TS_PACKET_SIZE)
return AVERROR_INVALIDDATA;
if (buf[0] != 0x47) {
@@ -2338,12 +2751,14 @@ int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt,
handle_packet(ts, buf);
buf += TS_PACKET_SIZE;
len -= TS_PACKET_SIZE;
+ if (ts->stop_parse == 1)
+ break;
}
}
return len1 - len;
}
-void ff_mpegts_parse_close(MpegTSContext *ts)
+void avpriv_mpegts_parse_close(MpegTSContext *ts)
{
mpegts_free(ts);
av_free(ts);
@@ -2357,8 +2772,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,
.priv_class = &mpegts_class,
};
@@ -2370,8 +2784,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 7dbbf8dfb0..84f30983c1 100644
--- a/libavformat/mpegts.h
+++ b/libavformat/mpegts.h
@@ -2,20 +2,20 @@
* MPEG2 transport stream defines
* Copyright (c) 2003 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -58,14 +58,15 @@
#define STREAM_TYPE_VIDEO_DIRAC 0xd1
#define STREAM_TYPE_AUDIO_AC3 0x81
-#define STREAM_TYPE_AUDIO_DTS 0x8a
+#define STREAM_TYPE_AUDIO_DTS 0x82
+#define STREAM_TYPE_AUDIO_TRUEHD 0x83
typedef struct MpegTSContext MpegTSContext;
-MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s);
-int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt,
- const uint8_t *buf, int len);
-void ff_mpegts_parse_close(MpegTSContext *ts);
+MpegTSContext *avpriv_mpegts_parse_open(AVFormatContext *s);
+int avpriv_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt,
+ const uint8_t *buf, int len);
+void avpriv_mpegts_parse_close(MpegTSContext *ts);
typedef struct SLConfigDescr {
int use_au_start;
@@ -105,4 +106,10 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
Mp4Descr *mp4_descr, int mp4_descr_count, int pid,
MpegTSContext *ts);
+/**
+ * Check presence of H264 startcode
+ * @return <0 to stop processing
+ */
+int ff_check_h264_startcode(AVFormatContext *s, const AVStream *st, const AVPacket *pkt);
+
#endif /* AVFORMAT_MPEGTS_H */
diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
index 1be4e55683..a56ca0eaa5 100644
--- a/libavformat/mpegtsenc.c
+++ b/libavformat/mpegtsenc.c
@@ -2,23 +2,24 @@
* MPEG2 transport stream (aka DVB) muxer
* Copyright (c) 2003 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/avassert.h"
#include "libavutil/bswap.h"
#include "libavutil/crc.h"
#include "libavutil/dict.h"
@@ -56,6 +57,16 @@ typedef struct MpegTSService {
int pcr_packet_period;
} MpegTSService;
+// service_type values as defined in ETSI 300 468
+enum {
+ MPEGTS_SERVICE_TYPE_DIGITAL_TV = 0x01,
+ MPEGTS_SERVICE_TYPE_DIGITAL_RADIO = 0x02,
+ MPEGTS_SERVICE_TYPE_TELETEXT = 0x03,
+ MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_RADIO = 0x0A,
+ MPEGTS_SERVICE_TYPE_MPEG2_DIGITAL_HDTV = 0x11,
+ MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_SDTV = 0x16,
+ MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_HDTV = 0x19
+};
typedef struct MpegTSWrite {
const AVClass *av_class;
MpegTSSection pat; /* MPEG2 pat table */
@@ -75,6 +86,7 @@ typedef struct MpegTSWrite {
int transport_stream_id;
int original_network_id;
int service_id;
+ int service_type;
int pmt_start_pid;
int start_pid;
@@ -86,6 +98,10 @@ typedef struct MpegTSWrite {
#define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01
#define MPEGTS_FLAG_AAC_LATM 0x02
int flags;
+ int copyts;
+ int tables_version;
+
+ int omit_video_pes_length;
} MpegTSWrite;
/* a PES packet header is generated every DEFAULT_PES_HEADER_FREQ packets */
@@ -184,7 +200,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 */
@@ -198,6 +214,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;
@@ -219,11 +236,11 @@ static void mpegts_write_pat(AVFormatContext *s)
put16(&q, service->sid);
put16(&q, 0xe000 | service->pmt.pid);
}
- mpegts_write_section1(&ts->pat, PAT_TID, ts->tsid, 0, 0, 0,
+ mpegts_write_section1(&ts->pat, PAT_TID, ts->tsid, ts->tables_version, 0, 0,
data, q - data);
}
-static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
+static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
{
MpegTSWrite *ts = s->priv_data;
uint8_t data[SECTION_LENGTH], *q, *desc_length_ptr, *program_info_length_ptr;
@@ -246,7 +263,7 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
MpegTSWriteStream *ts_st = st->priv_data;
AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0);
- if (q - data > SECTION_LENGTH - 3 - 2 - 6) {
+ if (q - data > SECTION_LENGTH - 32) {
err = 1;
break;
}
@@ -285,10 +302,17 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
case AV_CODEC_ID_AC3:
stream_type = STREAM_TYPE_AUDIO_AC3;
break;
+ case AV_CODEC_ID_DTS:
+ stream_type = STREAM_TYPE_AUDIO_DTS;
+ break;
+ case AV_CODEC_ID_TRUEHD:
+ stream_type = STREAM_TYPE_AUDIO_TRUEHD;
+ break;
default:
stream_type = STREAM_TYPE_PRIVATE_DATA;
break;
}
+
*q++ = stream_type;
put16(&q, 0xe000 | ts_st->pid);
desc_length_ptr = q;
@@ -297,6 +321,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;
@@ -337,26 +375,82 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
break;
case AVMEDIA_TYPE_SUBTITLE:
{
- const char *language;
- language = lang && strlen(lang->value) == 3 ? lang->value : "eng";
- *q++ = 0x59;
- *q++ = 8;
- *q++ = language[0];
- *q++ = language[1];
- *q++ = language[2];
- *q++ = 0x10; /* normal subtitles (0x20 = if hearing pb) */
-
- if (q - data > SECTION_LENGTH - 4) {
- err = 1;
- break;
- }
-
- if (st->codec->extradata_size == 4) {
- memcpy(q, st->codec->extradata, 4);
- q += 4;
- } else {
- put16(&q, 1); /* page id */
- put16(&q, 1); /* ancillary page id */
+ const char default_language[] = "und";
+ const char *language = lang && strlen(lang->value) >= 3 ? lang->value : default_language;
+
+ if (st->codec->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
+ uint8_t *len_ptr;
+ int extradata_copied = 0;
+
+ *q++ = 0x59; /* subtitling_descriptor */
+ len_ptr = q++;
+
+ while (strlen(language) >= 3) {
+ if (sizeof(data) - (q - data) < 8) { /* 8 bytes per DVB subtitle substream data */
+ err = 1;
+ break;
+ }
+ *q++ = *language++;
+ *q++ = *language++;
+ *q++ = *language++;
+ /* Skip comma */
+ if (*language != '\0')
+ language++;
+
+ if (st->codec->extradata_size - extradata_copied >= 5) {
+ *q++ = st->codec->extradata[extradata_copied + 4]; /* subtitling_type */
+ memcpy(q, st->codec->extradata + extradata_copied, 4); /* composition_page_id and ancillary_page_id */
+ extradata_copied += 5;
+ q += 4;
+ } else {
+ /* subtitling_type:
+ * 0x10 - normal with no monitor aspect ratio criticality
+ * 0x20 - for the hard of hearing with no monitor aspect ratio criticality */
+ *q++ = (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED) ? 0x20 : 0x10;
+ if ((st->codec->extradata_size == 4) && (extradata_copied == 0)) {
+ /* support of old 4-byte extradata format */
+ memcpy(q, st->codec->extradata, 4); /* composition_page_id and ancillary_page_id */
+ extradata_copied += 4;
+ q += 4;
+ } else {
+ put16(&q, 1); /* composition_page_id */
+ put16(&q, 1); /* ancillary_page_id */
+ }
+ }
+ }
+
+ *len_ptr = q - len_ptr - 1;
+ } else if (st->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) {
+ uint8_t *len_ptr = NULL;
+ int extradata_copied = 0;
+
+ /* The descriptor tag. teletext_descriptor */
+ *q++ = 0x56;
+ len_ptr = q++;
+
+ while (strlen(language) >= 3 && q - data < sizeof(data) - 6) {
+ *q++ = *language++;
+ *q++ = *language++;
+ *q++ = *language++;
+ /* Skip comma */
+ if (*language != '\0')
+ language++;
+
+ if (st->codec->extradata_size - 1 > extradata_copied) {
+ memcpy(q, st->codec->extradata + extradata_copied, 2);
+ extradata_copied += 2;
+ q += 2;
+ } else {
+ /* The Teletext descriptor:
+ * teletext_type: This 5-bit field indicates the type of Teletext page indicated. (0x01 Initial Teletext page)
+ * teletext_magazine_number: This is a 3-bit field which identifies the magazine number.
+ * teletext_page_number: This is an 8-bit field giving two 4-bit hex digits identifying the page number. */
+ *q++ = 0x08;
+ *q++ = 0x00;
+ }
+ }
+
+ *len_ptr = q - len_ptr - 1;
}
}
break;
@@ -370,6 +464,16 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
*q++ = 'c';
}
break;
+ case AVMEDIA_TYPE_DATA:
+ if (st->codec->codec_id == AV_CODEC_ID_SMPTE_KLV) {
+ *q++ = 0x05; /* MPEG-2 registration descriptor */
+ *q++ = 4;
+ *q++ = 'K';
+ *q++ = 'L';
+ *q++ = 'V';
+ *q++ = 'A';
+ }
+ break;
}
val = 0xf000 | (q - desc_length_ptr - 2);
@@ -383,11 +487,12 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
"Try reducing the number of languages in the audio streams "
"or the total number of streams.\n", i);
- mpegts_write_section1(&service->pmt, PMT_TID, service->sid, 0, 0, 0,
+ mpegts_write_section1(&service->pmt, PMT_TID, service->sid, ts->tables_version, 0, 0,
data, q - data);
+ return 0;
}
-/* NOTE: str == NULL is accepted for an empty string */
+/* NOTE: !str is accepted for an empty string */
static void putstr8(uint8_t **q_ptr, const char *str)
{
uint8_t *q;
@@ -427,7 +532,7 @@ static void mpegts_write_sdt(AVFormatContext *s)
*q++ = 0x48;
desc_len_ptr = q;
q++;
- *q++ = 0x01; /* digital television service */
+ *q++ = ts->service_type;
putstr8(&q, service->provider_name);
putstr8(&q, service->name);
desc_len_ptr[0] = q - desc_len_ptr - 1;
@@ -438,7 +543,7 @@ static void mpegts_write_sdt(AVFormatContext *s)
desc_list_len_ptr[0] = val >> 8;
desc_list_len_ptr[1] = val;
}
- mpegts_write_section1(&ts->sdt, SDT_TID, ts->tsid, 0, 0, 0,
+ mpegts_write_section1(&ts->sdt, SDT_TID, ts->tsid, ts->tables_version, 0, 0,
data, q - data);
}
@@ -456,14 +561,17 @@ static MpegTSService *mpegts_add_service(MpegTSWrite *ts, int sid,
service->pcr_pid = 0x1fff;
service->provider_name = av_strdup(provider_name);
service->name = av_strdup(name);
- if (!service->provider_name || !service->name) {
- av_free(service->provider_name);
- av_free(service->name);
- av_free(service);
- return NULL;
- }
- dynarray_add(&ts->services, &ts->nb_services, service);
+ if (!service->provider_name || !service->name)
+ goto fail;
+ if (av_dynarray_add_nofree(&ts->services, &ts->nb_services, service) < 0)
+ goto fail;
+
return service;
+fail:
+ av_freep(&service->provider_name);
+ av_freep(&service->name);
+ av_free(service);
+ return NULL;
}
static int64_t get_pcr(const MpegTSWrite *ts, AVIOContext *pb)
@@ -541,10 +649,10 @@ static int mpegts_write_header(AVFormatContext *s)
ts->sdt.write_packet = section_write_packet;
ts->sdt.opaque = s;
- pids = av_malloc(s->nb_streams * sizeof(*pids));
+ pids = av_malloc_array(s->nb_streams, sizeof(*pids));
if (!pids) {
- av_free(service);
- return AVERROR(ENOMEM);
+ ret = AVERROR(ENOMEM);
+ goto fail;
}
/* assign pids to each stream */
@@ -631,7 +739,7 @@ static int mpegts_write_header(AVFormatContext *s)
}
}
- av_free(pids);
+ av_freep(&pids);
/* if no video stream, use the first stream as PCR */
if (service->pcr_pid == 0x1fff && s->nb_streams > 0) {
@@ -649,9 +757,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) {
@@ -669,6 +778,8 @@ static int mpegts_write_header(AVFormatContext *s)
service->pcr_packet_period =
ts_st->user_tb.den / (10 * ts_st->user_tb.num);
}
+ if (!service->pcr_packet_period)
+ service->pcr_packet_period = 1;
}
// output a PCR as soon as possible
@@ -693,13 +804,10 @@ static int mpegts_write_header(AVFormatContext *s)
}
}
- avio_flush(s->pb);
-
return 0;
fail:
- av_free(service);
- av_free(pids);
+ av_freep(&pids);
for (i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
ts_st = st->priv_data;
@@ -712,11 +820,19 @@ fail:
}
av_freep(&st->priv_data);
}
+
+ for (i = 0; i < ts->nb_services; i++) {
+ service = ts->services[i];
+ av_freep(&service->provider_name);
+ av_freep(&service->name);
+ av_freep(&service);
+ }
+ av_freep(&ts->services);
return ret;
}
/* send SDT, PAT and PMT tables regulary */
-static void retransmit_si_info(AVFormatContext *s)
+static void retransmit_si_info(AVFormatContext *s, int force_pat)
{
MpegTSWrite *ts = s->priv_data;
int i;
@@ -725,7 +841,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++)
@@ -807,7 +923,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
@@ -823,7 +939,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;
}
@@ -848,14 +964,16 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
MpegTSWrite *ts = s->priv_data;
uint8_t buf[TS_PACKET_SIZE];
uint8_t *q;
- int val, is_start, len, header_len, write_pcr, private_code, flags;
+ int val, is_start, len, header_len, write_pcr, is_dvb_subtitle, is_dvb_teletext, flags;
int afc_len, stuffing_len;
int64_t pcr = -1; /* avoid warning */
int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE);
+ int force_pat = st->codec->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key;
is_start = 1;
while (payload_size > 0) {
- retransmit_si_info(s);
+ retransmit_si_info(s, force_pat);
+ force_pat = 0;
write_pcr = 0;
if (ts_st->pid == ts_st->service->pcr_pid) {
@@ -911,11 +1029,13 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
}
if (is_start) {
int pes_extension = 0;
+ int pes_header_stuffing_bytes = 0;
/* write PES header */
*q++ = 0x00;
*q++ = 0x00;
*q++ = 0x01;
- private_code = 0;
+ is_dvb_subtitle = 0;
+ is_dvb_teletext = 0;
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
if (st->codec->codec_id == AV_CODEC_ID_DIRAC)
*q++ = 0xfd;
@@ -926,10 +1046,19 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
st->codec->codec_id == AV_CODEC_ID_MP3 ||
st->codec->codec_id == AV_CODEC_ID_AAC)) {
*q++ = 0xc0;
+ } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
+ st->codec->codec_id == AV_CODEC_ID_AC3 &&
+ ts->m2ts_mode) {
+ *q++ = 0xfd;
} else {
*q++ = 0xbd;
- if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE)
- private_code = 0x20;
+ if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+ if (st->codec->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
+ is_dvb_subtitle = 1;
+ } else if (st->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) {
+ is_dvb_teletext = 1;
+ }
+ }
}
header_len = 0;
flags = 0;
@@ -952,16 +1081,37 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
* one byte for extension id */
header_len += 3;
}
+ /* for Blu-ray AC3 Audio the PES Extension flag should be as follow
+ * otherwise it will not play sound on blu-ray
+ */
+ if (ts->m2ts_mode &&
+ st->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
+ st->codec->codec_id == AV_CODEC_ID_AC3) {
+ /* set PES_extension_flag */
+ pes_extension = 1;
+ flags |= 0x01;
+ header_len += 3;
+ }
+ if (is_dvb_teletext) {
+ pes_header_stuffing_bytes = 0x24 - header_len;
+ header_len = 0x24;
+ }
len = payload_size + header_len + 3;
- if (private_code != 0)
- len++;
+ /* 3 extra bytes should be added to DVB subtitle payload: 0x20 0x00 at the beginning and trailing 0xff */
+ if (is_dvb_subtitle) {
+ len += 3;
+ payload_size++;
+ }
if (len > 0xffff)
len = 0;
+ if (ts->omit_video_pes_length && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ len = 0;
+ }
*q++ = len >> 8;
*q++ = len;
val = 0x80;
- /* data alignment indicator is required for subtitle data */
- if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE)
+ /* data alignment indicator is required for subtitle and data streams */
+ if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE || st->codec->codec_type == AVMEDIA_TYPE_DATA)
val |= 0x04;
*q++ = val;
*q++ = flags;
@@ -982,8 +1132,28 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
* write the extended stream ID. */
*q++ = 0x00 | 0x60;
}
- if (private_code != 0)
- *q++ = private_code;
+ /* For Blu-ray AC3 Audio Setting extended flags */
+ if (ts->m2ts_mode &&
+ pes_extension &&
+ st->codec->codec_id == AV_CODEC_ID_AC3) {
+ flags = 0x01; /* set PES_extension_flag_2 */
+ *q++ = flags;
+ *q++ = 0x80 | 0x01; /* marker bit + extension length */
+ *q++ = 0x00 | 0x71; /* for AC3 Audio (specifically on blue-rays) */
+ }
+
+
+ if (is_dvb_subtitle) {
+ /* First two fields of DVB subtitles PES data:
+ * data_identifier: for DVB subtitle streams shall be coded with the value 0x20
+ * subtitle_stream_id: for DVB subtitle stream shall be identified by the value 0x00 */
+ *q++ = 0x20;
+ *q++ = 0x00;
+ }
+ if (is_dvb_teletext) {
+ memset(q, 0xff, pes_header_stuffing_bytes);
+ q += pes_header_stuffing_bytes;
+ }
is_start = 0;
}
/* header size */
@@ -1014,13 +1184,50 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
}
}
}
- memcpy(buf + TS_PACKET_SIZE - len, payload, len);
+
+ if (is_dvb_subtitle && payload_size == len) {
+ memcpy(buf + TS_PACKET_SIZE - len, payload, len - 1);
+ buf[TS_PACKET_SIZE - 1] = 0xff; /* end_of_PES_data_field_marker: an 8-bit field with fixed contents 0xff for DVB subtitle */
+ } else {
+ memcpy(buf + TS_PACKET_SIZE - len, payload, len);
+ }
+
payload += len;
payload_size -= len;
mpegts_prefix_m2ts_header(s);
avio_write(s->pb, buf, TS_PACKET_SIZE);
}
- avio_flush(s->pb);
+ ts_st->prev_payload_key = key;
+}
+
+int ff_check_h264_startcode(AVFormatContext *s, const AVStream *st, const AVPacket *pkt)
+{
+ if (pkt->size < 5 || AV_RB32(pkt->data) != 0x0000001) {
+ if (!st->nb_frames) {
+ av_log(s, AV_LOG_ERROR, "H.264 bitstream malformed, "
+ "no startcode found, use the video bitstream filter 'h264_mp4toannexb' to fix it "
+ "('-bsf:v h264_mp4toannexb' option with ffmpeg)\n");
+ return AVERROR_INVALIDDATA;
+ }
+ av_log(s, AV_LOG_WARNING, "H.264 bitstream error, startcode missing, size %d", pkt->size);
+ if (pkt->size) av_log(s, AV_LOG_WARNING, " data %08X", AV_RB32(pkt->data));
+ av_log(s, AV_LOG_WARNING, "\n");
+ }
+ return 0;
+}
+
+static int check_hevc_startcode(AVFormatContext *s, const AVStream *st, const AVPacket *pkt)
+{
+ if (pkt->size < 5 || AV_RB32(pkt->data) != 0x0000001) {
+ if (!st->nb_frames) {
+ av_log(s, AV_LOG_ERROR, "HEVC bitstream malformed, no startcode found\n");
+ return AVERROR_PATCHWELCOME;
+ }
+ av_log(s, AV_LOG_WARNING, "HEVC bitstream error, startcode missing, size %d", pkt->size);
+ if (pkt->size) av_log(s, AV_LOG_WARNING, " data %08X", AV_RB32(pkt->data));
+ av_log(s, AV_LOG_WARNING, "\n");
+ }
+ return 0;
}
static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
@@ -1031,8 +1238,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,
@@ -1047,13 +1254,15 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
ts->flags &= ~MPEGTS_FLAG_REEMIT_PAT_PMT;
}
- if (pkt->pts != AV_NOPTS_VALUE)
- pts = pkt->pts + delay;
- if (pkt->dts != AV_NOPTS_VALUE)
- dts = pkt->dts + delay;
+ if (ts->copyts < 1) {
+ if (pts != AV_NOPTS_VALUE)
+ pts += delay;
+ if (dts != AV_NOPTS_VALUE)
+ dts += delay;
+ }
if (ts_st->first_pts_check && pts == AV_NOPTS_VALUE) {
- av_log(s, AV_LOG_ERROR, "first pts value must set\n");
+ av_log(s, AV_LOG_ERROR, "first pts value must be set\n");
return AVERROR_INVALIDDATA;
}
ts_st->first_pts_check = 0;
@@ -1061,29 +1270,35 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
if (st->codec->codec_id == AV_CODEC_ID_H264) {
const uint8_t *p = buf, *buf_end = p + size;
uint32_t state = -1;
+ int extradd = (pkt->flags & AV_PKT_FLAG_KEY) ? st->codec->extradata_size : 0;
+ int ret = ff_check_h264_startcode(s, st, pkt);
+ if (ret < 0)
+ return ret;
- if (pkt->size < 5 || AV_RB32(pkt->data) != 0x0000001) {
- av_log(s, AV_LOG_ERROR, "H.264 bitstream malformed, "
- "no startcode found, use -bsf h264_mp4toannexb\n");
- return AVERROR_INVALIDDATA;
- }
+ if (extradd && AV_RB24(st->codec->extradata) > 1)
+ extradd = 0;
do {
p = avpriv_find_start_code(p, buf_end, &state);
av_dlog(s, "nal %d\n", state & 0x1f);
+ if ((state & 0x1f) == 7)
+ extradd = 0;
} while (p < buf_end && (state & 0x1f) != 9 &&
(state & 0x1f) != 5 && (state & 0x1f) != 1);
+ if ((state & 0x1f) != 5)
+ extradd = 0;
if ((state & 0x1f) != 9) { // AUD NAL
- data = av_malloc(pkt->size + 6);
+ data = av_malloc(pkt->size + 6 + extradd);
if (!data)
return AVERROR(ENOMEM);
- memcpy(data + 6, pkt->data, pkt->size);
+ memcpy(data + 6, st->codec->extradata, extradd);
+ memcpy(data + 6 + extradd, pkt->data, pkt->size);
AV_WB32(data, 0x00000001);
data[4] = 0x09;
data[5] = 0xf0; // any slice type (0xe) + rbsp stop one bit
buf = data;
- size = pkt->size + 6;
+ size = pkt->size + 6 + extradd;
}
} else if (st->codec->codec_id == AV_CODEC_ID_AAC) {
if (pkt->size < 2) {
@@ -1103,6 +1318,8 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
av_init_packet(&pkt2);
pkt2.data = pkt->data;
pkt2.size = pkt->size;
+ av_assert0(pkt->dts != AV_NOPTS_VALUE);
+ pkt2.dts = av_rescale_q(pkt->dts, st->time_base, ts_st->amux->streams[0]->time_base);
ret = avio_open_dyn_buf(&ts_st->amux->pb);
if (ret < 0)
@@ -1119,9 +1336,36 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
ts_st->amux->pb = NULL;
buf = data;
}
+ } else if (st->codec->codec_id == AV_CODEC_ID_HEVC) {
+ int ret = check_hevc_startcode(s, st, pkt);
+ if (ret < 0)
+ return ret;
}
- if (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);
@@ -1129,21 +1373,6 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
return 0;
}
- if (ts_st->payload_size + size > ts->pes_payload_size) {
- if (ts_st->payload_size) {
- mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_size,
- ts_st->payload_pts, ts_st->payload_dts,
- ts_st->payload_flags & AV_PKT_FLAG_KEY);
- ts_st->payload_size = 0;
- }
- if (size > ts->pes_payload_size) {
- mpegts_write_pes(s, st, buf, size, pts, dts,
- pkt->flags & AV_PKT_FLAG_KEY);
- av_free(data);
- return 0;
- }
- }
-
if (!ts_st->payload_size) {
ts_st->payload_pts = pts;
ts_st->payload_dts = dts;
@@ -1173,7 +1402,6 @@ static void mpegts_write_flush(AVFormatContext *s)
ts_st->payload_size = 0;
}
}
- avio_flush(s->pb);
}
static int mpegts_write_packet(AVFormatContext *s, AVPacket *pkt)
@@ -1209,9 +1437,9 @@ static int mpegts_write_end(AVFormatContext *s)
service = ts->services[i];
av_freep(&service->provider_name);
av_freep(&service->name);
- av_free(service);
+ av_freep(&service);
}
- av_free(ts->services);
+ av_freep(&ts->services);
return 0;
}
@@ -1226,15 +1454,39 @@ 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_service_type", "Set service_type field.",
+ offsetof(MpegTSWrite, service_type), AV_OPT_TYPE_INT,
+ { .i64 = 0x01 }, 0x01, 0xff, AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ { "digital_tv", "Digital Television.",
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_TV }, 0x01, 0xff,
+ AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ { "digital_radio", "Digital Radio.",
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_RADIO }, 0x01, 0xff,
+ AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ { "teletext", "Teletext.",
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_TELETEXT }, 0x01, 0xff,
+ AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ { "advanced_codec_digital_radio", "Advanced Codec Digital Radio.",
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_RADIO }, 0x01, 0xff,
+ AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ { "mpeg2_digital_hdtv", "MPEG2 Digital HDTV.",
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_MPEG2_DIGITAL_HDTV }, 0x01, 0xff,
+ AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ { "advanced_codec_digital_sdtv", "Advanced Codec Digital SDTV.",
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_SDTV }, 0x01, 0xff,
+ AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ { "advanced_codec_digital_hdtv", "Advanced Codec Digital HDTV.",
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_HDTV }, 0x01, 0xff,
+ AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
{ "mpegts_pmt_start_pid", "Set the first pid of the PMT.",
offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT,
- { .i64 = 0x1000 }, 0x1000, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM },
+ { .i64 = 0x1000 }, 0x0010, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM },
{ "mpegts_start_pid", "Set the first pid.",
offsetof(MpegTSWrite, start_pid), AV_OPT_TYPE_INT,
- { .i64 = 0x0100 }, 0x0100, 0x0f00, AV_OPT_FLAG_ENCODING_PARAM },
- {"mpegts_m2ts_mode", "Enable m2ts mode.",
+ { .i64 = 0x0100 }, 0x0020, 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},
+ { .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 },
@@ -1254,6 +1506,15 @@ static const AVOption options[] = {
{ "resend_headers", "Reemit PAT/PMT before writing the next packet",
offsetof(MpegTSWrite, reemit_pat_pmt), AV_OPT_TYPE_INT,
{ .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "mpegts_copyts", "don't offset dts/pts",
+ offsetof(MpegTSWrite, copyts), AV_OPT_TYPE_INT,
+ { .i64 = -1 }, -1, 1, AV_OPT_FLAG_ENCODING_PARAM },
+ { "tables_version", "set PAT, PMT and SDT version",
+ offsetof(MpegTSWrite, tables_version), AV_OPT_TYPE_INT,
+ { .i64 = 0 }, 0, 31, AV_OPT_FLAG_ENCODING_PARAM },
+ { "omit_video_pes_length", "Omit the PES packet length for video packets",
+ offsetof(MpegTSWrite, omit_video_pes_length), AV_OPT_TYPE_INT,
+ { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
{ "pcr_period", "PCR retransmission time",
offsetof(MpegTSWrite, pcr_period), AV_OPT_TYPE_INT,
{ .i64 = PCR_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
diff --git a/libavformat/mpegvideodec.c b/libavformat/mpegvideodec.c
index 96b95a58f1..ade76d860e 100644
--- a/libavformat/mpegvideodec.c
+++ b/libavformat/mpegvideodec.c
@@ -3,26 +3,28 @@
* Copyright (c) 2002-2003 Fabrice Bellard
* Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "avformat.h"
#include "rawdec.h"
+#include "libavutil/intreadwrite.h"
+
#define SEQ_START_CODE 0x000001b3
#define GOP_START_CODE 0x000001b8
#define PICTURE_START_CODE 0x00000100
@@ -34,24 +36,53 @@
static int mpegvideo_probe(AVProbeData *p)
{
uint32_t code= -1;
- int pic=0, seq=0, slice=0, pspack=0, pes=0;
- int i;
+ int pic=0, seq=0, slice=0, pspack=0, vpes=0, apes=0, res=0, sicle=0;
+ int i, j;
+ uint32_t last = 0;
for(i=0; i<p->buf_size; i++){
code = (code<<8) + p->buf[i];
if ((code & 0xffffff00) == 0x100) {
switch(code){
- case SEQ_START_CODE: seq++; break;
+ case SEQ_START_CODE:
+ if (!(p->buf[i+1+3+1+2] & 0x20))
+ break;
+ j = i;
+ if (p->buf[j+8] & 2)
+ j+= 64;
+ if (j >= p->buf_size)
+ break;
+ if (p->buf[j+8] & 1)
+ j+= 64;
+ if (j >= p->buf_size)
+ break;
+ if (AV_RB24(p->buf + j + 9) & 0xFFFFFE)
+ break;
+ seq++;
+ break;
case PICTURE_START_CODE: pic++; break;
- case SLICE_START_CODE: slice++; break;
case PACK_START_CODE: pspack++; break;
+ case 0x1b6:
+ res++; break;
+ }
+ if (code >= SLICE_START_CODE && code <= 0x1af) {
+ if (last >= SLICE_START_CODE && last <= 0x1af) {
+ if (code >= last) slice++;
+ else sicle++;
+ }else{
+ if (code == SLICE_START_CODE) slice++;
+ else sicle++;
+ }
}
- if ((code & 0x1f0) == VIDEO_ID) pes++;
- else if((code & 0x1e0) == AUDIO_ID) pes++;
+ if ((code & 0x1f0) == VIDEO_ID) vpes++;
+ else if((code & 0x1e0) == AUDIO_ID) apes++;
+ last = code;
}
}
- if(seq && seq*9<=pic*10 && pic*9<=slice*10 && !pspack && !pes)
- return pic>1 ? AVPROBE_SCORE_EXTENSION + 1 : AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg
+ if(seq && seq*9<=pic*10 && pic*9<=slice*10 && !pspack && !apes && !res && slice > sicle) {
+ if(vpes) return AVPROBE_SCORE_EXTENSION / 4;
+ else return pic>1 ? AVPROBE_SCORE_EXTENSION + 1 : AVPROBE_SCORE_EXTENSION / 2; // +1 for .mpg
+ }
return 0;
}
diff --git a/libavformat/mpjpeg.c b/libavformat/mpjpeg.c
index 9e21099eaf..7b975e28d6 100644
--- a/libavformat/mpjpeg.c
+++ b/libavformat/mpjpeg.c
@@ -2,33 +2,40 @@
* 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 "libavutil/opt.h"
#include "avformat.h"
/* Multipart JPEG */
-#define BOUNDARY_TAG "avserver"
+#define BOUNDARY_TAG "ffserver"
+
+typedef struct MPJPEGContext {
+ AVClass *class;
+ char *boundary_tag;
+} MPJPEGContext;
static int mpjpeg_write_header(AVFormatContext *s)
{
+ MPJPEGContext *mpj = s->priv_data;
uint8_t buf1[256];
- snprintf(buf1, sizeof(buf1), "--%s\n", BOUNDARY_TAG);
+ snprintf(buf1, sizeof(buf1), "--%s\r\n", mpj->boundary_tag);
avio_write(s->pb, buf1, strlen(buf1));
avio_flush(s->pb);
return 0;
@@ -36,13 +43,17 @@ static int mpjpeg_write_header(AVFormatContext *s)
static int mpjpeg_write_packet(AVFormatContext *s, AVPacket *pkt)
{
+ MPJPEGContext *mpj = s->priv_data;
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", mpj->boundary_tag);
avio_write(s->pb, buf1, strlen(buf1));
return 0;
}
@@ -52,15 +63,29 @@ static int mpjpeg_write_trailer(AVFormatContext *s)
return 0;
}
+static const AVOption options[] = {
+ { "boundary_tag", "Boundary tag", offsetof(MPJPEGContext, boundary_tag), AV_OPT_TYPE_STRING, {.str = BOUNDARY_TAG}, .flags = AV_OPT_FLAG_ENCODING_PARAM },
+ { NULL },
+};
+
+static const AVClass mpjpeg_muxer_class = {
+ .class_name = "mpjpeg_muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVOutputFormat ff_mpjpeg_muxer = {
.name = "mpjpeg",
.long_name = NULL_IF_CONFIG_SMALL("MIME multipart JPEG"),
.mime_type = "multipart/x-mixed-replace;boundary=" BOUNDARY_TAG,
.extensions = "mjpg",
+ .priv_data_size = sizeof(MPJPEGContext),
.audio_codec = AV_CODEC_ID_NONE,
.video_codec = AV_CODEC_ID_MJPEG,
.write_header = mpjpeg_write_header,
.write_packet = mpjpeg_write_packet,
.write_trailer = mpjpeg_write_trailer,
.flags = AVFMT_NOTIMESTAMPS,
+ .priv_class = &mpjpeg_muxer_class,
};
diff --git a/libavformat/mpl2dec.c b/libavformat/mpl2dec.c
new file mode 100644
index 0000000000..260b7be0ec
--- /dev/null
+++ b/libavformat/mpl2dec.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2012 Clément Bœsch
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * MPL2 subtitles format demuxer
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "subtitles.h"
+
+typedef struct {
+ FFDemuxSubtitlesQueue q;
+} MPL2Context;
+
+static int mpl2_probe(AVProbeData *p)
+{
+ int i;
+ char c;
+ int64_t start, end;
+ const unsigned char *ptr = p->buf;
+ const unsigned char *ptr_end = ptr + p->buf_size;
+
+ for (i = 0; i < 2; i++) {
+ if (sscanf(ptr, "[%"SCNd64"][%"SCNd64"]%c", &start, &end, &c) != 3 &&
+ sscanf(ptr, "[%"SCNd64"][]%c", &start, &c) != 2)
+ return 0;
+ ptr += ff_subtitles_next_line(ptr);
+ if (ptr >= ptr_end)
+ return 0;
+ }
+ return AVPROBE_SCORE_MAX;
+}
+
+static int read_ts(char **line, int64_t *pts_start, int *duration)
+{
+ char c;
+ int len;
+ int64_t end;
+
+ if (sscanf(*line, "[%"SCNd64"][]%c%n",
+ pts_start, &c, &len) >= 2) {
+ *duration = -1;
+ *line += len - 1;
+ return 0;
+ }
+ if (sscanf(*line, "[%"SCNd64"][%"SCNd64"]%c%n",
+ pts_start, &end, &c, &len) >= 3) {
+ *duration = end - *pts_start;
+ *line += len - 1;
+ return 0;
+ }
+ return -1;
+}
+
+static int mpl2_read_header(AVFormatContext *s)
+{
+ MPL2Context *mpl2 = s->priv_data;
+ AVStream *st = avformat_new_stream(s, NULL);
+ int res = 0;
+
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 64, 1, 10);
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_MPL2;
+
+ while (!avio_feof(s->pb)) {
+ char line[4096];
+ char *p = line;
+ const int64_t pos = avio_tell(s->pb);
+ int len = ff_get_line(s->pb, line, sizeof(line));
+ int64_t pts_start;
+ int duration;
+
+ if (!len)
+ break;
+
+ line[strcspn(line, "\r\n")] = 0;
+
+ if (!read_ts(&p, &pts_start, &duration)) {
+ AVPacket *sub;
+
+ sub = ff_subtitles_queue_insert(&mpl2->q, p, strlen(p), 0);
+ if (!sub)
+ return AVERROR(ENOMEM);
+ sub->pos = pos;
+ sub->pts = pts_start;
+ sub->duration = duration;
+ }
+ }
+
+ ff_subtitles_queue_finalize(&mpl2->q);
+ return res;
+}
+
+static int mpl2_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ MPL2Context *mpl2 = s->priv_data;
+ return ff_subtitles_queue_read_packet(&mpl2->q, pkt);
+}
+
+static int mpl2_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ MPL2Context *mpl2 = s->priv_data;
+ return ff_subtitles_queue_seek(&mpl2->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int mpl2_read_close(AVFormatContext *s)
+{
+ MPL2Context *mpl2 = s->priv_data;
+ ff_subtitles_queue_clean(&mpl2->q);
+ return 0;
+}
+
+AVInputFormat ff_mpl2_demuxer = {
+ .name = "mpl2",
+ .long_name = NULL_IF_CONFIG_SMALL("MPL2 subtitles"),
+ .priv_data_size = sizeof(MPL2Context),
+ .read_probe = mpl2_probe,
+ .read_header = mpl2_read_header,
+ .read_packet = mpl2_read_packet,
+ .read_seek2 = mpl2_read_seek,
+ .read_close = mpl2_read_close,
+ .extensions = "txt,mpl2",
+};
diff --git a/libavformat/mpsubdec.c b/libavformat/mpsubdec.c
new file mode 100644
index 0000000000..eddc594067
--- /dev/null
+++ b/libavformat/mpsubdec.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2012 Clément Bœsch
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * MPlayer subtitles format demuxer
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "subtitles.h"
+
+typedef struct {
+ FFDemuxSubtitlesQueue q;
+} MPSubContext;
+
+static int mpsub_probe(AVProbeData *p)
+{
+ const char *ptr = p->buf;
+ const char *ptr_end = p->buf + p->buf_size;
+
+ while (ptr < ptr_end) {
+ int inc;
+
+ if (!memcmp(ptr, "FORMAT=TIME", 11))
+ return AVPROBE_SCORE_EXTENSION;
+ if (!memcmp(ptr, "FORMAT=", 7))
+ return AVPROBE_SCORE_EXTENSION / 3;
+ inc = ff_subtitles_next_line(ptr);
+ if (!inc)
+ break;
+ ptr += inc;
+ }
+ return 0;
+}
+
+static int mpsub_read_header(AVFormatContext *s)
+{
+ MPSubContext *mpsub = s->priv_data;
+ AVStream *st;
+ AVBPrint buf;
+ AVRational pts_info = (AVRational){ 100, 1 }; // ts based by default
+ int res = 0;
+ float multiplier = 100.0;
+ float current_pts = 0;
+
+ av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+ while (!avio_feof(s->pb)) {
+ char line[1024];
+ float start, duration;
+ int fps, len = ff_get_line(s->pb, line, sizeof(line));
+
+ if (!len)
+ break;
+
+ line[strcspn(line, "\r\n")] = 0;
+
+ if (sscanf(line, "FORMAT=%d", &fps) == 1 && fps > 3 && fps < 100) {
+ /* frame based timing */
+ pts_info = (AVRational){ fps, 1 };
+ multiplier = 1.0;
+ } else if (sscanf(line, "%f %f", &start, &duration) == 2) {
+ AVPacket *sub;
+ const int64_t pos = avio_tell(s->pb);
+
+ ff_subtitles_read_chunk(s->pb, &buf);
+ if (buf.len) {
+ sub = ff_subtitles_queue_insert(&mpsub->q, buf.str, buf.len, 0);
+ if (!sub) {
+ res = AVERROR(ENOMEM);
+ goto end;
+ }
+ sub->pts = (int64_t)(current_pts + start*multiplier);
+ sub->duration = (int)(duration * multiplier);
+ current_pts += (start + duration) * multiplier;
+ sub->pos = pos;
+ }
+ }
+ }
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 64, pts_info.den, pts_info.num);
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_TEXT;
+
+ ff_subtitles_queue_finalize(&mpsub->q);
+
+end:
+ av_bprint_finalize(&buf, NULL);
+ return res;
+}
+
+static int mpsub_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ MPSubContext *mpsub = s->priv_data;
+ return ff_subtitles_queue_read_packet(&mpsub->q, pkt);
+}
+
+static int mpsub_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ MPSubContext *mpsub = s->priv_data;
+ return ff_subtitles_queue_seek(&mpsub->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int mpsub_read_close(AVFormatContext *s)
+{
+ MPSubContext *mpsub = s->priv_data;
+ ff_subtitles_queue_clean(&mpsub->q);
+ return 0;
+}
+
+AVInputFormat ff_mpsub_demuxer = {
+ .name = "mpsub",
+ .long_name = NULL_IF_CONFIG_SMALL("MPlayer subtitles"),
+ .priv_data_size = sizeof(MPSubContext),
+ .read_probe = mpsub_probe,
+ .read_header = mpsub_read_header,
+ .read_packet = mpsub_read_packet,
+ .read_seek2 = mpsub_read_seek,
+ .read_close = mpsub_read_close,
+ .extensions = "sub",
+};
diff --git a/libavformat/msnwc_tcp.c b/libavformat/msnwc_tcp.c
index b6d30feb9d..60225af61c 100644
--- a/libavformat/msnwc_tcp.c
+++ b/libavformat/msnwc_tcp.c
@@ -1,20 +1,20 @@
/*
* Copyright (C) 2008 Ramiro Polla
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -89,10 +89,10 @@ static int msnwc_tcp_read_header(AVFormatContext *ctx)
/* Some files start with "connected\r\n\r\n".
* So skip until we find the first byte of struct size */
- while(avio_r8(pb) != HEADER_SIZE && !pb->eof_reached);
+ while(avio_r8(pb) != HEADER_SIZE && !avio_feof(pb));
- if(pb->eof_reached) {
- av_log(ctx, AV_LOG_ERROR, "Could not find valid start.");
+ if(avio_feof(pb)) {
+ av_log(ctx, AV_LOG_ERROR, "Could not find valid start.\n");
return -1;
}
diff --git a/libavformat/mtv.c b/libavformat/mtv.c
index aad30d7a41..a91e4c8649 100644
--- a/libavformat/mtv.c
+++ b/libavformat/mtv.c
@@ -2,20 +2,20 @@
* mtv demuxer
* Copyright (c) 2006 Reynaldo H. Verdejo Pinochet
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -32,7 +32,8 @@
#define MTV_ASUBCHUNK_DATA_SIZE 500
#define MTV_HEADER_SIZE 512
#define MTV_AUDIO_PADDING_SIZE 12
-#define AUDIO_SAMPLING_RATE 44100
+#define MTV_IMAGE_DEFAULT_BPP 16
+#define MTV_AUDIO_SAMPLING_RATE 44100
typedef struct MTVDemuxContext {
@@ -42,20 +43,30 @@ typedef struct MTVDemuxContext {
unsigned int audio_br; ///< bitrate of audio channel (mp3)
unsigned int img_colorfmt; ///< frame colorfmt rgb 565/555
unsigned int img_bpp; ///< frame bits per pixel
- unsigned int img_width; //
- unsigned int img_height; //
+ unsigned int img_width;
+ unsigned int img_height;
unsigned int img_segment_size; ///< size of image segment
- unsigned int video_fps; //
+ unsigned int video_fps;
unsigned int full_segment_size;
} MTVDemuxContext;
static int mtv_probe(AVProbeData *p)
{
+ /* we need at least 57 bytes from the header
+ * to try parsing all required fields
+ */
+ if (p->buf_size < 57)
+ return 0;
+
/* Magic is 'AMV' */
if (*p->buf != 'A' || *(p->buf + 1) != 'M' || *(p->buf + 2) != 'V')
return 0;
+ /* Audio magic is always MP3 */
+ if (p->buf[43] != 'M' || p->buf[44] != 'P' || p->buf[45] != '3')
+ return 0;
+
/* Check for nonzero in bpp and (width|height) header fields */
if(!(p->buf[51] && AV_RL16(&p->buf[52]) | AV_RL16(&p->buf[54])))
return 0;
@@ -69,8 +80,20 @@ static int mtv_probe(AVProbeData *p)
return 0;
}
- if(p->buf[51] != 16)
- return AVPROBE_SCORE_EXTENSION / 2; // But we are going to assume 16bpp anyway ..
+ /* Image bpp is not an absolutely required
+ * field as we latter claim it should be 16
+ * no matter what. All samples in the wild
+ * are RGB565/555.
+ */
+ if(p->buf[51] != MTV_IMAGE_DEFAULT_BPP)
+ return AVPROBE_SCORE_EXTENSION / 2;
+
+ /* We had enough data to parse header values
+ * but we expect to be able to get 512 bytes
+ * of header to be sure.
+ */
+ if (p->buf_size < MTV_HEADER_SIZE)
+ return AVPROBE_SCORE_EXTENSION;
return AVPROBE_SCORE_MAX;
}
@@ -94,6 +117,15 @@ static int mtv_read_header(AVFormatContext *s)
mtv->img_height = avio_rl16(pb);
mtv->img_segment_size = avio_rl16(pb);
+ /* Assume 16bpp even if claimed otherwise.
+ * We know its going to be RGBG565/555 anyway
+ */
+ if (mtv->img_bpp != MTV_IMAGE_DEFAULT_BPP) {
+ av_log (s, AV_LOG_WARNING, "Header claims %dbpp (!= 16). Ignoring\n",
+ mtv->img_bpp);
+ mtv->img_bpp = MTV_IMAGE_DEFAULT_BPP;
+ }
+
/* Calculate width and height if missing from header */
if (!mtv->img_width && mtv->img_height > 0 && mtv->img_bpp >= 8)
@@ -104,8 +136,10 @@ static int mtv_read_header(AVFormatContext *s)
mtv->img_height=mtv->img_segment_size / (mtv->img_bpp>>3)
/ mtv->img_width;
- if (!mtv->img_width || !mtv->img_height)
+ if(!mtv->img_height || !mtv->img_width || !mtv->img_segment_size){
+ av_log(s, AV_LOG_ERROR, "width or height or segment_size is invalid and I cannot calculate them from other information\n");
return AVERROR_INVALIDDATA;
+ }
avio_skip(pb, 4);
audio_subsegments = avio_rl16(pb);
@@ -145,7 +179,7 @@ static int mtv_read_header(AVFormatContext *s)
if(!st)
return AVERROR(ENOMEM);
- avpriv_set_pts_info(st, 64, 1, AUDIO_SAMPLING_RATE);
+ avpriv_set_pts_info(st, 64, 1, MTV_AUDIO_SAMPLING_RATE);
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = AV_CODEC_ID_MP3;
st->codec->bit_rate = mtv->audio_br;
diff --git a/libavformat/mux.c b/libavformat/mux.c
index c09456bd88..72d8e942d5 100644
--- a/libavformat/mux.c
+++ b/libavformat/mux.c
@@ -1,21 +1,21 @@
/*
- * muxing functions for use within Libav
+ * muxing functions for use within FFmpeg
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -27,6 +27,7 @@
#include "libavutil/opt.h"
#include "libavutil/dict.h"
#include "libavutil/pixdesc.h"
+#include "libavutil/timestamp.h"
#include "metadata.h"
#include "id3v2.h"
#include "libavutil/avassert.h"
@@ -43,20 +44,136 @@
#include "network.h"
#endif
-#undef NDEBUG
-#include <assert.h>
-
/**
* @file
- * muxing functions for use within Libav
+ * muxing functions for use within libavformat
+ */
+
+/* fraction handling */
+
+/**
+ * f = val + (num / den) + 0.5.
+ *
+ * 'num' is normalized so that it is such as 0 <= num < den.
+ *
+ * @param f fractional number
+ * @param val integer value
+ * @param num must be >= 0
+ * @param den must be >= 1
+ */
+static void frac_init(AVFrac *f, int64_t val, int64_t num, int64_t den)
+{
+ num += (den >> 1);
+ if (num >= den) {
+ val += num / den;
+ num = num % den;
+ }
+ f->val = val;
+ f->num = num;
+ f->den = den;
+}
+
+/**
+ * Fractional addition to f: f = f + (incr / f->den).
+ *
+ * @param f fractional number
+ * @param incr increment, can be positive or negative
*/
+static void frac_add(AVFrac *f, int64_t incr)
+{
+ int64_t num, den;
+
+ num = f->num + incr;
+ den = f->den;
+ if (num < 0) {
+ f->val += num / den;
+ num = num % den;
+ if (num < 0) {
+ num += den;
+ f->val--;
+ }
+ } else if (num >= den) {
+ f->val += num / den;
+ num = num % den;
+ }
+ f->num = num;
+}
+
+AVRational ff_choose_timebase(AVFormatContext *s, AVStream *st, int min_precision)
+{
+ AVRational q;
+ int j;
+
+ q = st->time_base;
+
+ for (j=2; j<14; j+= 1+(j>2))
+ while (q.den / q.num < min_precision && q.num % j == 0)
+ q.num /= j;
+ while (q.den / q.num < min_precision && q.den < (1<<24))
+ q.den <<= 1;
+
+ return q;
+}
+
+int avformat_alloc_output_context2(AVFormatContext **avctx, AVOutputFormat *oformat,
+ const char *format, const char *filename)
+{
+ AVFormatContext *s = avformat_alloc_context();
+ int ret = 0;
+
+ *avctx = NULL;
+ if (!s)
+ goto nomem;
+
+ if (!oformat) {
+ if (format) {
+ oformat = av_guess_format(format, NULL, NULL);
+ if (!oformat) {
+ av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not a suitable output format\n", format);
+ ret = AVERROR(EINVAL);
+ goto error;
+ }
+ } else {
+ oformat = av_guess_format(NULL, filename, NULL);
+ if (!oformat) {
+ ret = AVERROR(EINVAL);
+ av_log(s, AV_LOG_ERROR, "Unable to find a suitable output format for '%s'\n",
+ filename);
+ goto error;
+ }
+ }
+ }
+
+ s->oformat = oformat;
+ if (s->oformat->priv_data_size > 0) {
+ s->priv_data = av_mallocz(s->oformat->priv_data_size);
+ if (!s->priv_data)
+ goto nomem;
+ if (s->oformat->priv_class) {
+ *(const AVClass**)s->priv_data= s->oformat->priv_class;
+ av_opt_set_defaults(s->priv_data);
+ }
+ } else
+ s->priv_data = NULL;
+
+ if (filename)
+ av_strlcpy(s->filename, filename, sizeof(s->filename));
+ *avctx = s;
+ return 0;
+nomem:
+ av_log(s, AV_LOG_ERROR, "Out of memory\n");
+ ret = AVERROR(ENOMEM);
+error:
+ avformat_free_context(s);
+ return ret;
+}
static int validate_codec_tag(AVFormatContext *s, AVStream *st)
{
const AVCodecTag *avctag;
int n;
enum AVCodecID id = AV_CODEC_ID_NONE;
- unsigned int tag = 0;
+ int64_t tag = -1;
/**
* Check that tag + id is in the table
@@ -79,7 +196,7 @@ static int validate_codec_tag(AVFormatContext *s, AVStream *st)
}
if (id != AV_CODEC_ID_NONE)
return 0;
- if (tag && (s->strict_std_compliance >= FF_COMPLIANCE_NORMAL))
+ if (tag >= 0 && (s->strict_std_compliance >= FF_COMPLIANCE_NORMAL))
return 0;
return 1;
}
@@ -92,12 +209,16 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options)
AVDictionary *tmp = NULL;
AVCodecContext *codec = NULL;
AVOutputFormat *of = s->oformat;
+ AVDictionaryEntry *e;
if (options)
av_dict_copy(&tmp, *options, 0);
if ((ret = av_opt_set_dict(s, &tmp)) < 0)
goto fail;
+ if (s->priv_data && s->oformat->priv_class && *(const AVClass**)s->priv_data==s->oformat->priv_class &&
+ (ret = av_opt_set_dict2(s->priv_data, &tmp, AV_OPT_SEARCH_CHILDREN)) < 0)
+ goto fail;
#if FF_API_LAVF_BITEXACT
if (s->nb_streams && s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT)
@@ -106,7 +227,7 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options)
// some sanity checks
if (s->nb_streams == 0 && !(of->flags & AVFMT_NOSTREAMS)) {
- av_log(s, AV_LOG_ERROR, "no streams\n");
+ av_log(s, AV_LOG_ERROR, "No streams to mux were specified\n");
ret = AVERROR(EINVAL);
goto fail;
}
@@ -152,18 +273,18 @@ FF_ENABLE_DEPRECATION_WARNINGS
ret = AVERROR(EINVAL);
goto fail;
}
-
- if (av_cmp_q(st->sample_aspect_ratio,
- codec->sample_aspect_ratio)) {
+ if (av_cmp_q(st->sample_aspect_ratio, codec->sample_aspect_ratio)
+ && FFABS(av_q2d(st->sample_aspect_ratio) - av_q2d(codec->sample_aspect_ratio)) > 0.004*av_q2d(st->sample_aspect_ratio)
+ ) {
if (st->sample_aspect_ratio.num != 0 &&
st->sample_aspect_ratio.den != 0 &&
- codec->sample_aspect_ratio.den != 0 &&
+ codec->sample_aspect_ratio.num != 0 &&
codec->sample_aspect_ratio.den != 0) {
av_log(s, AV_LOG_ERROR, "Aspect ratio mismatch between muxer "
- "(%d/%d) and encoder layer (%d/%d)\n",
- st->sample_aspect_ratio.num, st->sample_aspect_ratio.den,
- codec->sample_aspect_ratio.num,
- codec->sample_aspect_ratio.den);
+ "(%d/%d) and encoder layer (%d/%d)\n",
+ st->sample_aspect_ratio.num, st->sample_aspect_ratio.den,
+ codec->sample_aspect_ratio.num,
+ codec->sample_aspect_ratio.den);
ret = AVERROR(EINVAL);
goto fail;
}
@@ -172,21 +293,23 @@ FF_ENABLE_DEPRECATION_WARNINGS
}
if (of->codec_tag) {
- if (codec->codec_tag &&
- codec->codec_id == AV_CODEC_ID_RAWVIDEO &&
- !av_codec_get_tag(of->codec_tag, codec->codec_id) &&
- !validate_codec_tag(s, st)) {
+ if ( codec->codec_tag
+ && codec->codec_id == AV_CODEC_ID_RAWVIDEO
+ && ( av_codec_get_tag(of->codec_tag, codec->codec_id) == 0
+ || av_codec_get_tag(of->codec_tag, codec->codec_id) == MKTAG('r', 'a', 'w', ' '))
+ && !validate_codec_tag(s, st)) {
// the current rawvideo encoding system ends up setting
- // the wrong codec_tag for avi, we override it here
+ // the wrong codec_tag for avi/mov, we override it here
codec->codec_tag = 0;
}
if (codec->codec_tag) {
if (!validate_codec_tag(s, st)) {
- char tagbuf[32];
+ char tagbuf[32], tagbuf2[32];
av_get_codec_tag_string(tagbuf, sizeof(tagbuf), codec->codec_tag);
+ av_get_codec_tag_string(tagbuf2, sizeof(tagbuf2), av_codec_get_tag(s->oformat->codec_tag, codec->codec_id));
av_log(s, AV_LOG_ERROR,
- "Tag %s/0x%08x incompatible with output codec id '%d'\n",
- tagbuf, codec->codec_tag, codec->codec_id);
+ "Tag %s/0x%08x incompatible with output codec id '%d' (%s)\n",
+ tagbuf, codec->codec_tag, codec->codec_id, tagbuf2);
ret = AVERROR_INVALIDDATA;
goto fail;
}
@@ -213,7 +336,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
if (of->priv_class) {
*(const AVClass **)s->priv_data = of->priv_class;
av_opt_set_defaults(s->priv_data);
- if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
+ if ((ret = av_opt_set_dict2(s->priv_data, &tmp, AV_OPT_SEARCH_CHILDREN)) < 0)
goto fail;
}
}
@@ -221,6 +344,12 @@ FF_ENABLE_DEPRECATION_WARNINGS
/* set muxer identification string */
if (!(s->flags & AVFMT_FLAG_BITEXACT)) {
av_dict_set(&s->metadata, "encoder", LIBAVFORMAT_IDENT, 0);
+ } else {
+ av_dict_set(&s->metadata, "encoder", NULL, 0);
+ }
+
+ for (e = NULL; e = av_dict_get(s->metadata, "encoder-", e, AV_DICT_IGNORE_SUFFIX); ) {
+ av_dict_set(&s->metadata, e->key, NULL, 0);
}
if (options) {
@@ -235,6 +364,37 @@ fail:
return ret;
}
+static int init_pts(AVFormatContext *s)
+{
+ int i;
+ AVStream *st;
+
+ /* init PTS generation */
+ for (i = 0; i < s->nb_streams; i++) {
+ int64_t den = AV_NOPTS_VALUE;
+ st = s->streams[i];
+
+ switch (st->codec->codec_type) {
+ case AVMEDIA_TYPE_AUDIO:
+ den = (int64_t)st->time_base.num * st->codec->sample_rate;
+ break;
+ case AVMEDIA_TYPE_VIDEO:
+ den = (int64_t)st->time_base.num * st->codec->time_base.den;
+ break;
+ default:
+ break;
+ }
+ if (den != AV_NOPTS_VALUE) {
+ if (den <= 0)
+ return AVERROR_INVALIDDATA;
+
+ frac_init(&st->pts, 0, 0, den);
+ }
+ }
+
+ return 0;
+}
+
int avformat_write_header(AVFormatContext *s, AVDictionary **options)
{
int ret = 0;
@@ -244,11 +404,19 @@ int avformat_write_header(AVFormatContext *s, AVDictionary **options)
if (s->oformat->write_header) {
ret = s->oformat->write_header(s);
+ if (ret >= 0 && s->pb && s->pb->error < 0)
+ ret = s->pb->error;
if (ret < 0)
return ret;
+ if (s->flush_packets && s->pb && s->pb->error >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS)
+ avio_flush(s->pb);
}
- if (s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO) {
+ if ((ret = init_pts(s)) < 0)
+ return ret;
+
+ if (s->avoid_negative_ts < 0) {
+ av_assert2(s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO);
if (s->oformat->flags & (AVFMT_TS_NEGATIVE | AVFMT_NOTIMESTAMPS)) {
s->avoid_negative_ts = 0;
} else
@@ -258,17 +426,30 @@ int avformat_write_header(AVFormatContext *s, AVDictionary **options)
return 0;
}
+#define AV_PKT_FLAG_UNCODED_FRAME 0x2000
+
+/* Note: using sizeof(AVFrame) from outside lavu is unsafe in general, but
+ it is only being used internally to this file as a consistency check.
+ The value is chosen to be very unlikely to appear on its own and to cause
+ immediate failure if used anywhere as a real size. */
+#define UNCODED_FRAME_PACKET_SIZE (INT_MIN / 3 * 2 + (int)sizeof(AVFrame))
+
+
//FIXME merge with compute_pkt_fields
static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt)
{
- int delay = FFMAX(st->codec->has_b_frames, !!st->codec->max_b_frames);
+ int delay = FFMAX(st->codec->has_b_frames, st->codec->max_b_frames > 0);
int num, den, i;
+ int frame_size;
- av_dlog(s, "compute_pkt_fields2: pts:%" PRId64 " dts:%" PRId64 " cur_dts:%" PRId64 " b:%d size:%d st:%d\n",
- pkt->pts, pkt->dts, st->cur_dts, delay, pkt->size, pkt->stream_index);
+ av_dlog(s, "compute_pkt_fields2: pts:%s dts:%s cur_dts:%s b:%d size:%d st:%d\n",
+ av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts), delay, pkt->size, pkt->stream_index);
-/* if(pkt->pts == AV_NOPTS_VALUE && pkt->dts == AV_NOPTS_VALUE)
- * return AVERROR(EINVAL);*/
+ if (pkt->duration < 0 && st->codec->codec_type != AVMEDIA_TYPE_SUBTITLE) {
+ av_log(s, AV_LOG_WARNING, "Packet with invalid duration %d in stream %d\n",
+ pkt->duration, pkt->stream_index);
+ pkt->duration = 0;
+ }
/* duration field */
if (pkt->duration == 0) {
@@ -281,6 +462,18 @@ static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt)
if (pkt->pts == AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE && delay == 0)
pkt->pts = pkt->dts;
+ //XXX/FIXME this is a temporary hack until all encoders output pts
+ if ((pkt->pts == 0 || pkt->pts == AV_NOPTS_VALUE) && pkt->dts == AV_NOPTS_VALUE && !delay) {
+ static int warned;
+ if (!warned) {
+ av_log(s, AV_LOG_WARNING, "Encoder did not produce proper pts, making some up.\n");
+ warned = 1;
+ }
+ pkt->dts =
+// pkt->pts= st->cur_dts;
+ pkt->pts = st->pts.val;
+ }
+
//calculate dts from pts
if (pkt->pts != AV_NOPTS_VALUE && pkt->dts == AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY) {
st->pts_buffer[0] = pkt->pts;
@@ -294,67 +487,121 @@ static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt)
if (st->cur_dts && st->cur_dts != AV_NOPTS_VALUE &&
((!(s->oformat->flags & AVFMT_TS_NONSTRICT) &&
+ st->codec->codec_type != AVMEDIA_TYPE_SUBTITLE &&
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 %" PRId64 " < dts %" PRId64 " in stream %d\n",
- pkt->pts, pkt->dts,
+ "pts (%s) < dts (%s) in stream %d\n",
+ av_ts2str(pkt->pts), av_ts2str(pkt->dts),
st->index);
return AVERROR(EINVAL);
}
- av_dlog(s, "av_write_frame: pts2:%"PRId64" dts2:%"PRId64"\n",
- pkt->pts, pkt->dts);
+ av_dlog(s, "av_write_frame: pts2:%s dts2:%s\n",
+ av_ts2str(pkt->pts), av_ts2str(pkt->dts));
st->cur_dts = pkt->dts;
-
+ st->pts.val = pkt->dts;
+
+ /* update pts */
+ switch (st->codec->codec_type) {
+ case AVMEDIA_TYPE_AUDIO:
+ frame_size = (pkt->flags & AV_PKT_FLAG_UNCODED_FRAME) ?
+ ((AVFrame *)pkt->data)->nb_samples :
+ av_get_audio_frame_duration(st->codec, pkt->size);
+
+ /* HACK/FIXME, we skip the initial 0 size packets as they are most
+ * likely equal to the encoder delay, but it would be better if we
+ * had the real timestamps from the encoder */
+ if (frame_size >= 0 && (pkt->size || st->pts.num != st->pts.den >> 1 || st->pts.val)) {
+ frac_add(&st->pts, (int64_t)st->time_base.den * frame_size);
+ }
+ break;
+ case AVMEDIA_TYPE_VIDEO:
+ frac_add(&st->pts, (int64_t)st->time_base.den * st->codec->time_base.num);
+ break;
+ }
return 0;
}
-/*
+/**
+ * Make timestamps non negative, move side data from payload to internal struct, call muxer, and restore
+ * sidedata.
+ *
* FIXME: this function should NEVER get undefined pts/dts beside when the
* AVFMT_NOTIMESTAMPS is set.
* Those additional safety checks should be dropped once the correct checks
* are set in the callers.
*/
-
static int write_packet(AVFormatContext *s, AVPacket *pkt)
{
- int ret;
+ int ret, did_split;
+
+ if (s->output_ts_offset) {
+ AVStream *st = s->streams[pkt->stream_index];
+ int64_t offset = av_rescale_q(s->output_ts_offset, AV_TIME_BASE_Q, st->time_base);
+
+ if (pkt->dts != AV_NOPTS_VALUE)
+ pkt->dts += offset;
+ if (pkt->pts != AV_NOPTS_VALUE)
+ pkt->pts += offset;
+ }
+
if (s->avoid_negative_ts > 0) {
- AVRational time_base = s->streams[pkt->stream_index]->time_base;
- int64_t offset = 0;
+ AVStream *st = s->streams[pkt->stream_index];
+ int64_t offset = st->mux_ts_offset;
if (s->internal->offset == AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE &&
(pkt->dts < 0 || s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_MAKE_ZERO)) {
s->internal->offset = -pkt->dts;
- s->internal->offset_timebase = time_base;
+ s->internal->offset_timebase = st->time_base;
+ }
+
+ if (s->internal->offset != AV_NOPTS_VALUE && !offset) {
+ offset = st->mux_ts_offset =
+ av_rescale_q_rnd(s->internal->offset,
+ s->internal->offset_timebase,
+ st->time_base,
+ AV_ROUND_UP);
}
- if (s->internal->offset != AV_NOPTS_VALUE)
- offset = av_rescale_q(s->internal->offset, s->internal->offset_timebase, time_base);
if (pkt->dts != AV_NOPTS_VALUE)
pkt->dts += offset;
if (pkt->pts != AV_NOPTS_VALUE)
pkt->pts += offset;
+ av_assert2(pkt->dts == AV_NOPTS_VALUE || pkt->dts >= 0 || s->max_interleave_delta > 0);
if (pkt->dts != AV_NOPTS_VALUE && pkt->dts < 0) {
av_log(s, AV_LOG_WARNING,
"Packets poorly interleaved, failed to avoid negative "
- "timestamp %"PRId64" in stream %d.\n"
+ "timestamp %s in stream %d.\n"
"Try -max_interleave_delta 0 as a possible workaround.\n",
- pkt->dts, pkt->stream_index);
+ av_ts2str(pkt->dts),
+ pkt->stream_index
+ );
}
}
- ret = s->oformat->write_packet(s, pkt);
- if (s->pb && ret >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS)
+ did_split = av_packet_split_side_data(pkt);
+ if ((pkt->flags & AV_PKT_FLAG_UNCODED_FRAME)) {
+ AVFrame *frame = (AVFrame *)pkt->data;
+ av_assert0(pkt->size == UNCODED_FRAME_PACKET_SIZE);
+ ret = s->oformat->write_uncoded_frame(s, pkt->stream_index, &frame, 0);
+ av_frame_free(&frame);
+ } else {
+ ret = s->oformat->write_packet(s, pkt);
+ }
+
+ if (s->flush_packets && s->pb && ret >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS)
avio_flush(s->pb);
+ if (did_split)
+ av_packet_merge_side_data(pkt);
+
return ret;
}
@@ -386,8 +633,14 @@ int av_write_frame(AVFormatContext *s, AVPacket *pkt)
return ret;
if (!pkt) {
- if (s->oformat->flags & AVFMT_ALLOW_FLUSH)
- return s->oformat->write_packet(s, pkt);
+ if (s->oformat->flags & AVFMT_ALLOW_FLUSH) {
+ ret = s->oformat->write_packet(s, NULL);
+ if (s->flush_packets && s->pb && s->pb->error >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS)
+ avio_flush(s->pb);
+ if (ret >= 0 && s->pb && s->pb->error < 0)
+ ret = s->pb->error;
+ return ret;
+ }
return 1;
}
@@ -397,17 +650,23 @@ int av_write_frame(AVFormatContext *s, AVPacket *pkt)
return ret;
ret = write_packet(s, pkt);
+ if (ret >= 0 && s->pb && s->pb->error < 0)
+ ret = s->pb->error;
if (ret >= 0)
s->streams[pkt->stream_index]->nb_frames++;
return ret;
}
+#define CHUNK_START 0x1000
+
int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt,
int (*compare)(AVFormatContext *, AVPacket *, AVPacket *))
{
int ret;
AVPacketList **next_point, *this_pktl;
+ AVStream *st = s->streams[pkt->stream_index];
+ int chunked = s->max_chunk_size || s->max_chunk_duration;
this_pktl = av_mallocz(sizeof(AVPacketList));
if (!this_pktl)
@@ -419,27 +678,58 @@ FF_DISABLE_DEPRECATION_WARNINGS
FF_ENABLE_DEPRECATION_WARNINGS
#endif
pkt->buf = NULL;
- // Duplicate the packet if it uses non-allocated memory
- if ((ret = av_dup_packet(&this_pktl->pkt)) < 0) {
- av_free(this_pktl);
- return ret;
+ pkt->side_data = NULL;
+ pkt->side_data_elems = 0;
+ if ((pkt->flags & AV_PKT_FLAG_UNCODED_FRAME)) {
+ av_assert0(pkt->size == UNCODED_FRAME_PACKET_SIZE);
+ av_assert0(((AVFrame *)pkt->data)->buf);
+ } else {
+ // Duplicate the packet if it uses non-allocated memory
+ if ((ret = av_dup_packet(&this_pktl->pkt)) < 0) {
+ av_free(this_pktl);
+ return ret;
+ }
}
if (s->streams[pkt->stream_index]->last_in_packet_buffer) {
- next_point = &(s->streams[pkt->stream_index]->last_in_packet_buffer->next);
- } else
+ next_point = &(st->last_in_packet_buffer->next);
+ } else {
next_point = &s->internal->packet_buffer;
+ }
+ if (chunked) {
+ uint64_t max= av_rescale_q_rnd(s->max_chunk_duration, AV_TIME_BASE_Q, st->time_base, AV_ROUND_UP);
+ st->interleaver_chunk_size += pkt->size;
+ st->interleaver_chunk_duration += pkt->duration;
+ if ( (s->max_chunk_size && st->interleaver_chunk_size > s->max_chunk_size)
+ || (max && st->interleaver_chunk_duration > max)) {
+ st->interleaver_chunk_size = 0;
+ this_pktl->pkt.flags |= CHUNK_START;
+ if (max && st->interleaver_chunk_duration > max) {
+ int64_t syncoffset = (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)*max/2;
+ int64_t syncto = av_rescale(pkt->dts + syncoffset, 1, max)*max - syncoffset;
+
+ st->interleaver_chunk_duration += (pkt->dts - syncto)/8 - max;
+ } else
+ st->interleaver_chunk_duration = 0;
+ }
+ }
if (*next_point) {
+ if (chunked && !(this_pktl->pkt.flags & CHUNK_START))
+ goto next_non_null;
+
if (compare(s, &s->internal->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->internal->packet_buffer_end->next);
}
}
- assert(!*next_point);
+ av_assert1(!*next_point);
s->internal->packet_buffer_end = this_pktl;
next_non_null:
@@ -459,6 +749,16 @@ static int interleave_compare_dts(AVFormatContext *s, AVPacket *next,
AVStream *st2 = s->streams[next->stream_index];
int comp = av_compare_ts(next->dts, st2->time_base, pkt->dts,
st->time_base);
+ if (s->audio_preload && ((st->codec->codec_type == AVMEDIA_TYPE_AUDIO) != (st2->codec->codec_type == AVMEDIA_TYPE_AUDIO))) {
+ int64_t ts = av_rescale_q(pkt ->dts, st ->time_base, AV_TIME_BASE_Q) - s->audio_preload*(st ->codec->codec_type == AVMEDIA_TYPE_AUDIO);
+ int64_t ts2= av_rescale_q(next->dts, st2->time_base, AV_TIME_BASE_Q) - s->audio_preload*(st2->codec->codec_type == AVMEDIA_TYPE_AUDIO);
+ if (ts == ts2) {
+ ts= ( pkt ->dts* st->time_base.num*AV_TIME_BASE - s->audio_preload*(int64_t)(st ->codec->codec_type == AVMEDIA_TYPE_AUDIO)* st->time_base.den)*st2->time_base.den
+ -( next->dts*st2->time_base.num*AV_TIME_BASE - s->audio_preload*(int64_t)(st2->codec->codec_type == AVMEDIA_TYPE_AUDIO)*st2->time_base.den)* st->time_base.den;
+ ts2=0;
+ }
+ comp= (ts>ts2) - (ts<ts2);
+ }
if (comp == 0)
return pkt->stream_index < next->stream_index;
@@ -470,6 +770,7 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out,
{
AVPacketList *pktl;
int stream_count = 0;
+ int noninterleaved_count = 0;
int i, ret;
if (pkt) {
@@ -477,7 +778,24 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out,
return ret;
}
- if (s->max_interleave_delta > 0 && s->internal->packet_buffer && !flush) {
+ for (i = 0; i < s->nb_streams; i++) {
+ if (s->streams[i]->last_in_packet_buffer) {
+ ++stream_count;
+ } else if (s->streams[i]->codec->codec_type != AVMEDIA_TYPE_ATTACHMENT &&
+ s->streams[i]->codec->codec_id != AV_CODEC_ID_VP8 &&
+ s->streams[i]->codec->codec_id != AV_CODEC_ID_VP9) {
+ ++noninterleaved_count;
+ }
+ }
+
+ if (s->internal->nb_interleaved_streams == stream_count)
+ flush = 1;
+
+ if (s->max_interleave_delta > 0 &&
+ s->internal->packet_buffer &&
+ !flush &&
+ s->internal->nb_interleaved_streams == stream_count+noninterleaved_count
+ ) {
AVPacket *top_pkt = &s->internal->packet_buffer->pkt;
int64_t delta_dts = INT64_MIN;
int64_t top_dts = av_rescale_q(top_pkt->dts,
@@ -495,7 +813,6 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out,
s->streams[i]->time_base,
AV_TIME_BASE_Q);
delta_dts = FFMAX(delta_dts, last_dts - top_dts);
- stream_count++;
}
if (delta_dts > s->max_interleave_delta) {
@@ -505,23 +822,22 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out,
delta_dts, s->max_interleave_delta);
flush = 1;
}
- } else {
- for (i = 0; i < s->nb_streams; i++)
- stream_count += !!s->streams[i]->last_in_packet_buffer;
}
-
- if (stream_count && (s->internal->nb_interleaved_streams == stream_count || flush)) {
+ if (stream_count && flush) {
+ AVStream *st;
pktl = s->internal->packet_buffer;
*out = pktl->pkt;
+ st = s->streams[out->stream_index];
s->internal->packet_buffer = pktl->next;
if (!s->internal->packet_buffer)
s->internal->packet_buffer_end = NULL;
- if (s->streams[out->stream_index]->last_in_packet_buffer == pktl)
- s->streams[out->stream_index]->last_in_packet_buffer = NULL;
+ if (st->last_in_packet_buffer == pktl)
+ st->last_in_packet_buffer = NULL;
av_freep(&pktl);
+
return 1;
} else {
av_init_packet(out);
@@ -560,8 +876,8 @@ int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt)
if (pkt) {
AVStream *st = s->streams[pkt->stream_index];
- av_dlog(s, "av_interleaved_write_frame size:%d dts:%" PRId64 " pts:%" PRId64 "\n",
- pkt->size, pkt->dts, pkt->pts);
+ av_dlog(s, "av_interleaved_write_frame size:%d dts:%s pts:%s\n",
+ pkt->size, av_ts2str(pkt->dts), av_ts2str(pkt->pts));
if ((ret = compute_pkt_fields2(s, st, pkt)) < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))
goto fail;
@@ -593,6 +909,8 @@ int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt)
if (ret < 0)
return ret;
+ if(s->pb && s->pb->error)
+ return s->pb->error;
}
fail:
av_packet_unref(pkt);
@@ -606,7 +924,7 @@ int av_write_trailer(AVFormatContext *s)
for (;; ) {
AVPacket pkt;
ret = interleave_packet(s, &pkt, NULL, 1);
- if (ret < 0) //FIXME cleanup needed for ret<0 ?
+ if (ret < 0)
goto fail;
if (!ret)
break;
@@ -619,15 +937,22 @@ int av_write_trailer(AVFormatContext *s)
if (ret < 0)
goto fail;
+ if(s->pb && s->pb->error)
+ goto fail;
}
+fail:
if (s->oformat->write_trailer)
+ if (ret >= 0) {
ret = s->oformat->write_trailer(s);
+ } else {
+ s->oformat->write_trailer(s);
+ }
- if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb)
- 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);
@@ -638,10 +963,20 @@ fail:
return ret;
}
+int av_get_output_timestamp(struct AVFormatContext *s, int stream,
+ int64_t *dts, int64_t *wall)
+{
+ if (!s->oformat || !s->oformat->get_output_timestamp)
+ return AVERROR(ENOSYS);
+ s->oformat->get_output_timestamp(s, stream, dts, wall);
+ return 0;
+}
+
int ff_write_chained(AVFormatContext *dst, int dst_stream, AVPacket *pkt,
- AVFormatContext *src)
+ AVFormatContext *src, int interleave)
{
AVPacket local_pkt;
+ int ret;
local_pkt = *pkt;
local_pkt.stream_index = dst_stream;
@@ -653,5 +988,62 @@ int ff_write_chained(AVFormatContext *dst, int dst_stream, AVPacket *pkt,
local_pkt.dts = av_rescale_q(pkt->dts,
src->streams[pkt->stream_index]->time_base,
dst->streams[dst_stream]->time_base);
- return av_write_frame(dst, &local_pkt);
+ if (pkt->duration)
+ local_pkt.duration = av_rescale_q(pkt->duration,
+ src->streams[pkt->stream_index]->time_base,
+ dst->streams[dst_stream]->time_base);
+
+ if (interleave) ret = av_interleaved_write_frame(dst, &local_pkt);
+ else ret = av_write_frame(dst, &local_pkt);
+ pkt->buf = local_pkt.buf;
+ pkt->destruct = local_pkt.destruct;
+ return ret;
+}
+
+static int av_write_uncoded_frame_internal(AVFormatContext *s, int stream_index,
+ AVFrame *frame, int interleaved)
+{
+ AVPacket pkt, *pktp;
+
+ av_assert0(s->oformat);
+ if (!s->oformat->write_uncoded_frame)
+ return AVERROR(ENOSYS);
+
+ if (!frame) {
+ pktp = NULL;
+ } else {
+ pktp = &pkt;
+ av_init_packet(&pkt);
+ pkt.data = (void *)frame;
+ pkt.size = UNCODED_FRAME_PACKET_SIZE;
+ pkt.pts =
+ pkt.dts = frame->pts;
+ pkt.duration = av_frame_get_pkt_duration(frame);
+ pkt.stream_index = stream_index;
+ pkt.flags |= AV_PKT_FLAG_UNCODED_FRAME;
+ }
+
+ return interleaved ? av_interleaved_write_frame(s, pktp) :
+ av_write_frame(s, pktp);
+}
+
+int av_write_uncoded_frame(AVFormatContext *s, int stream_index,
+ AVFrame *frame)
+{
+ return av_write_uncoded_frame_internal(s, stream_index, frame, 0);
+}
+
+int av_interleaved_write_uncoded_frame(AVFormatContext *s, int stream_index,
+ AVFrame *frame)
+{
+ return av_write_uncoded_frame_internal(s, stream_index, frame, 1);
+}
+
+int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index)
+{
+ av_assert0(s->oformat);
+ if (!s->oformat->write_uncoded_frame)
+ return AVERROR(ENOSYS);
+ return s->oformat->write_uncoded_frame(s, stream_index, NULL,
+ AV_WRITE_UNCODED_FRAME_QUERY);
}
diff --git a/libavformat/mvdec.c b/libavformat/mvdec.c
index e21ec06b74..c8a5ebf119 100644
--- a/libavformat/mvdec.c
+++ b/libavformat/mvdec.c
@@ -2,20 +2,20 @@
* Silicon Graphics Movie demuxer
* Copyright (c) 2012 Peter Ross
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -57,7 +57,12 @@ static int mv_probe(AVProbeData *p)
static char *var_read_string(AVIOContext *pb, int size)
{
int n;
- char *str = av_malloc(size + 1);
+ char *str;
+
+ if (size < 0 || size == INT_MAX)
+ return NULL;
+
+ str = av_malloc(size + 1);
if (!str)
return NULL;
n = avio_get_str(pb, size, str, size + 1);
@@ -218,7 +223,7 @@ static int parse_video_var(AVFormatContext *avctx, AVStream *st,
return 0;
}
-static void read_table(AVFormatContext *avctx, AVStream *st,
+static int read_table(AVFormatContext *avctx, AVStream *st,
int (*parse)(AVFormatContext *avctx, AVStream *st,
const char *name, int size))
{
@@ -233,11 +238,16 @@ static void read_table(AVFormatContext *avctx, AVStream *st,
avio_read(pb, name, 16);
name[sizeof(name) - 1] = 0;
size = avio_rb32(pb);
+ if (size < 0) {
+ av_log(avctx, AV_LOG_ERROR, "entry size %d is invalid\n", size);
+ return AVERROR_INVALIDDATA;
+ }
if (parse(avctx, st, name, size) < 0) {
avpriv_request_sample(avctx, "Variable %s", name);
avio_skip(pb, size);
}
}
+ return 0;
}
static void read_index(AVIOContext *pb, AVStream *st)
@@ -261,8 +271,9 @@ static int mv_read_header(AVFormatContext *avctx)
{
MvContext *mv = avctx->priv_data;
AVIOContext *pb = avctx->pb;
- AVStream *ast = NULL, *vst = NULL;
+ AVStream *ast = NULL, *vst = NULL; //initialization to suppress warning
int version, i;
+ int ret;
avio_skip(pb, 4);
@@ -335,7 +346,8 @@ static int mv_read_header(AVFormatContext *avctx)
} else if (!version && avio_rb16(pb) == 3) {
avio_skip(pb, 4);
- read_table(avctx, NULL, parse_global_var);
+ if ((ret = read_table(avctx, NULL, parse_global_var)) < 0)
+ return ret;
if (mv->nb_audio_tracks > 1) {
avpriv_request_sample(avctx, "Multiple audio streams support");
@@ -345,7 +357,8 @@ static int mv_read_header(AVFormatContext *avctx)
if (!ast)
return AVERROR(ENOMEM);
ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
- read_table(avctx, ast, parse_audio_var);
+ if ((read_table(avctx, ast, parse_audio_var)) < 0)
+ return ret;
if (mv->acompression == 100 &&
mv->aformat == AUDIO_FORMAT_SIGNED &&
ast->codec->bits_per_coded_sample == 16) {
@@ -371,7 +384,8 @@ static int mv_read_header(AVFormatContext *avctx)
if (!vst)
return AVERROR(ENOMEM);
vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
- read_table(avctx, vst, parse_video_var);
+ if ((ret = read_table(avctx, vst, parse_video_var))<0)
+ return ret;
}
if (mv->nb_audio_tracks)
diff --git a/libavformat/mvi.c b/libavformat/mvi.c
index 03a1f406cf..a7cfcb9a7a 100644
--- a/libavformat/mvi.c
+++ b/libavformat/mvi.c
@@ -2,20 +2,20 @@
* Motion Pixels MVI Demuxer
* Copyright (c) 2008 Gregory Montoir (cyx@users.sourceforge.net)
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -54,8 +54,8 @@ static int read_header(AVFormatContext *s)
if (!vst)
return AVERROR(ENOMEM);
- vst->codec->extradata_size = 2;
- vst->codec->extradata = av_mallocz(2 + FF_INPUT_BUFFER_PADDING_SIZE);
+ if (ff_alloc_extradata(vst->codec, 2))
+ return AVERROR(ENOMEM);
version = avio_r8(pb);
vst->codec->extradata[0] = avio_r8(pb);
diff --git a/libavformat/mxf.c b/libavformat/mxf.c
index 19ee35f3af..14d143e5f0 100644
--- a/libavformat/mxf.c
+++ b/libavformat/mxf.c
@@ -2,20 +2,20 @@
* MXF
* Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier <baptiste dot coudurier at smartjog dot com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -28,6 +28,8 @@
const MXFCodecUL ff_mxf_data_definition_uls[] = {
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x01,0x03,0x02,0x02,0x01,0x00,0x00,0x00 }, 13, AVMEDIA_TYPE_VIDEO },
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x01,0x03,0x02,0x02,0x02,0x00,0x00,0x00 }, 13, AVMEDIA_TYPE_AUDIO },
+ { { 0x80,0x7D,0x00,0x60,0x08,0x14,0x3E,0x6F,0x6F,0x3C,0x8C,0xE1,0x6C,0xEF,0x11,0xD2 }, 16, AVMEDIA_TYPE_VIDEO }, /* LegacyPicture Avid Media Composer MXF */
+ { { 0x80,0x7D,0x00,0x60,0x08,0x14,0x3E,0x6F,0x78,0xE1,0xEB,0xE1,0x6C,0xEF,0x11,0xD2 }, 16, AVMEDIA_TYPE_AUDIO }, /* LegacySound Avid Media Composer MXF */
{ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AVMEDIA_TYPE_DATA },
};
@@ -44,9 +46,10 @@ const MXFCodecUL ff_mxf_codec_uls[] = {
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x01,0x01,0x02,0x01,0x00 }, 15, AV_CODEC_ID_RAWVIDEO }, /* Uncompressed 422 8-bit */
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x71,0x00,0x00,0x00 }, 13, AV_CODEC_ID_DNXHD }, /* SMPTE VC-3/DNxHD */
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x03,0x02,0x00,0x00 }, 14, AV_CODEC_ID_DNXHD }, /* SMPTE VC-3/DNxHD */
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0E,0x04,0x02,0x01,0x02,0x04,0x01,0x00 }, 16, AV_CODEC_ID_DNXHD }, /* SMPTE VC-3/DNxHD Legacy Avid Media Composer MXF */
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x01,0x32,0x00,0x00 }, 14, AV_CODEC_ID_H264 }, /* H.264/MPEG-4 AVC Intra */
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x01,0x31,0x11,0x01 }, 14, AV_CODEC_ID_H264 }, /* H.264/MPEG-4 AVC SPS/PPS in-band */
- { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x01,0x01,0x02,0x02,0x00 }, 15, AV_CODEC_ID_V210 }, /* V210 */
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x01,0x01,0x02,0x02,0x01 }, 16, AV_CODEC_ID_V210 }, /* V210 */
/* SoundEssenceCompression */
{ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x03,0x04,0x02,0x02,0x02,0x03,0x03,0x01,0x00 }, 14, AV_CODEC_ID_AAC }, /* MPEG2 AAC ADTS (legacy) */
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, 13, AV_CODEC_ID_PCM_S16LE }, /* Uncompressed */
@@ -91,6 +94,7 @@ static const struct {
{AV_PIX_FMT_RGB565BE,{'R', 5, 'G', 6, 'B', 5 }},
{AV_PIX_FMT_RGBA, {'R', 8, 'G', 8, 'B', 8, 'A', 8 }},
{AV_PIX_FMT_PAL8, {'P', 8 }},
+ {AV_PIX_FMT_GRAY8, {'A', 8 }},
};
static const int num_pixel_layouts = FF_ARRAY_ELEMS(ff_mxf_pixel_layouts);
diff --git a/libavformat/mxf.h b/libavformat/mxf.h
index 24432071ee..d9e17c6b3f 100644
--- a/libavformat/mxf.h
+++ b/libavformat/mxf.h
@@ -2,20 +2,20 @@
* MXF
* Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier <baptiste dot coudurier at smartjog dot com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef AVFORMAT_MXF_H
@@ -33,6 +33,7 @@ enum MXFMetadataSetType {
SourcePackage,
SourceClip,
TimecodeComponent,
+ PulldownComponent,
Sequence,
MultipleDescriptor,
Descriptor,
@@ -45,14 +46,15 @@ enum MXFMetadataSetType {
IndexTableSegment,
EssenceContainerData,
TypeBottom,// add metadata type before this
+ EssenceGroup,
};
enum MXFFrameLayout {
- FullFrame = 0,
- MixedFields,
- OneField,
- SegmentedFrame,
- SeparateFields
+ FullFrame = 0,
+ SeparateFields,
+ OneField,
+ MixedFields,
+ SegmentedFrame,
};
typedef struct KLVPacket {
@@ -67,7 +69,7 @@ typedef struct MXFCodecUL {
int id;
} MXFCodecUL;
-typedef struct MXFSamplesPerFrame {
+typedef struct {
struct AVRational time_base;
int samples_per_frame[6];
} MXFSamplesPerFrame;
diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
index 2b8bd8978a..1376a7a3be 100644
--- a/libavformat/mxfdec.c
+++ b/libavformat/mxfdec.c
@@ -2,20 +2,20 @@
* MXF demuxer.
* Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier <baptiste dot coudurier at smartjog dot com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -46,8 +46,11 @@
#include <inttypes.h>
#include "libavutil/aes.h"
+#include "libavutil/avassert.h"
#include "libavutil/mathematics.h"
#include "libavcodec/bytestream.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/timecode.h"
#include "avformat.h"
#include "internal.h"
#include "mxf.h"
@@ -69,7 +72,7 @@ typedef enum {
OP3b,
OP3c,
OPAtom,
- OPSonyOpt, /* FATE sample, violates the spec in places */
+ OPSONYOpt, /* FATE sample, violates the spec in places */
} MXFOP;
typedef struct MXFPartition {
@@ -86,6 +89,7 @@ typedef struct MXFPartition {
int64_t header_byte_count;
int64_t index_byte_count;
int pack_length;
+ int64_t pack_ofs; ///< absolute offset of pack in file, including run-in
} MXFPartition;
typedef struct MXFCryptoContext {
@@ -117,6 +121,29 @@ typedef struct MXFSequence {
typedef struct MXFTrack {
UID uid;
enum MXFMetadataSetType type;
+ int drop_frame;
+ int start_frame;
+ struct AVRational rate;
+ AVTimecode tc;
+} MXFTimecodeComponent;
+
+typedef struct {
+ UID uid;
+ enum MXFMetadataSetType type;
+ UID input_segment_ref;
+} MXFPulldownComponent;
+
+typedef struct {
+ UID uid;
+ enum MXFMetadataSetType type;
+ UID *structural_components_refs;
+ int structural_components_count;
+ int64_t duration;
+} MXFEssenceGroup;
+
+typedef struct {
+ UID uid;
+ enum MXFMetadataSetType type;
MXFSequence *sequence; /* mandatory, and only one */
UID sequence_ref;
int track_id;
@@ -142,6 +169,7 @@ typedef struct MXFDescriptor {
int field_dominance;
int channels;
int bits_per_sample;
+ int64_t duration; /* ContainerDuration optional property */
unsigned int component_depth;
unsigned int horiz_subsampling;
unsigned int vert_subsampling;
@@ -172,10 +200,12 @@ typedef struct MXFPackage {
UID uid;
enum MXFMetadataSetType type;
UID package_uid;
+ UID package_ul;
UID *tracks_refs;
int tracks_count;
MXFDescriptor *descriptor; /* only one */
UID descriptor_ref;
+ char *name;
} MXFPackage;
typedef struct MXFMetadataSet {
@@ -207,7 +237,6 @@ typedef struct MXFContext {
struct AVAES *aesc;
uint8_t *local_tags;
int local_tags_count;
- uint64_t last_partition;
uint64_t footer_partition;
KLVPacket current_klv_data;
int current_klv_index;
@@ -237,6 +266,8 @@ typedef struct MXFMetadataReadTableEntry {
enum MXFMetadataSetType type;
} MXFMetadataReadTableEntry;
+static int mxf_read_close(AVFormatContext *s);
+
/* partial keys to match */
static const uint8_t mxf_header_partition_pack_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02 };
static const uint8_t mxf_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01 };
@@ -249,9 +280,43 @@ static const uint8_t mxf_encrypted_triplet_key[] = { 0x06,0x0e,0x2b,0x
static const uint8_t mxf_encrypted_essence_container[] = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x0b,0x01,0x00 };
static const uint8_t mxf_random_index_pack_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x11,0x01,0x00 };
static const uint8_t mxf_sony_mpeg4_extradata[] = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0e,0x06,0x06,0x02,0x02,0x01,0x00,0x00 };
+static const uint8_t mxf_avid_project_name[] = { 0xa5,0xfb,0x7b,0x25,0xf6,0x15,0x94,0xb9,0x62,0xfc,0x37,0x17,0x49,0x2d,0x42,0xbf };
#define IS_KLV_KEY(x, y) (!memcmp(x, y, sizeof(y)))
+static void mxf_free_metadataset(MXFMetadataSet **ctx, int freectx)
+{
+ MXFIndexTableSegment *seg;
+ switch ((*ctx)->type) {
+ case Descriptor:
+ av_freep(&((MXFDescriptor *)*ctx)->extradata);
+ break;
+ case MultipleDescriptor:
+ av_freep(&((MXFDescriptor *)*ctx)->sub_descriptors_refs);
+ break;
+ case Sequence:
+ av_freep(&((MXFSequence *)*ctx)->structural_components_refs);
+ break;
+ case EssenceGroup:
+ av_freep(&((MXFEssenceGroup *)*ctx)->structural_components_refs);
+ break;
+ case SourcePackage:
+ case MaterialPackage:
+ av_freep(&((MXFPackage *)*ctx)->tracks_refs);
+ av_freep(&((MXFPackage *)*ctx)->name);
+ break;
+ case IndexTableSegment:
+ seg = (MXFIndexTableSegment *)*ctx;
+ av_freep(&seg->temporal_offset_entries);
+ av_freep(&seg->flag_entries);
+ av_freep(&seg->stream_offset_entries);
+ default:
+ break;
+ }
+ if (freectx)
+ av_freep(ctx);
+}
+
static int64_t klv_decode_ber_length(AVIOContext *pb)
{
uint64_t size = avio_r8(pb);
@@ -270,7 +335,7 @@ static int64_t klv_decode_ber_length(AVIOContext *pb)
static int mxf_read_sync(AVIOContext *pb, const uint8_t *key, unsigned size)
{
int i, b;
- for (i = 0; i < size && !pb->eof_reached; i++) {
+ for (i = 0; i < size && !avio_feof(pb); i++) {
b = avio_r8(pb);
if (b == key[0])
i = 0;
@@ -407,12 +472,18 @@ static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, U
avpriv_request_sample(pb, "Primer pack item length %d", item_len);
return AVERROR_PATCHWELCOME;
}
- if (item_num > UINT_MAX / item_len)
+ if (item_num > 65536) {
+ av_log(mxf->fc, AV_LOG_ERROR, "item_num %d is too large\n", item_num);
return AVERROR_INVALIDDATA;
- mxf->local_tags_count = item_num;
- mxf->local_tags = av_malloc(item_num*item_len);
+ }
+ if (mxf->local_tags)
+ av_log(mxf->fc, AV_LOG_VERBOSE, "Multiple primer packs\n");
+ av_free(mxf->local_tags);
+ mxf->local_tags_count = 0;
+ mxf->local_tags = av_calloc(item_num, item_len);
if (!mxf->local_tags)
return AVERROR(ENOMEM);
+ mxf->local_tags_count = item_num;
avio_read(pb, mxf->local_tags, item_num*item_len);
return 0;
}
@@ -420,17 +491,15 @@ static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, U
static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
{
MXFContext *mxf = arg;
- MXFPartition *partition;
+ MXFPartition *partition, *tmp_part;
UID op;
uint64_t footer_partition;
uint32_t nb_essence_containers;
- int err;
- if ((err = av_reallocp_array(&mxf->partitions, mxf->partitions_count + 1,
- sizeof(*mxf->partitions))) < 0) {
- mxf->partitions_count = 0;
- return err;
- }
+ tmp_part = av_realloc_array(mxf->partitions, mxf->partitions_count + 1, sizeof(*mxf->partitions));
+ if (!tmp_part)
+ return AVERROR(ENOMEM);
+ mxf->partitions = tmp_part;
if (mxf->parsing_backward) {
/* insert the new partition pack in the middle
@@ -447,6 +516,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size
memset(partition, 0, sizeof(*partition));
mxf->partitions_count++;
partition->pack_length = avio_tell(pb) - klv_offset + size;
+ partition->pack_ofs = klv_offset;
switch(uid[13]) {
case 2:
@@ -476,7 +546,10 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size
partition->index_sid = avio_rb32(pb);
avio_skip(pb, 8);
partition->body_sid = avio_rb32(pb);
- avio_read(pb, op, sizeof(UID));
+ if (avio_read(pb, op, sizeof(UID)) != sizeof(UID)) {
+ av_log(mxf->fc, AV_LOG_ERROR, "Failed reading UID\n");
+ return AVERROR_INVALIDDATA;
+ }
nb_essence_containers = avio_rb32(pb);
if (partition->this_partition &&
@@ -519,6 +592,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size
partition->index_sid, partition->body_sid);
/* sanity check PreviousPartition if set */
+ //NOTE: this isn't actually enough, see mxf_seek_to_previous_partition()
if (partition->previous_partition &&
mxf->run_in + partition->previous_partition >= klv_offset) {
av_log(mxf->fc, AV_LOG_ERROR,
@@ -535,7 +609,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size
else if (op[12] == 3 && op[13] == 1) mxf->op = OP3a;
else if (op[12] == 3 && op[13] == 2) mxf->op = OP3b;
else if (op[12] == 3 && op[13] == 3) mxf->op = OP3c;
- else if (op[12] == 64&& op[13] == 1) mxf->op = OPSonyOpt;
+ else if (op[12] == 64&& op[13] == 1) mxf->op = OPSONYOpt;
else if (op[12] == 0x10) {
/* SMPTE 390m: "There shall be exactly one essence container"
* The following block deals with files that violate this, namely:
@@ -563,7 +637,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size
av_log(mxf->fc, AV_LOG_WARNING, "invalid KAGSize %"PRId32" - guessing ",
partition->kag_size);
- if (mxf->op == OPSonyOpt)
+ if (mxf->op == OPSONYOpt)
partition->kag_size = 512;
else
partition->kag_size = 1;
@@ -576,13 +650,12 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size
static int mxf_add_metadata_set(MXFContext *mxf, void *metadata_set)
{
- int err;
+ MXFMetadataSet **tmp;
- if ((err = av_reallocp_array(&mxf->metadata_sets, mxf->metadata_sets_count + 1,
- sizeof(*mxf->metadata_sets))) < 0) {
- mxf->metadata_sets_count = 0;
- return err;
- }
+ tmp = av_realloc_array(mxf->metadata_sets, mxf->metadata_sets_count + 1, sizeof(*mxf->metadata_sets));
+ if (!tmp)
+ return AVERROR(ENOMEM);
+ mxf->metadata_sets = tmp;
mxf->metadata_sets[mxf->metadata_sets_count] = metadata_set;
mxf->metadata_sets_count++;
return 0;
@@ -603,10 +676,11 @@ static int mxf_read_content_storage(void *arg, AVIOContext *pb, int tag, int siz
MXFContext *mxf = arg;
switch (tag) {
case 0x1901:
+ if (mxf->packages_refs)
+ av_log(mxf->fc, AV_LOG_VERBOSE, "Multiple packages_refs\n");
+ av_free(mxf->packages_refs);
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 */
@@ -638,19 +712,29 @@ static int mxf_read_source_clip(void *arg, AVIOContext *pb, int tag, int size, U
return 0;
}
-static int mxf_read_material_package(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
+static int mxf_read_timecode_component(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
{
- MXFPackage *package = arg;
+ MXFTimecodeComponent *mxf_timecode = arg;
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));
- if (!package->tracks_refs)
- return AVERROR(ENOMEM);
- avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
- avio_read(pb, (uint8_t *)package->tracks_refs, package->tracks_count * sizeof(UID));
+ 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_pulldown_component(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
+{
+ MXFPulldownComponent *mxf_pulldown = arg;
+ switch(tag) {
+ case 0x0d01:
+ avio_read(pb, mxf_pulldown->input_segment_ref, 16);
break;
}
return 0;
@@ -692,11 +776,11 @@ 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));
- if (!sequence->structural_components_refs)
+ sequence->structural_components_refs = av_calloc(sequence->structural_components_count, sizeof(UID));
+ if (!sequence->structural_components_refs) {
+ sequence->structural_components_count = 0;
return AVERROR(ENOMEM);
+ }
avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
avio_read(pb, (uint8_t *)sequence->structural_components_refs, sequence->structural_components_count * sizeof(UID));
break;
@@ -704,28 +788,70 @@ static int mxf_read_sequence(void *arg, AVIOContext *pb, int tag, int size, UID
return 0;
}
-static int mxf_read_source_package(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
+static int mxf_read_essence_group(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
+{
+ MXFEssenceGroup *essence_group = arg;
+ switch (tag) {
+ case 0x0202:
+ essence_group->duration = avio_rb64(pb);
+ break;
+ case 0x0501:
+ essence_group->structural_components_count = avio_rb32(pb);
+ essence_group->structural_components_refs = av_calloc(essence_group->structural_components_count, sizeof(UID));
+ if (!essence_group->structural_components_refs) {
+ essence_group->structural_components_count = 0;
+ return AVERROR(ENOMEM);
+ }
+ avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
+ avio_read(pb, (uint8_t *)essence_group->structural_components_refs, essence_group->structural_components_count * sizeof(UID));
+ break;
+ }
+ return 0;
+}
+
+static int mxf_read_utf16_string(AVIOContext *pb, int size, char** str)
+{
+ int ret;
+ size_t buf_size;
+
+ if (size < 0)
+ return AVERROR(EINVAL);
+
+ buf_size = size + size / 2 + 1;
+ *str = av_malloc(buf_size);
+ if (!*str)
+ return AVERROR(ENOMEM);
+
+ if ((ret = avio_get_str16be(pb, size, *str, buf_size)) < 0) {
+ av_freep(str);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int mxf_read_package(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
{
MXFPackage *package = arg;
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 */
avio_read(pb, (uint8_t *)package->tracks_refs, package->tracks_count * sizeof(UID));
break;
case 0x4401:
- /* UMID, only get last 16 bytes */
- avio_skip(pb, 16);
+ /* UMID */
+ avio_read(pb, package->package_ul, 16);
avio_read(pb, package->package_uid, 16);
break;
case 0x4701:
avio_read(pb, package->descriptor_ref, 16);
break;
+ case 0x4402:
+ return mxf_read_utf16_string(pb, size, &package->name);
}
return 0;
}
@@ -735,27 +861,14 @@ 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);
+ 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)))) {
av_freep(&segment->temporal_offset_entries);
+ av_freep(&segment->flag_entries);
return AVERROR(ENOMEM);
}
@@ -809,7 +922,7 @@ 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);
@@ -819,7 +932,8 @@ static void mxf_read_pixel_layout(AVIOContext *pb, MXFDescriptor *descriptor)
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);
@@ -828,18 +942,18 @@ static void mxf_read_pixel_layout(AVIOContext *pb, MXFDescriptor *descriptor)
static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
{
MXFDescriptor *descriptor = arg;
- descriptor->pix_fmt = AV_PIX_FMT_NONE;
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 */
avio_read(pb, (uint8_t *)descriptor->sub_descriptors_refs, descriptor->sub_descriptors_count * sizeof(UID));
break;
+ case 0x3002: /* ContainerDuration */
+ descriptor->duration = avio_rb64(pb);
+ break;
case 0x3004:
avio_read(pb, descriptor->essence_container_ul, 16);
break;
@@ -893,9 +1007,11 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int
default:
/* Private uid used by SONY C0023S01.mxf */
if (IS_KLV_KEY(uid, mxf_sony_mpeg4_extradata)) {
+ if (descriptor->extradata)
+ av_log(NULL, AV_LOG_WARNING, "Duplicate sony_mpeg4_extradata\n");
av_free(descriptor->extradata);
descriptor->extradata_size = 0;
- descriptor->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
+ descriptor->extradata = av_malloc(size);
if (!descriptor->extradata)
return AVERROR(ENOMEM);
descriptor->extradata_size = size;
@@ -950,6 +1066,7 @@ static const MXFCodecUL mxf_picture_essence_container_uls[] = {
{ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x04,0x60,0x01 }, 14, AV_CODEC_ID_MPEG2VIDEO }, /* MPEG-ES Frame wrapped */
{ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x02,0x41,0x01 }, 14, AV_CODEC_ID_DVVIDEO }, /* DV 625 25mbps */
{ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x05,0x00,0x00 }, 14, AV_CODEC_ID_RAWVIDEO }, /* Uncompressed Picture */
+ { { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0xff,0x4b,0x46,0x41,0x41,0x00,0x0d,0x4d,0x4f }, 14, AV_CODEC_ID_RAWVIDEO }, /* Legacy ?? Uncompressed Picture */
{ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE },
};
@@ -978,10 +1095,10 @@ static const MXFCodecUL mxf_sound_essence_container_uls[] = {
static const MXFCodecUL mxf_data_essence_container_uls[] = {
{ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x09,0x0d,0x01,0x03,0x01,0x02,0x0e,0x00,0x00 }, 16, 0 },
- { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x09,0x0d,0x01,0x03,0x01,0x02,0x0e,0x00,0x00 }, 16, AV_CODEC_ID_NONE },
+ { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE },
};
-static const char* mxf_data_essence_descriptor[] = {
+static const char* const mxf_data_essence_descriptor[] = {
"vbi_vanc_smpte_436M",
};
@@ -999,9 +1116,8 @@ static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segment
if (!nb_segments)
return AVERROR_INVALIDDATA;
- *sorted_segments = av_mallocz(nb_segments * sizeof(**sorted_segments));
- unsorted_segments = av_mallocz(nb_segments * sizeof(*unsorted_segments));
- if (!*sorted_segments || !unsorted_segments) {
+ if (!(unsorted_segments = av_calloc(nb_segments, sizeof(*unsorted_segments))) ||
+ !(*sorted_segments = av_calloc(nb_segments, sizeof(**sorted_segments)))) {
av_freep(sorted_segments);
av_free(unsorted_segments);
return AVERROR(ENOMEM);
@@ -1173,14 +1289,8 @@ static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_ta
if (index_table->nb_ptses <= 0)
return 0;
- if (index_table->nb_ptses > INT_MAX / sizeof(AVIndexEntry))
- return AVERROR(ENOMEM);
-
- index_table->ptses = av_mallocz(index_table->nb_ptses *
- sizeof(int64_t));
- index_table->fake_index = av_mallocz(index_table->nb_ptses *
- sizeof(AVIndexEntry));
- if (!index_table->ptses || !index_table->fake_index) {
+ if (!(index_table->ptses = av_calloc(index_table->nb_ptses, sizeof(int64_t))) ||
+ !(index_table->fake_index = av_calloc(index_table->nb_ptses, sizeof(AVIndexEntry)))) {
av_freep(&index_table->ptses);
return AVERROR(ENOMEM);
}
@@ -1355,18 +1465,244 @@ 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_uid_to_str(UID uid, char **str)
+{
+ int i;
+ char *p;
+ p = *str = av_mallocz(sizeof(UID) * 2 + 4 + 1);
+ if (!p)
+ return AVERROR(ENOMEM);
+ for (i = 0; i < sizeof(UID); i++) {
+ snprintf(p, 2 + 1, "%.2x", uid[i]);
+ p += 2;
+ if (i == 3 || i == 5 || i == 7 || i == 9) {
+ snprintf(p, 1 + 1, "-");
+ p++;
+ }
+ }
+ return 0;
+}
+
+static int mxf_umid_to_str(UID ul, UID uid, char **str)
+{
+ int i;
+ char *p;
+ p = *str = av_mallocz(sizeof(UID) * 4 + 2 + 1);
+ if (!p)
+ return AVERROR(ENOMEM);
+ snprintf(p, 2 + 1, "0x");
+ p += 2;
+ for (i = 0; i < sizeof(UID); i++) {
+ snprintf(p, 2 + 1, "%.2X", ul[i]);
+ p += 2;
+
+ }
+ for (i = 0; i < sizeof(UID); i++) {
+ snprintf(p, 2 + 1, "%.2X", uid[i]);
+ p += 2;
+ }
+ return 0;
+}
+
+static int mxf_add_umid_metadata(AVDictionary **pm, const char *key, MXFPackage* package)
+{
+ char *str;
+ int ret;
+ if (!package)
+ return 0;
+ if ((ret = mxf_umid_to_str(package->package_ul, package->package_uid, &str)) < 0)
+ return ret;
+ av_dict_set(pm, key, str, AV_DICT_DONT_STRDUP_VAL);
+ return 0;
+}
+
+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 MXFTimecodeComponent* mxf_resolve_timecode_component(MXFContext *mxf, UID *strong_ref)
+{
+ MXFStructuralComponent *component = NULL;
+ MXFPulldownComponent *pulldown = NULL;
+
+ component = mxf_resolve_strong_ref(mxf, strong_ref, AnyType);
+ if (!component)
+ return NULL;
+
+ switch (component->type) {
+ case TimecodeComponent:
+ return (MXFTimecodeComponent*)component;
+ case PulldownComponent: /* timcode component may be located on a pulldown component */
+ pulldown = (MXFPulldownComponent*)component;
+ return mxf_resolve_strong_ref(mxf, &pulldown->input_segment_ref, TimecodeComponent);
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static MXFPackage* mxf_resolve_source_package(MXFContext *mxf, UID package_uid)
+{
+ MXFPackage *package = NULL;
+ int i;
+
+ for (i = 0; i < mxf->packages_count; i++) {
+ package = mxf_resolve_strong_ref(mxf, &mxf->packages_refs[i], SourcePackage);
+ if (!package)
+ continue;
+
+ if (!memcmp(package->package_uid, package_uid, 16))
+ return package;
+ }
+ return NULL;
+}
+
+static MXFDescriptor* mxf_resolve_multidescriptor(MXFContext *mxf, MXFDescriptor *descriptor, int track_id)
+{
+ MXFDescriptor *sub_descriptor = NULL;
+ int i;
+
+ if (!descriptor)
+ return NULL;
+
+ if (descriptor->type == MultipleDescriptor) {
+ for (i = 0; i < descriptor->sub_descriptors_count; i++) {
+ sub_descriptor = mxf_resolve_strong_ref(mxf, &descriptor->sub_descriptors_refs[i], Descriptor);
+
+ if (!sub_descriptor) {
+ av_log(mxf->fc, AV_LOG_ERROR, "could not resolve sub descriptor strong ref\n");
+ continue;
+ }
+ if (sub_descriptor->linked_track_id == track_id) {
+ return sub_descriptor;
+ }
+ }
+ } else if (descriptor->type == Descriptor)
+ return descriptor;
+
+ return NULL;
+}
+
+static MXFStructuralComponent* mxf_resolve_essence_group_choice(MXFContext *mxf, MXFEssenceGroup *essence_group)
+{
+ MXFStructuralComponent *component = NULL;
+ MXFPackage *package = NULL;
+ MXFDescriptor *descriptor = NULL;
+ int i;
+
+ if (!essence_group || !essence_group->structural_components_count)
+ return NULL;
+
+ /* essence groups contains multiple representations of the same media,
+ this return the first components with a valid Descriptor typically index 0 */
+ for (i =0; i < essence_group->structural_components_count; i++){
+ component = mxf_resolve_strong_ref(mxf, &essence_group->structural_components_refs[i], SourceClip);
+ if (!component)
+ continue;
+
+ if (!(package = mxf_resolve_source_package(mxf, component->source_package_uid)))
+ continue;
+
+ descriptor = mxf_resolve_strong_ref(mxf, &package->descriptor_ref, Descriptor);
+ if (descriptor)
+ return component;
+ }
+ return NULL;
+}
+
+static MXFStructuralComponent* mxf_resolve_sourceclip(MXFContext *mxf, UID *strong_ref)
+{
+ MXFStructuralComponent *component = NULL;
+
+ component = mxf_resolve_strong_ref(mxf, strong_ref, AnyType);
+ if (!component)
+ return NULL;
+ switch (component->type) {
+ case SourceClip:
+ return component;
+ case EssenceGroup:
+ return mxf_resolve_essence_group_choice(mxf, (MXFEssenceGroup*) component);
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static int mxf_parse_physical_source_package(MXFContext *mxf, MXFTrack *source_track, AVStream *st)
+{
+ MXFPackage *physical_package = NULL;
+ MXFTrack *physical_track = NULL;
+ MXFStructuralComponent *sourceclip = NULL;
+ MXFTimecodeComponent *mxf_tc = NULL;
+ int i, j, k;
+ AVTimecode tc;
+ int flags;
+ int64_t start_position;
+
+ for (i = 0; i < source_track->sequence->structural_components_count; i++) {
+ sourceclip = mxf_resolve_strong_ref(mxf, &source_track->sequence->structural_components_refs[i], SourceClip);
+ if (!sourceclip)
+ continue;
+
+ if (!(physical_package = mxf_resolve_source_package(mxf, sourceclip->source_package_uid)))
+ break;
+
+ mxf_add_umid_metadata(&st->metadata, "reel_umid", physical_package);
+
+ /* the name of physical source package is name of the reel or tape */
+ if (physical_package->name && physical_package->name[0])
+ av_dict_set(&st->metadata, "reel_name", physical_package->name, 0);
+
+ /* the source timecode is calculated by adding the start_position of the sourceclip from the file source package track
+ * to the start_frame of the timecode component located on one of the tracks of the physical source package.
+ */
+ for (j = 0; j < physical_package->tracks_count; j++) {
+ if (!(physical_track = mxf_resolve_strong_ref(mxf, &physical_package->tracks_refs[j], Track))) {
+ av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track strong ref\n");
+ continue;
+ }
+
+ if (!(physical_track->sequence = mxf_resolve_strong_ref(mxf, &physical_track->sequence_ref, Sequence))) {
+ av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track sequence strong ref\n");
+ continue;
+ }
+
+ for (k = 0; k < physical_track->sequence->structural_components_count; k++) {
+ if (!(mxf_tc = mxf_resolve_timecode_component(mxf, &physical_track->sequence->structural_components_refs[k])))
+ continue;
+
+ flags = mxf_tc->drop_frame == 1 ? AV_TIMECODE_FLAG_DROPFRAME : 0;
+ /* scale sourceclip start_position to match physical track edit rate */
+ start_position = av_rescale_q(sourceclip->start_position,
+ physical_track->edit_rate,
+ source_track->edit_rate);
+
+ if (av_timecode_init(&tc, mxf_tc->rate, flags, start_position + mxf_tc->start_frame, mxf->fc) == 0) {
+ mxf_add_timecode_metadata(&st->metadata, "timecode", &tc);
+ return 0;
+ }
+ }
+ }
+ }
+
+ return 0;
}
static int mxf_parse_structural_metadata(MXFContext *mxf)
{
MXFPackage *material_package = NULL;
- MXFPackage *temp_package = NULL;
int i, j, k, ret;
av_dlog(mxf->fc, "metadata sets count %d\n", mxf->metadata_sets_count);
@@ -1380,6 +1716,10 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
return AVERROR_INVALIDDATA;
}
+ mxf_add_umid_metadata(&mxf->fc->metadata, "material_package_umid", material_package);
+ if (material_package->name && material_package->name[0])
+ av_dict_set(&mxf->fc->metadata, "material_package_name", material_package->name, 0);
+
for (i = 0; i < material_package->tracks_count; i++) {
MXFPackage *source_package = NULL;
MXFTrack *material_track = NULL;
@@ -1387,38 +1727,53 @@ 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;
}
- /* 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);
+ component = mxf_resolve_strong_ref(mxf, &material_track->sequence->structural_components_refs[j], TimecodeComponent);
if (!component)
continue;
- for (k = 0; k < mxf->packages_count; k++) {
- temp_package = mxf_resolve_strong_ref(mxf, &mxf->packages_refs[k], SourcePackage);
- if (!temp_package)
- continue;
- if (!memcmp(temp_package->package_uid, component->source_package_uid, 16)) {
- source_package = temp_package;
- break;
- }
+ 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++) {
+ component = mxf_resolve_sourceclip(mxf, &material_track->sequence->structural_components_refs[j]);
+ if (!component)
+ continue;
+
+ source_package = mxf_resolve_source_package(mxf, component->source_package_uid);
if (!source_package) {
av_dlog(mxf->fc, "material track %d: no corresponding source package found\n", material_track->track_id);
break;
@@ -1463,7 +1818,17 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
}
st->id = source_track->track_id;
st->priv_data = source_track;
- source_track->original_duration = st->duration = component->duration;
+
+ source_package->descriptor = mxf_resolve_strong_ref(mxf, &source_package->descriptor_ref, AnyType);
+ descriptor = mxf_resolve_multidescriptor(mxf, source_package->descriptor, source_track->track_id);
+
+ /* A SourceClip from a EssenceGroup may only be a single frame of essence data. The clips duration is then how many
+ * frames its suppose to repeat for. Descriptor->duration, if present, contains the real duration of the essence data */
+ if (descriptor && descriptor->duration != AV_NOPTS_VALUE)
+ source_track->original_duration = st->duration = FFMIN(descriptor->duration, component->duration);
+ else
+ source_track->original_duration = st->duration = component->duration;
+
if (st->duration == -1)
st->duration = AV_NOPTS_VALUE;
st->start_time = component->start_position;
@@ -1486,24 +1851,6 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
codec_ul = mxf_get_codec_ul(ff_mxf_data_definition_uls, &source_track->sequence->data_definition_ul);
st->codec->codec_type = codec_ul->id;
- source_package->descriptor = mxf_resolve_strong_ref(mxf, &source_package->descriptor_ref, AnyType);
- if (source_package->descriptor) {
- if (source_package->descriptor->type == MultipleDescriptor) {
- for (j = 0; j < source_package->descriptor->sub_descriptors_count; j++) {
- MXFDescriptor *sub_descriptor = mxf_resolve_strong_ref(mxf, &source_package->descriptor->sub_descriptors_refs[j], Descriptor);
-
- if (!sub_descriptor) {
- av_log(mxf->fc, AV_LOG_ERROR, "could not resolve sub descriptor strong ref\n");
- continue;
- }
- if (sub_descriptor->linked_track_id == source_track->track_id) {
- descriptor = sub_descriptor;
- break;
- }
- }
- } else if (source_package->descriptor->type == Descriptor)
- descriptor = source_package->descriptor;
- }
if (!descriptor) {
av_log(mxf->fc, AV_LOG_INFO, "source track %d: stream %d, no descriptor found\n", source_track->track_id, st->index);
continue;
@@ -1526,7 +1873,22 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
/* TODO: drop PictureEssenceCoding and SoundEssenceCompression, only check EssenceContainer */
codec_ul = mxf_get_codec_ul(ff_mxf_codec_uls, &descriptor->essence_codec_ul);
- st->codec->codec_id = codec_ul->id;
+ st->codec->codec_id = (enum AVCodecID)codec_ul->id;
+ av_log(mxf->fc, AV_LOG_VERBOSE, "%s: Universal Label: ",
+ avcodec_get_name(st->codec->codec_id));
+ for (k = 0; k < 16; k++) {
+ av_log(mxf->fc, AV_LOG_VERBOSE, "%.2x",
+ descriptor->essence_codec_ul[k]);
+ if (!(k+1 & 19) || k == 5)
+ av_log(mxf->fc, AV_LOG_VERBOSE, ".");
+ }
+ av_log(mxf->fc, AV_LOG_VERBOSE, "\n");
+
+ mxf_add_umid_metadata(&st->metadata, "file_package_umid", source_package);
+ if (source_package->name && source_package->name[0])
+ av_dict_set(&st->metadata, "file_package_name", source_package->name, 0);
+
+ mxf_parse_physical_source_package(mxf, source_track, st);
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
source_track->intra_only = mxf_is_intra_only(descriptor);
@@ -1534,28 +1896,24 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
if (st->codec->codec_id == AV_CODEC_ID_NONE)
st->codec->codec_id = container_ul->id;
st->codec->width = descriptor->width;
- /* Field height, not frame height */
- st->codec->height = descriptor->height;
+ st->codec->height = descriptor->height; /* Field height, not frame height */
switch (descriptor->frame_layout) {
case SegmentedFrame:
/* This one is a weird layout I don't fully understand. */
- av_log(mxf->fc, AV_LOG_INFO,
- "SegmentedFrame layout isn't currently supported\n");
+ av_log(mxf->fc, AV_LOG_INFO, "SegmentedFrame layout isn't currently supported\n");
break;
case FullFrame:
st->codec->field_order = AV_FIELD_PROGRESSIVE;
break;
case OneField:
/* Every other line is stored and needs to be duplicated. */
- av_log(mxf->fc, AV_LOG_INFO,
- "OneField frame layout isn't currently supported\n");
+ av_log(mxf->fc, AV_LOG_INFO, "OneField frame layout isn't currently supported\n");
+ break; /* The correct thing to do here is fall through, but by breaking we might be
+ able to decode some streams at half the vertical resolution, rather than not al all.
+ It's also for compatibility with the old behavior. */
+ case MixedFields:
break;
- /* The correct thing to do here is fall through, but by
- * breaking we might be able to decode some streams at half
- * the vertical resolution, rather than not al all.
- * It's also for compatibility with the old behavior. */
case SeparateFields:
- case MixedFields:
switch (descriptor->field_dominance) {
case MXF_TFF:
st->codec->field_order = AV_FIELD_TT;
@@ -1567,22 +1925,21 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
avpriv_request_sample(mxf->fc,
"Field dominance %d support",
descriptor->field_dominance);
+ case 0: // we already have many samples with field_dominance == unknown
break;
}
/* Turn field height into frame height. */
st->codec->height *= 2;
break;
default:
- av_log(mxf->fc, AV_LOG_INFO,
- "Unknown frame layout type: %d\n",
- descriptor->frame_layout);
+ av_log(mxf->fc, AV_LOG_INFO, "Unknown frame layout type: %d\n", descriptor->frame_layout);
}
if (st->codec->codec_id == AV_CODEC_ID_RAWVIDEO) {
st->codec->pix_fmt = descriptor->pix_fmt;
if (st->codec->pix_fmt == AV_PIX_FMT_NONE) {
pix_fmt_ul = mxf_get_codec_ul(ff_mxf_pixel_format_uls,
&descriptor->essence_codec_ul);
- st->codec->pix_fmt = pix_fmt_ul->id;
+ st->codec->pix_fmt = (enum AVPixelFormat)pix_fmt_ul->id;
if (st->codec->pix_fmt == AV_PIX_FMT_NONE) {
/* support files created before RP224v10 by defaulting to UYVY422
if subsampling is 4:2:2 and component depth is 8-bit */
@@ -1596,19 +1953,18 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
}
st->need_parsing = AVSTREAM_PARSE_HEADERS;
if (material_track->sequence->origin) {
- char material_origin[3];
- snprintf(material_origin, sizeof(material_origin), "%d", material_track->sequence->origin);
- av_dict_set(&st->metadata, "material_track_origin", material_origin, 0);
+ av_dict_set_int(&st->metadata, "material_track_origin", material_track->sequence->origin, 0);
}
if (source_track->sequence->origin) {
- char source_origin[3];
- snprintf(source_origin, sizeof(source_origin), "%d", source_track->sequence->origin);
- av_dict_set(&st->metadata, "source_track_origin", source_origin, 0);
+ av_dict_set_int(&st->metadata, "source_track_origin", source_track->sequence->origin, 0);
}
+ if (descriptor->aspect_ratio.num && descriptor->aspect_ratio.den)
+ st->display_aspect_ratio = descriptor->aspect_ratio;
} else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
container_ul = mxf_get_codec_ul(mxf_sound_essence_container_uls, essence_container_ul);
- if (st->codec->codec_id == AV_CODEC_ID_NONE)
- st->codec->codec_id = container_ul->id;
+ /* Only overwrite existing codec ID if it is unset or A-law, which is the default according to SMPTE RP 224. */
+ if (st->codec->codec_id == AV_CODEC_ID_NONE || (st->codec->codec_id == AV_CODEC_ID_PCM_ALAW && (enum AVCodecID)container_ul->id != AV_CODEC_ID_NONE))
+ st->codec->codec_id = (enum AVCodecID)container_ul->id;
st->codec->channels = descriptor->channels;
st->codec->bits_per_coded_sample = descriptor->bits_per_sample;
@@ -1653,10 +2009,8 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
}
}
if (descriptor->extradata) {
- st->codec->extradata = av_mallocz(descriptor->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
- if (st->codec->extradata) {
+ if (!ff_alloc_extradata(st->codec, descriptor->extradata_size)) {
memcpy(st->codec->extradata, descriptor->extradata, descriptor->extradata_size);
- st->codec->extradata_size = descriptor->extradata_size;
}
} else if (st->codec->codec_id == AV_CODEC_ID_H264) {
ret = ff_generate_avci_extradata(st);
@@ -1674,45 +2028,6 @@ fail_and_free:
return ret;
}
-static int mxf_read_utf16_string(AVIOContext *pb, int size, char** str)
-{
- int ret;
- size_t buf_size;
-
- if (size < 0)
- return AVERROR(EINVAL);
-
- buf_size = size + size / 2 + 1;
- *str = av_malloc(buf_size);
- if (!*str)
- return AVERROR(ENOMEM);
-
- if ((ret = avio_get_str16be(pb, size, *str, buf_size)) < 0) {
- av_freep(str);
- return ret;
- }
-
- return ret;
-}
-
-static int mxf_uid_to_str(UID uid, char **str)
-{
- int i;
- char *p;
- p = *str = av_mallocz(sizeof(UID) * 2 + 4 + 1);
- if (!p)
- return AVERROR(ENOMEM);
- for (i = 0; i < sizeof(UID); i++) {
- snprintf(p, 2 + 1, "%.2x", uid[i]);
- p += 2;
- if (i == 3 || i == 5 || i == 7 || i == 9) {
- snprintf(p, 1 + 1, "-");
- p++;
- }
- }
- return 0;
-}
-
static int mxf_timestamp_to_str(uint64_t timestamp, char **str)
{
struct tm time = { 0 };
@@ -1798,9 +2113,21 @@ static int mxf_read_identification_metadata(void *arg, AVIOContext *pb, int tag,
return 0;
}
+static int mxf_read_preface_metadata(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
+{
+ MXFContext *mxf = arg;
+ AVFormatContext *s = mxf->fc;
+ int ret;
+ char *str = NULL;
+
+ if (tag >= 0x8000 && (IS_KLV_KEY(uid, mxf_avid_project_name))) {
+ SET_STR_METADATA(pb, "project_name", str);
+ }
+ return 0;
+}
+
static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = {
{ { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x05,0x01,0x00 }, mxf_read_primer_pack },
-// { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x2f,0x00 }, mxf_read_preface_pack },
{ { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x01,0x00 }, mxf_read_partition_pack },
{ { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x02,0x00 }, mxf_read_partition_pack },
{ { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x03,0x00 }, mxf_read_partition_pack },
@@ -1811,11 +2138,13 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = {
{ { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x03,0x04,0x00 }, mxf_read_partition_pack },
{ { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x04,0x02,0x00 }, mxf_read_partition_pack },
{ { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x04,0x04,0x00 }, mxf_read_partition_pack },
+ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x2f,0x00 }, mxf_read_preface_metadata },
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x30,0x00 }, mxf_read_identification_metadata },
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x18,0x00 }, mxf_read_content_storage, 0, AnyType },
- { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x37,0x00 }, mxf_read_source_package, sizeof(MXFPackage), SourcePackage },
- { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x36,0x00 }, mxf_read_material_package, sizeof(MXFPackage), MaterialPackage },
- { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x0F,0x00 }, mxf_read_sequence, sizeof(MXFSequence), Sequence },
+ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x37,0x00 }, mxf_read_package, sizeof(MXFPackage), SourcePackage },
+ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x36,0x00 }, mxf_read_package, sizeof(MXFPackage), MaterialPackage },
+ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x0f,0x00 }, mxf_read_sequence, sizeof(MXFSequence), Sequence },
+ { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x05,0x00 }, mxf_read_essence_group, sizeof(MXFEssenceGroup), EssenceGroup},
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x11,0x00 }, mxf_read_source_clip, sizeof(MXFStructuralComponent), SourceClip },
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x44,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), MultipleDescriptor },
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x42,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* Generic Sound */
@@ -1829,11 +2158,27 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = {
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5e,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* MPEG2AudioDescriptor */
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3A,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Static Track */
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3B,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Generic Track */
+ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x14,0x00 }, mxf_read_timecode_component, sizeof(MXFTimecodeComponent), TimecodeComponent },
+ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x0c,0x00 }, mxf_read_pulldown_component, sizeof(MXFPulldownComponent), PulldownComponent },
{ { 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 },
};
+static int mxf_metadataset_init(MXFMetadataSet *ctx, enum MXFMetadataSetType type)
+{
+ switch (type){
+ case MultipleDescriptor:
+ case Descriptor:
+ ((MXFDescriptor*)ctx)->pix_fmt = AV_PIX_FMT_NONE;
+ ((MXFDescriptor*)ctx)->duration = AV_NOPTS_VALUE;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadFunc *read_child, int ctx_size, enum MXFMetadataSetType type)
{
AVIOContext *pb = mxf->fc->pb;
@@ -1842,7 +2187,8 @@ 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) {
+ mxf_metadataset_init(ctx, type);
+ while (avio_tell(pb) + 4 < klv_end && !avio_feof(pb)) {
int ret;
int tag = avio_rb16(pb);
int size = avio_rb16(pb); /* KLV specified by 0x53 */
@@ -1865,16 +2211,20 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF
}
}
}
- if (ctx_size && tag == 0x3C0A)
+ if (ctx_size && tag == 0x3C0A) {
avio_read(pb, ctx->uid, 16);
- else if ((ret = read_child(ctx, pb, tag, size, uid, -1)) < 0)
+ } else if ((ret = read_child(ctx, pb, tag, size, uid, -1)) < 0) {
+ mxf_free_metadataset(&ctx, !!ctx_size);
return ret;
+ }
/* 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);
+ if (ctx_size) {
+ ctx->type = type;
+ mxf_free_metadataset(&ctx, !!ctx_size);
+ }
av_log(mxf->fc, AV_LOG_ERROR,
"local tag %#04x extends past end of local set @ %#"PRIx64"\n",
@@ -1888,23 +2238,98 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF
}
/**
- * Seeks to the previous partition, if possible
+ * Matches any partition pack key, in other words:
+ * - HeaderPartition
+ * - BodyPartition
+ * - FooterPartition
+ * @return non-zero if the key is a partition pack key, zero otherwise
+ */
+static int mxf_is_partition_pack_key(UID key)
+{
+ //NOTE: this is a little lax since it doesn't constraint key[14]
+ return !memcmp(key, mxf_header_partition_pack_key, 13) &&
+ key[13] >= 2 && key[13] <= 4;
+}
+
+/**
+ * Parses a metadata KLV
+ * @return <0 on error, 0 otherwise
+ */
+static int mxf_parse_klv(MXFContext *mxf, KLVPacket klv, MXFMetadataReadFunc *read,
+ int ctx_size, enum MXFMetadataSetType type)
+{
+ AVFormatContext *s = mxf->fc;
+ int res;
+ if (klv.key[5] == 0x53) {
+ res = mxf_read_local_tags(mxf, &klv, read, ctx_size, type);
+ } else {
+ uint64_t next = avio_tell(s->pb) + klv.length;
+ res = read(mxf, s->pb, 0, klv.length, klv.key, klv.offset);
+
+ /* only seek forward, else this can loop for a long time */
+ if (avio_tell(s->pb) > next) {
+ av_log(s, AV_LOG_ERROR, "read past end of KLV @ %#"PRIx64"\n",
+ klv.offset);
+ return AVERROR_INVALIDDATA;
+ }
+
+ avio_seek(s->pb, next, SEEK_SET);
+ }
+ if (res < 0) {
+ av_log(s, AV_LOG_ERROR, "error reading header metadata\n");
+ return res;
+ }
+ return 0;
+}
+
+/**
+ * Seeks to the previous partition and parses it, if possible
* @return <= 0 if we should stop parsing, > 0 if we should keep going
*/
static int mxf_seek_to_previous_partition(MXFContext *mxf)
{
AVIOContext *pb = mxf->fc->pb;
+ KLVPacket klv;
+ int64_t current_partition_ofs;
+ int ret;
if (!mxf->current_partition ||
mxf->run_in + mxf->current_partition->previous_partition <= mxf->last_forward_tell)
return 0; /* we've parsed all partitions */
/* seek to previous partition */
+ current_partition_ofs = mxf->current_partition->pack_ofs; //includes run-in
avio_seek(pb, mxf->run_in + mxf->current_partition->previous_partition, SEEK_SET);
mxf->current_partition = NULL;
av_dlog(mxf->fc, "seeking to previous partition\n");
+ /* Make sure this is actually a PartitionPack, and if so parse it.
+ * See deadlock2.mxf
+ */
+ if ((ret = klv_read_packet(&klv, pb)) < 0) {
+ av_log(mxf->fc, AV_LOG_ERROR, "failed to read PartitionPack KLV\n");
+ return ret;
+ }
+
+ if (!mxf_is_partition_pack_key(klv.key)) {
+ av_log(mxf->fc, AV_LOG_ERROR, "PreviousPartition @ %" PRIx64 " isn't a PartitionPack\n", klv.offset);
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* We can't just check ofs >= current_partition_ofs because PreviousPartition
+ * can point to just before the current partition, causing klv_read_packet()
+ * to sync back up to it. See deadlock3.mxf
+ */
+ if (klv.offset >= current_partition_ofs) {
+ av_log(mxf->fc, AV_LOG_ERROR, "PreviousPartition for PartitionPack @ %"
+ PRIx64 " indirectly points to itself\n", current_partition_ofs);
+ return AVERROR_INVALIDDATA;
+ }
+
+ if ((ret = mxf_parse_klv(mxf, klv, mxf_read_partition_pack, 0, 0)) < 0)
+ return ret;
+
return 1;
}
@@ -1920,30 +2345,27 @@ static int mxf_parse_handle_essence(MXFContext *mxf)
if (mxf->parsing_backward) {
return mxf_seek_to_previous_partition(mxf);
} else {
- uint64_t offset = mxf->footer_partition ? mxf->footer_partition
- : mxf->last_partition;
-
- if (!offset) {
- av_dlog(mxf->fc, "no last partition\n");
+ if (!mxf->footer_partition) {
+ av_dlog(mxf->fc, "no FooterPartition\n");
return 0;
}
- av_dlog(mxf->fc, "seeking to last partition\n");
+ av_dlog(mxf->fc, "seeking to FooterPartition\n");
/* remember where we were so we don't end up seeking further back than this */
mxf->last_forward_tell = avio_tell(pb);
if (!pb->seekable) {
- av_log(mxf->fc, AV_LOG_INFO, "file is not seekable - not parsing last partition\n");
+ av_log(mxf->fc, AV_LOG_INFO, "file is not seekable - not parsing FooterPartition\n");
return -1;
}
- /* seek to last partition and parse backward */
- if ((ret = avio_seek(pb, mxf->run_in + offset, SEEK_SET)) < 0) {
+ /* seek to FooterPartition and parse backward */
+ if ((ret = avio_seek(pb, mxf->run_in + mxf->footer_partition, SEEK_SET)) < 0) {
av_log(mxf->fc, AV_LOG_ERROR,
- "failed to seek to last partition @ 0x%" PRIx64
+ "failed to seek to FooterPartition @ 0x%" PRIx64
" (%"PRId64") - partial file?\n",
- mxf->run_in + offset, ret);
+ mxf->run_in + mxf->footer_partition, ret);
return ret;
}
@@ -1964,8 +2386,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)
{
@@ -1982,7 +2403,7 @@ static void mxf_compute_essence_containers(MXFContext *mxf)
continue; /* BodySID == 0 -> no essence */
if (x >= mxf->partitions_count - 1)
- break; /* last partition - can't compute length (and we don't need to) */
+ break; /* FooterPartition - can't compute length (and we don't need to) */
/* essence container spans to the next partition */
p->essence_length = mxf->partitions[x+1].this_partition - p->essence_offset;
@@ -2005,38 +2426,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 */
@@ -2073,20 +2462,95 @@ static void mxf_handle_small_eubc(AVFormatContext *s)
mxf->edit_units_per_packet = 1920;
}
+/**
+ * Deal with the case where OPAtom files does not have any IndexTableSegments.
+ */
+static int mxf_handle_missing_index_segment(MXFContext *mxf)
+{
+ AVFormatContext *s = mxf->fc;
+ AVStream *st = NULL;
+ MXFIndexTableSegment *segment = NULL;
+ MXFPartition *p = NULL;
+ int essence_partition_count = 0;
+ int i, ret;
+
+ if (mxf->op != OPAtom)
+ return 0;
+
+ /* TODO: support raw video without a index if they exist */
+ if (s->nb_streams != 1 || s->streams[0]->codec->codec_type != AVMEDIA_TYPE_AUDIO || !is_pcm(s->streams[0]->codec->codec_id))
+ return 0;
+
+ /* check if file already has a IndexTableSegment */
+ for (i = 0; i < mxf->metadata_sets_count; i++) {
+ if (mxf->metadata_sets[i]->type == IndexTableSegment)
+ return 0;
+ }
+
+ /* find the essence partition */
+ for (i = 0; i < mxf->partitions_count; i++) {
+ /* BodySID == 0 -> no essence */
+ if (!mxf->partitions[i].body_sid)
+ continue;
+
+ p = &mxf->partitions[i];
+ essence_partition_count++;
+ }
+
+ /* only handle files with a single essence partition */
+ if (essence_partition_count != 1)
+ return 0;
+
+ if (!(segment = av_mallocz(sizeof(*segment))))
+ return AVERROR(ENOMEM);
+
+ if ((ret = mxf_add_metadata_set(mxf, segment))) {
+ mxf_free_metadataset((MXFMetadataSet**)&segment, 1);
+ return ret;
+ }
+
+ st = s->streams[0];
+ segment->type = IndexTableSegment;
+ /* stream will be treated as small EditUnitByteCount */
+ segment->edit_unit_byte_count = (av_get_bits_per_sample(st->codec->codec_id) * st->codec->channels) >> 3;
+ segment->index_start_position = 0;
+ segment->index_duration = s->streams[0]->duration;
+ segment->index_sid = p->index_sid;
+ segment->body_sid = p->body_sid;
+ return 0;
+}
+
static void mxf_read_random_index_pack(AVFormatContext *s)
{
MXFContext *mxf = s->priv_data;
uint32_t length;
- int64_t file_size;
+ int64_t file_size, max_rip_length, min_rip_length;
KLVPacket klv;
if (!s->pb->seekable)
return;
file_size = avio_size(s->pb);
+
+ /* S377m says to check the RIP length for "silly" values, without defining "silly".
+ * The limit below assumes a file with nothing but partition packs and a RIP.
+ * Before changing this, consider that a muxer may place each sample in its own partition.
+ *
+ * 105 is the size of the smallest possible PartitionPack
+ * 12 is the size of each RIP entry
+ * 28 is the size of the RIP header and footer, assuming an 8-byte BER
+ */
+ max_rip_length = ((file_size - mxf->run_in) / 105) * 12 + 28;
+ max_rip_length = FFMIN(max_rip_length, INT_MAX); //2 GiB and up is also silly
+
+ /* We're only interested in RIPs with at least two entries.. */
+ min_rip_length = 16+1+24+4;
+
+ /* See S377m section 11 */
avio_seek(s->pb, file_size - 4, SEEK_SET);
length = avio_rb32(s->pb);
- if (length <= 32 || length >= FFMIN(file_size, INT_MAX))
+
+ if (length < min_rip_length || length > max_rip_length)
goto end;
avio_seek(s->pb, file_size - length, SEEK_SET);
if (klv_read_packet(&klv, s->pb) < 0 ||
@@ -2095,7 +2559,13 @@ static void mxf_read_random_index_pack(AVFormatContext *s)
goto end;
avio_skip(s->pb, klv.length - 12);
- mxf->last_partition = avio_rb64(s->pb);
+ mxf->footer_partition = avio_rb64(s->pb);
+
+ /* sanity check */
+ if (mxf->run_in + mxf->footer_partition >= file_size) {
+ av_log(s, AV_LOG_WARNING, "bad FooterPartition in RIP - ignoring\n");
+ mxf->footer_partition = 0;
+ }
end:
avio_seek(s->pb, mxf->run_in, SEEK_SET);
@@ -2121,8 +2591,7 @@ static int mxf_read_header(AVFormatContext *s)
mxf_read_random_index_pack(s);
- while (!s->pb->eof_reached) {
-
+ while (!avio_feof(s->pb)) {
const MXFMetadataReadTableEntry *metadata;
if (klv_read_packet(&klv, s->pb) < 0) {
@@ -2141,13 +2610,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)
@@ -2157,8 +2645,7 @@ static int mxf_read_header(AVFormatContext *s)
if (mxf_parse_handle_essence(mxf) <= 0)
break;
continue;
- } else if (!memcmp(klv.key, mxf_header_partition_pack_key, 13) &&
- klv.key[13] >= 2 && klv.key[13] <= 4 && mxf->current_partition) {
+ } else if (mxf_is_partition_pack_key(klv.key) && mxf->current_partition) {
/* next partition pack - keep going, seek to previous partition or stop */
if(mxf_parse_handle_partition_or_eof(mxf) <= 0)
break;
@@ -2169,26 +2656,8 @@ static int mxf_read_header(AVFormatContext *s)
for (metadata = mxf_metadata_read_table; metadata->read; metadata++) {
if (IS_KLV_KEY(klv.key, metadata->key)) {
- int res;
- if (klv.key[5] == 0x53) {
- res = mxf_read_local_tags(mxf, &klv, metadata->read, metadata->ctx_size, metadata->type);
- } else {
- uint64_t next = avio_tell(s->pb) + klv.length;
- res = metadata->read(mxf, s->pb, 0, klv.length, klv.key, klv.offset);
-
- /* only seek forward, else this can loop for a long time */
- if (avio_tell(s->pb) > next) {
- av_log(s, AV_LOG_ERROR, "read past end of KLV @ %#"PRIx64"\n",
- klv.offset);
- return AVERROR_INVALIDDATA;
- }
-
- avio_seek(s->pb, next, SEEK_SET);
- }
- if (res < 0) {
- av_log(s, AV_LOG_ERROR, "error reading header metadata\n");
- return res;
- }
+ if ((ret = mxf_parse_klv(mxf, klv, metadata->read, metadata->ctx_size, metadata->type)) < 0)
+ goto fail;
break;
} else {
av_log(s, AV_LOG_VERBOSE, "Dark key " PRIxUID "\n",
@@ -2201,7 +2670,8 @@ static int mxf_read_header(AVFormatContext *s)
/* FIXME avoid seek */
if (!essence_offset) {
av_log(s, AV_LOG_ERROR, "no essence\n");
- return AVERROR_INVALIDDATA;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
}
avio_seek(s->pb, essence_offset, SEEK_SET);
@@ -2210,10 +2680,11 @@ static int mxf_read_header(AVFormatContext *s)
/* we need to do this before computing the index tables
* to be able to fill in zero IndexDurations with st->duration */
if ((ret = mxf_parse_structural_metadata(mxf)) < 0)
- return ret;
+ goto fail;
+ mxf_handle_missing_index_segment(mxf);
if ((ret = mxf_compute_index_tables(mxf)) < 0)
- return ret;
+ goto fail;
if (mxf->nb_index_tables > 1) {
/* TODO: look up which IndexSID to use via EssenceContainerData */
@@ -2221,12 +2692,17 @@ static int mxf_read_header(AVFormatContext *s)
mxf->nb_index_tables, mxf->index_tables[0].index_sid);
} else if (mxf->nb_index_tables == 0 && mxf->op == OPAtom) {
av_log(mxf->fc, AV_LOG_ERROR, "cannot demux OPAtom without an index\n");
- return AVERROR_INVALIDDATA;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
}
mxf_handle_small_eubc(s);
return 0;
+fail:
+ mxf_read_close(s);
+
+ return ret;
}
/**
@@ -2243,11 +2719,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) {
@@ -2265,8 +2739,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;
@@ -2304,8 +2777,7 @@ static int mxf_compute_sample_count(MXFContext *mxf, int stream_index,
size++;
}
- if (!size)
- return 0;
+ av_assert2(size);
*sample_count = (mxf->current_edit_unit / size) * (uint64_t)total;
for (i = 0; i < mxf->current_edit_unit % size; i++) {
@@ -2326,10 +2798,11 @@ static int mxf_set_audio_pts(MXFContext *mxf, AVCodecContext *codec,
pkt->pts = track->sample_count;
- if (codec->channels <= 0 || codec->channels * bits_per_sample < 8)
- return AVERROR_INVALIDDATA;
-
- track->sample_count += pkt->size / (codec->channels * bits_per_sample / 8);
+ if ( codec->channels <= 0
+ || bits_per_sample <= 0
+ || codec->channels * (int64_t)bits_per_sample < 8)
+ return AVERROR(EINVAL);
+ track->sample_count += pkt->size / (codec->channels * (int64_t)bits_per_sample / 8);
return 0;
}
@@ -2339,9 +2812,7 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
MXFContext *mxf = s->priv_data;
int ret;
- while (!s->pb->eof_reached) {
- if ((ret = klv_read_packet(&klv, s->pb)) < 0)
- return ret;
+ while ((ret = klv_read_packet(&klv, s->pb)) == 0) {
PRINT_KEY(s, "read packet", klv.key);
av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset);
if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) {
@@ -2377,9 +2848,8 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
next_ofs = mxf_set_current_edit_unit(mxf, klv.offset);
if (next_ofs >= 0 && next_klv > next_ofs) {
- /* if this check is hit then it's possible OPAtom was treated
- * as OP1a truncate the packet since it's probably very large
- * (>2 GiB is common) */
+ /* if this check is hit then it's possible OPAtom was treated as OP1a
+ * truncate the packet since it's probably very large (>2 GiB is common) */
avpriv_request_sample(s,
"OPAtom misinterpreted as OP1a?"
"KLV for edit unit %i extending into "
@@ -2404,21 +2874,19 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
pkt->stream_index = index;
pkt->pos = klv.offset;
- codec = s->streams[index]->codec;
+ codec = st->codec;
if (codec->codec_type == AVMEDIA_TYPE_VIDEO && next_ofs >= 0) {
/* mxf->current_edit_unit good - see if we have an
* index table to derive timestamps from */
MXFIndexTable *t = &mxf->index_tables[0];
- if (mxf->nb_index_tables >= 1 &&
- mxf->current_edit_unit < t->nb_ptses) {
+ if (mxf->nb_index_tables >= 1 && mxf->current_edit_unit < t->nb_ptses) {
pkt->dts = mxf->current_edit_unit + t->first_dts;
pkt->pts = t->ptses[mxf->current_edit_unit];
} else if (track->intra_only) {
/* intra-only -> PTS = EditUnit.
- * let utils.c figure out DTS since it can be
- * < PTS if low_delay = 0 (Sony IMX30) */
+ * let utils.c figure out DTS since it can be < PTS if low_delay = 0 (Sony IMX30) */
pkt->pts = mxf->current_edit_unit;
}
} else if (codec->codec_type == AVMEDIA_TYPE_AUDIO) {
@@ -2435,7 +2903,7 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
skip:
avio_skip(s->pb, klv.length);
}
- return AVERROR_EOF;
+ return avio_feof(s->pb) ? AVERROR_EOF : ret;
}
static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
@@ -2479,8 +2947,8 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
if ((ret64 = avio_seek(s->pb, pos, SEEK_SET)) < 0)
return ret64;
- if ((ret = av_get_packet(s->pb, pkt, size)) != size)
- return ret < 0 ? ret : AVERROR_EOF;
+ if ((size = av_get_packet(s->pb, pkt, size)) < 0)
+ return size;
pkt->stream_index = 0;
@@ -2499,11 +2967,9 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
return 0;
}
-
static int mxf_read_close(AVFormatContext *s)
{
MXFContext *mxf = s->priv_data;
- MXFIndexTableSegment *seg;
int i;
av_freep(&mxf->packages_refs);
@@ -2512,40 +2978,19 @@ static int mxf_read_close(AVFormatContext *s)
s->streams[i]->priv_data = NULL;
for (i = 0; i < mxf->metadata_sets_count; i++) {
- switch (mxf->metadata_sets[i]->type) {
- case Descriptor:
- av_freep(&((MXFDescriptor *)mxf->metadata_sets[i])->extradata);
- break;
- case MultipleDescriptor:
- av_freep(&((MXFDescriptor *)mxf->metadata_sets[i])->sub_descriptors_refs);
- break;
- case Sequence:
- av_freep(&((MXFSequence *)mxf->metadata_sets[i])->structural_components_refs);
- break;
- case SourcePackage:
- case MaterialPackage:
- av_freep(&((MXFPackage *)mxf->metadata_sets[i])->tracks_refs);
- break;
- case IndexTableSegment:
- seg = (MXFIndexTableSegment *)mxf->metadata_sets[i];
- av_freep(&seg->temporal_offset_entries);
- av_freep(&seg->flag_entries);
- av_freep(&seg->stream_offset_entries);
- break;
- default:
- break;
- }
- av_freep(&mxf->metadata_sets[i]);
+ mxf_free_metadataset(mxf->metadata_sets + i, 1);
}
av_freep(&mxf->partitions);
av_freep(&mxf->metadata_sets);
av_freep(&mxf->aesc);
av_freep(&mxf->local_tags);
- for (i = 0; i < mxf->nb_index_tables; i++) {
- av_freep(&mxf->index_tables[i].segments);
- av_freep(&mxf->index_tables[i].ptses);
- av_freep(&mxf->index_tables[i].fake_index);
+ if (mxf->index_tables) {
+ for (i = 0; i < mxf->nb_index_tables; i++) {
+ av_freep(&mxf->index_tables[i].segments);
+ av_freep(&mxf->index_tables[i].ptses);
+ av_freep(&mxf->index_tables[i].fake_index);
+ }
}
av_freep(&mxf->index_tables);
@@ -2553,18 +2998,27 @@ static int mxf_read_close(AVFormatContext *s)
}
static int mxf_probe(AVProbeData *p) {
- uint8_t *bufp = p->buf;
- uint8_t *end = p->buf + p->buf_size;
+ const uint8_t *bufp = p->buf;
+ const uint8_t *end = p->buf + p->buf_size;
if (p->buf_size < sizeof(mxf_header_partition_pack_key))
return 0;
/* Must skip Run-In Sequence and search for MXF header partition pack key SMPTE 377M 5.5 */
end -= sizeof(mxf_header_partition_pack_key);
- for (; bufp < end; bufp++) {
- if (IS_KLV_KEY(bufp, mxf_header_partition_pack_key))
- return AVPROBE_SCORE_MAX;
+
+ for (; bufp < end;) {
+ if (!((bufp[13] - 1) & 0xF2)){
+ if (AV_RN32(bufp ) == AV_RN32(mxf_header_partition_pack_key ) &&
+ AV_RN32(bufp+ 4) == AV_RN32(mxf_header_partition_pack_key+ 4) &&
+ AV_RN32(bufp+ 8) == AV_RN32(mxf_header_partition_pack_key+ 8) &&
+ AV_RN16(bufp+12) == AV_RN16(mxf_header_partition_pack_key+12))
+ return AVPROBE_SCORE_MAX;
+ bufp ++;
+ } else
+ bufp += 10;
}
+
return 0;
}
@@ -2615,7 +3069,7 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
sample_time = FFMIN(sample_time, source_track->original_duration - 1);
}
- if ((ret = mxf_edit_unit_absolute_offset(mxf, t, sample_time, &sample_time, &seekpos, 1)) << 0)
+ if ((ret = mxf_edit_unit_absolute_offset(mxf, t, sample_time, &sample_time, &seekpos, 1)) < 0)
return ret;
ff_update_cur_dts(s, st, sample_time);
diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c
index 69c0dd8c19..dfa647de3c 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
*/
@@ -26,6 +26,7 @@
* SMPTE 377M MXF File Format Specifications
* SMPTE 379M MXF Generic Container
* SMPTE 381M Mapping MPEG Streams into the MXF Generic Container
+ * SMPTE 422M Mapping JPEG 2000 Codestreams into the MXF Generic Container
* SMPTE RP210: SMPTE Metadata Dictionary
* SMPTE RP224: Registry of SMPTE Universal Labels
*/
@@ -34,18 +35,24 @@
#include <math.h>
#include <time.h>
+#include "libavutil/opt.h"
#include "libavutil/random_seed.h"
+#include "libavutil/timecode.h"
+#include "libavutil/avassert.h"
#include "libavutil/time_internal.h"
#include "libavcodec/bytestream.h"
+#include "libavcodec/dnxhddata.h"
+#include "libavcodec/h264.h"
+#include "libavcodec/internal.h"
#include "audiointerleave.h"
#include "avformat.h"
+#include "avio_internal.h"
#include "internal.h"
#include "mxf.h"
-
-static const int NTSC_samples_per_frame[] = { 1602, 1601, 1602, 1601, 1602, 0 };
-static const int PAL_samples_per_frame[] = { 1920, 0 };
+#include "config.h"
extern AVOutputFormat ff_mxf_d10_muxer;
+extern AVOutputFormat ff_mxf_opatom_muxer;
#define EDIT_UNITS_PER_BODY 250
#define KAG_SIZE 512
@@ -69,9 +76,12 @@ typedef struct MXFStreamContext {
const UID *codec_ul;
int order; ///< interleaving order if dts are equal
int interlaced; ///< whether picture is interlaced
+ int field_dominance; ///< tff=1, bff=2
+ int component_depth;
int temporal_reordering;
AVRational aspect_ratio; ///< display aspect ratio
int closed_gop; ///< gop is closed, used in mpeg-2 frame parsing
+ int video_bit_rate;
} MXFStreamContext;
typedef struct MXFContainerEssenceEntry {
@@ -88,6 +98,10 @@ 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_JPEG2000, 34 },
+ { AV_CODEC_ID_H264, 35 },
{ AV_CODEC_ID_NONE }
};
@@ -164,6 +178,111 @@ static const MXFContainerEssenceEntry mxf_essence_container_uls[] = {
{ 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x06,0x01,0x10,0x00 },
{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 },
mxf_write_generic_sound_desc },
+ // DV Unknown
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x7F,0x01 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x00,0x00,0x00 },
+ mxf_write_cdci_desc },
+ // DV25 525/60
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x40,0x01 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x01,0x00 },
+ mxf_write_cdci_desc },
+ // DV25 625/50
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x41,0x01 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x02,0x00 },
+ mxf_write_cdci_desc },
+ // DV50 525/60
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x50,0x01 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x03,0x00 },
+ mxf_write_cdci_desc },
+ // DV50 625/50
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x51,0x01 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x04,0x00 },
+ mxf_write_cdci_desc },
+ // DV100 1080/60
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x60,0x01 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x05,0x00 },
+ mxf_write_cdci_desc },
+ // DV100 1080/50
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x61,0x01 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x06,0x00 },
+ mxf_write_cdci_desc },
+ // DV100 720/60
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x62,0x01 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x07,0x00 },
+ mxf_write_cdci_desc },
+ // DV100 720/50
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x63,0x01 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x08,0x00 },
+ mxf_write_cdci_desc },
+ // DNxHD 1080p 10bit high
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x01,0x00,0x00 },
+ mxf_write_cdci_desc },
+ // DNxHD 1080p 8bit medium
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x03,0x00,0x00 },
+ mxf_write_cdci_desc },
+ // DNxHD 1080p 8bit high
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x04,0x00,0x00 },
+ mxf_write_cdci_desc },
+ // DNxHD 1080i 10bit high
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x07,0x00,0x00 },
+ mxf_write_cdci_desc },
+ // DNxHD 1080i 8bit medium
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x08,0x00,0x00 },
+ mxf_write_cdci_desc },
+ // DNxHD 1080i 8bit high
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 },
+ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x09,0x00,0x00 },
+ mxf_write_cdci_desc },
+ // DNxHD 720p 10bit
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x11,0x01,0x00 },
+ { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x05,0x00 },
+ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x10,0x00,0x00 },
+ mxf_write_cdci_desc },
+ // DNxHD 720p 8bit high
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x11,0x01,0x00 },
+ { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x05,0x00 },
+ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x11,0x00,0x00 },
+ mxf_write_cdci_desc },
+ // DNxHD 720p 8bit medium
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x11,0x01,0x00 },
+ { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x05,0x00 },
+ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x12,0x00,0x00 },
+ mxf_write_cdci_desc },
+ // DNxHD 720p 8bit low
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x11,0x01,0x00 },
+ { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x05,0x00 },
+ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x13,0x00,0x00 },
+ mxf_write_cdci_desc },
+ // JPEG2000
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x0c,0x01,0x00 },
+ { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x08,0x00 },
+ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x07,0x04,0x01,0x02,0x02,0x03,0x01,0x01,0x00 },
+ mxf_write_cdci_desc },
+ // H.264
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x0D,0x01,0x03,0x01,0x02,0x10,0x60,0x01 },
+ { 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,0x01,0x00,0x00,0x00 },
+ mxf_write_mpegvideo_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 +290,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,14 +304,14 @@ typedef struct MXFContext {
unsigned body_partitions_count;
int last_key_index; ///< index of last key frame
uint64_t duration;
+ AVTimecode tc; ///< timecode context
AVStream *timecode_track;
int timecode_base; ///< rounded time code base (25 or 30)
- int timecode_start; ///< frame number computed from mpeg-2 gop header timecode
- int timecode_drop_frame; ///< time code use drop frame method frop mpeg-2 essence gop header
int edit_unit_byte_count; ///< fixed edit unit byte count
uint64_t body_offset;
uint32_t instance_number;
uint8_t umid[16]; ///< unique material identifier
+ int channel_count;
} MXFContext;
static const uint8_t uuid_base[] = { 0xAD,0xAB,0x44,0x24,0x2f,0x25,0x4d,0xc7,0x92,0xff,0x29,0xbd };
@@ -201,6 +321,7 @@ static const uint8_t umid_ul[] = { 0x06,0x0A,0x2B,0x34,0x01,0x01,0x
* complete key for operation pattern, partitions, and primer pack
*/
static const uint8_t op1a_ul[] = { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x01,0x09,0x00 };
+static const uint8_t opatom_ul[] = { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x02,0x01,0x10,0x03,0x00,0x00 };
static const uint8_t footer_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x04,0x04,0x00 }; // ClosedComplete
static const uint8_t primer_pack_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x05,0x01,0x00 };
static const uint8_t index_table_segment_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x10,0x01,0x00 };
@@ -280,6 +401,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 */
@@ -363,6 +485,12 @@ static void klv_encode_ber4_length(AVIOContext *pb, int len)
avio_wb24(pb, len);
}
+static void klv_encode_ber9_length(AVIOContext *pb, uint64_t len)
+{
+ avio_w8(pb, 0x80 + 8);
+ avio_wb64(pb, len);
+}
+
/*
* Get essence container ul index
*/
@@ -427,18 +555,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 +583,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 + 16LL * 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);
@@ -474,10 +609,13 @@ static void mxf_write_preface(AVFormatContext *s)
// operational pattern
mxf_write_local_tag(pb, 16, 0x3B09);
- avio_write(pb, op1a_ul, 16);
+ if (s->oformat == &ff_mxf_opatom_muxer)
+ avio_write(pb, opatom_ul, 16);
+ else
+ 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 +638,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 +803,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 +811,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)
@@ -785,8 +923,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 +950,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 +962,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 +989,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)
@@ -858,24 +1005,31 @@ static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st)
static void mxf_write_mpegvideo_desc(AVFormatContext *s, AVStream *st)
{
AVIOContext *pb = s->pb;
+ MXFStreamContext *sc = st->priv_data;
int profile_and_level = (st->codec->profile<<4) | st->codec->level;
- mxf_write_cdci_common(s, st, mxf_mpegvideo_descriptor_key, 8+5);
+ if (st->codec->codec_id != AV_CODEC_ID_H264) {
+ mxf_write_cdci_common(s, st, mxf_mpegvideo_descriptor_key, 8+5);
- // bit rate
- mxf_write_local_tag(pb, 4, 0x8000);
- avio_wb32(pb, st->codec->bit_rate);
+ // bit rate
+ mxf_write_local_tag(pb, 4, 0x8000);
+ avio_wb32(pb, sc->video_bit_rate);
- // profile and level
- mxf_write_local_tag(pb, 1, 0x8007);
- if (!st->codec->profile)
- profile_and_level |= 0x80; // escape bit
- avio_w8(pb, profile_and_level);
+ // profile and level
+ mxf_write_local_tag(pb, 1, 0x8007);
+ if (!st->codec->profile)
+ profile_and_level |= 0x80; // escape bit
+ avio_w8(pb, profile_and_level);
+ } else {
+ mxf_write_cdci_common(s, st, mxf_mpegvideo_descriptor_key, 0);
+ }
}
static void mxf_write_generic_sound_common(AVFormatContext *s, AVStream *st, const UID key, unsigned size)
{
AVIOContext *pb = s->pb;
+ MXFContext *mxf = s->priv_data;
+ int show_warnings = !mxf->footer_partition_offset;
mxf_write_generic_desc(s, st, key, size+5+12+8+8);
@@ -889,7 +1043,21 @@ static void mxf_write_generic_sound_common(AVFormatContext *s, AVStream *st, con
avio_wb32(pb, 1);
mxf_write_local_tag(pb, 4, 0x3D07);
- avio_wb32(pb, st->codec->channels);
+ if (mxf->channel_count == -1) {
+ if (show_warnings && (s->oformat == &ff_mxf_d10_muxer) && (st->codec->channels != 4) && (st->codec->channels != 8))
+ av_log(s, AV_LOG_WARNING, "the number of audio channels shall be 4 or 8 : the output will not comply to MXF D-10 specs, use -d10_channelcount to fix this\n");
+ avio_wb32(pb, st->codec->channels);
+ } else if (s->oformat == &ff_mxf_d10_muxer) {
+ if (show_warnings && (mxf->channel_count < st->codec->channels))
+ av_log(s, AV_LOG_WARNING, "d10_channelcount < actual number of audio channels : some channels will be discarded\n");
+ if (show_warnings && (mxf->channel_count != 4) && (mxf->channel_count != 8))
+ av_log(s, AV_LOG_WARNING, "d10_channelcount shall be set to 4 or 8 : the output will not comply to MXF D-10 specs\n");
+ avio_wb32(pb, mxf->channel_count);
+ } else {
+ if (show_warnings && mxf->channel_count != -1)
+ av_log(s, AV_LOG_ERROR, "-d10_channelcount requires MXF D-10 and will be ignored\n");
+ avio_wb32(pb, st->codec->channels);
+ }
mxf_write_local_tag(pb, 4, 0x3D01);
avio_wb32(pb, av_get_bits_per_sample(st->codec->codec_id));
@@ -1177,9 +1345,8 @@ static void mxf_write_klv_fill(AVFormatContext *s)
avio_write(s->pb, klv_fill_key, 16);
pad -= 16 + 4;
klv_encode_ber4_length(s->pb, pad);
- for (; pad; pad--)
- avio_w8(s->pb, 0);
- assert(!(avio_tell(s->pb) & (KAG_SIZE-1)));
+ ffio_fill(s->pb, 0, pad);
+ av_assert1(!(avio_tell(s->pb) & (KAG_SIZE-1)));
}
}
@@ -1206,7 +1373,7 @@ static int mxf_write_partition(AVFormatContext *s, int bodysid,
index_byte_count += klv_fill_size(index_byte_count);
}
- if (!memcmp(key, body_partition_key, 16)) {
+ if (key && !memcmp(key, body_partition_key, 16)) {
if ((err = av_reallocp_array(&mxf->body_partition_offset, mxf->body_partitions_count + 1,
sizeof(*mxf->body_partition_offset))) < 0) {
mxf->body_partitions_count = 0;
@@ -1216,8 +1383,12 @@ static int mxf_write_partition(AVFormatContext *s, int bodysid,
}
// write klv
- avio_write(pb, key, 16);
- klv_encode_ber_length(pb, 88 + 16LL * mxf->essence_container_count);
+ if (key)
+ avio_write(pb, key, 16);
+ else
+ avio_write(pb, body_partition_key, 16);
+
+ klv_encode_ber_length(pb, 88 + 16LL * DESCRIPTOR_COUNT(mxf->essence_container_count));
// write partition value
avio_wb16(pb, 1); // majorVersion
@@ -1226,9 +1397,9 @@ static int mxf_write_partition(AVFormatContext *s, int bodysid,
avio_wb64(pb, partition_offset); // ThisPartition
- if (!memcmp(key, body_partition_key, 16) && mxf->body_partitions_count > 1)
+ if (key && !memcmp(key, body_partition_key, 16) && mxf->body_partitions_count > 1)
avio_wb64(pb, mxf->body_partition_offset[mxf->body_partitions_count-2]); // PreviousPartition
- else if (!memcmp(key, footer_partition_key, 16) && mxf->body_partitions_count)
+ else if (key && !memcmp(key, footer_partition_key, 16) && mxf->body_partitions_count)
avio_wb64(pb, mxf->body_partition_offset[mxf->body_partitions_count-1]); // PreviousPartition
else
avio_wb64(pb, 0);
@@ -1244,15 +1415,18 @@ static int mxf_write_partition(AVFormatContext *s, int bodysid,
avio_wb32(pb, index_byte_count ? indexsid : 0); // indexSID
// BodyOffset
- if (bodysid && mxf->edit_units_count && mxf->body_partitions_count) {
+ if (bodysid && mxf->edit_units_count && mxf->body_partitions_count && s->oformat != &ff_mxf_opatom_muxer)
avio_wb64(pb, mxf->body_offset);
- } else
+ else
avio_wb64(pb, 0);
avio_wb32(pb, bodysid); // bodySID
// operational pattern
- avio_write(pb, op1a_ul, 16);
+ if (s->oformat == &ff_mxf_opatom_muxer)
+ avio_write(pb, opatom_ul, 16);
+ else
+ avio_write(pb, op1a_ul, 16);
// essence container
mxf_write_essence_container_refs(s);
@@ -1275,11 +1449,259 @@ static int mxf_write_partition(AVFormatContext *s, int bodysid,
avio_seek(pb, pos, SEEK_SET);
}
- avio_flush(pb);
+ if(key)
+ avio_flush(pb);
return 0;
}
+static int mxf_parse_dnxhd_frame(AVFormatContext *s, AVStream *st,
+AVPacket *pkt)
+{
+ MXFContext *mxf = s->priv_data;
+ MXFStreamContext *sc = st->priv_data;
+ int i, cid;
+ uint8_t* header_cid;
+ int frame_size = 0;
+
+ if (mxf->header_written)
+ return 1;
+
+ if (pkt->size < 43)
+ return -1;
+
+ header_cid = pkt->data + 0x28;
+ cid = header_cid[0] << 24 | header_cid[1] << 16 | header_cid[2] << 8 | header_cid[3];
+
+ if ((frame_size = avpriv_dnxhd_get_frame_size(cid)) < 0)
+ return -1;
+
+ switch (cid) {
+ case 1235:
+ sc->index = 24;
+ sc->component_depth = 10;
+ break;
+ case 1237:
+ sc->index = 25;
+ break;
+ case 1238:
+ sc->index = 26;
+ break;
+ case 1241:
+ sc->index = 27;
+ sc->component_depth = 10;
+ break;
+ case 1242:
+ sc->index = 28;
+ break;
+ case 1243:
+ sc->index = 29;
+ break;
+ case 1250:
+ sc->index = 30;
+ sc->component_depth = 10;
+ break;
+ case 1251:
+ sc->index = 31;
+ break;
+ case 1252:
+ sc->index = 32;
+ break;
+ case 1253:
+ sc->index = 33;
+ break;
+ default:
+ return -1;
+ }
+
+ sc->codec_ul = &mxf_essence_container_uls[sc->index].codec_ul;
+ sc->aspect_ratio = (AVRational){ 16, 9 };
+
+ if(s->oformat == &ff_mxf_opatom_muxer){
+ mxf->edit_unit_byte_count = frame_size;
+ return 1;
+ }
+
+ 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;
+
+ if(s->oformat == &ff_mxf_opatom_muxer) {
+ mxf->edit_unit_byte_count = frame_size;
+ return 1;
+ }
+
+ 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 struct {
+ UID uid;
+ int frame_size;
+ int profile;
+ uint8_t interlaced;
+} mxf_h264_codec_uls[] = {
+ {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x20,0x01 }, 0, 110, 0 }, // AVC High 10 Intra
+ {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x01 }, 232960, 0, 1 }, // AVC Intra 50 1080i60
+ {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x02 }, 281088, 0, 1 }, // AVC Intra 50 1080i50
+ {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x03 }, 232960, 0, 0 }, // AVC Intra 50 1080p30
+ {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x04 }, 281088, 0, 0 }, // AVC Intra 50 1080p25
+ {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x08 }, 116736, 0, 0 }, // AVC Intra 50 720p60
+ {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x09 }, 140800, 0, 0 }, // AVC Intra 50 720p50
+ {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x30,0x01 }, 0, 122, 0 }, // AVC High 422 Intra
+ {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x01 }, 472576, 0, 1 }, // AVC Intra 100 1080i60
+ {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x02 }, 568832, 0, 1 }, // AVC Intra 100 1080i50
+ {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x03 }, 472576, 0, 0 }, // AVC Intra 100 1080p30
+ {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x04 }, 568832, 0, 0 }, // AVC Intra 100 1080p25
+ {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x08 }, 236544, 0, 0 }, // AVC Intra 100 720p60
+ {{ 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x32,0x31,0x09 }, 284672, 0, 0 }, // AVC Intra 100 720p50
+};
+
+static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st,
+ AVPacket *pkt, MXFIndexEntry *e)
+{
+ MXFContext *mxf = s->priv_data;
+ MXFStreamContext *sc = st->priv_data;
+ static const int mxf_h264_num_codec_uls = sizeof(mxf_h264_codec_uls) / sizeof(mxf_h264_codec_uls[0]);
+ const uint8_t *buf = pkt->data;
+ const uint8_t *buf_end = pkt->data + pkt->size;
+ uint32_t state = -1;
+ int extra_size = 512; // support AVC Intra files without SPS/PPS header
+ int i, frame_size;
+ uint8_t uid_found;
+
+ if (pkt->size > extra_size)
+ buf_end -= pkt->size - extra_size; // no need to parse beyond SPS/PPS header
+
+ for (;;) {
+ buf = avpriv_find_start_code(buf, buf_end, &state);
+ if (buf >= buf_end)
+ break;
+ --buf;
+ switch (state & 0x1f) {
+ case NAL_SPS:
+ st->codec->profile = buf[1];
+ e->flags |= 0x40;
+ break;
+ case NAL_PPS:
+ if (e->flags & 0x40) { // sequence header present
+ e->flags |= 0x80; // random access
+ extra_size = 0;
+ buf = buf_end;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (mxf->header_written)
+ return 1;
+
+ sc->aspect_ratio = (AVRational){ 16, 9 }; // 16:9 is mandatory for broadcast HD
+ sc->component_depth = 10; // AVC Intra is always 10 Bit
+ sc->interlaced = st->codec->field_order != AV_FIELD_PROGRESSIVE ? 1 : 0;
+ if (sc->interlaced)
+ sc->field_dominance = 1; // top field first is mandatory for AVC Intra
+
+ uid_found = 0;
+ frame_size = pkt->size + extra_size;
+ for (i = 0; i < mxf_h264_num_codec_uls; i++) {
+ if (frame_size == mxf_h264_codec_uls[i].frame_size && sc->interlaced == mxf_h264_codec_uls[i].interlaced) {
+ sc->codec_ul = &mxf_h264_codec_uls[i].uid;
+ return 1;
+ } else if (st->codec->profile == mxf_h264_codec_uls[i].profile) {
+ sc->codec_ul = &mxf_h264_codec_uls[i].uid;
+ uid_found = 1;
+ }
+ }
+
+ if (!uid_found) {
+ av_log(s, AV_LOG_ERROR, "AVC Intra 50/100 supported only\n");
+ return 0;
+ }
+
+ 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
@@ -1317,7 +1739,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;
@@ -1329,6 +1750,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
@@ -1337,21 +1760,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) {
@@ -1408,21 +1816,27 @@ static void mxf_gen_umid(AVFormatContext *s)
AV_WB64(mxf->umid , umid);
AV_WB64(mxf->umid+8, umid>>8);
- mxf->instance_number = seed;
+ mxf->instance_number = seed & 0xFFFFFF;
}
static int mxf_write_header(AVFormatContext *s)
{
MXFContext *mxf = s->priv_data;
- int i;
+ int i, ret;
uint8_t present[FF_ARRAY_ELEMS(mxf_essence_container_uls)] = {0};
- const int *samples_per_frame = NULL;
+ const MXFSamplesPerFrame *spf = NULL;
AVDictionaryEntry *t;
int64_t timestamp = 0;
+ AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
if (!s->nb_streams)
return -1;
+ if (s->oformat == &ff_mxf_opatom_muxer && s->nb_streams !=1){
+ av_log(s, AV_LOG_ERROR, "there must be exactly one stream for mxf opatom\n");
+ return -1;
+ }
+
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MXFStreamContext *sc = av_mallocz(sizeof(*sc));
@@ -1430,45 +1844,55 @@ static int mxf_write_header(AVFormatContext *s)
return AVERROR(ENOMEM);
st->priv_data = sc;
+ if ((i == 0) ^ (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)) {
+ av_log(s, AV_LOG_ERROR, "there must be exactly one video stream and it must be the first one\n");
+ return -1;
+ }
+
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
- if (i != 0) {
- av_log(s, AV_LOG_ERROR, "video stream must be first track\n");
- return -1;
- }
// TODO: should be avg_frame_rate
- if (fabs(av_q2d(st->time_base) - 1/25.0) < 0.0001) {
- samples_per_frame = PAL_samples_per_frame;
- mxf->time_base = (AVRational){ 1, 25 };
- mxf->timecode_base = 25;
- } else if (fabs(av_q2d(st->time_base) - 1001/30000.0) < 0.0001) {
- samples_per_frame = NTSC_samples_per_frame;
- mxf->time_base = (AVRational){ 1001, 30000 };
- mxf->timecode_base = 30;
- } else {
- av_log(s, AV_LOG_ERROR, "unsupported video frame rate\n");
- return -1;
+ AVRational rate, tbc = st->time_base;
+ // Default component depth to 8
+ sc->component_depth = 8;
+ mxf->timecode_base = (tbc.den + tbc.num/2) / tbc.num;
+ spf = ff_mxf_get_samples_per_frame(s, tbc);
+ if (!spf) {
+ av_log(s, AV_LOG_ERROR, "Unsupported video frame rate %d/%d\n",
+ tbc.den, tbc.num);
+ return AVERROR(EINVAL);
}
+ mxf->time_base = spf->time_base;
+ rate = av_inv_q(mxf->time_base);
avpriv_set_pts_info(st, 64, mxf->time_base.num, mxf->time_base.den);
+ if (!tcr)
+ tcr = av_dict_get(st->metadata, "timecode", NULL, 0);
+ if (tcr)
+ ret = av_timecode_init_from_string(&mxf->tc, rate, tcr->value, s);
+ else
+ ret = av_timecode_init(&mxf->tc, rate, 0, 0, s);
+ if (ret < 0)
+ return ret;
+ sc->video_bit_rate = st->codec->bit_rate ? st->codec->bit_rate : st->codec->rc_max_rate;
if (s->oformat == &ff_mxf_d10_muxer) {
- if (st->codec->bit_rate == 50000000)
+ if (sc->video_bit_rate == 50000000) {
if (mxf->time_base.den == 25) sc->index = 3;
else sc->index = 5;
- else if (st->codec->bit_rate == 40000000)
+ } else if (sc->video_bit_rate == 40000000) {
if (mxf->time_base.den == 25) sc->index = 7;
else sc->index = 9;
- else if (st->codec->bit_rate == 30000000)
+ } else if (sc->video_bit_rate == 30000000) {
if (mxf->time_base.den == 25) sc->index = 11;
else sc->index = 13;
- else {
+ } else {
av_log(s, AV_LOG_ERROR, "error MXF D-10 only support 30/40/50 mbit/s\n");
return -1;
}
mxf->edit_unit_byte_count = KAG_SIZE; // system element
- mxf->edit_unit_byte_count += 16 + 4 + (uint64_t)st->codec->bit_rate *
+ mxf->edit_unit_byte_count += 16 + 4 + (uint64_t)sc->video_bit_rate *
mxf->time_base.num / (8*mxf->time_base.den);
mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count);
- mxf->edit_unit_byte_count += 16 + 4 + 4 + samples_per_frame[0]*8*4;
+ mxf->edit_unit_byte_count += 16 + 4 + 4 + spf->samples_per_frame[0]*8*4;
mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count);
}
} else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
@@ -1511,7 +1935,7 @@ static int mxf_write_header(AVFormatContext *s)
present[sc->index]++;
}
- if (s->oformat == &ff_mxf_d10_muxer) {
+ if (s->oformat == &ff_mxf_d10_muxer || s->oformat == &ff_mxf_opatom_muxer) {
mxf->essence_container_count = 1;
}
@@ -1522,7 +1946,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))
@@ -1539,10 +1966,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;
@@ -1551,24 +1978,6 @@ static int mxf_write_header(AVFormatContext *s)
static const uint8_t system_metadata_pack_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x03,0x01,0x04,0x01,0x01,0x00 };
static const uint8_t system_metadata_package_set_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x43,0x01,0x01,0x0D,0x01,0x03,0x01,0x04,0x01,0x02,0x01 };
-static uint32_t framenum_to_12m_time_code(unsigned frame, int drop, int fps)
-{
- return (0 << 31) | // color frame flag
- (drop << 30) | // drop frame flag
- ( ((frame % fps) / 10) << 28) | // tens of frames
- ( ((frame % fps) % 10) << 24) | // units of frames
- (0 << 23) | // field phase (NTSC), b0 (PAL)
- ((((frame / fps) % 60) / 10) << 20) | // tens of seconds
- ((((frame / fps) % 60) % 10) << 16) | // units of seconds
- (0 << 15) | // b0 (NTSC), b2 (PAL)
- ((((frame / (fps * 60)) % 60) / 10) << 12) | // tens of minutes
- ((((frame / (fps * 60)) % 60) % 10) << 8) | // units of minutes
- (0 << 7) | // b1
- (0 << 6) | // b2 (NTSC), field phase (PAL)
- ((((frame / (fps * 3600) % 24)) / 10) << 4) | // tens of hours
- ( (frame / (fps * 3600) % 24)) % 10; // units of hours
-}
-
static void mxf_write_system_item(AVFormatContext *s)
{
MXFContext *mxf = s->priv_data;
@@ -1576,7 +1985,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);
@@ -1585,7 +1994,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 {
@@ -1597,8 +2006,7 @@ static void mxf_write_system_item(AVFormatContext *s)
avio_wb64(pb, 0); // creation date/time stamp
avio_w8(pb, 0x81); // SMPTE 12M time code
- time_code = framenum_to_12m_time_code(frame, mxf->timecode_drop_frame,
- mxf->timecode_base);
+ time_code = av_timecode_get_smpte_from_framenum(&mxf->tc, frame);
avio_wb32(pb, time_code);
avio_wb32(pb, 0); // binary group data
avio_wb64(pb, 0);
@@ -1615,7 +2023,8 @@ static void mxf_write_d10_video_packet(AVFormatContext *s, AVStream *st, AVPacke
{
MXFContext *mxf = s->priv_data;
AVIOContext *pb = s->pb;
- int packet_size = (uint64_t)st->codec->bit_rate*mxf->time_base.num /
+ MXFStreamContext *sc = st->priv_data;
+ int packet_size = (uint64_t)sc->video_bit_rate*mxf->time_base.num /
(8*mxf->time_base.den); // frame size
int pad;
@@ -1631,13 +2040,11 @@ static void mxf_write_d10_video_packet(AVFormatContext *s, AVStream *st, AVPacke
avio_write(s->pb, klv_fill_key, 16);
pad -= 16 + 4;
klv_encode_ber4_length(s->pb, pad);
- for (; pad; pad--)
- avio_w8(s->pb, 0);
- assert(!(avio_tell(s->pb) & (KAG_SIZE-1)));
+ ffio_fill(s->pb, 0, pad);
+ av_assert1(!(avio_tell(s->pb) & (KAG_SIZE-1)));
} else {
av_log(s, AV_LOG_WARNING, "cannot fill d-10 video packet\n");
- for (; pad > 0; pad--)
- avio_w8(s->pb, 0);
+ ffio_fill(s->pb, 0, pad);
}
}
@@ -1673,6 +2080,58 @@ static void mxf_write_d10_audio_packet(AVFormatContext *s, AVStream *st, AVPacke
}
}
+static int mxf_write_opatom_body_partition(AVFormatContext *s)
+{
+ MXFContext *mxf = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVStream *st = s->streams[0];
+ MXFStreamContext *sc = st->priv_data;
+ const uint8_t *key = NULL;
+
+ int err;
+
+ if (!mxf->header_written)
+ key = body_partition_key;
+
+ if ((err = mxf_write_partition(s, 1, 0, key, 0)) < 0)
+ return err;
+ mxf_write_klv_fill(s);
+ avio_write(pb, sc->track_essence_element_key, 16);
+ klv_encode_ber9_length(pb, mxf->body_offset);
+ return 0;
+}
+
+static int mxf_write_opatom_packet(AVFormatContext *s, AVPacket *pkt, MXFIndexEntry *ie)
+{
+ MXFContext *mxf = s->priv_data;
+ AVIOContext *pb = s->pb;
+
+ int err;
+
+ if (!mxf->header_written) {
+ if ((err = mxf_write_partition(s, 0, 0, header_open_partition_key, 1)) < 0)
+ return err;
+ mxf_write_klv_fill(s);
+
+ if ((err = mxf_write_opatom_body_partition(s)) < 0)
+ return err;
+ mxf->header_written = 1;
+ }
+
+ if (!mxf->edit_unit_byte_count) {
+ mxf->index_entries[mxf->edit_units_count].offset = mxf->body_offset;
+ mxf->index_entries[mxf->edit_units_count].flags = ie->flags;
+ mxf->index_entries[mxf->edit_units_count].temporal_ref = ie->temporal_ref;
+ }
+ mxf->edit_units_count++;
+ avio_write(pb, pkt->data, pkt->size);
+ mxf->body_offset += pkt->size;
+
+ avio_flush(pb);
+
+ return 0;
+}
+
static int mxf_write_packet(AVFormatContext *s, AVPacket *pkt)
{
MXFContext *mxf = s->priv_data;
@@ -1696,8 +2155,26 @@ 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;
+ }
+ } else if (st->codec->codec_id == AV_CODEC_ID_H264) {
+ if (!mxf_parse_h264_frame(s, st, pkt, &ie)) {
+ av_log(s, AV_LOG_ERROR, "could not get h264 profile\n");
+ return -1;
+ }
}
+ if (s->oformat == &ff_mxf_opatom_muxer)
+ return mxf_write_opatom_packet(s, pkt, &ie);
+
if (!mxf->header_written) {
if (mxf->edit_unit_byte_count) {
if ((err = mxf_write_partition(s, 1, 2, header_open_partition_key, 1)) < 0)
@@ -1765,7 +2242,7 @@ static void mxf_write_random_index_pack(AVFormatContext *s)
avio_write(pb, random_index_pack_key, 16);
klv_encode_ber_length(pb, 28 + 12LL*mxf->body_partitions_count);
- if (mxf->edit_unit_byte_count)
+ if (mxf->edit_unit_byte_count && s->oformat != &ff_mxf_opatom_muxer)
avio_wb32(pb, 1); // BodySID of header partition
else
avio_wb32(pb, 0);
@@ -1792,7 +2269,7 @@ static int mxf_write_footer(AVFormatContext *s)
mxf_write_klv_fill(s);
mxf->footer_partition_offset = avio_tell(pb);
- if (mxf->edit_unit_byte_count) { // no need to repeat index
+ if (mxf->edit_unit_byte_count && s->oformat != &ff_mxf_opatom_muxer) { // no need to repeat index
if ((err = mxf_write_partition(s, 0, 0, footer_partition_key, 0)) < 0)
return err;
} else {
@@ -1806,8 +2283,15 @@ static int mxf_write_footer(AVFormatContext *s)
mxf_write_random_index_pack(s);
if (s->pb->seekable) {
+ if (s->oformat == &ff_mxf_opatom_muxer){
+ /* rewrite body partition to update lengths */
+ avio_seek(pb, mxf->body_partition_offset[0], SEEK_SET);
+ if ((err = mxf_write_opatom_body_partition(s)) < 0)
+ return err;
+ }
+
avio_seek(pb, 0, SEEK_SET);
- if (mxf->edit_unit_byte_count) {
+ if (mxf->edit_unit_byte_count && s->oformat != &ff_mxf_opatom_muxer) {
if ((err = mxf_write_partition(s, 1, 2, header_closed_partition_key, 1)) < 0)
return err;
mxf_write_klv_fill(s);
@@ -1900,6 +2384,19 @@ static int mxf_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int
mxf_interleave_get_packet, mxf_compare_timestamps);
}
+static const AVOption d10_options[] = {
+ { "d10_channelcount", "Force/set channelcount in generic sound essence descriptor",
+ offsetof(MXFContext, channel_count), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 8, AV_OPT_FLAG_ENCODING_PARAM},
+ { NULL },
+};
+
+static const AVClass mxf_d10_muxer_class = {
+ .class_name = "MXF-D10 muxer",
+ .item_name = av_default_item_name,
+ .option = d10_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVOutputFormat ff_mxf_muxer = {
.name = "mxf",
.long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format)"),
@@ -1927,4 +2424,20 @@ AVOutputFormat ff_mxf_d10_muxer = {
.write_trailer = mxf_write_footer,
.flags = AVFMT_NOTIMESTAMPS,
.interleave_packet = mxf_interleave,
+ .priv_class = &mxf_d10_muxer_class,
+};
+
+AVOutputFormat ff_mxf_opatom_muxer = {
+ .name = "mxf_opatom",
+ .long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format) Operational Pattern Atom"),
+ .mime_type = "application/mxf",
+ .extensions = "mxf",
+ .priv_data_size = sizeof(MXFContext),
+ .audio_codec = AV_CODEC_ID_PCM_S16LE,
+ .video_codec = AV_CODEC_ID_DNXHD,
+ .write_header = mxf_write_header,
+ .write_packet = mxf_write_packet,
+ .write_trailer = mxf_write_footer,
+ .flags = AVFMT_NOTIMESTAMPS,
+ .interleave_packet = mxf_interleave,
};
diff --git a/libavformat/mxg.c b/libavformat/mxg.c
index 1d1488c059..34977b8197 100644
--- a/libavformat/mxg.c
+++ b/libavformat/mxg.c
@@ -2,20 +2,20 @@
* MxPEG clip file demuxer
* Copyright (c) 2010 Anatoly Nenashev
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -75,7 +75,7 @@ static int mxg_read_header(AVFormatContext *s)
static uint8_t* mxg_find_startmarker(uint8_t *p, uint8_t *end)
{
for (; p < end - 3; p += 4) {
- uint32_t x = *(uint32_t*)p;
+ uint32_t x = AV_RN32(p);
if (x & (~(x+0x01010101)) & 0x80808080) {
if (p[0] == 0xff) {
@@ -102,17 +102,19 @@ static int mxg_update_cache(AVFormatContext *s, unsigned int cache_size)
MXGContext *mxg = s->priv_data;
unsigned int current_pos = mxg->buffer_ptr - mxg->buffer;
unsigned int soi_pos;
+ uint8_t *buffer;
int ret;
/* reallocate internal buffer */
if (current_pos > current_pos + cache_size)
return AVERROR(ENOMEM);
- if (mxg->soi_ptr) soi_pos = mxg->soi_ptr - mxg->buffer;
- mxg->buffer = av_fast_realloc(mxg->buffer, &mxg->buffer_size,
- current_pos + cache_size +
- FF_INPUT_BUFFER_PADDING_SIZE);
- if (!mxg->buffer)
+ soi_pos = mxg->soi_ptr - mxg->buffer;
+ buffer = av_fast_realloc(mxg->buffer, &mxg->buffer_size,
+ current_pos + cache_size +
+ FF_INPUT_BUFFER_PADDING_SIZE);
+ if (!buffer)
return AVERROR(ENOMEM);
+ mxg->buffer = buffer;
mxg->buffer_ptr = mxg->buffer + current_pos;
if (mxg->soi_ptr) mxg->soi_ptr = mxg->buffer + soi_pos;
@@ -134,7 +136,7 @@ static int mxg_read_packet(AVFormatContext *s, AVPacket *pkt)
uint8_t *startmarker_ptr, *end, *search_end, marker;
MXGContext *mxg = s->priv_data;
- while (!s->pb->eof_reached && !s->pb->error){
+ while (!avio_feof(s->pb) && !s->pb->error){
if (mxg->cache_size <= OVERREAD_SIZE) {
/* update internal buffer */
ret = mxg_update_cache(s, DEFAULT_PACKET_SIZE + OVERREAD_SIZE);
diff --git a/libavformat/ncdec.c b/libavformat/ncdec.c
index 40d8dac3a7..062899f154 100644
--- a/libavformat/ncdec.c
+++ b/libavformat/ncdec.c
@@ -3,20 +3,20 @@
* Copyright (c) 2009 Nicolas Martin (martinic at iro dot umontreal dot ca)
* Edouard Auvinet
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -67,7 +67,7 @@ static int nc_read_packet(AVFormatContext *s, AVPacket *pkt)
uint32_t state=-1;
while (state != NC_VIDEO_FLAG) {
- if (s->pb->eof_reached)
+ if (avio_feof(s->pb))
return AVERROR(EIO);
state = (state<<8) + avio_r8(s->pb);
}
diff --git a/libavformat/network.c b/libavformat/network.c
index 6d308ebd01..e9eab297e2 100644
--- a/libavformat/network.c
+++ b/libavformat/network.c
@@ -1,20 +1,20 @@
/*
- * Copyright (c) 2007 The Libav Project
+ * Copyright (c) 2007 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -22,11 +22,15 @@
#include "network.h"
#include "url.h"
#include "libavcodec/internal.h"
+#include "libavutil/avutil.h"
#include "libavutil/mem.h"
+#include "libavutil/time.h"
#if HAVE_THREADS
#if HAVE_PTHREADS
#include <pthread.h>
+#elif HAVE_OS2THREADS
+#include "compat/os2threads.h"
#else
#include "compat/w32pthreads.h"
#endif
@@ -37,7 +41,6 @@
static int openssl_init;
#if HAVE_THREADS
#include <openssl/crypto.h>
-#include "libavutil/avutil.h"
pthread_mutex_t *openssl_mutexes;
static void openssl_lock(int mode, int type, const char *file, int line)
{
@@ -63,7 +66,7 @@ GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif
#endif
-void ff_tls_init(void)
+int ff_tls_init(void)
{
avpriv_lock_avformat();
#if CONFIG_OPENSSL
@@ -73,7 +76,9 @@ void ff_tls_init(void)
#if HAVE_THREADS
if (!CRYPTO_get_locking_callback()) {
int i;
- openssl_mutexes = av_malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
+ openssl_mutexes = av_malloc_array(sizeof(pthread_mutex_t), CRYPTO_num_locks());
+ if (!openssl_mutexes)
+ return AVERROR(ENOMEM);
for (i = 0; i < CRYPTO_num_locks(); i++)
pthread_mutex_init(&openssl_mutexes[i], NULL);
CRYPTO_set_locking_callback(openssl_lock);
@@ -93,6 +98,8 @@ void ff_tls_init(void)
gnutls_global_init();
#endif
avpriv_unlock_avformat();
+
+ return 0;
}
void ff_tls_deinit(void)
@@ -147,6 +154,26 @@ int ff_network_wait_fd(int fd, int write)
return ret < 0 ? ff_neterrno() : p.revents & (ev | POLLERR | POLLHUP) ? 0 : AVERROR(EAGAIN);
}
+int ff_network_wait_fd_timeout(int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb)
+{
+ int ret;
+ int64_t wait_start = 0;
+
+ while (1) {
+ if (ff_check_interrupt(int_cb))
+ return AVERROR_EXIT;
+ ret = ff_network_wait_fd(fd, write);
+ if (ret != AVERROR(EAGAIN))
+ return ret;
+ if (timeout > 0) {
+ if (!wait_start)
+ wait_start = av_gettime_relative();
+ else if (av_gettime_relative() - wait_start > timeout)
+ return AVERROR(ETIMEDOUT);
+ }
+ }
+}
+
void ff_network_close(void)
{
#if HAVE_WINSOCK2_H
@@ -202,7 +229,7 @@ static int ff_poll_interrupt(struct pollfd *p, nfds_t nfds, int timeout,
ret = poll(p, nfds, POLLING_TIME);
if (ret != 0)
break;
- } while (timeout < 0 || runs-- > 0);
+ } while (timeout <= 0 || runs-- > 0);
if (!ret)
return AVERROR(ETIMEDOUT);
@@ -222,8 +249,10 @@ int ff_socket(int af, int type, int proto)
{
fd = socket(af, type, proto);
#if HAVE_FCNTL
- if (fd != -1)
- fcntl(fd, F_SETFD, FD_CLOEXEC);
+ if (fd != -1) {
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
+ av_log(NULL, AV_LOG_DEBUG, "Failed to set close on exec\n");
+ }
#endif
}
return fd;
@@ -235,7 +264,9 @@ int ff_listen_bind(int fd, const struct sockaddr *addr,
int ret;
int reuse = 1;
struct pollfd lp = { fd, POLLIN, 0 };
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) {
+ av_log(NULL, AV_LOG_WARNING, "setsockopt(SO_REUSEADDR) failed\n");
+ }
ret = bind(fd, addr, addrlen);
if (ret)
return ff_neterrno();
@@ -254,7 +285,9 @@ int ff_listen_bind(int fd, const struct sockaddr *addr,
closesocket(fd);
- ff_socket_nonblock(ret, 1);
+ if (ff_socket_nonblock(ret, 1) < 0)
+ av_log(NULL, AV_LOG_DEBUG, "ff_socket_nonblock failed\n");
+
return ret;
}
@@ -266,7 +299,8 @@ int ff_listen_connect(int fd, const struct sockaddr *addr,
int ret;
socklen_t optlen;
- ff_socket_nonblock(fd, 1);
+ if (ff_socket_nonblock(fd, 1) < 0)
+ av_log(NULL, AV_LOG_DEBUG, "ff_socket_nonblock failed\n");
while ((ret = connect(fd, addr, addrlen))) {
ret = ff_neterrno();
diff --git a/libavformat/network.h b/libavformat/network.h
index 09cee58a5c..86fb656164 100644
--- a/libavformat/network.h
+++ b/libavformat/network.h
@@ -1,20 +1,20 @@
/*
- * Copyright (c) 2007 The Libav Project
+ * Copyright (c) 2007 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -27,6 +27,7 @@
#include "config.h"
#include "libavutil/error.h"
#include "os_support.h"
+#include "avio.h"
#include "url.h"
#if HAVE_UNISTD_H
@@ -77,11 +78,23 @@ extern int ff_network_inited_globally;
int ff_network_init(void);
void ff_network_close(void);
-void ff_tls_init(void);
+int ff_tls_init(void);
void ff_tls_deinit(void);
int ff_network_wait_fd(int fd, int write);
+/**
+ * This works similarly to ff_network_wait_fd, but waits up to 'timeout' microseconds
+ * Uses ff_network_wait_fd in a loop
+ *
+ * @fd Socket descriptor
+ * @write Set 1 to wait for socket able to be read, 0 to be written
+ * @timeout Timeout interval, in microseconds. Actual precision is 100000 mcs, due to ff_network_wait_fd usage
+ * @param int_cb Interrupt callback, is checked before each ff_network_wait_fd call
+ * @return 0 if data can be read/written, AVERROR(ETIMEDOUT) if timeout expired, or negative error code
+ */
+int ff_network_wait_fd_timeout(int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb);
+
int ff_inet_aton (const char * str, struct in_addr * add);
#if !HAVE_STRUCT_SOCKADDR_STORAGE
@@ -98,6 +111,14 @@ struct sockaddr_storage {
};
#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */
+typedef union sockaddr_union {
+ struct sockaddr_storage storage;
+ struct sockaddr_in in;
+#if HAVE_STRUCT_SOCKADDR_IN6
+ struct sockaddr_in6 in6;
+#endif
+} sockaddr_union;
+
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
diff --git a/libavformat/nistspheredec.c b/libavformat/nistspheredec.c
new file mode 100644
index 0000000000..2f17f9eeb0
--- /dev/null
+++ b/libavformat/nistspheredec.c
@@ -0,0 +1,131 @@
+/*
+ * NIST Sphere demuxer
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/avstring.h"
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "internal.h"
+#include "pcm.h"
+
+static int nist_probe(AVProbeData *p)
+{
+ if (AV_RL64(p->buf) == AV_RL64("NIST_1A\x0a"))
+ return AVPROBE_SCORE_MAX;
+ return 0;
+}
+
+static int nist_read_header(AVFormatContext *s)
+{
+ char buffer[32], coding[32] = "pcm", format[32] = "01";
+ int bps = 0, be = 0;
+ int32_t header_size = -1;
+ AVStream *st;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+
+ ff_get_line(s->pb, buffer, sizeof(buffer));
+ ff_get_line(s->pb, buffer, sizeof(buffer));
+ sscanf(buffer, "%"SCNd32, &header_size);
+ if (header_size <= 0)
+ return AVERROR_INVALIDDATA;
+
+ while (!avio_feof(s->pb)) {
+ ff_get_line(s->pb, buffer, sizeof(buffer));
+
+ if (avio_tell(s->pb) >= header_size)
+ return AVERROR_INVALIDDATA;
+
+ if (!memcmp(buffer, "end_head", 8)) {
+ if (!st->codec->bits_per_coded_sample)
+ st->codec->bits_per_coded_sample = bps << 3;
+
+ if (!av_strcasecmp(coding, "pcm")) {
+ st->codec->codec_id = ff_get_pcm_codec_id(st->codec->bits_per_coded_sample,
+ 0, be, 0xFFFF);
+ } else if (!av_strcasecmp(coding, "alaw")) {
+ st->codec->codec_id = AV_CODEC_ID_PCM_ALAW;
+ } else if (!av_strcasecmp(coding, "ulaw") ||
+ !av_strcasecmp(coding, "mu-law")) {
+ st->codec->codec_id = AV_CODEC_ID_PCM_MULAW;
+ } else {
+ avpriv_request_sample(s, "coding %s", coding);
+ }
+
+ avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+
+ st->codec->block_align = st->codec->bits_per_coded_sample * st->codec->channels / 8;
+
+ if (avio_tell(s->pb) > header_size)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(s->pb, header_size - avio_tell(s->pb));
+
+ return 0;
+ } else if (!memcmp(buffer, "channel_count", 13)) {
+ sscanf(buffer, "%*s %*s %"SCNd32, &st->codec->channels);
+ } else if (!memcmp(buffer, "sample_byte_format", 18)) {
+ sscanf(buffer, "%*s %*s %31s", format);
+
+ if (!av_strcasecmp(format, "01")) {
+ be = 0;
+ } else if (!av_strcasecmp(format, "10")) {
+ be = 1;
+ } else if (av_strcasecmp(format, "1")) {
+ avpriv_request_sample(s, "sample byte format %s", format);
+ return AVERROR_PATCHWELCOME;
+ }
+ } else if (!memcmp(buffer, "sample_coding", 13)) {
+ sscanf(buffer, "%*s %*s %31s", coding);
+ } else if (!memcmp(buffer, "sample_count", 12)) {
+ sscanf(buffer, "%*s %*s %"SCNd64, &st->duration);
+ } else if (!memcmp(buffer, "sample_n_bytes", 14)) {
+ sscanf(buffer, "%*s %*s %"SCNd32, &bps);
+ } else if (!memcmp(buffer, "sample_rate", 11)) {
+ sscanf(buffer, "%*s %*s %"SCNd32, &st->codec->sample_rate);
+ } else if (!memcmp(buffer, "sample_sig_bits", 15)) {
+ sscanf(buffer, "%*s %*s %"SCNd32, &st->codec->bits_per_coded_sample);
+ } else {
+ char key[32], value[32];
+ if (sscanf(buffer, "%31s %*s %31s", key, value) == 3) {
+ av_dict_set(&s->metadata, key, value, AV_DICT_APPEND);
+ } else {
+ av_log(s, AV_LOG_ERROR, "Failed to parse '%s' as metadata\n", buffer);
+ }
+ }
+ }
+
+ return AVERROR_EOF;
+}
+
+AVInputFormat ff_nistsphere_demuxer = {
+ .name = "nistsphere",
+ .long_name = NULL_IF_CONFIG_SMALL("NIST SPeech HEader REsources"),
+ .read_probe = nist_probe,
+ .read_header = nist_read_header,
+ .read_packet = ff_pcm_read_packet,
+ .read_seek = ff_pcm_read_seek,
+ .extensions = "nist,sph",
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/noproxy-test.c b/libavformat/noproxy-test.c
index e6cc421413..4524764cbf 100644
--- a/libavformat/noproxy-test.c
+++ b/libavformat/noproxy-test.c
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2013 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/nsvdec.c b/libavformat/nsvdec.c
index c8ac74c033..59b7938200 100644
--- a/libavformat/nsvdec.c
+++ b/libavformat/nsvdec.c
@@ -1,23 +1,23 @@
/*
* NSV demuxer
- * Copyright (c) 2004 The Libav Project
+ * Copyright (c) 2004 The FFmpeg Project
*
* first version by Francois Revol <revol@free.fr>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -26,6 +26,7 @@
#include "avformat.h"
#include "internal.h"
#include "libavutil/dict.h"
+#include "libavutil/intreadwrite.h"
/* max bytes to crawl for trying to resync
* stupid streaming servers don't start at chunk boundaries...
@@ -69,11 +70,7 @@
* so the header seems to not be mandatory. (for streaming).
*
* index slice duration check (excepts nsvtrailer.nsv):
- * for f in [^n]*.nsv; do
- * DUR="$(avconv -i "$f" 2> /dev/null | grep 'NSVf duration' | cut -d ' ' -f 4)"
- * IC="$(avconv -i "$f" 2> /dev/null | grep 'INDEX ENTRIES' | cut -d ' ' -f 2)"
- * echo "duration $DUR, slite time $(($DUR/$IC))"
- * done
+ * for f in [^n]*.nsv; do DUR="$(ffmpeg -i "$f" 2>/dev/null | grep 'NSVf duration' | cut -d ' ' -f 4)"; IC="$(ffmpeg -i "$f" 2>/dev/null | grep 'INDEX ENTRIES' | cut -d ' ' -f 2)"; echo "duration $DUR, slite time $(($DUR/$IC))"; done
*/
/*
@@ -99,8 +96,8 @@ struct NSVs_header {
uint32_t chunk_tag; /* 'NSVs' */
uint32_t v4cc; /* or 'NONE' */
uint32_t a4cc; /* or 'NONE' */
- uint16_t vwidth; /* assert(vwidth%16==0) */
- uint16_t vheight; /* assert(vheight%16==0) */
+ uint16_t vwidth; /* av_assert0(vwidth%16==0) */
+ uint16_t vheight; /* av_assert0(vheight%16==0) */
uint8_t framerate; /* value = (framerate&0x80)?frtable[frameratex0x7f]:framerate */
uint16_t unknown;
};
@@ -192,6 +189,7 @@ static const AVCodecTag nsv_codec_video_tags[] = {
{ AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '0') },
{ AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '1') },
{ AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '2') },
+ { AV_CODEC_ID_VP8, MKTAG('V', 'P', '8', '0') },
/*
{ AV_CODEC_ID_VP4, MKTAG('V', 'P', '4', ' ') },
{ AV_CODEC_ID_VP4, MKTAG('V', 'P', '4', '0') },
@@ -205,6 +203,7 @@ static const AVCodecTag nsv_codec_audio_tags[] = {
{ AV_CODEC_ID_MP3, MKTAG('M', 'P', '3', ' ') },
{ AV_CODEC_ID_AAC, MKTAG('A', 'A', 'C', ' ') },
{ AV_CODEC_ID_AAC, MKTAG('A', 'A', 'C', 'P') },
+ { AV_CODEC_ID_AAC, MKTAG('V', 'L', 'B', ' ') },
{ AV_CODEC_ID_SPEEX, MKTAG('S', 'P', 'X', ' ') },
{ AV_CODEC_ID_PCM_U16LE, MKTAG('P', 'C', 'M', ' ') },
{ AV_CODEC_ID_NONE, 0 },
@@ -233,7 +232,7 @@ static int nsv_resync(AVFormatContext *s)
//nsv->state = NSV_UNSYNC;
for (i = 0; i < NSV_MAX_RESYNC; i++) {
- if (pb->eof_reached) {
+ if (avio_feof(pb)) {
av_dlog(s, "NSV EOF\n");
nsv->state = NSV_UNSYNC;
return -1;
@@ -300,7 +299,7 @@ static int nsv_parse_NSVf_header(AVFormatContext *s)
table_entries_used = avio_rl32(pb);
av_dlog(s, "NSV NSVf info-strings size: %d, table entries: %d, bis %d\n",
strings_size, table_entries, table_entries_used);
- if (pb->eof_reached)
+ if (avio_feof(pb))
return -1;
av_dlog(s, "NSV got header; filepos %"PRId64"\n", avio_tell(pb));
@@ -337,7 +336,7 @@ static int nsv_parse_NSVf_header(AVFormatContext *s)
}
av_free(strings);
}
- if (pb->eof_reached)
+ if (avio_feof(pb))
return -1;
av_dlog(s, "NSV got infos; filepos %"PRId64"\n", avio_tell(pb));
@@ -369,7 +368,7 @@ static int nsv_parse_NSVf_header(AVFormatContext *s)
avio_seek(pb, nsv->base_offset + size, SEEK_SET); /* required for dumbdriving-271.nsv (2 extra bytes) */
- if (pb->eof_reached)
+ if (avio_feof(pb))
return -1;
nsv->state = NSV_HAS_READ_NSVF;
return 0;
@@ -540,6 +539,7 @@ static int nsv_read_chunk(AVFormatContext *s, int fill_header)
uint32_t vsize;
uint16_t asize;
uint16_t auxsize;
+ int ret;
av_dlog(s, "%s(%d)\n", __FUNCTION__, fill_header);
@@ -547,7 +547,7 @@ static int nsv_read_chunk(AVFormatContext *s, int fill_header)
return 0; //-1; /* hey! eat what you've in your plate first! */
null_chunk_retry:
- if (pb->eof_reached)
+ if (avio_feof(pb))
return -1;
for (i = 0; i < NSV_MAX_RESYNC_TRIES && nsv->state < NSV_FOUND_NSVS && !err; i++)
@@ -582,7 +582,7 @@ null_chunk_retry:
vsize -= auxsize + sizeof(uint16_t) + sizeof(uint32_t); /* that's becoming braindead */
}
- if (pb->eof_reached)
+ if (avio_feof(pb))
return -1;
if (!vsize && !asize) {
nsv->state = NSV_UNSYNC;
@@ -598,7 +598,8 @@ null_chunk_retry:
if (vsize && st[NSV_ST_VIDEO]) {
nst = st[NSV_ST_VIDEO]->priv_data;
pkt = &nsv->ahead[NSV_ST_VIDEO];
- av_get_packet(pb, pkt, vsize);
+ if ((ret = av_get_packet(pb, pkt, vsize)) < 0)
+ return ret;
pkt->stream_index = st[NSV_ST_VIDEO]->index;//NSV_ST_VIDEO;
pkt->dts = nst->frame_offset;
pkt->flags |= nsv->state == NSV_HAS_READ_NSVS ? AV_PKT_FLAG_KEY : 0; /* keyframe only likely on a sync frame */
@@ -639,7 +640,8 @@ null_chunk_retry:
av_dlog(s, "NSV RAWAUDIO: bps %d, nchan %d, srate %d\n", bps, channels, samplerate);
}
}
- av_get_packet(pb, pkt, asize);
+ if ((ret = av_get_packet(pb, pkt, asize)) < 0)
+ return ret;
pkt->stream_index = st[NSV_ST_AUDIO]->index;//NSV_ST_AUDIO;
pkt->flags |= nsv->state == NSV_HAS_READ_NSVS ? AV_PKT_FLAG_KEY : 0; /* keyframe only likely on a sync frame */
if( nsv->state == NSV_HAS_READ_NSVS && st[NSV_ST_VIDEO] ) {
@@ -719,10 +721,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 */
@@ -734,19 +734,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..7c08c396e8 100644
--- a/libavformat/nullenc.c
+++ b/libavformat/nullenc.c
@@ -2,20 +2,20 @@
* RAW null muxer
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -32,5 +32,5 @@ AVOutputFormat ff_null_muxer = {
.audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE),
.video_codec = AV_CODEC_ID_RAWVIDEO,
.write_packet = null_write_packet,
- .flags = AVFMT_NOFILE | AVFMT_NOTIMESTAMPS | AVFMT_RAWPICTURE,
+ .flags = AVFMT_VARIABLE_FPS | AVFMT_NOFILE | AVFMT_NOTIMESTAMPS | AVFMT_RAWPICTURE,
};
diff --git a/libavformat/nut.c b/libavformat/nut.c
index 43ae8a0f0e..c6fdb0bff8 100644
--- a/libavformat/nut.c
+++ b/libavformat/nut.c
@@ -2,20 +2,20 @@
* nut
* Copyright (c) 2004-2007 Michael Niedermayer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -27,9 +27,10 @@
const AVCodecTag ff_nut_subtitle_tags[] = {
{ AV_CODEC_ID_TEXT, MKTAG('U', 'T', 'F', '8') },
- { AV_CODEC_ID_SSA, MKTAG('S', 'S', 'A', 0) },
+ { AV_CODEC_ID_SSA, MKTAG('S', 'S', 'A', 0 ) },
{ AV_CODEC_ID_DVD_SUBTITLE, MKTAG('D', 'V', 'D', 'S') },
{ AV_CODEC_ID_DVB_SUBTITLE, MKTAG('D', 'V', 'B', 'S') },
+ { AV_CODEC_ID_DVB_TELETEXT, MKTAG('D', 'V', 'B', 'T') },
{ AV_CODEC_ID_NONE, 0 }
};
@@ -39,25 +40,31 @@ const AVCodecTag ff_nut_data_tags[] = {
};
const AVCodecTag ff_nut_video_tags[] = {
+ { AV_CODEC_ID_GIF, MKTAG('G', 'I', 'F', 0 ) },
+ { AV_CODEC_ID_XFACE, MKTAG('X', 'F', 'A', 'C') },
{ AV_CODEC_ID_VP9, MKTAG('V', 'P', '9', '0') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 15) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 15) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 16) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 16) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(15, 'B', 'G', 'R') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(15, 'R', 'G', 'B') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 'B', 'G', 'R') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 'R', 'G', 'B') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 12) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 12) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(12, 'B', 'G', 'R') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(12, 'R', 'G', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 15 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 15 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 16 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 16 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(15 , 'B', 'G', 'R') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(15 , 'R', 'G', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 'B', 'G', 'R') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 'R', 'G', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 12 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 12 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 'B', 'G', 'R') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 'R', 'G', 'B') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 'A') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 0 ) },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 'A') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 0 ) },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'B', 'G', 'R') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG( 0 , 'B', 'G', 'R') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'R', 'G', 'B') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 24) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 24) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG( 0 , 'R', 'G', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 24 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 24 ) },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('4', '1', '1', 'P') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('4', '2', '2', 'P') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('4', '2', '2', 'P') },
@@ -67,36 +74,50 @@ const AVCodecTag ff_nut_video_tags[] = {
{ AV_CODEC_ID_RAWVIDEO, MKTAG('4', '4', '4', 'P') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('B', '1', 'W', '0') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('B', '0', 'W', '1') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 8) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 8) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 4) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 4) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 8 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 8 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 4 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 4 ) },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('B', '4', 'B', 'Y') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('R', '4', 'B', 'Y') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 48) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 48) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(48, 'B', 'G', 'R') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(48, 'R', 'G', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 48 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 48 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(48 , 'B', 'G', 'R') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(48 , 'R', 'G', 'B') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'B', 'A', 64 ) },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'R', 'A', 64 ) },
{ AV_CODEC_ID_RAWVIDEO, MKTAG(64 , 'R', 'B', 'A') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG(64 , 'B', 'R', 'A') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11, 10) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(10, 11, '3', 'Y') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10, 10) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(10, 10, '3', 'Y') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0, 10) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(10, 0, '3', 'Y') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '1', 0, 16) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 0, '1', 'Y') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11, 16) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 11, '3', 'Y') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10, 16) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 10, '3', 'Y') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0, 16) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 0, '3', 'Y') },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 11, 8) },
- { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '2', 0, 8) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 10 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 11 , '3', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 10 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 10 , '3', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 10 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 0 , '3', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 12 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 11 , '3', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 12 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 10 , '3', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 12 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 0 , '3', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 14 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(14 , 11 , '3', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 14 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(14 , 10 , '3', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 14 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(14 , 0 , '3', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '1', 0 , 16 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 0 , '1', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 16 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 11 , '3', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 16 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 10 , '3', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 16 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 0 , '3', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 11 , 8 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 10 , 8 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 0 , 8 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '2', 0 , 8 ) },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '1', 0, 9) },
{ AV_CODEC_ID_RAWVIDEO, MKTAG(9, 0, '1', 'Y') },
@@ -125,40 +146,77 @@ const AVCodecTag ff_nut_video_tags[] = {
{ AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 0, 16) },
{ AV_CODEC_ID_RAWVIDEO, MKTAG(16, 0, '4', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '3', 0, 8) },
+
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '3', 0, 9) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG( 9, 0, '3', 'G') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '3', 0, 10) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(10, 0, '3', 'G') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '3', 0, 12) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(12, 0, '3', 'G') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '3', 0, 14) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(14, 0, '3', 'G') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '3', 0, 16) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 0, '3', 'G') },
+
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('X', 'Y', 'Z' , 36 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(36 , 'Z' , 'Y', 'X') },
+
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'B', 'G', 8 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'B', 'G', 16 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 'G', 'B', 0xBA) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'R', 'G', 8 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'R', 'G', 16 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 'G', 'R', 0xBA) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'G', 'B', 8 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'G', 'B', 16 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 'B', 'G', 0xBA) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'G', 'R', 8 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(0xBA, 'G', 'R', 16 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(16, 'R', 'G', 0xBA) },
+
{ AV_CODEC_ID_NONE, 0 }
};
-const AVCodecTag ff_nut_audio_tags[] = {
+const AVCodecTag ff_nut_audio_extra_tags[] = {
+ { AV_CODEC_ID_COMFORT_NOISE, MKTAG('3', '3', '8', '9') },
{ AV_CODEC_ID_PCM_ALAW, MKTAG('A', 'L', 'A', 'W') },
{ AV_CODEC_ID_PCM_MULAW, MKTAG('U', 'L', 'A', 'W') },
- { AV_CODEC_ID_PCM_F32BE, MKTAG(32, 'D', 'F', 'P') },
- { AV_CODEC_ID_PCM_F32LE, MKTAG('P', 'F', 'D', 32) },
- { AV_CODEC_ID_PCM_F64BE, MKTAG(64, 'D', 'F', 'P') },
- { AV_CODEC_ID_PCM_F64LE, MKTAG('P', 'F', 'D', 64) },
- { AV_CODEC_ID_PCM_S16BE, MKTAG(16, 'D', 'S', 'P') },
- { AV_CODEC_ID_PCM_S16LE, MKTAG('P', 'S', 'D', 16) },
- { AV_CODEC_ID_PCM_S24BE, MKTAG(24, 'D', 'S', 'P') },
- { AV_CODEC_ID_PCM_S24LE, MKTAG('P', 'S', 'D', 24) },
- { AV_CODEC_ID_PCM_S32BE, MKTAG(32, 'D', 'S', 'P') },
- { AV_CODEC_ID_PCM_S32LE, MKTAG('P', 'S', 'D', 32) },
- { AV_CODEC_ID_PCM_S8, MKTAG('P', 'S', 'D', 8) },
- { AV_CODEC_ID_PCM_U16BE, MKTAG(16, 'D', 'U', 'P') },
- { AV_CODEC_ID_PCM_U16LE, MKTAG('P', 'U', 'D', 16) },
- { AV_CODEC_ID_PCM_U24BE, MKTAG(24, 'D', 'U', 'P') },
- { AV_CODEC_ID_PCM_U24LE, MKTAG('P', 'U', 'D', 24) },
- { AV_CODEC_ID_PCM_U32BE, MKTAG(32, 'D', 'U', 'P') },
- { AV_CODEC_ID_PCM_U32LE, MKTAG('P', 'U', 'D', 32) },
- { AV_CODEC_ID_PCM_U8, MKTAG('P', 'U', 'D', 8) },
- { AV_CODEC_ID_PCM_S16LE_PLANAR, MKTAG('P', 'S', 'P', 16) },
- { AV_CODEC_ID_PCM_S24LE_PLANAR, MKTAG('P', 'S', 'P', 24) },
- { AV_CODEC_ID_PCM_S32LE_PLANAR, MKTAG('P', 'S', 'P', 32) },
{ AV_CODEC_ID_MP3, MKTAG('M', 'P', '3', ' ') },
- { AV_CODEC_ID_NONE, 0 }
+ { AV_CODEC_ID_WAVPACK, MKTAG('w', 'v', 'p', 'k') },
+ { AV_CODEC_ID_NONE, 0 }
+};
+
+const AVCodecTag ff_nut_audio_tags[] = {
+ { AV_CODEC_ID_PCM_F32BE, MKTAG(32 , 'D', 'F', 'P') },
+ { AV_CODEC_ID_PCM_F32LE, MKTAG('P', 'F', 'D', 32 ) },
+ { AV_CODEC_ID_PCM_F64BE, MKTAG(64 , 'D', 'F', 'P') },
+ { AV_CODEC_ID_PCM_F64LE, MKTAG('P', 'F', 'D', 64 ) },
+ { AV_CODEC_ID_PCM_S16BE, MKTAG(16 , 'D', 'S', 'P') },
+ { AV_CODEC_ID_PCM_S16LE, MKTAG('P', 'S', 'D', 16 ) },
+ { AV_CODEC_ID_PCM_S24BE, MKTAG(24 , 'D', 'S', 'P') },
+ { AV_CODEC_ID_PCM_S24LE, MKTAG('P', 'S', 'D', 24 ) },
+ { AV_CODEC_ID_PCM_S32BE, MKTAG(32 , 'D', 'S', 'P') },
+ { AV_CODEC_ID_PCM_S32LE, MKTAG('P', 'S', 'D', 32 ) },
+ { AV_CODEC_ID_PCM_S8, MKTAG('P', 'S', 'D', 8 ) },
+ { AV_CODEC_ID_PCM_U16BE, MKTAG(16 , 'D', 'U', 'P') },
+ { AV_CODEC_ID_PCM_U16LE, MKTAG('P', 'U', 'D', 16 ) },
+ { AV_CODEC_ID_PCM_U24BE, MKTAG(24 , 'D', 'U', 'P') },
+ { AV_CODEC_ID_PCM_U24LE, MKTAG('P', 'U', 'D', 24 ) },
+ { AV_CODEC_ID_PCM_U32BE, MKTAG(32 , 'D', 'U', 'P') },
+ { AV_CODEC_ID_PCM_U32LE, MKTAG('P', 'U', 'D', 32 ) },
+ { AV_CODEC_ID_PCM_U8, MKTAG('P', 'U', 'D', 8 ) },
+ { AV_CODEC_ID_PCM_S8_PLANAR, MKTAG('P', 'S', 'P', 8 ) },
+ { AV_CODEC_ID_PCM_S16BE_PLANAR, MKTAG(16 , 'P', 'S', 'P') },
+ { AV_CODEC_ID_PCM_S16LE_PLANAR, MKTAG('P', 'S', 'P', 16 ) },
+ { AV_CODEC_ID_PCM_S24LE_PLANAR, MKTAG('P', 'S', 'P', 24 ) },
+ { AV_CODEC_ID_PCM_S32LE_PLANAR, MKTAG('P', 'S', 'P', 32 ) },
+ { AV_CODEC_ID_NONE, 0 }
};
const AVCodecTag * const ff_nut_codec_tags[] = {
ff_nut_video_tags, ff_nut_audio_tags, ff_nut_subtitle_tags,
- ff_codec_bmp_tags, ff_codec_wav_tags, ff_nut_data_tags, 0
+ ff_codec_bmp_tags, ff_codec_wav_tags, ff_nut_audio_extra_tags, ff_nut_data_tags, 0
};
void ff_nut_reset_ts(NUTContext *nut, AVRational time_base, int64_t val)
@@ -174,7 +232,7 @@ void ff_nut_reset_ts(NUTContext *nut, AVRational time_base, int64_t val)
int64_t ff_lsb2full(StreamContext *stream, int64_t lsb)
{
- int64_t mask = (1 << stream->msb_pts_shift) - 1;
+ int64_t mask = (1ULL << stream->msb_pts_shift) - 1;
int64_t delta = stream->last_pts - mask / 2;
return ((lsb - delta) & mask) + delta;
}
@@ -200,6 +258,8 @@ int ff_nut_add_sp(NUTContext *nut, int64_t pos, int64_t back_ptr, int64_t ts)
return AVERROR(ENOMEM);
}
+ nut->sp_count++;
+
sp->pos = pos;
sp->back_ptr = back_ptr;
sp->ts = ts;
diff --git a/libavformat/nut.h b/libavformat/nut.h
index 16f3c123a1..943081caf6 100644
--- a/libavformat/nut.h
+++ b/libavformat/nut.h
@@ -2,20 +2,20 @@
* "NUT" Container Format (de)muxer
* Copyright (c) 2006 Michael Niedermayer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -48,6 +48,7 @@ typedef enum{
FLAG_SIZE_MSB = 32, ///<if set, data_size_msb is at frame header, otherwise data_size_msb is 0
FLAG_CHECKSUM = 64, ///<if set, the frame header contains a checksum
FLAG_RESERVED = 128, ///<if set, reserved_count is coded in the frame header
+ FLAG_SM_DATA = 256, ///<if set, side / meta data is stored in the frame header.
FLAG_HEADER_IDX =1024, ///<If set, header_idx is coded in the frame header.
FLAG_MATCH_TIME =2048, ///<If set, match_time_delta is coded in the frame header
FLAG_CODED =4096, ///<if set, coded_flags are stored in the frame header
@@ -80,6 +81,7 @@ typedef struct StreamContext {
int msb_pts_shift;
int max_pts_distance;
int decode_delay; //FIXME duplicate of has_b_frames
+ int64_t *keyframe_pts;
} StreamContext;
typedef struct ChapterContext {
@@ -103,15 +105,21 @@ typedef struct NUTContext {
int header_count;
AVRational *time_base;
struct AVTreeNode *syncpoints;
+ int sp_count;
+ int write_index;
+ int64_t max_pts;
+ AVRational *max_pts_tb;
#define NUT_BROADCAST 1 // use extended syncpoints
#define NUT_PIPE 2 // do not write syncpoints
int flags;
int version; // version currently in use
+ int minor_version;
} NUTContext;
extern const AVCodecTag ff_nut_subtitle_tags[];
extern const AVCodecTag ff_nut_video_tags[];
extern const AVCodecTag ff_nut_audio_tags[];
+extern const AVCodecTag ff_nut_audio_extra_tags[];
extern const AVCodecTag ff_nut_data_tags[];
extern const AVCodecTag * const ff_nut_codec_tags[];
diff --git a/libavformat/nutdec.c b/libavformat/nutdec.c
index 00da7dded0..00e86bb50b 100644
--- a/libavformat/nutdec.c
+++ b/libavformat/nutdec.c
@@ -3,37 +3,41 @@
* Copyright (c) 2004-2006 Michael Niedermayer
* Copyright (c) 2003 Alex Beregszaszi
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libavutil/avstring.h"
+#include "libavutil/avassert.h"
#include "libavutil/bswap.h"
#include "libavutil/dict.h"
+#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
#include "libavutil/tree.h"
+#include "libavcodec/bytestream.h"
#include "avio_internal.h"
+#include "isom.h"
#include "nut.h"
#include "riff.h"
-#undef NDEBUG
-#include <assert.h>
-
#define NUT_MAX_STREAMS 256 /* arbitrary sanity check value */
+static int64_t nut_read_timestamp(AVFormatContext *s, int stream_index,
+ int64_t *pos_arg, int64_t pos_limit);
+
static int get_str(AVIOContext *bc, char *string, unsigned int maxlen)
{
unsigned int len = ffio_read_varlen(bc);
@@ -72,8 +76,10 @@ static uint64_t get_fourcc(AVIOContext *bc)
return avio_rl16(bc);
else if (len == 4)
return avio_rl32(bc);
- else
+ else {
+ av_log(NULL, AV_LOG_ERROR, "Unsupported fourcc length %d\n", len);
return -1;
+ }
}
#ifdef TRACE
@@ -97,8 +103,18 @@ static inline int64_t get_s_trace(AVIOContext *bc, const char *file,
return v;
}
+static inline uint64_t get_4cc_trace(AVIOContext *bc, char *file,
+ char *func, int line)
+{
+ uint64_t v = get_fourcc(bc);
+
+ av_log(NULL, AV_LOG_DEBUG, "get_fourcc %5"PRId64" / %"PRIX64" in %s %s:%d\n",
+ v, v, file, func, line);
+ return v;
+}
#define ffio_read_varlen(bc) get_v_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__)
#define get_s(bc) get_s_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__)
+#define get_fourcc(bc) get_4cc_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__)
#endif
static int get_packetheader(NUTContext *nut, AVIOContext *bc,
@@ -130,7 +146,7 @@ static uint64_t find_any_startcode(AVIOContext *bc, int64_t pos)
/* Note, this may fail if the stream is not seekable, but that should
* not matter, as in this case we simply start where we currently are */
avio_seek(bc, pos, SEEK_SET);
- while (!bc->eof_reached) {
+ while (!avio_feof(bc)) {
state = (state << 8) | avio_r8(bc);
if ((state >> 56) != 'N')
continue;
@@ -168,11 +184,11 @@ static int64_t find_startcode(AVIOContext *bc, uint64_t code, int64_t pos)
static int nut_probe(AVProbeData *p)
{
int i;
- uint64_t code = 0;
- for (i = 0; i < p->buf_size; i++) {
- code = (code << 8) | p->buf[i];
- if (code == MAIN_STARTCODE)
+ for (i = 0; i < p->buf_size-8; i++) {
+ if (AV_RB32(p->buf+i) != MAIN_STARTCODE>>32)
+ continue;
+ if (AV_RB32(p->buf+i+4) == (MAIN_STARTCODE & 0xFFFFFFFF))
return AVPROBE_SCORE_MAX;
}
return 0;
@@ -220,6 +236,8 @@ static int decode_main_header(NUTContext *nut)
nut->version);
return AVERROR(ENOSYS);
}
+ if (nut->version > 3)
+ nut->minor_version = ffio_read_varlen(bc);
GET_V(stream_count, tmp > 0 && tmp <= NUT_MAX_STREAMS);
@@ -231,6 +249,8 @@ static int decode_main_header(NUTContext *nut)
GET_V(nut->time_base_count, tmp > 0 && tmp < INT_MAX / sizeof(AVRational));
nut->time_base = av_malloc(nut->time_base_count * sizeof(AVRational));
+ if (!nut->time_base)
+ return AVERROR(ENOMEM);
for (i = 0; i < nut->time_base_count; i++) {
GET_V(nut->time_base[i].num, tmp > 0 && tmp < (1ULL << 31));
@@ -298,7 +318,7 @@ static int decode_main_header(NUTContext *nut)
nut->frame_code[i].header_idx = tmp_head_idx;
}
}
- assert(nut->frame_code['N'].flags == FLAG_INVALID);
+ av_assert0(nut->frame_code['N'].flags == FLAG_INVALID);
if (end > avio_tell(bc) + 4) {
int rem = 1024;
@@ -318,11 +338,11 @@ static int decode_main_header(NUTContext *nut)
avio_read(bc, hdr, nut->header_len[i]);
nut->header[i] = hdr;
}
- assert(nut->header_len[0] == 0);
+ av_assert0(nut->header_len[0] == 0);
}
// flags had been effectively introduced in version 4
- if (nut->version > NUT_STABLE_VERSION) {
+ if (nut->version > 3 && end > avio_tell(bc) + 4) {
nut->flags = ffio_read_varlen(bc);
}
@@ -331,7 +351,9 @@ static int decode_main_header(NUTContext *nut)
return AVERROR_INVALIDDATA;
}
- nut->stream = av_mallocz(sizeof(StreamContext) * stream_count);
+ nut->stream = av_calloc(stream_count, sizeof(StreamContext));
+ if (!nut->stream)
+ return AVERROR(ENOMEM);
for (i = 0; i < stream_count; i++)
avformat_new_stream(s, NULL);
@@ -365,6 +387,7 @@ static int decode_stream_header(NUTContext *nut)
st->codec->codec_id = av_codec_get_id((const AVCodecTag * const []) {
ff_nut_video_tags,
ff_codec_bmp_tags,
+ ff_codec_movvideo_tags,
0
},
tmp);
@@ -374,6 +397,7 @@ static int decode_stream_header(NUTContext *nut)
st->codec->codec_id = av_codec_get_id((const AVCodecTag * const []) {
ff_nut_audio_tags,
ff_codec_wav_tags,
+ ff_nut_audio_extra_tags,
0
},
tmp);
@@ -399,13 +423,13 @@ static int decode_stream_header(NUTContext *nut)
GET_V(stc->msb_pts_shift, tmp < 16);
stc->max_pts_distance = ffio_read_varlen(bc);
GET_V(stc->decode_delay, tmp < 1000); // sanity limit, raise this if Moore's law is true
+ st->codec->has_b_frames = stc->decode_delay;
ffio_read_varlen(bc); // stream flags
GET_V(st->codec->extradata_size, tmp < (1 << 30));
if (st->codec->extradata_size) {
- st->codec->extradata = av_mallocz(st->codec->extradata_size +
- FF_INPUT_BUFFER_PADDING_SIZE);
- avio_read(bc, st->codec->extradata, st->codec->extradata_size);
+ if (ff_get_extradata(st->codec, bc, st->codec->extradata_size) < 0)
+ return AVERROR(ENOMEM);
}
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
@@ -526,6 +550,14 @@ static int decode_info_header(NUTContext *nut)
set_disposition_bits(s, str_value, stream_id_plus1 - 1);
continue;
}
+
+ if (stream_id_plus1 && !strcmp(name, "r_frame_rate")) {
+ sscanf(str_value, "%d/%d", &st->r_frame_rate.num, &st->r_frame_rate.den);
+ if (st->r_frame_rate.num >= 1000LL*st->r_frame_rate.den)
+ st->r_frame_rate.num = st->r_frame_rate.den = 0;
+ continue;
+ }
+
if (metadata && av_strcasecmp(name, "Uses") &&
av_strcasecmp(name, "Depends") && av_strcasecmp(name, "Replaces")) {
if (event_flags)
@@ -546,7 +578,8 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr)
{
AVFormatContext *s = nut->avf;
AVIOContext *bc = s->pb;
- int64_t end, tmp;
+ int64_t end;
+ uint64_t tmp;
int ret;
nut->last_syncpoint_pos = avio_tell(bc) - 8;
@@ -557,7 +590,7 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr)
tmp = ffio_read_varlen(bc);
*back_ptr = nut->last_syncpoint_pos - 16 * ffio_read_varlen(bc);
if (*back_ptr < 0)
- return -1;
+ return AVERROR_INVALIDDATA;
ff_nut_reset_ts(nut, nut->time_base[tmp % nut->time_base_count],
tmp / nut->time_base_count);
@@ -575,8 +608,8 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr)
return AVERROR_INVALIDDATA;
}
- *ts = tmp / s->nb_streams *
- av_q2d(nut->time_base[tmp % s->nb_streams]) * AV_TIME_BASE;
+ *ts = tmp / nut->time_base_count *
+ av_q2d(nut->time_base[tmp % nut->time_base_count]) * AV_TIME_BASE;
if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, *back_ptr, *ts)) < 0)
return ret;
@@ -584,6 +617,19 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr)
return 0;
}
+//FIXME calculate exactly, this is just a good approximation.
+static int64_t find_duration(NUTContext *nut, int64_t filesize)
+{
+ AVFormatContext *s = nut->avf;
+ int64_t duration = 0;
+
+ ff_find_last_ts(s, -1, &duration, NULL, nut_read_timestamp);
+
+ if(duration > 0)
+ s->duration_estimation_method = AVFMT_DURATION_FROM_PTS;
+ return duration;
+}
+
static int find_and_decode_index(NUTContext *nut)
{
AVFormatContext *s = nut->avf;
@@ -592,23 +638,39 @@ static int find_and_decode_index(NUTContext *nut)
int i, j, syncpoint_count;
int64_t filesize = avio_size(bc);
int64_t *syncpoints;
+ uint64_t max_pts;
int8_t *has_keyframe;
int ret = AVERROR_INVALIDDATA;
+ if(filesize <= 0)
+ return -1;
+
avio_seek(bc, filesize - 12, SEEK_SET);
avio_seek(bc, filesize - avio_rb64(bc), SEEK_SET);
if (avio_rb64(bc) != INDEX_STARTCODE) {
av_log(s, AV_LOG_ERROR, "no index at the end\n");
+
+ if(s->duration<=0)
+ s->duration = find_duration(nut, filesize);
return ret;
}
end = get_packetheader(nut, bc, 1, INDEX_STARTCODE);
end += avio_tell(bc);
- ffio_read_varlen(bc); // max_pts
+ max_pts = ffio_read_varlen(bc);
+ s->duration = av_rescale_q(max_pts / nut->time_base_count,
+ nut->time_base[max_pts % nut->time_base_count],
+ AV_TIME_BASE_Q);
+ s->duration_estimation_method = AVFMT_DURATION_FROM_PTS;
+
GET_V(syncpoint_count, tmp < INT_MAX / 8 && tmp > 0);
- syncpoints = av_malloc(sizeof(int64_t) * syncpoint_count);
- has_keyframe = av_malloc(sizeof(int8_t) * (syncpoint_count + 1));
+ syncpoints = av_malloc_array(syncpoint_count, sizeof(int64_t));
+ has_keyframe = av_malloc_array(syncpoint_count + 1, sizeof(int8_t));
+ if (!syncpoints || !has_keyframe) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
for (i = 0; i < syncpoint_count; i++) {
syncpoints[i] = ffio_read_varlen(bc);
if (syncpoints[i] <= 0)
@@ -628,7 +690,7 @@ static int find_and_decode_index(NUTContext *nut)
int flag = x & 1;
x >>= 1;
if (n + x >= syncpoint_count + 1) {
- av_log(s, AV_LOG_ERROR, "index overflow A\n");
+ av_log(s, AV_LOG_ERROR, "index overflow A %d + %"PRIu64" >= %d\n", n, x, syncpoint_count + 1);
goto fail;
}
while (x--)
@@ -648,7 +710,7 @@ static int find_and_decode_index(NUTContext *nut)
av_log(s, AV_LOG_ERROR, "keyframe before first syncpoint in index\n");
goto fail;
}
- assert(n <= syncpoint_count + 1);
+ av_assert0(n <= syncpoint_count + 1);
for (; j < n && j < syncpoint_count; j++) {
if (has_keyframe[j]) {
uint64_t B, A = ffio_read_varlen(bc);
@@ -735,13 +797,123 @@ static int nut_read_header(AVFormatContext *s)
find_and_decode_index(nut);
avio_seek(bc, orig_pos, SEEK_SET);
}
- assert(nut->next_startcode == SYNCPOINT_STARTCODE);
+ av_assert0(nut->next_startcode == SYNCPOINT_STARTCODE);
ff_metadata_conv_ctx(s, NULL, ff_nut_metadata_conv);
return 0;
}
+static int read_sm_data(AVFormatContext *s, AVIOContext *bc, AVPacket *pkt, int is_meta, int64_t maxpos)
+{
+ int count = ffio_read_varlen(bc);
+ int skip_start = 0;
+ int skip_end = 0;
+ int channels = 0;
+ int64_t channel_layout = 0;
+ int sample_rate = 0;
+ int width = 0;
+ int height = 0;
+ int i;
+
+ for (i=0; i<count; i++) {
+ uint8_t name[256], str_value[256], type_str[256];
+ int value;
+ if (avio_tell(bc) >= maxpos)
+ return AVERROR_INVALIDDATA;
+ get_str(bc, name, sizeof(name));
+ value = get_s(bc);
+
+ if (value == -1) {
+ get_str(bc, str_value, sizeof(str_value));
+ av_log(s, AV_LOG_WARNING, "Unknown string %s / %s\n", name, str_value);
+ } else if (value == -2) {
+ uint8_t *dst = NULL;
+ int64_t v64, value_len;
+
+ get_str(bc, type_str, sizeof(type_str));
+ value_len = ffio_read_varlen(bc);
+ if (avio_tell(bc) + value_len >= maxpos)
+ return AVERROR_INVALIDDATA;
+ if (!strcmp(name, "Palette")) {
+ dst = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, value_len);
+ } else if (!strcmp(name, "Extradata")) {
+ dst = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, value_len);
+ } else if (sscanf(name, "CodecSpecificSide%"SCNd64"", &v64) == 1) {
+ dst = av_packet_new_side_data(pkt, AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, value_len + 8);
+ if(!dst)
+ return AVERROR(ENOMEM);
+ AV_WB64(dst, v64);
+ dst += 8;
+ } else if (!strcmp(name, "ChannelLayout") && value_len == 8) {
+ channel_layout = avio_rl64(bc);
+ continue;
+ } else {
+ av_log(s, AV_LOG_WARNING, "Unknown data %s / %s\n", name, type_str);
+ avio_skip(bc, value_len);
+ continue;
+ }
+ if(!dst)
+ return AVERROR(ENOMEM);
+ avio_read(bc, dst, value_len);
+ } else if (value == -3) {
+ value = get_s(bc);
+ } else if (value == -4) {
+ value = ffio_read_varlen(bc);
+ } else if (value < -4) {
+ get_s(bc);
+ } else {
+ if (!strcmp(name, "SkipStart")) {
+ skip_start = value;
+ } else if (!strcmp(name, "SkipEnd")) {
+ skip_end = value;
+ } else if (!strcmp(name, "Channels")) {
+ channels = value;
+ } else if (!strcmp(name, "SampleRate")) {
+ sample_rate = value;
+ } else if (!strcmp(name, "Width")) {
+ width = value;
+ } else if (!strcmp(name, "Height")) {
+ height = value;
+ } else {
+ av_log(s, AV_LOG_WARNING, "Unknown integer %s\n", name);
+ }
+ }
+ }
+
+ if (channels || channel_layout || sample_rate || width || height) {
+ uint8_t *dst = av_packet_new_side_data(pkt, AV_PKT_DATA_PARAM_CHANGE, 28);
+ if (!dst)
+ return AVERROR(ENOMEM);
+ bytestream_put_le32(&dst,
+ AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT*(!!channels) +
+ AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT*(!!channel_layout) +
+ AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE*(!!sample_rate) +
+ AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS*(!!(width|height))
+ );
+ if (channels)
+ bytestream_put_le32(&dst, channels);
+ if (channel_layout)
+ bytestream_put_le64(&dst, channel_layout);
+ if (sample_rate)
+ bytestream_put_le32(&dst, sample_rate);
+ if (width || height){
+ bytestream_put_le32(&dst, width);
+ bytestream_put_le32(&dst, height);
+ }
+ }
+
+ if (skip_start || skip_end) {
+ uint8_t *dst = av_packet_new_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 10);
+ if (!dst)
+ return AVERROR(ENOMEM);
+ AV_WL32(dst, skip_start);
+ AV_WL32(dst+4, skip_end);
+ }
+
+ return 0;
+}
+
static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id,
uint8_t *header_idx, int frame_code)
{
@@ -781,7 +953,7 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id,
if (coded_pts < (1 << stc->msb_pts_shift)) {
*pts = ff_lsb2full(stc, coded_pts);
} else
- *pts = coded_pts - (1 << stc->msb_pts_shift);
+ *pts = coded_pts - (1LL << stc->msb_pts_shift);
} else
*pts = stc->last_pts + pts_delta;
if (flags & FLAG_SIZE_MSB)
@@ -852,7 +1024,23 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code)
return ret;
memcpy(pkt->data, nut->header[header_idx], nut->header_len[header_idx]);
pkt->pos = avio_tell(bc); // FIXME
- avio_read(bc, pkt->data + nut->header_len[header_idx], size);
+ if (stc->last_flags & FLAG_SM_DATA) {
+ int sm_size;
+ if (read_sm_data(s, bc, pkt, 0, pkt->pos + size) < 0)
+ return AVERROR_INVALIDDATA;
+ if (read_sm_data(s, bc, pkt, 1, pkt->pos + size) < 0)
+ return AVERROR_INVALIDDATA;
+ sm_size = avio_tell(bc) - pkt->pos;
+ size -= sm_size;
+ pkt->size -= sm_size;
+ }
+
+ ret = avio_read(bc, pkt->data + nut->header_len[header_idx], size);
+ if (ret != size) {
+ if (ret < 0)
+ return ret;
+ }
+ av_shrink_packet(pkt, nut->header_len[header_idx] + ret);
pkt->stream_index = stream_id;
if (stc->last_flags & FLAG_KEY)
@@ -878,7 +1066,7 @@ static int nut_read_packet(AVFormatContext *s, AVPacket *pkt)
pos -= 8;
} else {
frame_code = avio_r8(bc);
- if (bc->eof_reached)
+ if (avio_feof(bc))
return AVERROR_EOF;
if (frame_code == 'N') {
tmp = frame_code;
@@ -932,21 +1120,18 @@ static int64_t nut_read_timestamp(AVFormatContext *s, int stream_index,
do {
pos = find_startcode(bc, SYNCPOINT_STARTCODE, pos) + 1;
if (pos < 1) {
- assert(nut->next_startcode == 0);
av_log(s, AV_LOG_ERROR, "read_timestamp failed.\n");
return AV_NOPTS_VALUE;
}
} while (decode_syncpoint(nut, &pts, &back_ptr) < 0);
*pos_arg = pos - 1;
- assert(nut->last_syncpoint_pos == *pos_arg);
+ av_assert0(nut->last_syncpoint_pos == *pos_arg);
av_log(s, AV_LOG_DEBUG, "return %"PRId64" %"PRId64"\n", pts, back_ptr);
- if (stream_index == -1)
- return pts;
- else if (stream_index == -2)
+ if (stream_index == -2)
return back_ptr;
-
- return AV_NOPTS_VALUE;
+ av_assert0(stream_index == -1);
+ return pts;
}
static int read_seek(AVFormatContext *s, int stream_index,
@@ -967,6 +1152,8 @@ static int read_seek(AVFormatContext *s, int stream_index,
if (st->index_entries) {
int index = av_index_search_timestamp(st, pts, flags);
if (index < 0)
+ index = av_index_search_timestamp(st, pts, flags ^ AVSEEK_FLAG_BACKWARD);
+ if (index < 0)
return -1;
pos2 = st->index_entries[index].pos;
@@ -999,7 +1186,7 @@ static int read_seek(AVFormatContext *s, int stream_index,
sp = av_tree_find(nut->syncpoints, &dummy, (void *) ff_nut_sp_pos_cmp,
NULL);
- assert(sp);
+ av_assert0(sp);
pos2 = sp->back_ptr - 15;
}
av_log(NULL, AV_LOG_DEBUG, "SEEKTO: %"PRId64"\n", pos2);
@@ -1031,6 +1218,7 @@ static int nut_read_close(AVFormatContext *s)
AVInputFormat ff_nut_demuxer = {
.name = "nut",
.long_name = NULL_IF_CONFIG_SMALL("NUT"),
+ .flags = AVFMT_SEEK_TO_PTS,
.priv_data_size = sizeof(NUTContext),
.read_probe = nut_probe,
.read_header = nut_read_header,
diff --git a/libavformat/nutenc.c b/libavformat/nutenc.c
index 3b43954dbd..c335e6455a 100644
--- a/libavformat/nutenc.c
+++ b/libavformat/nutenc.c
@@ -2,20 +2,20 @@
* nut muxer
* Copyright (c) 2004-2007 Michael Niedermayer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -25,8 +25,10 @@
#include "libavutil/mathematics.h"
#include "libavutil/tree.h"
#include "libavutil/dict.h"
+#include "libavutil/avassert.h"
#include "libavutil/time.h"
#include "libavutil/opt.h"
+#include "libavcodec/bytestream.h"
#include "libavcodec/mpegaudiodata.h"
#include "nut.h"
#include "internal.h"
@@ -64,12 +66,9 @@ static int find_expected_header(AVCodecContext *c, int size, int key_frame,
lsf = sample_rate < (24000 + 32000) / 2;
mpeg25 = sample_rate < (12000 + 16000) / 2;
sample_rate <<= lsf + mpeg25;
- if (sample_rate < (32000 + 44100) / 2)
- sample_rate_index = 2;
- else if (sample_rate < (44100 + 48000) / 2)
- sample_rate_index = 0;
- else
- sample_rate_index = 1;
+ if (sample_rate < (32000 + 44100) / 2) sample_rate_index = 2;
+ else if (sample_rate < (44100 + 48000) / 2) sample_rate_index = 0;
+ else sample_rate_index = 1;
sample_rate = avpriv_mpa_freq_tab[sample_rate_index] >> (lsf + mpeg25);
@@ -103,8 +102,7 @@ static int find_expected_header(AVCodecContext *c, int size, int key_frame,
return 0;
}
-static int find_header_idx(AVFormatContext *s, AVCodecContext *c, int size,
- int frame_type)
+static int find_header_idx(AVFormatContext *s, AVCodecContext *c, int size, int frame_type)
{
NUTContext *nut = s->priv_data;
uint8_t out[64];
@@ -170,10 +168,22 @@ static void build_frame_code(AVFormatContext *s)
int start2 = start + (end - start) * stream_id / s->nb_streams;
int end2 = start + (end - start) * (stream_id + 1) / s->nb_streams;
AVCodecContext *codec = s->streams[stream_id]->codec;
- const AVCodecDescriptor *desc = avcodec_descriptor_get(codec->codec_id);
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) {
@@ -189,31 +199,34 @@ 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 (desc && desc->props & AV_CODEC_PROP_REORDER) {
+ if (codec->has_b_frames) {
pred_count = 5;
pred_table[0] = -2;
pred_table[1] = -1;
@@ -234,6 +247,8 @@ static void build_frame_code(AVFormatContext *s)
int start3 = start2 + (end2 - start2) * pred / pred_count;
int end3 = start2 + (end2 - start2) * (pred + 1) / pred_count;
+ pred_table[pred] *= frame_size;
+
for (index = start3; index < end3; index++) {
FrameCode *ft = &nut->frame_code[index];
ft->flags = FLAG_KEY * key_frame;
@@ -248,15 +263,13 @@ static void build_frame_code(AVFormatContext *s)
}
}
}
- memmove(&nut->frame_code['N' + 1], &nut->frame_code['N'],
- sizeof(FrameCode) * (255 - 'N'));
+ memmove(&nut->frame_code['N' + 1], &nut->frame_code['N'], sizeof(FrameCode) * (255 - 'N'));
nut->frame_code[0].flags =
nut->frame_code[255].flags =
nut->frame_code['N'].flags = FLAG_INVALID;
}
-static void put_tt(NUTContext *nut, AVRational *time_base, AVIOContext *bc,
- uint64_t val)
+static void put_tt(NUTContext *nut, AVRational *time_base, AVIOContext *bc, uint64_t val)
{
val *= nut->time_base_count;
val += time_base - nut->time_base;
@@ -287,8 +300,7 @@ static inline void ff_put_v_trace(AVIOContext *bc, uint64_t v, const char *file,
ff_put_v(bc, v);
}
-static inline void put_s_trace(AVIOContext *bc, int64_t v, const char *file,
- const char *func, int line)
+static inline void put_s_trace(AVIOContext *bc, int64_t v, const char *file, const char *func, int line)
{
av_log(NULL, AV_LOG_DEBUG, "put_s %5"PRId64" / %"PRIX64" in %s %s:%d\n", v, v, file, func, line);
@@ -329,6 +341,8 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc)
int64_t tmp_match;
ff_put_v(bc, nut->version);
+ if (nut->version > 3)
+ ff_put_v(bc, nut->minor_version = 1);
ff_put_v(bc, nut->avf->nb_streams);
ff_put_v(bc, nut->max_distance);
ff_put_v(bc, nut->time_base_count);
@@ -347,17 +361,12 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc)
tmp_fields = 0;
tmp_size = 0;
// tmp_res=0;
- if (tmp_pts != nut->frame_code[i].pts_delta)
- tmp_fields = 1;
- if (tmp_mul != nut->frame_code[i].size_mul)
- tmp_fields = 2;
- if (tmp_stream != nut->frame_code[i].stream_id)
- tmp_fields = 3;
- if (tmp_size != nut->frame_code[i].size_lsb)
- tmp_fields = 4;
-// if(tmp_res != nut->frame_code[i].res ) tmp_fields=5;
- if (tmp_head_idx != nut->frame_code[i].header_idx)
- tmp_fields = 8;
+ if (tmp_pts != nut->frame_code[i].pts_delta ) tmp_fields = 1;
+ if (tmp_mul != nut->frame_code[i].size_mul ) tmp_fields = 2;
+ if (tmp_stream != nut->frame_code[i].stream_id ) tmp_fields = 3;
+ if (tmp_size != nut->frame_code[i].size_lsb ) tmp_fields = 4;
+// if (tmp_res != nut->frame_code[i].res ) tmp_fields=5;
+ if (tmp_head_idx != nut->frame_code[i].header_idx) tmp_fields = 8;
tmp_pts = nut->frame_code[i].pts_delta;
tmp_flags = nut->frame_code[i].flags;
@@ -386,22 +395,14 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc)
ff_put_v(bc, tmp_flags);
ff_put_v(bc, tmp_fields);
- if (tmp_fields > 0)
- put_s(bc, tmp_pts);
- if (tmp_fields > 1)
- ff_put_v(bc, tmp_mul);
- if (tmp_fields > 2)
- ff_put_v(bc, tmp_stream);
- if (tmp_fields > 3)
- ff_put_v(bc, tmp_size);
- if (tmp_fields > 4)
- ff_put_v(bc, 0 /*tmp_res*/);
- if (tmp_fields > 5)
- ff_put_v(bc, j);
- if (tmp_fields > 6)
- ff_put_v(bc, tmp_match);
- if (tmp_fields > 7)
- ff_put_v(bc, tmp_head_idx);
+ if (tmp_fields > 0) put_s(bc, tmp_pts);
+ if (tmp_fields > 1) ff_put_v(bc, tmp_mul);
+ if (tmp_fields > 2) ff_put_v(bc, tmp_stream);
+ if (tmp_fields > 3) ff_put_v(bc, tmp_size);
+ if (tmp_fields > 4) ff_put_v(bc, 0 /*tmp_res*/);
+ if (tmp_fields > 5) ff_put_v(bc, j);
+ if (tmp_fields > 6) ff_put_v(bc, tmp_match);
+ if (tmp_fields > 7) ff_put_v(bc, tmp_head_idx);
}
ff_put_v(bc, nut->header_count - 1);
for (i = 1; i < nut->header_count; i++) {
@@ -409,7 +410,7 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc)
avio_write(bc, nut->header[i], nut->header_len[i]);
}
// flags had been effectively introduced in version 4
- if (nut->version > NUT_STABLE_VERSION)
+ if (nut->version > 3)
ff_put_v(bc, nut->flags);
}
@@ -418,31 +419,17 @@ static int write_streamheader(AVFormatContext *avctx, AVIOContext *bc,
{
NUTContext *nut = avctx->priv_data;
AVCodecContext *codec = st->codec;
- const AVCodecDescriptor *desc = avcodec_descriptor_get(codec->codec_id);
- 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);
@@ -451,7 +438,7 @@ static int write_streamheader(AVFormatContext *avctx, AVIOContext *bc,
ff_put_v(bc, nut->stream[i].time_base - nut->time_base);
ff_put_v(bc, nut->stream[i].msb_pts_shift);
ff_put_v(bc, nut->stream[i].max_pts_distance);
- ff_put_v(bc, (desc && desc->props & AV_CODEC_PROP_REORDER) ? 16 : 0);
+ ff_put_v(bc, codec->has_b_frames);
avio_w8(bc, 0); /* flags: 0x1 - fixed_fps, 0x2 - index_present */
ff_put_v(bc, codec->extradata_size);
@@ -518,20 +505,31 @@ 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];
+ if (st->r_frame_rate.num>0 && st->r_frame_rate.den>0)
+ snprintf(buf, sizeof(buf), "%d/%d", st->r_frame_rate.num, st->r_frame_rate.den);
+ else
+ 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) {
@@ -577,6 +575,58 @@ static int write_chapter(NUTContext *nut, AVIOContext *bc, int id)
return 0;
}
+static int write_index(NUTContext *nut, AVIOContext *bc) {
+ int i;
+ Syncpoint dummy= { .pos= 0 };
+ Syncpoint *next_node[2] = { NULL };
+ int64_t startpos = avio_tell(bc);
+ int64_t payload_size;
+
+ put_tt(nut, nut->max_pts_tb, bc, nut->max_pts);
+
+ ff_put_v(bc, nut->sp_count);
+
+ for (i=0; i<nut->sp_count; i++) {
+ av_tree_find(nut->syncpoints, &dummy, (void *) ff_nut_sp_pos_cmp, (void**)next_node);
+ ff_put_v(bc, (next_node[1]->pos >> 4) - (dummy.pos>>4));
+ dummy.pos = next_node[1]->pos;
+ }
+
+ for (i=0; i<nut->avf->nb_streams; i++) {
+ StreamContext *nus= &nut->stream[i];
+ int64_t last_pts= -1;
+ int j, k;
+ for (j=0; j<nut->sp_count; j++) {
+ int flag;
+ int n = 0;
+
+ if (j && nus->keyframe_pts[j] == nus->keyframe_pts[j-1]) {
+ av_log(nut->avf, AV_LOG_WARNING, "Multiple keyframes with same PTS\n");
+ nus->keyframe_pts[j] = AV_NOPTS_VALUE;
+ }
+
+ flag = (nus->keyframe_pts[j] != AV_NOPTS_VALUE) ^ (j+1 == nut->sp_count);
+ for (; j<nut->sp_count && (nus->keyframe_pts[j] != AV_NOPTS_VALUE) == flag; j++)
+ n++;
+
+ ff_put_v(bc, 1 + 2*flag + 4*n);
+ for (k= j - n; k<=j && k<nut->sp_count; k++) {
+ if (nus->keyframe_pts[k] == AV_NOPTS_VALUE)
+ continue;
+ av_assert0(nus->keyframe_pts[k] > last_pts);
+ ff_put_v(bc, nus->keyframe_pts[k] - last_pts);
+ last_pts = nus->keyframe_pts[k];
+ }
+ }
+ }
+
+ payload_size = avio_tell(bc) - startpos + 8 + 4;
+
+ avio_wb64(bc, 8 + payload_size + av_log2(payload_size) / 7 + 1 + 4*(payload_size > 4096));
+
+ return 0;
+}
+
static int write_headers(AVFormatContext *avctx, AVIOContext *bc)
{
NUTContext *nut = avctx->priv_data;
@@ -650,8 +700,8 @@ static int nut_write_header(AVFormatContext *s)
nut->avf = s;
- nut->version = NUT_STABLE_VERSION + !!nut->flags;
- if (nut->flags && s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
+ nut->version = FFMAX(NUT_STABLE_VERSION, 3 + !!nut->flags);
+ if (nut->version > 3 && s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
av_log(s, AV_LOG_ERROR,
"The additional syncpoint modes require version %d, "
"that is currently not finalized, "
@@ -660,12 +710,11 @@ static int nut_write_header(AVFormatContext *s)
return AVERROR_EXPERIMENTAL;
}
- nut->stream = av_mallocz(sizeof(StreamContext) * s->nb_streams);
- if (s->nb_chapters)
- nut->chapter = av_mallocz(sizeof(ChapterContext) * s->nb_chapters);
- nut->time_base = av_mallocz(sizeof(AVRational) * (s->nb_streams +
- s->nb_chapters));
- if (!nut->stream || (s->nb_chapters && !nut->chapter) || !nut->time_base) {
+ nut->stream = av_calloc(s->nb_streams, sizeof(*nut->stream ));
+ nut->chapter = av_calloc(s->nb_chapters, sizeof(*nut->chapter));
+ nut->time_base= av_calloc(s->nb_streams +
+ s->nb_chapters, sizeof(*nut->time_base));
+ if (!nut->stream || !nut->chapter || !nut->time_base) {
av_freep(&nut->stream);
av_freep(&nut->chapter);
av_freep(&nut->time_base);
@@ -676,8 +725,13 @@ static int nut_write_header(AVFormatContext *s)
AVStream *st = s->streams[i];
int ssize;
AVRational time_base;
- ff_parse_specific_params(st, &time_base.den, &ssize,
- &time_base.num);
+ ff_parse_specific_params(st, &time_base.den, &ssize, &time_base.num);
+
+ if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && st->codec->sample_rate) {
+ time_base = (AVRational) {1, st->codec->sample_rate};
+ } else {
+ time_base = ff_choose_timebase(s, st, 48000);
+ }
avpriv_set_pts_info(st, 64, time_base.num, time_base.den);
@@ -714,7 +768,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);
@@ -722,9 +776,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;
}
@@ -742,6 +797,8 @@ static int get_needed_flags(NUTContext *nut, StreamContext *nus, FrameCode *fc,
flags |= FLAG_SIZE_MSB;
if (pkt->pts - nus->last_pts != fc->pts_delta)
flags |= FLAG_CODED_PTS;
+ if (pkt->side_data_elems && nut->version > 3)
+ flags |= FLAG_SM_DATA;
if (pkt->size > 2 * nut->max_distance)
flags |= FLAG_CHECKSUM;
if (FFABS(pkt->pts - nus->last_pts) > nus->max_pts_distance)
@@ -774,24 +831,164 @@ static int find_best_header_idx(NUTContext *nut, AVPacket *pkt)
return best_i;
}
+static int write_sm_data(AVFormatContext *s, AVIOContext *bc, AVPacket *pkt, int is_meta)
+{
+ int ret, i, dyn_size;
+ unsigned flags;
+ AVIOContext *dyn_bc;
+ int sm_data_count = 0;
+ uint8_t tmp[256];
+ uint8_t *dyn_buf;
+
+ ret = avio_open_dyn_buf(&dyn_bc);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i<pkt->side_data_elems; i++) {
+ const uint8_t *data = pkt->side_data[i].data;
+ int size = pkt->side_data[i].size;
+ const uint8_t *data_end = data + size;
+
+ if (is_meta) {
+ if ( pkt->side_data[i].type == AV_PKT_DATA_METADATA_UPDATE
+ || pkt->side_data[i].type == AV_PKT_DATA_STRINGS_METADATA) {
+ if (!size || data[size-1]) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ while (data < data_end) {
+ const uint8_t *key = data;
+ const uint8_t *val = data + strlen(key) + 1;
+
+ if(val >= data_end) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ put_str(dyn_bc, key);
+ put_s(dyn_bc, -1);
+ put_str(dyn_bc, val);
+ data = val + strlen(val) + 1;
+ sm_data_count++;
+ }
+ }
+ } else {
+ switch (pkt->side_data[i].type) {
+ case AV_PKT_DATA_PALETTE:
+ case AV_PKT_DATA_NEW_EXTRADATA:
+ case AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL:
+ default:
+ if (pkt->side_data[i].type == AV_PKT_DATA_PALETTE) {
+ put_str(dyn_bc, "Palette");
+ } else if(pkt->side_data[i].type == AV_PKT_DATA_NEW_EXTRADATA) {
+ put_str(dyn_bc, "Extradata");
+ } else if(pkt->side_data[i].type == AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL) {
+ snprintf(tmp, sizeof(tmp), "CodecSpecificSide%"PRId64"", AV_RB64(data));
+ put_str(dyn_bc, tmp);
+ } else {
+ snprintf(tmp, sizeof(tmp), "UserData%s-SD-%d",
+ (s->flags & AVFMT_FLAG_BITEXACT) ? "Lavf" : LIBAVFORMAT_IDENT,
+ pkt->side_data[i].type);
+ put_str(dyn_bc, tmp);
+ }
+ put_s(dyn_bc, -2);
+ put_str(dyn_bc, "bin");
+ ff_put_v(dyn_bc, pkt->side_data[i].size);
+ avio_write(dyn_bc, data, pkt->side_data[i].size);
+ sm_data_count++;
+ break;
+ case AV_PKT_DATA_PARAM_CHANGE:
+ flags = bytestream_get_le32(&data);
+ if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT) {
+ put_str(dyn_bc, "Channels");
+ put_s(dyn_bc, bytestream_get_le32(&data));
+ sm_data_count++;
+ }
+ if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT) {
+ put_str(dyn_bc, "ChannelLayout");
+ put_s(dyn_bc, -2);
+ put_str(dyn_bc, "u64");
+ ff_put_v(bc, 8);
+ avio_write(dyn_bc, data, 8); data+=8;
+ sm_data_count++;
+ }
+ if (flags & AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE) {
+ put_str(dyn_bc, "SampleRate");
+ put_s(dyn_bc, bytestream_get_le32(&data));
+ sm_data_count++;
+ }
+ if (flags & AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS) {
+ put_str(dyn_bc, "Width");
+ put_s(dyn_bc, bytestream_get_le32(&data));
+ put_str(dyn_bc, "Height");
+ put_s(dyn_bc, bytestream_get_le32(&data));
+ sm_data_count+=2;
+ }
+ break;
+ case AV_PKT_DATA_SKIP_SAMPLES:
+ if (AV_RL32(data)) {
+ put_str(dyn_bc, "SkipStart");
+ put_s(dyn_bc, (unsigned)AV_RL32(data));
+ sm_data_count++;
+ }
+ if (AV_RL32(data+4)) {
+ put_str(dyn_bc, "SkipEnd");
+ put_s(dyn_bc, (unsigned)AV_RL32(data+4));
+ sm_data_count++;
+ }
+ break;
+ case AV_PKT_DATA_METADATA_UPDATE:
+ case AV_PKT_DATA_STRINGS_METADATA:
+ // belongs into meta, not side data
+ break;
+ }
+ }
+ }
+
+fail:
+ ff_put_v(bc, sm_data_count);
+ dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf);
+ avio_write(bc, dyn_buf, dyn_size);
+ av_freep(&dyn_buf);
+
+ return ret;
+}
+
static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
{
NUTContext *nut = s->priv_data;
StreamContext *nus = &nut->stream[pkt->stream_index];
- AVIOContext *bc = s->pb, *dyn_bc;
+ AVIOContext *bc = s->pb, *dyn_bc, *sm_bc = NULL;
FrameCode *fc;
int64_t coded_pts;
- int best_length, frame_code, flags, needed_flags, i, header_idx,
- best_header_idx;
+ int best_length, frame_code, flags, needed_flags, i, header_idx;
+ int best_header_idx;
int key_frame = !!(pkt->flags & AV_PKT_FLAG_KEY);
int store_sp = 0;
- int ret;
+ int ret = 0;
+ int sm_size = 0;
+ int data_size = pkt->size;
+ uint8_t *sm_buf = NULL;
if (pkt->pts < 0) {
av_log(s, AV_LOG_ERROR,
"Negative pts not supported stream %d, pts %"PRId64"\n",
pkt->stream_index, pkt->pts);
- return AVERROR_INVALIDDATA;
+ if (pkt->pts == AV_NOPTS_VALUE)
+ av_log(s, AV_LOG_ERROR, "Try to enable the genpts flag\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (pkt->side_data_elems && nut->version > 3) {
+ ret = avio_open_dyn_buf(&sm_bc);
+ if (ret < 0)
+ return ret;
+ ret = write_sm_data(s, sm_bc, pkt, 0);
+ if (ret >= 0)
+ ret = write_sm_data(s, sm_bc, pkt, 1);
+ sm_size = avio_close_dyn_buf(sm_bc, &sm_buf);
+ if (ret < 0)
+ goto fail;
+ data_size += sm_size;
}
if (1LL << (20 + 3 * nut->header_count) <= avio_tell(bc))
@@ -800,15 +997,14 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
if (key_frame && !(nus->last_flags & FLAG_KEY))
store_sp = 1;
- if (pkt->size + 30 /*FIXME check*/ + avio_tell(bc) >=
- nut->last_syncpoint_pos + nut->max_distance)
+ if (data_size + 30 /*FIXME check*/ + avio_tell(bc) >= nut->last_syncpoint_pos + nut->max_distance)
store_sp = 1;
//FIXME: Ensure store_sp is 1 in the first place.
if (store_sp &&
(!(nut->flags & NUT_PIPE) || nut->last_syncpoint_pos == INT_MIN)) {
- Syncpoint *sp, dummy = { .pos = INT64_MAX };
+ int64_t sp_pos = INT64_MAX;
ff_nut_reset_ts(nut, *nus->time_base, pkt->dts);
for (i = 0; i < s->nb_streams; i++) {
@@ -819,20 +1015,23 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
AV_ROUND_DOWN);
int index = av_index_search_timestamp(st, dts_tb,
AVSEEK_FLAG_BACKWARD);
- if (index >= 0)
- dummy.pos = FFMIN(dummy.pos, st->index_entries[index].pos);
+ if (index >= 0) {
+ sp_pos = FFMIN(sp_pos, st->index_entries[index].pos);
+ if (!nut->write_index && 2*index > st->nb_index_entries) {
+ memmove(st->index_entries,
+ st->index_entries + index,
+ sizeof(*st->index_entries) * (st->nb_index_entries - index));
+ st->nb_index_entries -= index;
+ }
+ }
}
- if (dummy.pos == INT64_MAX)
- dummy.pos = 0;
- sp = av_tree_find(nut->syncpoints, &dummy, (void *)ff_nut_sp_pos_cmp,
- NULL);
nut->last_syncpoint_pos = avio_tell(bc);
ret = avio_open_dyn_buf(&dyn_bc);
if (ret < 0)
- return ret;
+ goto fail;
put_tt(nut, nus->time_base, dyn_bc, pkt->dts);
- ff_put_v(dyn_bc, sp ? (nut->last_syncpoint_pos - sp->pos) >> 4 : 0);
+ ff_put_v(dyn_bc, sp_pos != INT64_MAX ? (nut->last_syncpoint_pos - sp_pos) >> 4 : 0);
if (nut->flags & NUT_BROADCAST) {
put_tt(nut, nus->time_base, dyn_bc,
@@ -840,10 +1039,25 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
}
put_packet(nut, bc, dyn_bc, 1, SYNCPOINT_STARTCODE);
+ if (nut->write_index) {
if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts)) < 0)
- return ret;
+ goto fail;
+
+ if ((1ll<<60) % nut->sp_count == 0)
+ for (i=0; i<s->nb_streams; i++) {
+ int j;
+ StreamContext *nus = &nut->stream[i];
+ av_reallocp_array(&nus->keyframe_pts, 2*nut->sp_count, sizeof(*nus->keyframe_pts));
+ if (!nus->keyframe_pts) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ for (j=nut->sp_count == 1 ? 0 : nut->sp_count; j<2*nut->sp_count; j++)
+ nus->keyframe_pts[j] = AV_NOPTS_VALUE;
+ }
+ }
}
- assert(nus->last_pts != AV_NOPTS_VALUE);
+ av_assert0(nus->last_pts != AV_NOPTS_VALUE);
coded_pts = pkt->pts & ((1 << nus->msb_pts_shift) - 1);
if (ff_lsb2full(nus, coded_pts) != pkt->pts)
@@ -876,10 +1090,10 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
if (flags & FLAG_STREAM_ID)
length += ff_get_v_length(pkt->stream_index);
- if (pkt->size % fc->size_mul != fc->size_lsb)
+ if (data_size % fc->size_mul != fc->size_lsb)
continue;
if (flags & FLAG_SIZE_MSB)
- length += ff_get_v_length(pkt->size / fc->size_mul);
+ length += ff_get_v_length(data_size / fc->size_mul);
if (flags & FLAG_CHECKSUM)
length += 4;
@@ -887,9 +1101,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;
}
@@ -908,9 +1121,7 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
frame_code = i;
}
}
-
- if (frame_code < 0)
- return AVERROR_BUG;
+ av_assert0(frame_code != -1);
fc = &nut->frame_code[frame_code];
flags = fc->flags;
@@ -923,27 +1134,24 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
ff_put_v(bc, (flags ^ needed_flags) & ~(FLAG_CODED));
flags = needed_flags;
}
- if (flags & FLAG_STREAM_ID)
- ff_put_v(bc, pkt->stream_index);
- if (flags & FLAG_CODED_PTS)
- ff_put_v(bc, coded_pts);
- if (flags & FLAG_SIZE_MSB)
- ff_put_v(bc, pkt->size / fc->size_mul);
- if (flags & FLAG_HEADER_IDX)
- ff_put_v(bc, header_idx = best_header_idx);
+ if (flags & FLAG_STREAM_ID) ff_put_v(bc, pkt->stream_index);
+ if (flags & FLAG_CODED_PTS) ff_put_v(bc, coded_pts);
+ if (flags & FLAG_SIZE_MSB ) ff_put_v(bc, data_size / fc->size_mul);
+ if (flags & FLAG_HEADER_IDX) ff_put_v(bc, header_idx = best_header_idx);
- if (flags & FLAG_CHECKSUM)
- avio_wl32(bc, ffio_get_checksum(bc));
- else
- ffio_get_checksum(bc);
+ if (flags & FLAG_CHECKSUM) avio_wl32(bc, ffio_get_checksum(bc));
+ else ffio_get_checksum(bc);
+
+ if (flags & FLAG_SM_DATA) {
+ avio_write(bc, sm_buf, sm_size);
+ }
+ avio_write(bc, pkt->data + nut->header_len[header_idx], pkt->size - nut->header_len[header_idx]);
- avio_write(bc, pkt->data + nut->header_len[header_idx],
- pkt->size - nut->header_len[header_idx]);
nus->last_flags = flags;
nus->last_pts = pkt->pts;
//FIXME just store one per syncpoint
- if (flags & FLAG_KEY && !(nut->flags & NUT_PIPE))
+ if (flags & FLAG_KEY && !(nut->flags & NUT_PIPE)) {
av_add_index_entry(
s->streams[pkt->stream_index],
nut->last_syncpoint_pos,
@@ -951,19 +1159,41 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
0,
0,
AVINDEX_KEYFRAME);
+ if (nus->keyframe_pts && nus->keyframe_pts[nut->sp_count] == AV_NOPTS_VALUE)
+ nus->keyframe_pts[nut->sp_count] = pkt->pts;
+ }
- return 0;
+ if (!nut->max_pts_tb || av_compare_ts(nut->max_pts, *nut->max_pts_tb, pkt->pts, *nus->time_base) < 0) {
+ nut->max_pts = pkt->pts;
+ nut->max_pts_tb = nus->time_base;
+ }
+
+fail:
+ av_freep(&sm_buf);
+
+ return ret;
}
static int nut_write_trailer(AVFormatContext *s)
{
NUTContext *nut = s->priv_data;
- AVIOContext *bc = s->pb;
+ AVIOContext *bc = s->pb, *dyn_bc;
+ int i, ret;
while (nut->header_count < 3)
write_headers(s, bc);
+ ret = avio_open_dyn_buf(&dyn_bc);
+ if (ret >= 0 && nut->sp_count) {
+ av_assert1(nut->write_index);
+ write_index(nut, dyn_bc);
+ put_packet(nut, bc, dyn_bc, 1, INDEX_STARTCODE);
+ }
+
ff_nut_free_sp(nut);
+ for (i=0; i<s->nb_streams; i++)
+ av_freep(&nut->stream[i].keyframe_pts);
+
av_freep(&nut->stream);
av_freep(&nut->chapter);
av_freep(&nut->time_base);
@@ -978,6 +1208,7 @@ static const AVOption options[] = {
{ "default", "", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, E, "syncpoints" },
{ "none", "Disable syncpoints, low overhead and unseekable", 0, AV_OPT_TYPE_CONST, {.i64 = NUT_PIPE}, INT_MIN, INT_MAX, E, "syncpoints" },
{ "timestamped", "Extend syncpoints with a wallclock timestamp", 0, AV_OPT_TYPE_CONST, {.i64 = NUT_BROADCAST}, INT_MIN, INT_MAX, E, "syncpoints" },
+ { "write_index", "Write index", OFFSET(write_index), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E, },
{ NULL },
};
diff --git a/libavformat/nuv.c b/libavformat/nuv.c
index c9fa38aa5b..001d9c8860 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
*/
@@ -73,7 +73,7 @@ static int get_codec_data(AVIOContext *pb, AVStream *vst,
if (!vst && !myth)
return 1; // no codec data needed
- while (!pb->eof_reached) {
+ while (!avio_feof(pb)) {
int size, subtype;
frametype = avio_r8(pb);
@@ -87,11 +87,8 @@ static int get_codec_data(AVIOContext *pb, AVStream *vst,
av_freep(&vst->codec->extradata);
vst->codec->extradata_size = 0;
}
- vst->codec->extradata = av_malloc(size);
- if (!vst->codec->extradata)
+ if (ff_get_extradata(vst->codec, pb, size) < 0)
return AVERROR(ENOMEM);
- vst->codec->extradata_size = size;
- avio_read(pb, vst->codec->extradata, size);
size = 0;
if (!myth)
return 0;
@@ -200,6 +197,9 @@ static int nuv_header(AVFormatContext *s)
vst->codec->bits_per_coded_sample = 10;
vst->sample_aspect_ratio = av_d2q(aspect * height / width,
10000);
+#if FF_API_R_FRAME_RATE
+ vst->r_frame_rate =
+#endif
vst->avg_frame_rate = av_d2q(fps, 60000);
avpriv_set_pts_info(vst, 32, 1, 1000);
} else
@@ -241,7 +241,7 @@ static int nuv_packet(AVFormatContext *s, AVPacket *pkt)
nuv_frametype frametype;
int ret, size;
- while (!pb->eof_reached) {
+ while (!avio_feof(pb)) {
int copyhdrsize = ctx->rtjpg_video ? HDRSIZE : 0;
uint64_t pos = avio_tell(pb);
@@ -267,10 +267,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);
@@ -308,6 +307,81 @@ static int nuv_packet(AVFormatContext *s, AVPacket *pkt)
return AVERROR(EIO);
}
+/**
+ * \brief looks for the string RTjjjjjjjjjj in the stream too resync reading
+ * \return 1 if the syncword is found 0 otherwise.
+ */
+static int nuv_resync(AVFormatContext *s, int64_t pos_limit) {
+ AVIOContext *pb = s->pb;
+ uint32_t tag = 0;
+ while(!avio_feof(pb) && avio_tell(pb) < pos_limit) {
+ tag = (tag << 8) | avio_r8(pb);
+ if (tag == MKBETAG('R','T','j','j') &&
+ (tag = avio_rb32(pb)) == MKBETAG('j','j','j','j') &&
+ (tag = avio_rb32(pb)) == MKBETAG('j','j','j','j'))
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \brief attempts to read a timestamp from stream at the given stream position
+ * \return timestamp if successful and AV_NOPTS_VALUE if failure
+ */
+static int64_t nuv_read_dts(AVFormatContext *s, int stream_index,
+ int64_t *ppos, int64_t pos_limit)
+{
+ NUVContext *ctx = s->priv_data;
+ AVIOContext *pb = s->pb;
+ uint8_t hdr[HDRSIZE];
+ nuv_frametype frametype;
+ int size, key, idx;
+ int64_t pos, dts;
+
+ if (avio_seek(pb, *ppos, SEEK_SET) < 0)
+ return AV_NOPTS_VALUE;
+
+ if (!nuv_resync(s, pos_limit))
+ return AV_NOPTS_VALUE;
+
+ while (!avio_feof(pb) && avio_tell(pb) < pos_limit) {
+ if (avio_read(pb, hdr, HDRSIZE) < HDRSIZE)
+ return AV_NOPTS_VALUE;
+ frametype = hdr[0];
+ size = PKTSIZE(AV_RL32(&hdr[8]));
+ switch (frametype) {
+ case NUV_SEEKP:
+ break;
+ case NUV_AUDIO:
+ case NUV_VIDEO:
+ if (frametype == NUV_VIDEO) {
+ idx = ctx->v_id;
+ key = hdr[2] == 0;
+ } else {
+ idx = ctx->a_id;
+ key = 1;
+ }
+ if (stream_index == idx) {
+
+ pos = avio_tell(s->pb) - HDRSIZE;
+ dts = AV_RL32(&hdr[4]);
+
+ // TODO - add general support in av_gen_search, so it adds positions after reading timestamps
+ av_add_index_entry(s->streams[stream_index], pos, dts, size + HDRSIZE, 0,
+ key ? AVINDEX_KEYFRAME : 0);
+
+ *ppos = pos;
+ return dts;
+ }
+ default:
+ avio_skip(pb, size);
+ break;
+ }
+ }
+ return AV_NOPTS_VALUE;
+}
+
+
AVInputFormat ff_nuv_demuxer = {
.name = "nuv",
.long_name = NULL_IF_CONFIG_SMALL("NuppelVideo"),
@@ -315,5 +389,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 760cc25b8c..3f152a4434 100644
--- a/libavformat/oggdec.c
+++ b/libavformat/oggdec.c
@@ -28,8 +28,9 @@
DEALINGS IN THE SOFTWARE.
*/
-
#include <stdio.h>
+#include "libavutil/avassert.h"
+#include "libavutil/intreadwrite.h"
#include "oggdec.h"
#include "avformat.h"
#include "internal.h"
@@ -57,6 +58,9 @@ static const struct ogg_codec * const ogg_codecs[] = {
NULL
};
+static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts);
+static int ogg_new_stream(AVFormatContext *s, uint32_t serial);
+
//FIXME We could avoid some structure duplication
static int ogg_save(AVFormatContext *s)
{
@@ -74,6 +78,8 @@ static int ogg_save(AVFormatContext *s)
struct ogg_stream *os = ogg->streams + i;
os->buf = av_mallocz(os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE);
memcpy(os->buf, ost->streams[i].buf, os->bufpos);
+ os->new_metadata = NULL;
+ os->new_metadata_size = 0;
}
ogg->state = ost;
@@ -96,9 +102,10 @@ static int ogg_restore(AVFormatContext *s, int discard)
if (!discard) {
for (i = 0; i < ogg->nstreams; i++)
- av_free(ogg->streams[i].buf);
+ av_freep(&ogg->streams[i].buf);
avio_seek(bc, ost->pos, SEEK_SET);
+ ogg->page_pos = -1;
ogg->curidx = ost->curidx;
ogg->nstreams = ost->nstreams;
if ((err = av_reallocp_array(&ogg->streams, ogg->nstreams,
@@ -115,9 +122,11 @@ static int ogg_restore(AVFormatContext *s, int discard)
return 0;
}
-static int ogg_reset(struct ogg *ogg)
+static int ogg_reset(AVFormatContext *s)
{
+ struct ogg *ogg = s->priv_data;
int i;
+ int64_t start_pos = avio_tell(s->pb);
for (i = 0; i < ogg->nstreams; i++) {
struct ogg_stream *os = ogg->streams + i;
@@ -132,8 +141,16 @@ static int ogg_reset(struct ogg *ogg)
os->nsegs = 0;
os->segp = 0;
os->incomplete = 0;
+ os->got_data = 0;
+ if (start_pos <= s->internal->data_offset) {
+ os->lastpts = 0;
+ }
+ os->end_trimming = 0;
+ av_freep(&os->new_metadata);
+ os->new_metadata_size = 0;
}
+ ogg->page_pos = -1;
ogg->curidx = -1;
return 0;
@@ -151,38 +168,105 @@ static const struct ogg_codec *ogg_find_codec(uint8_t *buf, int size)
return NULL;
}
-static int ogg_new_stream(AVFormatContext *s, uint32_t serial, int new_avstream)
+/**
+ * Replace the current stream with a new one. This is a typical webradio
+ * situation where a new audio stream spawn (identified with a new serial) and
+ * must replace the previous one (track switch).
+ */
+static int ogg_replace_stream(AVFormatContext *s, uint32_t serial, int nsegs)
{
struct ogg *ogg = s->priv_data;
- int idx = ogg->nstreams++;
- AVStream *st;
struct ogg_stream *os;
+ const struct ogg_codec *codec;
+ int i = 0;
+
+ if (s->pb->seekable) {
+ uint8_t magic[8];
+ int64_t pos = avio_tell(s->pb);
+ avio_skip(s->pb, nsegs);
+ avio_read(s->pb, magic, sizeof(magic));
+ avio_seek(s->pb, pos, SEEK_SET);
+ codec = ogg_find_codec(magic, sizeof(magic));
+ if (!codec) {
+ av_log(s, AV_LOG_ERROR, "Cannot identify new stream\n");
+ return AVERROR_INVALIDDATA;
+ }
+ for (i = 0; i < ogg->nstreams; i++) {
+ if (ogg->streams[i].codec == codec)
+ break;
+ }
+ if (i >= ogg->nstreams)
+ return ogg_new_stream(s, serial);
+ } else if (ogg->nstreams != 1) {
+ avpriv_report_missing_feature(s, "Changing stream parameters in multistream ogg");
+ return AVERROR_PATCHWELCOME;
+ }
- os = av_realloc(ogg->streams, ogg->nstreams * sizeof(*ogg->streams));
+ os = &ogg->streams[i];
- if (!os)
- return AVERROR(ENOMEM);
+ os->serial = serial;
+ return i;
- ogg->streams = os;
+#if 0
+ buf = os->buf;
+ bufsize = os->bufsize;
+ codec = os->codec;
- memset(ogg->streams + idx, 0, sizeof(*ogg->streams));
+ if (!ogg->state || ogg->state->streams[i].private != os->private)
+ av_freep(&ogg->streams[i].private);
- os = ogg->streams + idx;
+ /* Set Ogg stream settings similar to what is done in ogg_new_stream(). We
+ * also re-use the ogg_stream allocated buffer */
+ memset(os, 0, sizeof(*os));
+ os->serial = serial;
+ os->bufsize = bufsize;
+ os->buf = buf;
+ os->header = -1;
+ os->codec = codec;
+
+ return i;
+#endif
+}
+
+static int ogg_new_stream(AVFormatContext *s, uint32_t serial)
+{
+ struct ogg *ogg = s->priv_data;
+ int idx = ogg->nstreams;
+ AVStream *st;
+ struct ogg_stream *os;
+ size_t size;
+
+ if (ogg->state) {
+ av_log(s, AV_LOG_ERROR, "New streams are not supposed to be added "
+ "in between Ogg context save/restore operations.\n");
+ return AVERROR_BUG;
+ }
+
+ /* Allocate and init a new Ogg Stream */
+ if (av_size_mult(ogg->nstreams + 1, sizeof(*ogg->streams), &size) < 0 ||
+ !(os = av_realloc(ogg->streams, size)))
+ return AVERROR(ENOMEM);
+ ogg->streams = os;
+ os = ogg->streams + idx;
+ memset(os, 0, sizeof(*os));
os->serial = serial;
os->bufsize = DECODER_BUFFER_SIZE;
os->buf = av_malloc(os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE);
os->header = -1;
os->start_granule = OGG_NOGRANULE_VALUE;
+ if (!os->buf)
+ return AVERROR(ENOMEM);
- if (new_avstream) {
- st = avformat_new_stream(s, NULL);
- if (!st)
- return AVERROR(ENOMEM);
-
- st->id = idx;
- avpriv_set_pts_info(st, 64, 1, 1000000);
+ /* Create the associated AVStream */
+ st = avformat_new_stream(s, NULL);
+ if (!st) {
+ av_freep(&os->buf);
+ return AVERROR(ENOMEM);
}
+ st->id = idx;
+ avpriv_set_pts_info(st, 64, 1, 1000000);
+ ogg->nstreams++;
return idx;
}
@@ -204,7 +288,17 @@ static int ogg_new_buf(struct ogg *ogg, int idx)
return 0;
}
-static int ogg_read_page(AVFormatContext *s, int *str)
+static int data_packets_seen(const struct ogg *ogg)
+{
+ int i;
+
+ for (i = 0; i < ogg->nstreams; i++)
+ if (ogg->streams[i].got_data)
+ return 1;
+ return 0;
+}
+
+static int ogg_read_page(AVFormatContext *s, int *sid)
{
AVIOContext *bc = s->pb;
struct ogg *ogg = s->priv_data;
@@ -229,9 +323,15 @@ static int ogg_read_page(AVFormatContext *s, int *str)
sync[(sp + 2) & 3] == 'g' && sync[(sp + 3) & 3] == 'S')
break;
+ if(!i && bc->seekable && ogg->page_pos > 0) {
+ memset(sync, 0, 4);
+ avio_seek(bc, ogg->page_pos+4, SEEK_SET);
+ ogg->page_pos = -1;
+ }
+
c = avio_r8(bc);
- if (bc->eof_reached)
+ if (avio_feof(bc))
return AVERROR_EOF;
sync[sp++ & 3] = c;
@@ -242,8 +342,10 @@ static int ogg_read_page(AVFormatContext *s, int *str)
return AVERROR_INVALIDDATA;
}
- if (avio_r8(bc) != 0) /* version */
+ if (avio_r8(bc) != 0) { /* version */
+ av_log (s, AV_LOG_ERROR, "ogg page, unsupported version\n");
return AVERROR_INVALIDDATA;
+ }
flags = avio_r8(bc);
gp = avio_rl64(bc);
@@ -253,28 +355,19 @@ static int ogg_read_page(AVFormatContext *s, int *str)
idx = ogg_find_stream(ogg, serial);
if (idx < 0) {
- if (ogg->headers) {
- int n;
-
- for (n = 0; n < ogg->nstreams; n++) {
- av_freep(&ogg->streams[n].buf);
- if (!ogg->state ||
- ogg->state->streams[n].private != ogg->streams[n].private)
- av_freep(&ogg->streams[n].private);
- }
+ if (data_packets_seen(ogg))
+ idx = ogg_replace_stream(s, serial, nsegs);
+ else
+ idx = ogg_new_stream(s, serial);
- ogg->curidx = -1;
- ogg->nstreams = 0;
-
- idx = ogg_new_stream(s, serial, 0);
- } else {
- idx = ogg_new_stream(s, serial, 1);
- }
- if (idx < 0)
+ if (idx < 0) {
+ av_log(s, AV_LOG_ERROR, "failed to create or replace stream\n");
return idx;
+ }
}
os = ogg->streams + idx;
+ ogg->page_pos =
os->page_pos = avio_tell(bc) - 27;
if (os->psize > 0)
@@ -291,8 +384,14 @@ static int ogg_read_page(AVFormatContext *s, int *str)
for (i = 0; i < nsegs; i++)
size += os->segments[i];
+ if (!(flags & OGG_FLAG_BOS))
+ os->got_data = 1;
+
if (flags & OGG_FLAG_CONT || os->incomplete) {
if (!os->psize) {
+ // If this is the very first segment we started
+ // playback in the middle of a continuation packet.
+ // Discard it since we missed the start of it.
while (os->segp < os->nsegs) {
int seg = os->segments[os->segp++];
os->pstart += seg;
@@ -324,13 +423,20 @@ static int ogg_read_page(AVFormatContext *s, int *str)
os->flags = flags;
memset(os->buf + os->bufpos, 0, FF_INPUT_BUFFER_PADDING_SIZE);
- if (str)
- *str = idx;
+ if (sid)
+ *sid = idx;
return 0;
}
-static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize,
+/**
+ * @brief find the next Ogg packet
+ * @param *sid is set to the stream for the packet or -1 if there is
+ * no matching stream, in that case assume all other return
+ * values to be uninitialized.
+ * @return negative value on error or EOF.
+ */
+static int ogg_packet(AVFormatContext *s, int *sid, int *dstart, int *dsize,
int64_t *fpos)
{
struct ogg *ogg = s->priv_data;
@@ -340,6 +446,8 @@ static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize,
int segp = 0, psize = 0;
av_dlog(s, "ogg_packet: curidx=%i\n", ogg->curidx);
+ if (sid)
+ *sid = -1;
do {
idx = ogg->curidx;
@@ -390,8 +498,6 @@ static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize,
}
} while (!complete);
- av_dlog(s, "ogg_packet: idx %i, frame size %i, start %i\n",
- idx, os->psize, os->pstart);
if (os->granule == -1)
av_log(s, AV_LOG_WARNING,
@@ -435,8 +541,8 @@ static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize,
os->pduration = 0;
if (os->codec && os->codec->packet)
os->codec->packet(s, idx);
- if (str)
- *str = idx;
+ if (sid)
+ *sid = idx;
if (dstart)
*dstart = os->pstart;
if (dsize)
@@ -445,6 +551,8 @@ static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize,
*fpos = os->sync_pos;
os->pstart += os->psize;
os->psize = 0;
+ if(os->pstart == os->bufpos)
+ os->bufpos = os->pstart = 0;
os->sync_pos = os->page_pos;
}
@@ -463,43 +571,12 @@ static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize,
return 0;
}
-static int ogg_get_headers(AVFormatContext *s)
-{
- struct ogg *ogg = s->priv_data;
- int ret, i;
-
- do {
- ret = ogg_packet(s, NULL, NULL, NULL, NULL);
- if (ret < 0)
- return ret;
- } while (!ogg->headers);
-
- for (i = 0; i < ogg->nstreams; i++) {
- struct ogg_stream *os = ogg->streams + i;
-
- if (os->codec && os->codec->nb_header &&
- os->nb_header < os->codec->nb_header) {
- av_log(s, AV_LOG_ERROR,
- "Headers mismatch for stream %d: "
- "expected %d received %d.\n",
- i, os->codec->nb_header, os->nb_header);
- if (s->error_recognition & AV_EF_EXPLODE)
- return AVERROR_INVALIDDATA;
- }
- if (os->start_granule != OGG_NOGRANULE_VALUE)
- os->lastpts = s->streams[i]->start_time =
- ogg_gptopts(s, i, os->start_granule, NULL);
- }
- av_dlog(s, "found headers\n");
-
- return 0;
-}
-
static int ogg_get_length(AVFormatContext *s)
{
struct ogg *ogg = s->priv_data;
int i;
int64_t size, end;
+ int streams_left=0;
if (!s->pb->seekable)
return 0;
@@ -515,19 +592,44 @@ static int ogg_get_length(AVFormatContext *s)
ogg_save(s);
avio_seek(s->pb, end, SEEK_SET);
+ ogg->page_pos = -1;
while (!ogg_read_page(s, &i)) {
if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 &&
ogg->streams[i].codec) {
s->streams[i]->duration =
ogg_gptopts(s, i, ogg->streams[i].granule, NULL);
- if (s->streams[i]->start_time != AV_NOPTS_VALUE)
+ if (s->streams[i]->start_time != AV_NOPTS_VALUE) {
s->streams[i]->duration -= s->streams[i]->start_time;
+ streams_left-= (ogg->streams[i].got_start==-1);
+ ogg->streams[i].got_start= 1;
+ } else if(!ogg->streams[i].got_start) {
+ ogg->streams[i].got_start= -1;
+ streams_left++;
+ }
}
}
ogg_restore(s, 0);
+ ogg_save (s);
+ avio_seek (s->pb, s->internal->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;
}
@@ -537,14 +639,18 @@ static int ogg_read_close(AVFormatContext *s)
int i;
for (i = 0; i < ogg->nstreams; i++) {
- av_free(ogg->streams[i].buf);
+ av_freep(&ogg->streams[i].buf);
if (ogg->streams[i].codec &&
ogg->streams[i].codec->cleanup) {
ogg->streams[i].codec->cleanup(s, i);
}
- av_free(ogg->streams[i].private);
+ av_freep(&ogg->streams[i].private);
+ av_freep(&ogg->streams[i].new_metadata);
}
- av_free(ogg->streams);
+
+ ogg->nstreams = 0;
+
+ av_freep(&ogg->streams);
return 0;
}
@@ -552,22 +658,41 @@ static int ogg_read_header(AVFormatContext *s)
{
struct ogg *ogg = s->priv_data;
int ret, i;
+
ogg->curidx = -1;
+
//linear headers seek from start
- ret = ogg_get_headers(s);
- if (ret < 0) {
- ogg_read_close(s);
- return ret;
- }
+ do {
+ ret = ogg_packet(s, NULL, NULL, NULL, NULL);
+ if (ret < 0) {
+ ogg_read_close(s);
+ return ret;
+ }
+ } while (!ogg->headers);
+ av_dlog(s, "found headers\n");
- for (i = 0; i < ogg->nstreams; i++)
- if (ogg->streams[i].header < 0)
+ for (i = 0; i < ogg->nstreams; i++) {
+ struct ogg_stream *os = ogg->streams + i;
+
+ if (ogg->streams[i].header < 0) {
+ av_log(s, AV_LOG_ERROR, "Header parsing failed for stream %d\n", i);
ogg->streams[i].codec = NULL;
+ } else if (os->codec && os->nb_header < os->codec->nb_header) {
+ av_log(s, AV_LOG_WARNING,
+ "Headers mismatch for stream %d: "
+ "expected %d received %d.\n",
+ i, os->codec->nb_header, os->nb_header);
+ if (s->error_recognition & AV_EF_EXPLODE)
+ return AVERROR_INVALIDDATA;
+ }
+ if (os->start_granule != OGG_NOGRANULE_VALUE)
+ os->lastpts = s->streams[i]->start_time =
+ ogg_gptopts(s, i, os->start_granule, NULL);
+ }
//linear granulepos seek from end
ogg_get_length(s);
- //fill the extradata in the per codec callbacks
return 0;
}
@@ -601,14 +726,40 @@ static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts)
return pts;
}
+static void ogg_validate_keyframe(AVFormatContext *s, int idx, int pstart, int psize)
+{
+ struct ogg *ogg = s->priv_data;
+ struct ogg_stream *os = ogg->streams + idx;
+ int invalid = 0;
+ if (psize) {
+ switch (s->streams[idx]->codec->codec_id) {
+ case AV_CODEC_ID_THEORA:
+ invalid = !!(os->pflags & AV_PKT_FLAG_KEY) != !(os->buf[pstart] & 0x40);
+ break;
+ case AV_CODEC_ID_VP8:
+ invalid = !!(os->pflags & AV_PKT_FLAG_KEY) != !(os->buf[pstart] & 1);
+ }
+ if (invalid) {
+ os->pflags ^= AV_PKT_FLAG_KEY;
+ av_log(s, AV_LOG_WARNING, "Broken file, %skeyframe not correctly marked.\n",
+ (os->pflags & AV_PKT_FLAG_KEY) ? "" : "non-");
+ }
+ }
+}
+
static int ogg_read_packet(AVFormatContext *s, AVPacket *pkt)
{
struct ogg *ogg;
struct ogg_stream *os;
- int idx = -1, ret;
+ int idx, ret;
int pstart, psize;
int64_t fpos, pts, dts;
+ if (s->io_repositioned) {
+ ogg_reset(s);
+ s->io_repositioned = 0;
+ }
+
//Get an ogg packet
retry:
do {
@@ -622,6 +773,7 @@ retry:
// pflags might not be set until after this
pts = ogg_calc_pts(s, idx, &dts);
+ ogg_validate_keyframe(s, idx, pstart, psize);
if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY))
goto retry;
@@ -640,7 +792,32 @@ retry:
pkt->duration = os->pduration;
pkt->pos = fpos;
+ if (os->end_trimming) {
+ uint8_t *side_data = av_packet_new_side_data(pkt,
+ AV_PKT_DATA_SKIP_SAMPLES,
+ 10);
+ if(!side_data)
+ goto fail;
+ AV_WL32(side_data + 4, os->end_trimming);
+ os->end_trimming = 0;
+ }
+
+ if (os->new_metadata) {
+ uint8_t *side_data = av_packet_new_side_data(pkt,
+ AV_PKT_DATA_METADATA_UPDATE,
+ os->new_metadata_size);
+ if(!side_data)
+ goto fail;
+
+ memcpy(side_data, os->new_metadata, os->new_metadata_size);
+ av_freep(&os->new_metadata);
+ os->new_metadata_size = 0;
+ }
+
return psize;
+fail:
+ av_free_packet(pkt);
+ return AVERROR(ENOMEM);
}
static int64_t ogg_read_timestamp(AVFormatContext *s, int stream_index,
@@ -649,22 +826,38 @@ static int64_t ogg_read_timestamp(AVFormatContext *s, int stream_index,
struct ogg *ogg = s->priv_data;
AVIOContext *bc = s->pb;
int64_t pts = AV_NOPTS_VALUE;
- int i = -1;
+ int64_t keypos = -1;
+ int i;
+ int pstart, psize;
avio_seek(bc, *pos_arg, SEEK_SET);
- ogg_reset(ogg);
+ ogg_reset(s);
- while (avio_tell(bc) < pos_limit &&
- !ogg_packet(s, &i, NULL, NULL, pos_arg)) {
+ while ( avio_tell(bc) <= pos_limit
+ && !ogg_packet(s, &i, &pstart, &psize, pos_arg)) {
if (i == stream_index) {
struct ogg_stream *os = ogg->streams + stream_index;
+ // Do not trust the last timestamps of a ogm video
+ if ( (os->flags & OGG_FLAG_EOS)
+ && !(os->flags & OGG_FLAG_BOS)
+ && os->codec == &ff_ogm_video_codec)
+ continue;
pts = ogg_calc_pts(s, i, NULL);
- if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY))
- pts = AV_NOPTS_VALUE;
+ ogg_validate_keyframe(s, i, pstart, psize);
+ if (os->pflags & AV_PKT_FLAG_KEY) {
+ keypos = *pos_arg;
+ } else if (os->keyframe_seek) {
+ // if we had a previous keyframe but no pts for it,
+ // return that keyframe with this pts value.
+ if (keypos >= 0)
+ *pos_arg = keypos;
+ else
+ pts = AV_NOPTS_VALUE;
+ }
}
if (pts != AV_NOPTS_VALUE)
break;
}
- ogg_reset(ogg);
+ ogg_reset(s);
return pts;
}
@@ -675,6 +868,11 @@ static int ogg_read_seek(AVFormatContext *s, int stream_index,
struct ogg_stream *os = ogg->streams + stream_index;
int ret;
+ av_assert0(stream_index < ogg->nstreams);
+ // Ensure everything is reset even when seeking via
+ // the generated index.
+ ogg_reset(s);
+
// Try seeking to a keyframe first. If this fails (very possible),
// av_seek_frame will fall back to ignoring keyframes
if (s->streams[stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO
@@ -706,5 +904,5 @@ AVInputFormat ff_ogg_demuxer = {
.read_seek = ogg_read_seek,
.read_timestamp = ogg_read_timestamp,
.extensions = "ogg",
- .flags = AVFMT_GENERIC_INDEX | AVFMT_NOBINSEARCH,
+ .flags = AVFMT_GENERIC_INDEX | AVFMT_TS_DISCONT | AVFMT_NOBINSEARCH,
};
diff --git a/libavformat/oggdec.h b/libavformat/oggdec.h
index 807ac4a316..7dc7716036 100644
--- a/libavformat/oggdec.h
+++ b/libavformat/oggdec.h
@@ -81,7 +81,12 @@ struct ogg_stream {
int incomplete; ///< whether we're expecting a continuation in the next page
int page_end; ///< current packet is the last one completed in the page
int keyframe_seek;
+ int got_start;
+ int got_data; ///< 1 if the stream got some data (non-initial packets), 0 otherwise
int nb_header; ///< set to the number of parsed headers
+ int end_trimming; ///< set the number of packets to drop from the end
+ uint8_t *new_metadata;
+ unsigned int new_metadata_size;
void *private;
};
@@ -98,6 +103,7 @@ struct ogg {
int nstreams;
int headers;
int curidx;
+ int64_t page_pos; ///< file offset of the current page
struct ogg_state *state;
};
@@ -105,7 +111,7 @@ struct ogg {
#define OGG_FLAG_BOS 2
#define OGG_FLAG_EOS 4
-#define OGG_NOGRANULE_VALUE -1ull
+#define OGG_NOGRANULE_VALUE (-1ull)
extern const struct ogg_codec ff_celt_codec;
extern const struct ogg_codec ff_dirac_codec;
diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c
index 70c7817fa8..ca40063f0d 100644
--- a/libavformat/oggenc.c
+++ b/libavformat/oggenc.c
@@ -2,20 +2,20 @@
* Ogg muxer
* Copyright (c) 2007 Baptiste Coudurier <baptiste dot coudurier at free dot fr>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -80,6 +80,8 @@ typedef struct OGGContext {
static const AVOption options[] = {
{ "serial_offset", "serial number offset",
OFFSET(serial_offset), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, PARAM },
+ { "oggpagesize", "Set preferred Ogg page size.",
+ OFFSET(pref_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, MAX_PAGE_SIZE, PARAM},
{ "pagesize", "preferred page size in bytes (deprecated)",
OFFSET(pref_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MAX_PAGE_SIZE, PARAM },
{ "page_duration", "preferred page duration, in microseconds",
@@ -87,9 +89,9 @@ static const AVOption options[] = {
{ NULL },
};
-#define OGG_CLASS(flavor)\
+#define OGG_CLASS(flavor, name)\
static const AVClass flavor ## _muxer_class = {\
- .class_name = #flavor " muxer",\
+ .class_name = #name " muxer",\
.item_name = av_default_item_name,\
.option = options,\
.version = LIBAVUTIL_VERSION_INT,\
@@ -142,6 +144,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)
@@ -212,9 +219,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;
@@ -273,7 +285,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;
@@ -339,7 +351,7 @@ static int ogg_build_speex_headers(AVCodecContext *avctx,
uint8_t *p;
if (avctx->extradata_size < SPEEX_HEADER_SIZE)
- return -1;
+ return AVERROR_INVALIDDATA;
// first packet: Speex header
p = av_mallocz(SPEEX_HEADER_SIZE);
@@ -368,7 +380,7 @@ static int ogg_build_opus_headers(AVCodecContext *avctx,
uint8_t *p;
if (avctx->extradata_size < OPUS_HEADER_SIZE)
- return -1;
+ return AVERROR_INVALIDDATA;
/* first packet: Opus header */
p = av_mallocz(avctx->extradata_size);
@@ -413,7 +425,7 @@ static void ogg_write_pages(AVFormatContext *s, int flush)
static int ogg_write_header(AVFormatContext *s)
{
OGGContext *ogg = s->priv_data;
- OGGStreamContext *oggstream;
+ OGGStreamContext *oggstream = NULL;
int i, j;
if (ogg->pref_size)
@@ -423,12 +435,13 @@ static int ogg_write_header(AVFormatContext *s)
AVStream *st = s->streams[i];
unsigned serial_num = i + ogg->serial_offset;
- if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO)
+ if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
if (st->codec->codec_id == AV_CODEC_ID_OPUS)
/* Opus requires a fixed 48kHz clock */
avpriv_set_pts_info(st, 64, 1, 48000);
else
avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+ }
if (st->codec->codec_id != AV_CODEC_ID_VORBIS &&
st->codec->codec_id != AV_CODEC_ID_THEORA &&
@@ -436,14 +449,17 @@ static int ogg_write_header(AVFormatContext *s)
st->codec->codec_id != AV_CODEC_ID_FLAC &&
st->codec->codec_id != AV_CODEC_ID_OPUS) {
av_log(s, AV_LOG_ERROR, "Unsupported codec id in stream %d\n", i);
- return -1;
+ return AVERROR(EINVAL);
}
if (!st->codec->extradata || !st->codec->extradata_size) {
av_log(s, AV_LOG_ERROR, "No extradata present\n");
- return -1;
+ return AVERROR_INVALIDDATA;
}
oggstream = av_mallocz(sizeof(*oggstream));
+ if (!oggstream)
+ return AVERROR(ENOMEM);
+
oggstream->page.stream_index = i;
if (!(s->flags & AVFMT_FLAG_BITEXACT))
@@ -457,11 +473,13 @@ static int ogg_write_header(AVFormatContext *s)
} while (j < i);
oggstream->serial_num = serial_num;
+ av_dict_copy(&st->metadata, s->metadata, AV_DICT_DONT_OVERWRITE);
+
st->priv_data = oggstream;
if (st->codec->codec_id == AV_CODEC_ID_FLAC) {
int err = ogg_build_flac_headers(st->codec, oggstream,
s->flags & AVFMT_FLAG_BITEXACT,
- &s->metadata);
+ &st->metadata);
if (err) {
av_log(s, AV_LOG_ERROR, "Error writing FLAC headers\n");
av_freep(&st->priv_data);
@@ -470,7 +488,7 @@ static int ogg_write_header(AVFormatContext *s)
} else if (st->codec->codec_id == AV_CODEC_ID_SPEEX) {
int err = ogg_build_speex_headers(st->codec, oggstream,
s->flags & AVFMT_FLAG_BITEXACT,
- &s->metadata);
+ &st->metadata);
if (err) {
av_log(s, AV_LOG_ERROR, "Error writing Speex headers\n");
av_freep(&st->priv_data);
@@ -479,7 +497,7 @@ static int ogg_write_header(AVFormatContext *s)
} else if (st->codec->codec_id == AV_CODEC_ID_OPUS) {
int err = ogg_build_opus_headers(st->codec, oggstream,
s->flags & AVFMT_FLAG_BITEXACT,
- &s->metadata);
+ &st->metadata);
if (err) {
av_log(s, AV_LOG_ERROR, "Error writing Opus headers\n");
av_freep(&st->priv_data);
@@ -493,14 +511,14 @@ static int ogg_write_header(AVFormatContext *s)
if (avpriv_split_xiph_headers(st->codec->extradata, st->codec->extradata_size,
st->codec->codec_id == AV_CODEC_ID_VORBIS ? 30 : 42,
- oggstream->header, oggstream->header_len) < 0) {
+ (const uint8_t**)oggstream->header, oggstream->header_len) < 0) {
av_log(s, AV_LOG_ERROR, "Extradata corrupted\n");
av_freep(&st->priv_data);
- return -1;
+ return AVERROR_INVALIDDATA;
}
p = ogg_write_vorbiscomment(7, s->flags & AVFMT_FLAG_BITEXACT,
- &oggstream->header_len[1], &s->metadata,
+ &oggstream->header_len[1], &st->metadata,
framing_bit);
oggstream->header[1] = p;
if (!p)
@@ -623,7 +641,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);
@@ -632,12 +650,19 @@ static int ogg_write_trailer(AVFormatContext *s)
}
#if CONFIG_OGG_MUXER
-OGG_CLASS(ogg)
+OGG_CLASS(ogg, Ogg)
AVOutputFormat ff_ogg_muxer = {
.name = "ogg",
.long_name = NULL_IF_CONFIG_SMALL("Ogg"),
.mime_type = "application/ogg",
- .extensions = "ogg,ogv",
+ .extensions = "ogg,ogv"
+#if !CONFIG_SPX_MUXER
+ ",spx"
+#endif
+#if !CONFIG_OPUS_MUXER
+ ",opus"
+#endif
+ ,
.priv_data_size = sizeof(OGGContext),
.audio_codec = CONFIG_LIBVORBIS_ENCODER ?
AV_CODEC_ID_VORBIS : AV_CODEC_ID_FLAC,
@@ -651,7 +676,7 @@ AVOutputFormat ff_ogg_muxer = {
#endif
#if CONFIG_OGA_MUXER
-OGG_CLASS(oga)
+OGG_CLASS(oga, Ogg audio)
AVOutputFormat ff_oga_muxer = {
.name = "oga",
.long_name = NULL_IF_CONFIG_SMALL("Ogg Audio"),
@@ -669,7 +694,7 @@ AVOutputFormat ff_oga_muxer = {
#endif
#if CONFIG_SPX_MUXER
-OGG_CLASS(spx)
+OGG_CLASS(spx, Ogg Speex)
AVOutputFormat ff_spx_muxer = {
.name = "spx",
.long_name = NULL_IF_CONFIG_SMALL("Ogg Speex"),
@@ -686,7 +711,7 @@ AVOutputFormat ff_spx_muxer = {
#endif
#if CONFIG_OPUS_MUXER
-OGG_CLASS(opus)
+OGG_CLASS(opus, Ogg Opus)
AVOutputFormat ff_opus_muxer = {
.name = "opus",
.long_name = NULL_IF_CONFIG_SMALL("Ogg Opus"),
diff --git a/libavformat/oggparsecelt.c b/libavformat/oggparsecelt.c
index 7084e768a3..2c0c511c7b 100644
--- a/libavformat/oggparsecelt.c
+++ b/libavformat/oggparsecelt.c
@@ -1,21 +1,21 @@
/*
- * Xiph CELT / Opus parser for Ogg
+ * Xiph CELT parser for Ogg
* Copyright (c) 2011 Nicolas George
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -44,13 +44,11 @@ static int celt_header(AVFormatContext *s, int idx)
uint32_t version, sample_rate, nb_channels;
uint32_t overlap, extra_headers;
- uint8_t *extradata;
- extradata = av_malloc(2 * sizeof(uint32_t) +
- FF_INPUT_BUFFER_PADDING_SIZE);
priv = av_malloc(sizeof(struct oggcelt_private));
- if (!extradata || !priv) {
- av_free(extradata);
+ if (!priv)
+ return AVERROR(ENOMEM);
+ if (ff_alloc_extradata(st->codec, 2 * sizeof(uint32_t)) < 0) {
av_free(priv);
return AVERROR(ENOMEM);
}
@@ -61,20 +59,17 @@ static int celt_header(AVFormatContext *s, int idx)
overlap = AV_RL32(p + 48);
/* unused bytes per packet field skipped */
extra_headers = AV_RL32(p + 56);
- av_free(os->private);
- av_free(st->codec->extradata);
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = AV_CODEC_ID_CELT;
st->codec->sample_rate = sample_rate;
st->codec->channels = nb_channels;
- st->codec->extradata = extradata;
- st->codec->extradata_size = 2 * sizeof(uint32_t);
if (sample_rate)
avpriv_set_pts_info(st, 64, 1, sample_rate);
priv->extra_headers_left = 1 + extra_headers;
+ av_free(os->private);
os->private = priv;
- AV_WL32(extradata + 0, overlap);
- AV_WL32(extradata + 4, version);
+ AV_WL32(st->codec->extradata + 0, overlap);
+ AV_WL32(st->codec->extradata + 4, version);
return 1;
} else if (priv && priv->extra_headers_left) {
/* Extra headers (vorbiscomment) */
diff --git a/libavformat/oggparsedirac.c b/libavformat/oggparsedirac.c
index 55a0b59127..10fb07e92d 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
*/
@@ -43,7 +43,7 @@ static int dirac_header(AVFormatContext *s, int idx)
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
st->codec->codec_id = AV_CODEC_ID_DIRAC;
// dirac in ogg always stores timestamps as though the video were interlaced
- avpriv_set_pts_info(st, 64, st->codec->time_base.num, 2*st->codec->time_base.den);
+ avpriv_set_pts_info(st, 64, st->codec->framerate.den, 2*st->codec->framerate.num);
return 1;
}
diff --git a/libavformat/oggparseflac.c b/libavformat/oggparseflac.c
index 57bba68079..c7fb44618d 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
*/
@@ -61,10 +61,9 @@ flac_header (AVFormatContext * s, int idx)
st->codec->codec_id = AV_CODEC_ID_FLAC;
st->need_parsing = AVSTREAM_PARSE_HEADERS;
- st->codec->extradata =
- av_malloc(FLAC_STREAMINFO_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
- memcpy(st->codec->extradata, streaminfo_start, FLAC_STREAMINFO_SIZE);
- st->codec->extradata_size = FLAC_STREAMINFO_SIZE;
+ if (ff_alloc_extradata(st->codec, FLAC_STREAMINFO_SIZE) < 0)
+ return AVERROR(ENOMEM);
+ memcpy(st->codec->extradata, streaminfo_start, st->codec->extradata_size);
samplerate = AV_RB24(st->codec->extradata + 10) >> 4;
if (!samplerate)
@@ -81,11 +80,32 @@ flac_header (AVFormatContext * s, int idx)
static int
old_flac_header (AVFormatContext * s, int idx)
{
+ struct ogg *ogg = s->priv_data;
AVStream *st = s->streams[idx];
+ struct ogg_stream *os = ogg->streams + idx;
+ AVCodecParserContext *parser = av_parser_init(AV_CODEC_ID_FLAC);
+ int size;
+ uint8_t *data;
+
+ if (!parser)
+ return -1;
+
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = AV_CODEC_ID_FLAC;
- return 0;
+ parser->flags = PARSER_FLAG_COMPLETE_FRAMES;
+ av_parser_parse2(parser, st->codec,
+ &data, &size, os->buf + os->pstart, os->psize,
+ AV_NOPTS_VALUE, AV_NOPTS_VALUE, -1);
+
+ av_parser_close(parser);
+
+ if (st->codec->sample_rate) {
+ avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+ return 0;
+ }
+
+ return 1;
}
const struct ogg_codec ff_flac_codec = {
diff --git a/libavformat/oggparseogm.c b/libavformat/oggparseogm.c
index 913282ab36..54024e0a0a 100644
--- a/libavformat/oggparseogm.c
+++ b/libavformat/oggparseogm.c
@@ -23,6 +23,7 @@
**/
#include <stdlib.h>
+#include "libavutil/avassert.h"
#include "libavutil/intreadwrite.h"
#include "libavcodec/get_bits.h"
#include "libavcodec/bytestream.h"
@@ -40,6 +41,7 @@ ogm_header(AVFormatContext *s, int idx)
GetByteContext p;
uint64_t time_unit;
uint64_t spu;
+ uint32_t size;
bytestream2_init(&p, os->buf + os->pstart, os->psize);
if (!(bytestream2_peek_byte(&p) & 1))
@@ -68,11 +70,13 @@ ogm_header(AVFormatContext *s, int idx)
acid[4] = 0;
cid = strtol(acid, NULL, 16);
st->codec->codec_id = ff_codec_get_id(ff_codec_wav_tags, cid);
- st->need_parsing = AVSTREAM_PARSE_FULL;
+ // our parser completely breaks AAC in Ogg
+ if (st->codec->codec_id != AV_CODEC_ID_AAC)
+ st->need_parsing = AVSTREAM_PARSE_FULL;
}
- bytestream2_skip(&p, 4); /* useless size field */
-
+ size = bytestream2_get_le32(&p);
+ size = FFMIN(size, os->psize);
time_unit = bytestream2_get_le64(&p);
spu = bytestream2_get_le64(&p);
if (!time_unit || !spu) {
@@ -93,6 +97,16 @@ ogm_header(AVFormatContext *s, int idx)
st->codec->bit_rate = bytestream2_get_le32(&p) * 8;
st->codec->sample_rate = spu * 10000000 / time_unit;
avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+ if (size >= 56 && st->codec->codec_id == AV_CODEC_ID_AAC) {
+ bytestream2_skip(&p, 4);
+ size -= 4;
+ }
+ if (size > 52) {
+ av_assert0(FF_INPUT_BUFFER_PADDING_SIZE <= 52);
+ size -= 52;
+ ff_alloc_extradata(st->codec, size);
+ bytestream2_get_buffer(&p, st->codec->extradata, st->codec->extradata_size);
+ }
}
} else if (bytestream2_peek_byte(&p) == 3) {
bytestream2_skip(&p, 7);
@@ -117,15 +131,23 @@ ogm_dshow_header(AVFormatContext *s, int idx)
if(*p != 1)
return 1;
+ if (os->psize < 100)
+ return AVERROR_INVALIDDATA;
t = AV_RL32(p + 96);
if(t == 0x05589f80){
+ if (os->psize < 184)
+ return AVERROR_INVALIDDATA;
+
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, AV_RL32(p + 68));
avpriv_set_pts_info(st, 64, AV_RL64(p + 164), 10000000);
st->codec->width = AV_RL32(p + 176);
st->codec->height = AV_RL32(p + 180);
} else if(t == 0x05589f81){
+ if (os->psize < 136)
+ return AVERROR_INVALIDDATA;
+
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = ff_codec_get_id(ff_codec_wav_tags, AV_RL16(p + 124));
st->codec->channels = AV_RL16(p + 126);
diff --git a/libavformat/oggparseopus.c b/libavformat/oggparseopus.c
index 5931ab543b..c8b02fab4d 100644
--- a/libavformat/oggparseopus.c
+++ b/libavformat/oggparseopus.c
@@ -2,20 +2,20 @@
* Opus parser for Ogg
* Copyright (c) 2012 Nicolas George
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -32,6 +32,7 @@ struct oggopus_private {
int64_t cur_dts;
};
+#define OPUS_SEEK_PREROLL_MS 80
#define OPUS_HEAD_SIZE 19
static int opus_header(AVFormatContext *avf, int idx)
@@ -41,31 +42,34 @@ static int opus_header(AVFormatContext *avf, int idx)
AVStream *st = avf->streams[idx];
struct oggopus_private *priv = os->private;
uint8_t *packet = os->buf + os->pstart;
- uint8_t *extradata;
if (!priv) {
priv = os->private = av_mallocz(sizeof(*priv));
if (!priv)
return AVERROR(ENOMEM);
}
+
if (os->flags & OGG_FLAG_BOS) {
if (os->psize < OPUS_HEAD_SIZE || (AV_RL8(packet + 8) & 0xF0) != 0)
return AVERROR_INVALIDDATA;
-
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = AV_CODEC_ID_OPUS;
- st->codec->channels = AV_RL8(packet + 9);
+ st->codec->channels = AV_RL8 (packet + 9);
priv->pre_skip = AV_RL16(packet + 10);
+ st->codec->delay = priv->pre_skip;
+ /*orig_sample_rate = AV_RL32(packet + 12);*/
+ /*gain = AV_RL16(packet + 16);*/
+ /*channel_map = AV_RL8 (packet + 18);*/
- extradata = av_malloc(os->psize + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!extradata)
+ if (ff_alloc_extradata(st->codec, os->psize))
return AVERROR(ENOMEM);
- memcpy(extradata, packet, os->psize);
- st->codec->extradata = extradata;
- st->codec->extradata_size = os->psize;
+ memcpy(st->codec->extradata, packet, os->psize);
st->codec->sample_rate = 48000;
+ av_codec_set_seek_preroll(st->codec,
+ av_rescale(OPUS_SEEK_PREROLL_MS,
+ st->codec->sample_rate, 1000));
avpriv_set_pts_info(st, 64, 1, 48000);
priv->need_comments = 1;
return 1;
@@ -82,6 +86,26 @@ static int opus_header(AVFormatContext *avf, int idx)
return 0;
}
+static int opus_duration(uint8_t *src, int size)
+{
+ unsigned nb_frames = 1;
+ unsigned toc = src[0];
+ unsigned toc_config = toc >> 3;
+ unsigned toc_count = toc & 3;
+ unsigned frame_size = toc_config < 12 ? FFMAX(480, 960 * (toc_config & 3)) :
+ toc_config < 16 ? 480 << (toc_config & 1) :
+ 120 << (toc_config & 3);
+ if (toc_count == 3) {
+ if (size<2)
+ return AVERROR_INVALIDDATA;
+ nb_frames = src[1] & 0x3F;
+ } else if (toc_count) {
+ nb_frames = 2;
+ }
+
+ return frame_size * nb_frames;
+}
+
static int opus_packet(AVFormatContext *avf, int idx)
{
struct ogg *ogg = avf->priv_data;
@@ -89,26 +113,43 @@ static int opus_packet(AVFormatContext *avf, int idx)
AVStream *st = avf->streams[idx];
struct oggopus_private *priv = os->private;
uint8_t *packet = os->buf + os->pstart;
- unsigned toc, toc_config, toc_count, frame_size, nb_frames = 1;
+ int ret;
if (!os->psize)
return AVERROR_INVALIDDATA;
- toc = *packet;
- toc_config = toc >> 3;
- toc_count = toc & 3;
- frame_size = toc_config < 12 ? FFMAX(480, 960 * (toc_config & 3)) :
- toc_config < 16 ? 480 << (toc_config & 1) :
- 120 << (toc_config & 3);
- if (toc_count == 3) {
- if (os->psize < 2)
- return AVERROR_INVALIDDATA;
- nb_frames = packet[1] & 0x3F;
- } else if (toc_count) {
- nb_frames = 2;
+ if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) {
+ int seg, d;
+ int duration;
+ uint8_t *last_pkt = os->buf + os->pstart;
+ uint8_t *next_pkt = last_pkt;
+
+ duration = 0;
+ seg = os->segp;
+ d = opus_duration(last_pkt, os->psize);
+ if (d < 0) {
+ os->pflags |= AV_PKT_FLAG_CORRUPT;
+ return 0;
+ }
+ duration += d;
+ last_pkt = next_pkt = next_pkt + os->psize;
+ for (; seg < os->nsegs; seg++) {
+ next_pkt += os->segments[seg];
+ if (os->segments[seg] < 255 && next_pkt != last_pkt) {
+ int d = opus_duration(last_pkt, next_pkt - last_pkt);
+ if (d > 0)
+ duration += d;
+ last_pkt = next_pkt;
+ }
+ }
+ os->lastpts =
+ os->lastdts = os->granule - duration;
}
- os->pduration = frame_size * nb_frames;
+ if ((ret = opus_duration(packet, os->psize)) < 0)
+ return ret;
+
+ os->pduration = ret;
if (os->lastpts != AV_NOPTS_VALUE) {
if (st->start_time == AV_NOPTS_VALUE)
st->start_time = os->lastpts;
@@ -121,10 +162,10 @@ static int opus_packet(AVFormatContext *avf, int idx)
skip = FFMIN(skip, os->pduration);
if (skip > 0) {
os->pduration = skip < os->pduration ? os->pduration - skip : 1;
- av_log(avf, AV_LOG_WARNING,
- "Last packet is truncated to %d (because of unimplemented end trim support).\n",
+ os->end_trimming = skip;
+ av_log(avf, AV_LOG_DEBUG,
+ "Last packet was truncated to %d due to end trimming.\n",
os->pduration);
- return AVERROR_PATCHWELCOME;
}
}
@@ -137,6 +178,5 @@ const struct ogg_codec ff_opus_codec = {
.magicsize = 8,
.header = opus_header,
.packet = opus_packet,
- .granule_is_start = 1,
.nb_header = 1,
};
diff --git a/libavformat/oggparseskeleton.c b/libavformat/oggparseskeleton.c
index 5333e17de6..6c2105f324 100644
--- a/libavformat/oggparseskeleton.c
+++ b/libavformat/oggparseskeleton.c
@@ -1,20 +1,20 @@
/*
* Copyright (C) 2010 David Conrad
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -36,6 +36,9 @@ static int skeleton_header(AVFormatContext *s, int idx)
st->codec->codec_type = AVMEDIA_TYPE_DATA;
+ if ((os->flags & OGG_FLAG_EOS) && os->psize == 0)
+ return 1;
+
if (os->psize < 8)
return -1;
@@ -46,7 +49,7 @@ static int skeleton_header(AVFormatContext *s, int idx)
version_major = AV_RL16(buf+8);
version_minor = AV_RL16(buf+10);
- if (version_major != 3) {
+ if (version_major != 3 && version_major != 4) {
av_log(s, AV_LOG_WARNING, "Unknown skeleton version %d.%d\n",
version_major, version_minor);
return -1;
@@ -60,7 +63,7 @@ static int skeleton_header(AVFormatContext *s, int idx)
start_num = AV_RL64(buf+12);
start_den = AV_RL64(buf+20);
- if (start_den) {
+ if (start_den > 0 && start_num > 0) {
int base_den;
av_reduce(&start_time, &base_den, start_num, start_den, INT_MAX);
avpriv_set_pts_info(st, 64, 1, base_den);
@@ -73,12 +76,16 @@ static int skeleton_header(AVFormatContext *s, int idx)
target_idx = ogg_find_stream(ogg, AV_RL32(buf+12));
start_granule = AV_RL64(buf+36);
+ if (target_idx < 0) {
+ av_log(s, AV_LOG_WARNING, "Serial number in fisbone doesn't match any stream\n");
+ return 1;
+ }
+ os = ogg->streams + target_idx;
if (os->start_granule != OGG_NOGRANULE_VALUE) {
- avpriv_report_missing_feature(s,
- "Multiple fisbone for the same stream");
+ av_log(s, AV_LOG_WARNING, "Multiple fisbone for the same stream\n");
return 1;
}
- if (target_idx >= 0 && start_granule != OGG_NOGRANULE_VALUE) {
+ if (start_granule != OGG_NOGRANULE_VALUE) {
os->start_granule = start_granule;
}
}
diff --git a/libavformat/oggparsespeex.c b/libavformat/oggparsespeex.c
index b2779e74c1..9b5c65f453 100644
--- a/libavformat/oggparsespeex.c
+++ b/libavformat/oggparsespeex.c
@@ -58,6 +58,11 @@ static int speex_header(AVFormatContext *s, int idx) {
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = AV_CODEC_ID_SPEEX;
+ if (os->psize < 68) {
+ av_log(s, AV_LOG_ERROR, "speex packet too small\n");
+ return AVERROR_INVALIDDATA;
+ }
+
st->codec->sample_rate = AV_RL32(p + 36);
st->codec->channels = AV_RL32(p + 48);
if (st->codec->channels < 1 || st->codec->channels > 2) {
@@ -72,9 +77,8 @@ static int speex_header(AVFormatContext *s, int idx) {
if (frames_per_packet)
spxp->packet_size *= frames_per_packet;
- st->codec->extradata_size = os->psize;
- st->codec->extradata = av_malloc(st->codec->extradata_size
- + FF_INPUT_BUFFER_PADDING_SIZE);
+ if (ff_alloc_extradata(st->codec, os->psize) < 0)
+ return AVERROR(ENOMEM);
memcpy(st->codec->extradata, p, st->codec->extradata_size);
avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
diff --git a/libavformat/oggparsetheora.c b/libavformat/oggparsetheora.c
index d78f02d67a..91c70dfec9 100644
--- a/libavformat/oggparsetheora.c
+++ b/libavformat/oggparsetheora.c
@@ -122,6 +122,7 @@ static int theora_header(AVFormatContext *s, int idx)
return AVERROR_INVALIDDATA;
break;
default:
+ av_log(s, AV_LOG_ERROR, "Unknown header type %X\n", os->buf[os->pstart]);
return AVERROR_INVALIDDATA;
}
@@ -130,6 +131,8 @@ static int theora_header(AVFormatContext *s, int idx)
st->codec->extradata_size = 0;
return err;
}
+ memset(st->codec->extradata + cds, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+
cdp = st->codec->extradata + st->codec->extradata_size;
*cdp++ = os->psize >> 8;
*cdp++ = os->psize & 0xff;
@@ -165,10 +168,47 @@ static uint64_t theora_gptopts(AVFormatContext *ctx, int idx, uint64_t gp,
return iframe + pframe;
}
+static int theora_packet(AVFormatContext *s, int idx)
+{
+ struct ogg *ogg = s->priv_data;
+ struct ogg_stream *os = ogg->streams + idx;
+ int duration;
+
+ /* first packet handling
+ here we parse the duration of each packet in the first page and compare
+ the total duration to the page granule to find the encoder delay and
+ set the first timestamp */
+
+ if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) {
+ int seg;
+
+ duration = 1;
+ for (seg = os->segp; seg < os->nsegs; seg++) {
+ if (os->segments[seg] < 255)
+ duration ++;
+ }
+
+ os->lastpts = os->lastdts = theora_gptopts(s, idx, os->granule, NULL) - duration;
+ if(s->streams[idx]->start_time == AV_NOPTS_VALUE) {
+ s->streams[idx]->start_time = os->lastpts;
+ if (s->streams[idx]->duration)
+ s->streams[idx]->duration -= s->streams[idx]->start_time;
+ }
+ }
+
+ /* parse packet duration */
+ if (os->psize > 0) {
+ os->pduration = 1;
+ }
+
+ return 0;
+}
+
const struct ogg_codec ff_theora_codec = {
.magic = "\200theora",
.magicsize = 7,
.header = theora_header,
+ .packet = theora_packet,
.gptopts = theora_gptopts,
.nb_header = 3,
};
diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c
index b2f9c8b03e..dd44337437 100644
--- a/libavformat/oggparsevorbis.c
+++ b/libavformat/oggparsevorbis.c
@@ -156,9 +156,10 @@ int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m,
char *pict = av_malloc(vl);
if (!pict) {
+ av_log(as, AV_LOG_WARNING, "out-of-memory error. Skipping cover art block.\n");
av_freep(&tt);
av_freep(&ct);
- return AVERROR(ENOMEM);
+ continue;
}
if ((ret = av_base64_decode(pict, ct, vl)) > 0)
ret = ff_flac_parse_picture(as, pict, ret);
@@ -171,16 +172,20 @@ int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m,
}
} else if (!ogm_chapter(as, tt, ct)) {
updates++;
+ if (av_dict_get(*m, tt, NULL, 0)) {
+ av_dict_set(m, tt, ";", AV_DICT_APPEND);
+ }
av_dict_set(m, tt, ct,
AV_DICT_DONT_STRDUP_KEY |
- AV_DICT_DONT_STRDUP_VAL);
+ AV_DICT_APPEND);
+ av_freep(&ct);
}
}
}
if (p != end)
av_log(as, AV_LOG_INFO,
- "%ti bytes of comment header remain\n", end - p);
+ "%"PTRDIFF_SPECIFIER" bytes of comment header remain\n", end - p);
if (n > 0)
av_log(as, AV_LOG_INFO,
"truncated comment header, %i comments not found\n", n);
@@ -218,12 +223,15 @@ static int fixup_vorbis_headers(AVFormatContext *as,
uint8_t **buf)
{
int i, offset, len, err;
+ int buf_len;
unsigned char *ptr;
len = priv->len[0] + priv->len[1] + priv->len[2];
- ptr = *buf = av_mallocz(len + len / 255 + 64);
+ buf_len = len + len / 255 + 64;
+ ptr = *buf = av_realloc(NULL, buf_len);
if (!ptr)
return AVERROR(ENOMEM);
+ memset(*buf, '\0', buf_len);
ptr[0] = 2;
offset = 1;
@@ -252,6 +260,36 @@ static void vorbis_cleanup(AVFormatContext *s, int idx)
}
}
+static int vorbis_update_metadata(AVFormatContext *s, int idx)
+{
+ struct ogg *ogg = s->priv_data;
+ struct ogg_stream *os = ogg->streams + idx;
+ AVStream *st = s->streams[idx];
+ int ret;
+
+ if (os->psize <= 8)
+ return 0;
+
+ /* New metadata packet; release old data. */
+ av_dict_free(&st->metadata);
+ ret = ff_vorbis_stream_comment(s, st, os->buf + os->pstart + 7,
+ os->psize - 8);
+ if (ret < 0)
+ return ret;
+
+ /* Update the metadata if possible. */
+ av_freep(&os->new_metadata);
+ if (st->metadata) {
+ os->new_metadata = av_packet_pack_dictionary(st->metadata, &os->new_metadata_size);
+ /* Send an empty dictionary to indicate that metadata has been cleared. */
+ } else {
+ os->new_metadata = av_malloc(1);
+ os->new_metadata_size = 0;
+ }
+
+ return ret;
+}
+
static int vorbis_header(AVFormatContext *s, int idx)
{
struct ogg *ogg = s->priv_data;
@@ -266,14 +304,14 @@ static int vorbis_header(AVFormatContext *s, int idx)
return AVERROR(ENOMEM);
}
+ priv = os->private;
+
if (!(pkt_type & 1))
- return 0;
+ return priv->vp ? 0 : AVERROR_INVALIDDATA;
if (os->psize < 1 || pkt_type > 5)
return AVERROR_INVALIDDATA;
- priv = os->private;
-
if (priv->packet[pkt_type >> 1])
return AVERROR_INVALIDDATA;
if (pkt_type > 1 && !priv->packet[0] || pkt_type > 3 && !priv->packet[1])
@@ -288,6 +326,7 @@ static int vorbis_header(AVFormatContext *s, int idx)
const uint8_t *p = os->buf + os->pstart + 7; /* skip "\001vorbis" tag */
unsigned blocksize, bs0, bs1;
int srate;
+ int channels;
if (os->psize != 30)
return AVERROR_INVALIDDATA;
@@ -295,7 +334,12 @@ static int vorbis_header(AVFormatContext *s, int idx)
if (bytestream_get_le32(&p) != 0) /* vorbis_version */
return AVERROR_INVALIDDATA;
- st->codec->channels = bytestream_get_byte(&p);
+ channels = bytestream_get_byte(&p);
+ if (st->codec->channels && channels != st->codec->channels) {
+ av_log(s, AV_LOG_ERROR, "Channel change is not supported\n");
+ return AVERROR_PATCHWELCOME;
+ }
+ st->codec->channels = channels;
srate = bytestream_get_le32(&p);
p += 4; // skip maximum bitrate
st->codec->bit_rate = bytestream_get_le32(&p); // nominal bitrate
@@ -321,9 +365,7 @@ static int vorbis_header(AVFormatContext *s, int idx)
avpriv_set_pts_info(st, 64, 1, srate);
}
} else if (os->buf[os->pstart] == 3) {
- if (os->psize > 8 &&
- ff_vorbis_stream_comment(s, st, os->buf + os->pstart + 7,
- os->psize - 8) >= 0) {
+ if (vorbis_update_metadata(s, idx) >= 0 && priv->len[1] > 10) {
unsigned new_len;
int ret = ff_replaygain_export(st, st->metadata);
@@ -350,7 +392,7 @@ static int vorbis_header(AVFormatContext *s, int idx)
if (!priv->vp) {
av_freep(&st->codec->extradata);
st->codec->extradata_size = 0;
- return ret;
+ return AVERROR_UNKNOWN;
}
}
@@ -362,29 +404,40 @@ static int vorbis_packet(AVFormatContext *s, int idx)
struct ogg *ogg = s->priv_data;
struct ogg_stream *os = ogg->streams + idx;
struct oggvorbis_private *priv = os->private;
- int duration;
+ int duration, flags = 0;
/* first packet handling
* here we parse the duration of each packet in the first page and compare
* the total duration to the page granule to find the encoder delay and
* set the first timestamp */
- if (!os->lastpts) {
- int seg;
+ if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS) && (int64_t)os->granule>=0) {
+ int seg, d;
uint8_t *last_pkt = os->buf + os->pstart;
uint8_t *next_pkt = last_pkt;
- int first_duration = 0;
av_vorbis_parse_reset(priv->vp);
duration = 0;
- for (seg = 0; seg < os->nsegs; seg++) {
+ seg = os->segp;
+ d = av_vorbis_parse_frame_flags(priv->vp, last_pkt, 1, &flags);
+ if (d < 0) {
+ os->pflags |= AV_PKT_FLAG_CORRUPT;
+ return 0;
+ } else if (flags & VORBIS_FLAG_COMMENT) {
+ vorbis_update_metadata(s, idx);
+ flags = 0;
+ }
+ duration += d;
+ last_pkt = next_pkt = next_pkt + os->psize;
+ for (; seg < os->nsegs; seg++) {
if (os->segments[seg] < 255) {
- int d = av_vorbis_parse_frame(priv->vp, last_pkt, 1);
+ int d = av_vorbis_parse_frame_flags(priv->vp, last_pkt, 1, &flags);
if (d < 0) {
duration = os->granule;
break;
+ } else if (flags & VORBIS_FLAG_COMMENT) {
+ vorbis_update_metadata(s, idx);
+ flags = 0;
}
- if (!duration)
- first_duration = d;
duration += d;
last_pkt = next_pkt + os->segments[seg];
}
@@ -392,20 +445,28 @@ static int vorbis_packet(AVFormatContext *s, int idx)
}
os->lastpts =
os->lastdts = os->granule - duration;
- s->streams[idx]->start_time = os->lastpts + first_duration;
- if (s->streams[idx]->duration)
- s->streams[idx]->duration -= s->streams[idx]->start_time;
- s->streams[idx]->cur_dts = AV_NOPTS_VALUE;
+
+ if (!os->granule && duration) //hack to deal with broken files (Ticket3710)
+ os->lastpts = os->lastdts = AV_NOPTS_VALUE;
+
+ if (s->streams[idx]->start_time == AV_NOPTS_VALUE) {
+ s->streams[idx]->start_time = FFMAX(os->lastpts, 0);
+ if (s->streams[idx]->duration != AV_NOPTS_VALUE)
+ s->streams[idx]->duration -= s->streams[idx]->start_time;
+ }
priv->final_pts = AV_NOPTS_VALUE;
av_vorbis_parse_reset(priv->vp);
}
/* parse packet duration */
if (os->psize > 0) {
- duration = av_vorbis_parse_frame(priv->vp, os->buf + os->pstart, 1);
- if (duration <= 0) {
+ duration = av_vorbis_parse_frame_flags(priv->vp, os->buf + os->pstart, 1, &flags);
+ if (duration < 0) {
os->pflags |= AV_PKT_FLAG_CORRUPT;
return 0;
+ } else if (flags & VORBIS_FLAG_COMMENT) {
+ vorbis_update_metadata(s, idx);
+ flags = 0;
}
os->pduration = duration;
}
diff --git a/libavformat/oggparsevp8.c b/libavformat/oggparsevp8.c
index b4f71de017..7aed8abad3 100644
--- a/libavformat/oggparsevp8.c
+++ b/libavformat/oggparsevp8.c
@@ -2,20 +2,20 @@
* On2 VP8 parser for Ogg
* Copyright (C) 2013 James Almer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/oma.c b/libavformat/oma.c
index 27b598857c..27028674db 100644
--- a/libavformat/oma.c
+++ b/libavformat/oma.c
@@ -1,20 +1,20 @@
/*
* Sony OpenMG (OMA) common data
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/oma.h b/libavformat/oma.h
index 9a35da2623..e2a187bacc 100644
--- a/libavformat/oma.h
+++ b/libavformat/oma.h
@@ -1,20 +1,20 @@
/*
* Sony OpenMG (OMA) common data
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/omadec.c b/libavformat/omadec.c
index 8dc35d17c2..2cb63ff604 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
*/
@@ -175,13 +175,9 @@ static int nprobe(AVFormatContext *s, uint8_t *enc_header, unsigned size,
taglen = AV_RB32(&enc_header[pos + 32]);
datalen = AV_RB32(&enc_header[pos + 36]) >> 4;
- pos += 44;
- if (size - pos < taglen)
- return -1;
-
- pos += taglen;
+ pos += 44LL + taglen;
- if (datalen << 4 > size - pos)
+ if (pos + (((uint64_t)datalen) << 4) > size)
return -1;
av_des_init(&av_des, n_val, 192, 1);
@@ -300,7 +296,7 @@ static int oma_read_header(AVFormatContext *s)
ID3v2ExtraMeta *extra_meta = NULL;
OMAContext *oc = s->priv_data;
- ff_id3v2_read(s, ID3v2_EA3_MAGIC, &extra_meta);
+ ff_id3v2_read(s, ID3v2_EA3_MAGIC, &extra_meta, 0);
ret = avio_read(s->pb, buf, EA3_HEADER_SIZE);
if (ret < EA3_HEADER_SIZE)
return -1;
@@ -356,12 +352,10 @@ static int oma_read_header(AVFormatContext *s)
/* fake the ATRAC3 extradata
* (wav format, makes stream copy to wav work) */
- st->codec->extradata_size = 14;
- edata = av_mallocz(14 + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!edata)
+ if (ff_alloc_extradata(st->codec, 14))
return AVERROR(ENOMEM);
- st->codec->extradata = edata;
+ edata = st->codec->extradata;
AV_WL16(&edata[0], 1); // always 1
AV_WL32(&edata[2], samplerate); // samples rate
AV_WL16(&edata[6], jsflag); // coding mode
@@ -391,7 +385,7 @@ static int oma_read_header(AVFormatContext *s)
avpriv_set_pts_info(st, 64, 1, samplerate);
break;
case OMA_CODECID_MP3:
- st->need_parsing = AVSTREAM_PARSE_FULL;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
framesize = 1024;
break;
case OMA_CODECID_LPCM:
@@ -436,9 +430,9 @@ static int oma_read_packet(AVFormatContext *s, AVPacket *pkt)
pkt->stream_index = 0;
- if (pos > 0) {
+ if (pos >= oc->content_start && byte_rate > 0) {
pkt->pts =
- pkt->dts = av_rescale(pos, st->time_base.den,
+ pkt->dts = av_rescale(pos - oc->content_start, st->time_base.den,
byte_rate * (int64_t)st->time_base.num);
}
@@ -466,7 +460,7 @@ static int oma_read_probe(AVProbeData *p)
/* This check cannot overflow as tag_len has at most 28 bits */
if (p->buf_size < tag_len + 5)
/* EA3 header comes late, might be outside of the probe buffer */
- return tag_len ? AVPROBE_SCORE_EXTENSION : 0;
+ return tag_len ? AVPROBE_SCORE_EXTENSION/2 : 0;
buf += tag_len;
diff --git a/libavformat/omaenc.c b/libavformat/omaenc.c
index bfd552a03d..fe0669f5c9 100644
--- a/libavformat/omaenc.c
+++ b/libavformat/omaenc.c
@@ -3,20 +3,20 @@
*
* Copyright (c) 2011 Michael Karcher
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -61,7 +61,7 @@ static av_cold int oma_write_header(AVFormatContext *s)
switch(format->codec_tag) {
case OMA_CODECID_ATRAC3:
if (format->channels != 2) {
- av_log(s, AV_LOG_ERROR, "ATRAC3 in OMA is only supported with 2 channels");
+ av_log(s, AV_LOG_ERROR, "ATRAC3 in OMA is only supported with 2 channels\n");
return AVERROR(EINVAL);
}
if (format->extradata_size == 14) /* WAV format extradata */
@@ -84,8 +84,9 @@ static av_cold int oma_write_header(AVFormatContext *s)
(format->block_align/8 - 1));
break;
default:
- av_log(s, AV_LOG_ERROR, "OMA: unsupported codec tag %d for write\n",
+ av_log(s, AV_LOG_ERROR, "unsupported codec tag %d for write\n",
format->codec_tag);
+ return AVERROR(EINVAL);
}
for (i = 0; i < (EA3_HEADER_SIZE - 36)/4; i++)
avio_wl32(s->pb, 0); /* Padding */
diff --git a/libavformat/options.c b/libavformat/options.c
index 227c36f95f..22b922f89a 100644
--- a/libavformat/options.c
+++ b/libavformat/options.c
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "avformat.h"
@@ -77,6 +77,13 @@ static const AVClass *format_child_class_next(const AVClass *prev)
return NULL;
}
+static AVClassCategory get_category(void *ptr)
+{
+ AVFormatContext* s = ptr;
+ if(s->iformat) return AV_CLASS_CATEGORY_DEMUXER;
+ else return AV_CLASS_CATEGORY_MUXER;
+}
+
static const AVClass av_format_context_class = {
.class_name = "AVFormatContext",
.item_name = format_to_name,
@@ -84,6 +91,8 @@ static const AVClass av_format_context_class = {
.version = LIBAVUTIL_VERSION_INT,
.child_next = format_child_next,
.child_class_next = format_child_class_next,
+ .category = AV_CLASS_CATEGORY_MUXER,
+ .get_category = get_category,
};
static void avformat_get_context_defaults(AVFormatContext *s)
@@ -112,6 +121,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 8372ef3650..40f1e0ae43 100644
--- a/libavformat/options_table.h
+++ b/libavformat/options_table.h
@@ -1,18 +1,20 @@
/*
- * This file is part of Libav.
+ * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
*
- * Libav is free software; you can redistribute it and/or
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -23,6 +25,7 @@
#include "libavutil/opt.h"
#include "avformat.h"
+#include "internal.h"
#define OFFSET(x) offsetof(AVFormatContext,x)
#define DEFAULT 0 //should be NAN but it does not work as it is not a constant in glibc as required by ANSI/ISO C
@@ -31,26 +34,37 @@
#define D AV_OPT_FLAG_DECODING_PARAM
static const AVOption avformat_options[] = {
-{"probesize", "set probing size", OFFSET(probesize), AV_OPT_TYPE_INT, {.i64 = 5000000 }, 32, INT_MAX, D},
+{"avioflags", NULL, OFFSET(avio_flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "avioflags"},
+{"direct", "reduce buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVIO_FLAG_DIRECT }, INT_MIN, INT_MAX, D|E, "avioflags"},
+{"probesize", "set probing size", OFFSET(probesize2), AV_OPT_TYPE_INT64, {.i64 = 5000000 }, 32, INT64_MAX, D},
+{"formatprobesize", "number of bytes to probe file format", OFFSET(format_probesize), AV_OPT_TYPE_INT, {.i64 = PROBE_BUF_MAX}, 0, INT_MAX-1, D},
{"packetsize", "set packet size", OFFSET(packet_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, E},
{"fflags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = AVFMT_FLAG_FLUSH_PACKETS }, INT_MIN, INT_MAX, D|E, "fflags"},
-{"flush_packets", "reduce the latency by flushing out packets immediately", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_FLUSH_PACKETS }, INT_MIN, INT_MAX, D, "fflags"},
+{"flush_packets", "reduce the latency by flushing out packets immediately", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_FLUSH_PACKETS }, INT_MIN, INT_MAX, E, "fflags"},
{"ignidx", "ignore index", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_IGNIDX }, INT_MIN, INT_MAX, D, "fflags"},
{"genpts", "generate pts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_GENPTS }, INT_MIN, INT_MAX, D, "fflags"},
{"nofillin", "do not fill in missing values that can be exactly calculated", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOFILLIN }, INT_MIN, INT_MAX, D, "fflags"},
{"noparse", "disable AVParsers, this needs nofillin too", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOPARSE }, INT_MIN, INT_MAX, D, "fflags"},
{"igndts", "ignore dts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_IGNDTS }, INT_MIN, INT_MAX, D, "fflags"},
{"discardcorrupt", "discard corrupted frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_DISCARD_CORRUPT }, INT_MIN, INT_MAX, D, "fflags"},
+{"sortdts", "try to interleave outputted packets by dts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_SORT_DTS }, INT_MIN, INT_MAX, D, "fflags"},
+{"keepside", "don't merge side data", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_KEEP_SIDE_DATA }, INT_MIN, INT_MAX, D, "fflags"},
+{"latm", "enable RTP MP4A-LATM payload", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_MP4A_LATM }, INT_MIN, INT_MAX, E, "fflags"},
{"nobuffer", "reduce the latency introduced by optional buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOBUFFER }, 0, INT_MAX, D, "fflags"},
+{"seek2any", "allow seeking to non-keyframes on demuxer level when supported", OFFSET(seek2any), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, D},
{"bitexact", "do not write random/volatile data", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_BITEXACT }, 0, 0, E, "fflags" },
-{"analyzeduration", "how many microseconds are analyzed to estimate duration", OFFSET(max_analyze_duration), AV_OPT_TYPE_INT, {.i64 = 5*AV_TIME_BASE }, 0, INT_MAX, D},
+{"analyzeduration", "specify how many microseconds are analyzed to probe the input", OFFSET(max_analyze_duration2), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, D},
{"cryptokey", "decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, {.dbl = 0}, 0, 0, D},
{"indexmem", "max memory used for timestamp index (per stream)", OFFSET(max_index_size), AV_OPT_TYPE_INT, {.i64 = 1<<20 }, 0, INT_MAX, D},
{"rtbufsize", "max memory used for buffering real-time frames", OFFSET(max_picture_buffer), AV_OPT_TYPE_INT, {.i64 = 3041280 }, 0, INT_MAX, D}, /* defaults to 1s of 15fps 352x288 YUYV422 video */
{"fdebug", "print specific debug info", OFFSET(debug), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, INT_MAX, E|D, "fdebug"},
{"ts", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_FDEBUG_TS }, INT_MIN, INT_MAX, E|D, "fdebug"},
{"max_delay", "maximum muxing or demuxing delay in microseconds", OFFSET(max_delay), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, E|D},
+{"start_time_realtime", "wall-clock time when stream begins (PTS==0)", OFFSET(start_time_realtime), AV_OPT_TYPE_INT64, {.i64 = AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX, E},
{"fpsprobesize", "number of frames used to probe fps", OFFSET(fps_probe_size), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX-1, D},
+{"audio_preload", "microseconds by which audio packets should be interleaved earlier", OFFSET(audio_preload), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E},
+{"chunk_duration", "microseconds for each chunk", OFFSET(max_chunk_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E},
+{"chunk_size", "size in bytes for each chunk", OFFSET(max_chunk_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E},
/* this is a crutch for avconv, since it cannot deal with identically named options in different contexts.
* to be removed when avconv is fixed */
{"f_err_detect", "set error detection flags (deprecated; use err_detect, save via avconv)", OFFSET(error_recognition), AV_OPT_TYPE_FLAGS, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, "err_detect"},
@@ -59,17 +73,32 @@ static const AVOption avformat_options[] = {
{"bitstream", "detect bitstream specification deviations", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BITSTREAM }, INT_MIN, INT_MAX, D, "err_detect"},
{"buffer", "detect improper bitstream length", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BUFFER }, INT_MIN, INT_MAX, D, "err_detect"},
{"explode", "abort decoding on minor error detection", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_EXPLODE }, INT_MIN, INT_MAX, D, "err_detect"},
+{"ignore_err", "ignore errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_IGNORE_ERR }, INT_MIN, INT_MAX, D, "err_detect"},
+{"careful", "consider things that violate the spec, are fast to check and have not been seen in the wild as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_CAREFUL }, INT_MIN, INT_MAX, D, "err_detect"},
+{"compliant", "consider all spec non compliancies as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_COMPLIANT }, INT_MIN, INT_MAX, D, "err_detect"},
+{"aggressive", "consider things that a sane encoder shouldn't do as an error", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_AGGRESSIVE }, INT_MIN, INT_MAX, D, "err_detect"},
+{"use_wallclock_as_timestamps", "use wallclock as timestamps", OFFSET(use_wallclock_as_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, D},
+{"skip_initial_bytes", "set number of bytes to skip before reading header and frames", OFFSET(skip_initial_bytes), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX-1, D},
+{"correct_ts_overflow", "correct single timestamp overflows", OFFSET(correct_ts_overflow), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, D},
+{"flush_packets", "enable flushing of the I/O context after each packet", OFFSET(flush_packets), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E},
+{"metadata_header_padding", "set number of bytes to be written as padding in a metadata header", OFFSET(metadata_header_padding), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, E},
+{"output_ts_offset", "set output timestamp offset", OFFSET(output_ts_offset), AV_OPT_TYPE_DURATION, {.i64 = 0}, -INT64_MAX, INT64_MAX, E},
{"max_interleave_delta", "maximum buffering duration for interleaving", OFFSET(max_interleave_delta), AV_OPT_TYPE_INT64, { .i64 = 10000000 }, 0, INT64_MAX, E },
{"f_strict", "how strictly to follow the standards (deprecated; use strict, save via avconv)", OFFSET(strict_std_compliance), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "strict"},
{"strict", "how strictly to follow the standards", OFFSET(strict_std_compliance), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "strict"},
{"strict", "strictly conform to all the things in the spec no matter what the consequences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_STRICT }, INT_MIN, INT_MAX, D|E, "strict"},
{"normal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_NORMAL }, INT_MIN, INT_MAX, D|E, "strict"},
+{"unofficial", "allow unofficial extensions", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_UNOFFICIAL }, INT_MIN, INT_MAX, D|E, "strict"},
{"experimental", "allow non-standardized experimental variants", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_EXPERIMENTAL }, INT_MIN, INT_MAX, D|E, "strict"},
{"max_ts_probe", "maximum number of packets to read while waiting for the first timestamp", OFFSET(max_ts_probe), AV_OPT_TYPE_INT, { .i64 = 50 }, 0, INT_MAX, D },
{"avoid_negative_ts", "shift timestamps so they start at 0", OFFSET(avoid_negative_ts), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 2, E, "avoid_negative_ts"},
{"auto", "enabled when required by target format", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_AUTO }, INT_MIN, INT_MAX, E, "avoid_negative_ts"},
+{"disabled", "do not change timestamps", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, INT_MIN, INT_MAX, E, "avoid_negative_ts"},
{"make_non_negative", "shift timestamps so they are non negative", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE }, INT_MIN, INT_MAX, E, "avoid_negative_ts"},
{"make_zero", "shift timestamps so they start at 0", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_MAKE_ZERO }, INT_MIN, INT_MAX, E, "avoid_negative_ts"},
+{"dump_separator", "set information dump field separator", OFFSET(dump_separator), AV_OPT_TYPE_STRING, {.str = ", "}, CHAR_MIN, CHAR_MAX, D|E},
+{"codec_whitelist", "List of decoders that are allowed to be used", OFFSET(codec_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D },
+{"format_whitelist", "List of demuxers that are allowed to be used", OFFSET(format_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D },
{NULL},
};
diff --git a/libavformat/os_support.c b/libavformat/os_support.c
index 650baea0d0..7950e44889 100644
--- a/libavformat/os_support.c
+++ b/libavformat/os_support.c
@@ -3,24 +3,25 @@
* 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
*/
/* needed by inet_aton() */
+#define _DEFAULT_SOURCE
#define _SVID_SOURCE
#include "config.h"
@@ -158,9 +159,9 @@ void ff_freeaddrinfo(struct addrinfo *res)
}
#endif /* HAVE_WINSOCK2_H */
- av_free(res->ai_canonname);
- av_free(res->ai_addr);
- av_free(res);
+ av_freep(&res->ai_canonname);
+ av_freep(&res->ai_addr);
+ av_freep(&res);
}
int ff_getnameinfo(const struct sockaddr *sa, int salen,
diff --git a/libavformat/os_support.h b/libavformat/os_support.h
index 6cc6d9aa2b..a3329119c0 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
*/
@@ -42,14 +42,31 @@
#if defined(_WIN32) && !defined(__MINGW32CE__)
# include <fcntl.h>
-# undef lseek
+# ifdef lseek
+# undef lseek
+# endif
# define lseek(f,p,w) _lseeki64((f), (p), (w))
-# undef stat
+# ifdef stat
+# undef stat
+# endif
# define stat _stati64
-# undef fstat
+# ifdef fstat
+# undef fstat
+# endif
# define fstat(f,s) _fstati64((f), (s))
#endif /* defined(_WIN32) && !defined(__MINGW32CE__) */
+
+#ifdef __ANDROID__
+# if HAVE_UNISTD_H
+# include <unistd.h>
+# endif
+# ifdef lseek
+# undef lseek
+# endif
+# define lseek(f,p,w) lseek64((f), (p), (w))
+#endif
+
static inline int is_dos_path(const char *path)
{
#if HAVE_DOS_PATHS
diff --git a/libavformat/paf.c b/libavformat/paf.c
index 9ee4cd8cb6..4fc5006f40 100644
--- a/libavformat/paf.c
+++ b/libavformat/paf.c
@@ -2,31 +2,29 @@
* Packed Animation File demuxer
* Copyright (c) 2012 Paul B Mahol
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libavutil/channel_layout.h"
-
+#include "libavcodec/paf.h"
#include "avformat.h"
#include "internal.h"
#define MAGIC "Packed Animation File V1.0\n(c) 1992-96 Amazing Studio\x0a\x1a"
-#define PAF_SOUND_SAMPLES 2205
-#define PAF_SOUND_FRAME_SIZE ((256 + PAF_SOUND_SAMPLES) * 2)
typedef struct PAFDemuxContext {
uint32_t buffer_size;
@@ -195,10 +193,13 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt)
{
PAFDemuxContext *p = s->priv_data;
AVIOContext *pb = s->pb;
- uint32_t count, offset;
- int size, i;
+ uint32_t count, offset;
+ int size, i;
+
+ if (p->current_frame >= p->nb_frames)
+ return AVERROR_EOF;
- if (p->current_frame >= p->nb_frames || pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR_EOF;
if (p->got_audio) {
diff --git a/libavformat/pcm.c b/libavformat/pcm.c
index e4f2a5e627..f62075fc82 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
*/
@@ -24,6 +24,24 @@
#include "internal.h"
#include "pcm.h"
+#define RAW_SAMPLES 1024
+
+int ff_pcm_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ int ret, size;
+
+ size= RAW_SAMPLES*s->streams[0]->codec->block_align;
+ if (size <= 0)
+ return AVERROR(EINVAL);
+
+ ret= av_get_packet(s->pb, pkt, size);
+
+ pkt->flags &= ~AV_PKT_FLAG_CORRUPT;
+ pkt->stream_index = 0;
+
+ return ret;
+}
+
int ff_pcm_read_seek(AVFormatContext *s,
int stream_index, int64_t timestamp, int flags)
{
diff --git a/libavformat/pcm.h b/libavformat/pcm.h
index 30cbc86ee3..9af36d5a2e 100644
--- a/libavformat/pcm.h
+++ b/libavformat/pcm.h
@@ -2,20 +2,20 @@
* PCM common functions
* Copyright (C) 2007 Aurelien Jacobs <aurel@gnuage.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -24,6 +24,7 @@
#include "avformat.h"
+int ff_pcm_read_packet(AVFormatContext *s, AVPacket *pkt);
int ff_pcm_read_seek(AVFormatContext *s,
int stream_index, int64_t timestamp, int flags);
diff --git a/libavformat/pcmdec.c b/libavformat/pcmdec.c
index 1bbbc66d17..2584c33c24 100644
--- a/libavformat/pcmdec.c
+++ b/libavformat/pcmdec.c
@@ -2,20 +2,20 @@
* RAW PCM demuxers
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -24,8 +24,7 @@
#include "pcm.h"
#include "libavutil/log.h"
#include "libavutil/opt.h"
-
-#define RAW_SAMPLES 1024
+#include "libavutil/avassert.h"
typedef struct PCMAudioDemuxerContext {
AVClass *class;
@@ -51,7 +50,7 @@ static int pcm_read_header(AVFormatContext *s)
st->codec->bits_per_coded_sample =
av_get_bits_per_sample(st->codec->codec_id);
- assert(st->codec->bits_per_coded_sample > 0);
+ av_assert0(st->codec->bits_per_coded_sample > 0);
st->codec->block_align =
st->codec->bits_per_coded_sample * st->codec->channels / 8;
@@ -60,33 +59,8 @@ static int pcm_read_header(AVFormatContext *s)
return 0;
}
-static int pcm_read_packet(AVFormatContext *s, AVPacket *pkt)
-{
- int ret, size, bps;
- // AVStream *st = s->streams[0];
-
- size= RAW_SAMPLES*s->streams[0]->codec->block_align;
-
- ret= av_get_packet(s->pb, pkt, size);
-
- pkt->stream_index = 0;
- if (ret < 0)
- return ret;
-
- bps= av_get_bits_per_sample(s->streams[0]->codec->codec_id);
- if (!bps) {
- av_log(s, AV_LOG_ERROR, "Unknown number of bytes per sample.\n");
- return AVERROR(EINVAL);
- }
-
- pkt->dts=
- pkt->pts= pkt->pos*8 / (bps * s->streams[0]->codec->channels);
-
- return ret;
-}
-
static const AVOption pcm_options[] = {
- { "sample_rate", "", offsetof(PCMAudioDemuxerContext, sample_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
+ { "sample_rate", "", offsetof(PCMAudioDemuxerContext, sample_rate), AV_OPT_TYPE_INT, {.i64 = 44100}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
{ "channels", "", offsetof(PCMAudioDemuxerContext, channels), AV_OPT_TYPE_INT, {.i64 = 1}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
{ NULL },
};
@@ -103,7 +77,7 @@ AVInputFormat ff_pcm_ ## name_ ## _demuxer = { \
.long_name = NULL_IF_CONFIG_SMALL(long_name_), \
.priv_data_size = sizeof(PCMAudioDemuxerContext), \
.read_header = pcm_read_header, \
- .read_packet = pcm_read_packet, \
+ .read_packet = ff_pcm_read_packet, \
.read_seek = ff_pcm_read_seek, \
.flags = AVFMT_GENERIC_INDEX, \
.extensions = ext, \
@@ -170,3 +144,29 @@ PCMDEF(alaw, "PCM A-law",
PCMDEF(mulaw, "PCM mu-law",
"ul", AV_CODEC_ID_PCM_MULAW)
+
+static const AVOption sln_options[] = {
+ { "sample_rate", "", offsetof(PCMAudioDemuxerContext, sample_rate), AV_OPT_TYPE_INT, {.i64 = 8000}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
+ { "channels", "", offsetof(PCMAudioDemuxerContext, channels), AV_OPT_TYPE_INT, {.i64 = 1}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+static const AVClass sln_demuxer_class = {
+ .class_name = "sln demuxer",
+ .item_name = av_default_item_name,
+ .option = sln_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_sln_demuxer = {
+ .name = "sln",
+ .long_name = NULL_IF_CONFIG_SMALL("Asterisk raw pcm"),
+ .priv_data_size = sizeof(PCMAudioDemuxerContext),
+ .read_header = pcm_read_header,
+ .read_packet = ff_pcm_read_packet,
+ .read_seek = ff_pcm_read_seek,
+ .flags = AVFMT_GENERIC_INDEX,
+ .extensions = "sln",
+ .raw_codec_id = AV_CODEC_ID_PCM_S16LE,
+ .priv_class = &sln_demuxer_class,
+};
diff --git a/libavformat/pcmenc.c b/libavformat/pcmenc.c
index e45ab8412a..3e4f308057 100644
--- a/libavformat/pcmenc.c
+++ b/libavformat/pcmenc.c
@@ -2,20 +2,20 @@
* RAW PCM muxers
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/pjsdec.c b/libavformat/pjsdec.c
new file mode 100644
index 0000000000..5129b70e5f
--- /dev/null
+++ b/libavformat/pjsdec.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2012 Clément Bœsch
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * PJS (Phoenix Japanimation Society) subtitles format demuxer
+ *
+ * @see http://subs.com.ru/page.php?al=pjs
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "subtitles.h"
+
+typedef struct {
+ FFDemuxSubtitlesQueue q;
+} PJSContext;
+
+static int pjs_probe(AVProbeData *p)
+{
+ char c;
+ int64_t start, end;
+ const unsigned char *ptr = p->buf;
+
+ if (sscanf(ptr, "%"SCNd64",%"SCNd64",%c", &start, &end, &c) == 3) {
+ size_t q1pos = strcspn(ptr, "\"");
+ size_t q2pos = q1pos + strcspn(ptr + q1pos + 1, "\"") + 1;
+ if (strcspn(ptr, "\r\n") > q2pos)
+ return AVPROBE_SCORE_MAX;
+ }
+ return 0;
+}
+
+static int64_t read_ts(char **line, int *duration)
+{
+ int64_t start, end;
+
+ if (sscanf(*line, "%"SCNd64",%"SCNd64, &start, &end) == 2) {
+ *line += strcspn(*line, "\"");
+ *line += !!**line;
+ *duration = end - start;
+ return start;
+ }
+ return AV_NOPTS_VALUE;
+}
+
+static int pjs_read_header(AVFormatContext *s)
+{
+ PJSContext *pjs = s->priv_data;
+ AVStream *st = avformat_new_stream(s, NULL);
+ int res = 0;
+
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 64, 1, 10);
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_PJS;
+
+ while (!avio_feof(s->pb)) {
+ char line[4096];
+ char *p = line;
+ const int64_t pos = avio_tell(s->pb);
+ int len = ff_get_line(s->pb, line, sizeof(line));
+ int64_t pts_start;
+ int duration;
+
+ if (!len)
+ break;
+
+ line[strcspn(line, "\r\n")] = 0;
+
+ pts_start = read_ts(&p, &duration);
+ if (pts_start != AV_NOPTS_VALUE) {
+ AVPacket *sub;
+
+ p[strcspn(p, "\"")] = 0;
+ sub = ff_subtitles_queue_insert(&pjs->q, p, strlen(p), 0);
+ if (!sub)
+ return AVERROR(ENOMEM);
+ sub->pos = pos;
+ sub->pts = pts_start;
+ sub->duration = duration;
+ }
+ }
+
+ ff_subtitles_queue_finalize(&pjs->q);
+ return res;
+}
+
+static int pjs_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ PJSContext *pjs = s->priv_data;
+ return ff_subtitles_queue_read_packet(&pjs->q, pkt);
+}
+
+static int pjs_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ PJSContext *pjs = s->priv_data;
+ return ff_subtitles_queue_seek(&pjs->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int pjs_read_close(AVFormatContext *s)
+{
+ PJSContext *pjs = s->priv_data;
+ ff_subtitles_queue_clean(&pjs->q);
+ return 0;
+}
+
+AVInputFormat ff_pjs_demuxer = {
+ .name = "pjs",
+ .long_name = NULL_IF_CONFIG_SMALL("PJS (Phoenix Japanimation Society) subtitles"),
+ .priv_data_size = sizeof(PJSContext),
+ .read_probe = pjs_probe,
+ .read_header = pjs_read_header,
+ .read_packet = pjs_read_packet,
+ .read_seek2 = pjs_read_seek,
+ .read_close = pjs_read_close,
+ .extensions = "pjs",
+};
diff --git a/libavformat/pmpdec.c b/libavformat/pmpdec.c
index 8ae147f391..ec966b3e1a 100644
--- a/libavformat/pmpdec.c
+++ b/libavformat/pmpdec.c
@@ -1,21 +1,21 @@
/*
- * PMP demuxer
+ * PMP demuxer.
* Copyright (c) 2011 Reimar Döffinger
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -23,18 +23,18 @@
#include "avformat.h"
#include "internal.h"
-typedef struct PMPContext {
- int cur_stream;
- int num_streams;
- int audio_packets;
- int current_packet;
+typedef struct {
+ int cur_stream;
+ int num_streams;
+ int audio_packets;
+ int current_packet;
uint32_t *packet_sizes;
- int packet_sizes_alloc;
+ int packet_sizes_alloc;
} PMPContext;
-static int pmp_probe(AVProbeData *p)
-{
- if (!memcmp(p->buf, "pmpm\1\0\0\0", 8))
+static int pmp_probe(AVProbeData *p) {
+ if (AV_RN32(p->buf) == AV_RN32("pmpm") &&
+ AV_RL32(p->buf + 4) == 1)
return AVPROBE_SCORE_MAX;
return 0;
}
@@ -44,11 +44,13 @@ static int pmp_header(AVFormatContext *s)
PMPContext *pmp = s->priv_data;
AVIOContext *pb = s->pb;
int tb_num, tb_den;
- int index_cnt;
+ uint32_t index_cnt;
int audio_codec_id = AV_CODEC_ID_NONE;
int srate, channels;
- int i;
+ unsigned i;
uint64_t pos;
+ int64_t fsize = avio_size(pb);
+
AVStream *vst = avformat_new_stream(s, NULL);
if (!vst)
return AVERROR(ENOMEM);
@@ -65,7 +67,7 @@ static int pmp_header(AVFormatContext *s)
av_log(s, AV_LOG_ERROR, "Unsupported video format\n");
break;
}
- index_cnt = avio_rl32(pb);
+ index_cnt = avio_rl32(pb);
vst->codec->width = avio_rl32(pb);
vst->codec->height = avio_rl32(pb);
@@ -73,14 +75,14 @@ static int pmp_header(AVFormatContext *s)
tb_den = avio_rl32(pb);
avpriv_set_pts_info(vst, 32, tb_num, tb_den);
vst->nb_frames = index_cnt;
- vst->duration = index_cnt;
+ vst->duration = index_cnt;
switch (avio_rl32(pb)) {
case 0:
audio_codec_id = AV_CODEC_ID_MP3;
break;
case 1:
- av_log(s, AV_LOG_WARNING, "AAC is not yet correctly supported\n");
+ av_log(s, AV_LOG_ERROR, "AAC not yet correctly supported\n");
audio_codec_id = AV_CODEC_ID_AAC;
break;
default:
@@ -89,26 +91,38 @@ static int pmp_header(AVFormatContext *s)
}
pmp->num_streams = avio_rl16(pb) + 1;
avio_skip(pb, 10);
- srate = avio_rl32(pb);
+ srate = avio_rl32(pb);
channels = avio_rl32(pb) + 1;
+ pos = avio_tell(pb) + 4LL*index_cnt;
+ for (i = 0; i < index_cnt; i++) {
+ uint32_t size = avio_rl32(pb);
+ int flags = size & 1 ? AVINDEX_KEYFRAME : 0;
+ if (avio_feof(pb)) {
+ av_log(s, AV_LOG_FATAL, "Encountered EOF while reading index.\n");
+ return AVERROR_INVALIDDATA;
+ }
+ size >>= 1;
+ if (size < 9 + 4*pmp->num_streams) {
+ av_log(s, AV_LOG_ERROR, "Packet too small\n");
+ return AVERROR_INVALIDDATA;
+ }
+ av_add_index_entry(vst, pos, i, size, 0, flags);
+ pos += size;
+ if (fsize > 0 && i == 0 && pos > fsize) {
+ av_log(s, AV_LOG_ERROR, "File ends before first packet\n");
+ return AVERROR_INVALIDDATA;
+ }
+ }
for (i = 1; i < pmp->num_streams; i++) {
AVStream *ast = avformat_new_stream(s, NULL);
if (!ast)
return AVERROR(ENOMEM);
- ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
- ast->codec->codec_id = audio_codec_id;
- ast->codec->channels = channels;
+ ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ ast->codec->codec_id = audio_codec_id;
+ ast->codec->channels = channels;
ast->codec->sample_rate = srate;
avpriv_set_pts_info(ast, 32, 1, srate);
}
- pos = avio_tell(pb) + 4 * index_cnt;
- for (i = 0; i < index_cnt; i++) {
- int size = avio_rl32(pb);
- int flags = size & 1 ? AVINDEX_KEYFRAME : 0;
- size >>= 1;
- av_add_index_entry(vst, pos, i, size, 0, flags);
- pos += size;
- }
return 0;
}
@@ -119,11 +133,12 @@ static int pmp_packet(AVFormatContext *s, AVPacket *pkt)
int ret = 0;
int i;
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR_EOF;
if (pmp->cur_stream == 0) {
int num_packets;
pmp->audio_packets = avio_r8(pb);
+
if (!pmp->audio_packets) {
av_log(s, AV_LOG_ERROR, "No audio packets.\n");
return AVERROR_INVALIDDATA;
@@ -143,22 +158,17 @@ static int pmp_packet(AVFormatContext *s, AVPacket *pkt)
pmp->packet_sizes[i] = avio_rl32(pb);
}
ret = av_get_packet(pb, pkt, pmp->packet_sizes[pmp->current_packet]);
- if (ret > 0) {
+ if (ret >= 0) {
ret = 0;
- // FIXME: this is a hack that should be removed once
- // compute_pkt_fields() can handle timestamps properly
- if (pmp->cur_stream == 0)
- pkt->dts = s->streams[0]->cur_dts++;
pkt->stream_index = pmp->cur_stream;
}
- pmp->current_packet++;
- if (pmp->current_packet == 1 || pmp->current_packet > pmp->audio_packets)
+ if (pmp->current_packet % pmp->audio_packets == 0)
pmp->cur_stream = (pmp->cur_stream + 1) % pmp->num_streams;
-
+ pmp->current_packet++;
return ret;
}
-static int pmp_seek(AVFormatContext *s, int stream_idx, int64_t ts, int flags)
+static int pmp_seek(AVFormatContext *s, int stream_index, int64_t ts, int flags)
{
PMPContext *pmp = s->priv_data;
pmp->cur_stream = 0;
diff --git a/libavformat/psxstr.c b/libavformat/psxstr.c
index e662ed788d..fd50e549ae 100644
--- a/libavformat/psxstr.c
+++ b/libavformat/psxstr.c
@@ -1,21 +1,21 @@
/*
* Sony Playstation (PSX) STR File Demuxer
- * Copyright (c) 2003 The ffmpeg Project
+ * Copyright (c) 2003 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -66,11 +66,13 @@ typedef struct StrDemuxContext {
StrChannel channels[32];
} StrDemuxContext;
-static const char sync_header[12] = {0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00};
+static const uint8_t sync_header[12] = {0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00};
static int str_probe(AVProbeData *p)
{
- uint8_t *sector= p->buf;
+ const uint8_t *sector= p->buf;
+ const uint8_t *end= sector + p->buf_size;
+ int aud=0, vid=0;
if (p->buf_size < RAW_CD_SECTOR_SIZE)
return 0;
@@ -82,20 +84,52 @@ static int str_probe(AVProbeData *p)
sector += RIFF_HEADER_SIZE;
}
- /* look for CD sync header (00, 0xFF x 10, 00) */
- if (memcmp(sector,sync_header,sizeof(sync_header)))
- return 0;
+ while (end - sector >= RAW_CD_SECTOR_SIZE) {
+ /* look for CD sync header (00, 0xFF x 10, 00) */
+ if (memcmp(sector,sync_header,sizeof(sync_header)))
+ return 0;
- if(sector[0x11] >= 32)
- return 0;
- if( (sector[0x12] & CDXA_TYPE_MASK) != CDXA_TYPE_VIDEO
- && (sector[0x12] & CDXA_TYPE_MASK) != CDXA_TYPE_AUDIO
- && (sector[0x12] & CDXA_TYPE_MASK) != CDXA_TYPE_DATA)
- return 0;
+ if (sector[0x11] >= 32)
+ return 0;
+
+ switch (sector[0x12] & CDXA_TYPE_MASK) {
+ case CDXA_TYPE_DATA:
+ case CDXA_TYPE_VIDEO: {
+ int current_sector = AV_RL16(&sector[0x1C]);
+ int sector_count = AV_RL16(&sector[0x1E]);
+ int frame_size = AV_RL32(&sector[0x24]);
+
+ if(!( frame_size>=0
+ && current_sector < sector_count
+ && sector_count*VIDEO_DATA_CHUNK_SIZE >=frame_size)){
+ return 0;
+ }
+ /*st->codec->width = AV_RL16(&sector[0x28]);
+ st->codec->height = AV_RL16(&sector[0x2A]);*/
+
+// if (current_sector == sector_count-1) {
+ vid++;
+// }
+
+ }
+ break;
+ case CDXA_TYPE_AUDIO:
+ if(sector[0x13]&0x2A)
+ return 0;
+ aud++;
+ break;
+ default:
+ if(sector[0x12] & CDXA_TYPE_MASK)
+ return 0;
+ }
+ sector += RAW_CD_SECTOR_SIZE;
+ }
/* MPEG files (like those ripped from VCDs) can also look like this;
* only return half certainty */
- return AVPROBE_SCORE_EXTENSION;
+ if(vid+aud > 3) return AVPROBE_SCORE_EXTENSION;
+ else if(vid+aud) return 1;
+ else return 0;
}
static int str_read_header(AVFormatContext *s)
@@ -187,6 +221,7 @@ static int str_read_packet(AVFormatContext *s,
av_free_packet(pkt);
if (av_new_packet(pkt, sector_count*VIDEO_DATA_CHUNK_SIZE))
return AVERROR(EIO);
+ memset(pkt->data, 0, sector_count*VIDEO_DATA_CHUNK_SIZE);
pkt->pos= avio_tell(pb) - RAW_CD_SECTOR_SIZE;
pkt->stream_index =
@@ -257,7 +292,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
break;
}
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR(EIO);
}
}
diff --git a/libavformat/pva.c b/libavformat/pva.c
index 2c485d6dcf..900ad6133d 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 PVAContext {
int continue_pes;
} PVAContext;
+static int pva_check(const uint8_t *p) {
+ int length = AV_RB16(p + 6);
+ if (AV_RB16(p) != PVA_MAGIC || !p[2] || p[2] > 2 || p[4] != 0x55 ||
+ (p[5] & 0xe0) || length > PVA_MAX_PAYLOAD_LENGTH)
+ return -1;
+ return length + 8;
+}
+
static int pva_probe(AVProbeData * pd) {
- unsigned char *buf = pd->buf;
+ const unsigned char *buf = pd->buf;
+ int len = pva_check(buf);
+
+ if (len < 0)
+ return 0;
- if (AV_RB16(buf) == PVA_MAGIC && buf[2] && buf[2] < 3 && buf[4] == 0x55)
+ if (pd->buf_size >= len + 8 &&
+ pva_check(buf + len) >= 0)
return AVPROBE_SCORE_EXTENSION;
- return 0;
+ return AVPROBE_SCORE_MAX / 4;
}
static int pva_read_header(AVFormatContext *s) {
@@ -72,6 +85,7 @@ static int read_part_of_packet(AVFormatContext *s, int64_t *pts,
PVAContext *pvactx = s->priv_data;
int syncword, streamid, reserved, flags, length, pts_flag;
int64_t pva_pts = AV_NOPTS_VALUE, startpos;
+ int ret;
recover:
startpos = avio_tell(pb);
@@ -120,8 +134,8 @@ recover:
pes_flags = avio_rb16(pb);
pes_header_data_length = avio_r8(pb);
- if (pes_signal != 1) {
- pva_log(s, AV_LOG_WARNING, "expected signaled PES packet, "
+ if (pes_signal != 1 || pes_header_data_length == 0) {
+ pva_log(s, AV_LOG_WARNING, "expected non empty signaled PES packet, "
"trying to recover\n");
avio_skip(pb, length - 9);
if (!read_packet)
@@ -129,15 +143,23 @@ recover:
goto recover;
}
- avio_read(pb, pes_header_data, pes_header_data_length);
+ ret = avio_read(pb, pes_header_data, pes_header_data_length);
+ if (ret != pes_header_data_length)
+ return ret < 0 ? ret : AVERROR_INVALIDDATA;
length -= 9 + pes_header_data_length;
pes_packet_length -= 3 + pes_header_data_length;
pvactx->continue_pes = pes_packet_length;
- if (pes_flags & 0x80 && (pes_header_data[0] & 0xf0) == 0x20)
+ if (pes_flags & 0x80 && (pes_header_data[0] & 0xf0) == 0x20) {
+ if (pes_header_data_length < 5) {
+ pva_log(s, AV_LOG_ERROR, "header too short\n");
+ avio_skip(pb, length);
+ return AVERROR_INVALIDDATA;
+ }
pva_pts = ff_parse_pes_pts(pes_header_data);
+ }
}
pvactx->continue_pes -= length;
diff --git a/libavformat/pvfdec.c b/libavformat/pvfdec.c
new file mode 100644
index 0000000000..c678046156
--- /dev/null
+++ b/libavformat/pvfdec.c
@@ -0,0 +1,75 @@
+/*
+ * PVF demuxer
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "pcm.h"
+
+static int pvf_probe(AVProbeData *p)
+{
+ if (!memcmp(p->buf, "PVF1\n", 5))
+ return AVPROBE_SCORE_MAX;
+ return 0;
+}
+
+static int pvf_read_header(AVFormatContext *s)
+{
+ char buffer[32];
+ AVStream *st;
+ int bps, channels, sample_rate;
+
+ avio_skip(s->pb, 5);
+ ff_get_line(s->pb, buffer, sizeof(buffer));
+ if (sscanf(buffer, "%d %d %d",
+ &channels,
+ &sample_rate,
+ &bps) != 3)
+ return AVERROR_INVALIDDATA;
+
+ if (channels <= 0 || bps <= 0 || sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->channels = channels;
+ st->codec->sample_rate = sample_rate;
+ st->codec->codec_id = ff_get_pcm_codec_id(bps, 0, 1, 0xFFFF);
+ st->codec->bits_per_coded_sample = bps;
+ st->codec->block_align = bps * st->codec->channels / 8;
+
+ avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+
+ return 0;
+}
+
+AVInputFormat ff_pvf_demuxer = {
+ .name = "pvf",
+ .long_name = NULL_IF_CONFIG_SMALL("PVF (Portable Voice Format)"),
+ .read_probe = pvf_probe,
+ .read_header = pvf_read_header,
+ .read_packet = ff_pcm_read_packet,
+ .read_seek = ff_pcm_read_seek,
+ .extensions = "pvf",
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/qcp.c b/libavformat/qcp.c
index be06e2f4ec..9e2eedfe85 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
*/
@@ -30,6 +30,7 @@
#include "libavutil/channel_layout.h"
#include "libavutil/intreadwrite.h"
#include "avformat.h"
+#include "riff.h"
typedef struct QCPContext {
uint32_t data_size; ///< size of data chunk
@@ -102,13 +103,12 @@ static int qcp_read_header(AVFormatContext *s)
if (is_qcelp_13k_guid(buf)) {
st->codec->codec_id = AV_CODEC_ID_QCELP;
} else if (!memcmp(buf, guid_evrc, 16)) {
- av_log(s, AV_LOG_ERROR, "EVRC codec is not supported.\n");
- return AVERROR_PATCHWELCOME;
+ st->codec->codec_id = AV_CODEC_ID_EVRC;
} else if (!memcmp(buf, guid_smv, 16)) {
- av_log(s, AV_LOG_ERROR, "SMV codec is not supported.\n");
- return AVERROR_PATCHWELCOME;
+ st->codec->codec_id = AV_CODEC_ID_SMV;
} else {
- av_log(s, AV_LOG_ERROR, "Unknown codec GUID.\n");
+ av_log(s, AV_LOG_ERROR, "Unknown codec GUID "FF_PRI_GUID".\n",
+ FF_ARG_GUID(buf));
return AVERROR_INVALIDDATA;
}
avio_skip(pb, 2 + 80); // codec-version + codec-name
@@ -141,7 +141,7 @@ static int qcp_read_packet(AVFormatContext *s, AVPacket *pkt)
QCPContext *c = s->priv_data;
unsigned int chunk_size, tag;
- while(!pb->eof_reached) {
+ while(!avio_feof(pb)) {
if (c->data_size) {
int pkt_size, ret, mode = avio_r8(pb);
diff --git a/libavformat/qtpalette.h b/libavformat/qtpalette.h
index 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 bcea462306..6dda1746dd 100644
--- a/libavformat/r3d.c
+++ b/libavformat/r3d.c
@@ -2,20 +2,20 @@
* R3D REDCODE demuxer
* Copyright (c) 2008 Baptiste Coudurier <baptiste dot coudurier at gmail dot com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -86,6 +86,9 @@ static int r3d_read_red1(AVFormatContext *s)
framerate.num = avio_rb16(s->pb);
framerate.den = avio_rb16(s->pb);
if (framerate.num > 0 && framerate.den > 0) {
+#if FF_API_R_FRAME_RATE
+ st->r_frame_rate =
+#endif
st->avg_frame_rate = framerate;
}
@@ -136,8 +139,7 @@ static int r3d_read_rdvo(AVFormatContext *s, Atom *atom)
if (st->avg_frame_rate.num)
st->duration = av_rescale_q(r3d->video_offsets_count,
- (AVRational){st->avg_frame_rate.den,
- st->avg_frame_rate.num},
+ av_inv_q(st->avg_frame_rate),
st->time_base);
av_dlog(s, "duration %"PRId64"\n", st->duration);
@@ -312,7 +314,8 @@ static int r3d_read_reda(AVFormatContext *s, AVPacket *pkt, Atom *atom)
pkt->stream_index = 1;
pkt->dts = dts;
- pkt->duration = av_rescale(samples, st->time_base.den, st->codec->sample_rate);
+ if (st->codec->sample_rate)
+ pkt->duration = av_rescale(samples, st->time_base.den, st->codec->sample_rate);
av_dlog(s, "pkt dts %"PRId64" duration %d samples %d sample rate %d\n",
pkt->dts, pkt->duration, samples, st->codec->sample_rate);
@@ -369,7 +372,7 @@ static int r3d_seek(AVFormatContext *s, int stream_index, int64_t sample_time, i
return -1;
frame_num = av_rescale_q(sample_time, st->time_base,
- (AVRational){st->avg_frame_rate.den, st->avg_frame_rate.num});
+ av_inv_q(st->avg_frame_rate));
av_dlog(s, "seek frame num %d timestamp %"PRId64"\n",
frame_num, sample_time);
diff --git a/libavformat/rawdec.c b/libavformat/rawdec.c
index 65cb6bb00f..b903e63fb4 100644
--- a/libavformat/rawdec.c
+++ b/libavformat/rawdec.c
@@ -3,20 +3,20 @@
* Copyright (c) 2001 Fabrice Bellard
* Copyright (c) 2005 Alex Beregszaszi
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -27,6 +27,7 @@
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "libavutil/pixdesc.h"
+#include "libavutil/avassert.h"
#include "libavutil/intreadwrite.h"
#define RAW_PACKET_SIZE 1024
@@ -46,12 +47,8 @@ int ff_raw_read_partial_packet(AVFormatContext *s, AVPacket *pkt)
if (ret < 0) {
av_free_packet(pkt);
return ret;
- } else if (ret < size) {
- /* initialize end of packet for partial reads to avoid reading
- * uninitialized data on allowed overreads */
- memset(pkt->data + ret, 0, FF_INPUT_BUFFER_PADDING_SIZE);
}
- pkt->size = ret;
+ av_shrink_packet(pkt, ret);
return ret;
}
@@ -62,7 +59,7 @@ int ff_raw_audio_read_header(AVFormatContext *s)
return AVERROR(ENOMEM);
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = s->iformat->raw_codec_id;
- st->need_parsing = AVSTREAM_PARSE_FULL;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
st->start_time = 0;
/* the parameters will be extracted from the compressed bitstream */
@@ -74,7 +71,6 @@ int ff_raw_video_read_header(AVFormatContext *s)
{
AVStream *st;
FFRawVideoDemuxerContext *s1 = s->priv_data;
- AVRational framerate;
int ret = 0;
@@ -86,95 +82,128 @@ int ff_raw_video_read_header(AVFormatContext *s)
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
st->codec->codec_id = s->iformat->raw_codec_id;
- st->need_parsing = AVSTREAM_PARSE_FULL;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
- if ((ret = av_parse_video_rate(&framerate, s1->framerate)) < 0) {
- av_log(s, AV_LOG_ERROR, "Could not parse framerate: %s.\n", s1->framerate);
- goto fail;
- }
-
- st->avg_frame_rate = framerate;
- avpriv_set_pts_info(st, 64, framerate.den, framerate.num);
+ st->codec->framerate = s1->framerate;
+ st->codec->time_base = av_inv_q(s1->framerate);
+ avpriv_set_pts_info(st, 64, 1, 1200000);
fail:
return ret;
}
+int ff_raw_data_read_header(AVFormatContext *s)
+{
+ AVStream *st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codec->codec_type = AVMEDIA_TYPE_DATA;
+ st->codec->codec_id = s->iformat->raw_codec_id;
+ st->start_time = 0;
+ return 0;
+}
+
/* Note: Do not forget to add new entries to the Makefile as well. */
#define OFFSET(x) offsetof(FFRawVideoDemuxerContext, x)
#define DEC AV_OPT_FLAG_DECODING_PARAM
const AVOption ff_rawvideo_options[] = {
- { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0, DEC},
+ { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, DEC},
{ NULL },
};
-#if CONFIG_LATM_DEMUXER
-
-#define LOAS_SYNC_WORD 0x2b7
-
-static int latm_read_probe(AVProbeData *p)
-{
- int max_frames = 0, first_frames = 0;
- int fsize, frames;
- uint8_t *buf0 = p->buf;
- uint8_t *buf2;
- uint8_t *buf;
- uint8_t *end = buf0 + p->buf_size - 3;
-
- buf = buf0;
-
- for (; buf < end; buf = buf2 + 1) {
- buf2 = buf;
-
- for (frames = 0; buf2 < end; frames++) {
- uint32_t header = AV_RB24(buf2);
- if ((header >> 13) != LOAS_SYNC_WORD) {
- if (buf != buf0) {
- // Found something that isn't a LOAS header, starting
- // from a position other than the start of the buffer.
- // Discard the count we've accumulated so far since it
- // probably was a false positive.
- frames = 0;
- }
- break;
- }
- fsize = (header & 0x1FFF) + 3;
- if (fsize < 7)
- break;
- buf2 += fsize;
- }
- max_frames = FFMAX(max_frames, frames);
- if (buf == buf0)
- first_frames = frames;
- }
+#if CONFIG_DATA_DEMUXER
+AVInputFormat ff_data_demuxer = {
+ .name = "data",
+ .long_name = NULL_IF_CONFIG_SMALL("raw data"),
+ .read_header = ff_raw_data_read_header,
+ .read_packet = ff_raw_read_partial_packet,
+ .raw_codec_id = AV_CODEC_ID_NONE,
+ .flags = AVFMT_NOTIMESTAMPS,
+};
+#endif
- if (first_frames >= 3)
- return AVPROBE_SCORE_EXTENSION + 1;
- else if (max_frames > 100)
- return AVPROBE_SCORE_EXTENSION;
- else if (max_frames >= 3)
- return AVPROBE_SCORE_EXTENSION / 2;
- else if (max_frames > 1)
- return 1;
- else
- return 0;
-}
+#if CONFIG_LATM_DEMUXER
AVInputFormat ff_latm_demuxer = {
.name = "latm",
.long_name = NULL_IF_CONFIG_SMALL("raw LOAS/LATM"),
- .read_probe = latm_read_probe,
.read_header = ff_raw_audio_read_header,
.read_packet = ff_raw_read_partial_packet,
- .flags = AVFMT_GENERIC_INDEX,
+ .flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS,
.extensions = "latm",
.raw_codec_id = AV_CODEC_ID_AAC_LATM,
};
#endif
#if CONFIG_MJPEG_DEMUXER
-FF_DEF_RAWVIDEO_DEMUXER(mjpeg, "raw MJPEG video", NULL, "mjpg,mjpeg", AV_CODEC_ID_MJPEG)
+static int mjpeg_probe(AVProbeData *p)
+{
+ int i;
+ int state = -1;
+ int nb_invalid = 0;
+ int nb_frames = 0;
+
+ for (i=0; i<p->buf_size-2; i++) {
+ int c;
+ if (p->buf[i] != 0xFF)
+ continue;
+ c = p->buf[i+1];
+ switch (c) {
+ case 0xD8:
+ state = 0xD8;
+ break;
+ case 0xC0:
+ case 0xC1:
+ case 0xC2:
+ case 0xC3:
+ case 0xC5:
+ case 0xC6:
+ case 0xC7:
+ case 0xF7:
+ if (state == 0xD8) {
+ state = 0xC0;
+ } else
+ nb_invalid++;
+ break;
+ case 0xDA:
+ if (state == 0xC0) {
+ state = 0xDA;
+ } else
+ nb_invalid++;
+ break;
+ case 0xD9:
+ if (state == 0xDA) {
+ state = 0xD9;
+ nb_frames++;
+ } else
+ nb_invalid++;
+ break;
+ default:
+ if ( (c >= 0x02 && c <= 0xBF)
+ || c == 0xC8) {
+ nb_invalid++;
+ }
+ }
+ }
+
+ if (nb_invalid*4 + 1 < nb_frames) {
+ static const char ct_jpeg[] = "\r\nContent-Type: image/jpeg\r\n\r\n";
+ int i;
+
+ for (i=0; i<FFMIN(p->buf_size - sizeof(ct_jpeg), 100); i++)
+ if (!memcmp(p->buf + i, ct_jpeg, sizeof(ct_jpeg) - 1))
+ return AVPROBE_SCORE_EXTENSION;
+
+ if (nb_invalid == 0 && nb_frames > 2)
+ return AVPROBE_SCORE_EXTENSION / 2;
+ return AVPROBE_SCORE_EXTENSION / 4;
+ }
+
+ return 0;
+}
+
+FF_DEF_RAWVIDEO_DEMUXER2(mjpeg, "raw MJPEG video", mjpeg_probe, "mjpg,mjpeg,mpo", AV_CODEC_ID_MJPEG, AVFMT_GENERIC_INDEX|AVFMT_NOTIMESTAMPS)
#endif
#if CONFIG_MLP_DEMUXER
@@ -183,7 +212,7 @@ AVInputFormat ff_mlp_demuxer = {
.long_name = NULL_IF_CONFIG_SMALL("raw MLP"),
.read_header = ff_raw_audio_read_header,
.read_packet = ff_raw_read_partial_packet,
- .flags = AVFMT_GENERIC_INDEX,
+ .flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS,
.extensions = "mlp",
.raw_codec_id = AV_CODEC_ID_MLP,
};
@@ -195,7 +224,7 @@ AVInputFormat ff_truehd_demuxer = {
.long_name = NULL_IF_CONFIG_SMALL("raw TrueHD"),
.read_header = ff_raw_audio_read_header,
.read_packet = ff_raw_read_partial_packet,
- .flags = AVFMT_GENERIC_INDEX,
+ .flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS,
.extensions = "thd",
.raw_codec_id = AV_CODEC_ID_TRUEHD,
};
@@ -207,12 +236,12 @@ AVInputFormat ff_shorten_demuxer = {
.long_name = NULL_IF_CONFIG_SMALL("raw Shorten"),
.read_header = ff_raw_audio_read_header,
.read_packet = ff_raw_read_partial_packet,
- .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK,
+ .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK|AVFMT_NOTIMESTAMPS,
.extensions = "shn",
.raw_codec_id = AV_CODEC_ID_SHORTEN,
};
#endif
#if CONFIG_VC1_DEMUXER
-FF_DEF_RAWVIDEO_DEMUXER(vc1, "raw VC-1", NULL, "vc1", AV_CODEC_ID_VC1)
+FF_DEF_RAWVIDEO_DEMUXER2(vc1, "raw VC-1", NULL, "vc1", AV_CODEC_ID_VC1, AVFMT_GENERIC_INDEX|AVFMT_NOTIMESTAMPS)
#endif
diff --git a/libavformat/rawdec.h b/libavformat/rawdec.h
index a5487784c9..a464bbb432 100644
--- a/libavformat/rawdec.h
+++ b/libavformat/rawdec.h
@@ -2,20 +2,20 @@
* RAW demuxers
* Copyright (C) 2007 Aurelien Jacobs <aurel@gnuage.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -30,7 +30,7 @@ typedef struct FFRawVideoDemuxerContext {
const AVClass *class; /**< Class for private options. */
char *video_size; /**< String describing video size, set by a private option. */
char *pixel_format; /**< Set by a private option. */
- char *framerate; /**< String describing framerate, set by a private option. */
+ AVRational framerate; /**< AVRational describing framerate, set by a private option. */
} FFRawVideoDemuxerContext;
extern const AVOption ff_rawvideo_options[];
@@ -41,6 +41,8 @@ int ff_raw_audio_read_header(AVFormatContext *s);
int ff_raw_video_read_header(AVFormatContext *s);
+int ff_raw_data_read_header(AVFormatContext *s);
+
#define FF_RAWVIDEO_DEMUXER_CLASS(name)\
static const AVClass name ## _demuxer_class = {\
.class_name = #name " demuxer",\
@@ -49,7 +51,7 @@ static const AVClass name ## _demuxer_class = {\
.version = LIBAVUTIL_VERSION_INT,\
};
-#define FF_DEF_RAWVIDEO_DEMUXER(shortname, longname, probe, ext, id)\
+#define FF_DEF_RAWVIDEO_DEMUXER2(shortname, longname, probe, ext, id, flag)\
FF_RAWVIDEO_DEMUXER_CLASS(shortname)\
AVInputFormat ff_ ## shortname ## _demuxer = {\
.name = #shortname,\
@@ -58,10 +60,36 @@ AVInputFormat ff_ ## shortname ## _demuxer = {\
.read_header = ff_raw_video_read_header,\
.read_packet = ff_raw_read_partial_packet,\
.extensions = ext,\
- .flags = AVFMT_GENERIC_INDEX,\
+ .flags = flag,\
.raw_codec_id = id,\
.priv_data_size = sizeof(FFRawVideoDemuxerContext),\
.priv_class = &shortname ## _demuxer_class,\
};
+#define FF_DEF_RAWVIDEO_DEMUXER(shortname, longname, probe, ext, id)\
+FF_DEF_RAWVIDEO_DEMUXER2(shortname, longname, probe, ext, id, AVFMT_GENERIC_INDEX)
+
+#define FF_RAWSUB_DEMUXER_CLASS(name)\
+static const AVClass name ## _demuxer_class = {\
+ .class_name = #name " demuxer",\
+ .item_name = av_default_item_name,\
+ .option = NULL,\
+ .version = LIBAVUTIL_VERSION_INT,\
+};
+
+#define FF_DEF_RAWSUB_DEMUXER(shortname, longname, probe, ext, id, flag)\
+FF_RAWVIDEO_DEMUXER_CLASS(shortname)\
+AVInputFormat ff_ ## shortname ## _demuxer = {\
+ .name = #shortname,\
+ .long_name = NULL_IF_CONFIG_SMALL(longname),\
+ .read_probe = probe,\
+ .read_header = ff_raw_data_read_header,\
+ .read_packet = ff_raw_read_partial_packet,\
+ .extensions = ext,\
+ .flags = flag,\
+ .raw_codec_id = id,\
+ .priv_data_size = 0,\
+ .priv_class = &shortname ## _demuxer_class,\
+};
+
#endif /* AVFORMAT_RAWDEC_H */
diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c
index 5d6164656d..9b77cdcab2 100644
--- a/libavformat/rawenc.c
+++ b/libavformat/rawenc.c
@@ -3,20 +3,20 @@
* Copyright (c) 2001 Fabrice Bellard
* Copyright (c) 2005 Alex Beregszaszi
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -29,6 +29,16 @@ int ff_raw_write_packet(AVFormatContext *s, AVPacket *pkt)
return 0;
}
+static int force_one_stream(AVFormatContext *s)
+{
+ if (s->nb_streams != 1) {
+ av_log(s, AV_LOG_ERROR, "%s files have exactly one stream\n",
+ s->oformat->name);
+ return AVERROR(EINVAL);
+ }
+ return 0;
+}
+
/* Note: Do not forget to add new entries to the Makefile as well. */
#if CONFIG_AC3_MUXER
@@ -39,6 +49,7 @@ AVOutputFormat ff_ac3_muxer = {
.extensions = "ac3",
.audio_codec = AV_CODEC_ID_AC3,
.video_codec = AV_CODEC_ID_NONE,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -51,6 +62,7 @@ AVOutputFormat ff_adx_muxer = {
.extensions = "adx",
.audio_codec = AV_CODEC_ID_ADPCM_ADX,
.video_codec = AV_CODEC_ID_NONE,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -63,6 +75,17 @@ AVOutputFormat ff_cavsvideo_muxer = {
.extensions = "cavs",
.audio_codec = AV_CODEC_ID_NONE,
.video_codec = AV_CODEC_ID_CAVS,
+ .write_header = force_one_stream,
+ .write_packet = ff_raw_write_packet,
+ .flags = AVFMT_NOTIMESTAMPS,
+};
+#endif
+
+#if CONFIG_DATA_MUXER
+AVOutputFormat ff_data_muxer = {
+ .name = "data",
+ .long_name = NULL_IF_CONFIG_SMALL("raw data"),
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -75,6 +98,7 @@ AVOutputFormat ff_dirac_muxer = {
.extensions = "drc",
.audio_codec = AV_CODEC_ID_NONE,
.video_codec = AV_CODEC_ID_DIRAC,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -87,6 +111,7 @@ AVOutputFormat ff_dnxhd_muxer = {
.extensions = "dnxhd",
.audio_codec = AV_CODEC_ID_NONE,
.video_codec = AV_CODEC_ID_DNXHD,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -100,6 +125,7 @@ AVOutputFormat ff_dts_muxer = {
.extensions = "dts",
.audio_codec = AV_CODEC_ID_DTS,
.video_codec = AV_CODEC_ID_NONE,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -113,6 +139,7 @@ AVOutputFormat ff_eac3_muxer = {
.extensions = "eac3",
.audio_codec = AV_CODEC_ID_EAC3,
.video_codec = AV_CODEC_ID_NONE,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -126,6 +153,21 @@ AVOutputFormat ff_g722_muxer = {
.extensions = "g722",
.audio_codec = AV_CODEC_ID_ADPCM_G722,
.video_codec = AV_CODEC_ID_NONE,
+ .write_header = force_one_stream,
+ .write_packet = ff_raw_write_packet,
+ .flags = AVFMT_NOTIMESTAMPS,
+};
+#endif
+
+#if CONFIG_G723_1_MUXER
+AVOutputFormat ff_g723_1_muxer = {
+ .name = "g723_1",
+ .long_name = NULL_IF_CONFIG_SMALL("raw G.723.1"),
+ .mime_type = "audio/g723",
+ .extensions = "tco,rco",
+ .audio_codec = AV_CODEC_ID_G723_1,
+ .video_codec = AV_CODEC_ID_NONE,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -139,6 +181,7 @@ AVOutputFormat ff_h261_muxer = {
.extensions = "h261",
.audio_codec = AV_CODEC_ID_NONE,
.video_codec = AV_CODEC_ID_H261,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -152,6 +195,7 @@ AVOutputFormat ff_h263_muxer = {
.extensions = "h263",
.audio_codec = AV_CODEC_ID_NONE,
.video_codec = AV_CODEC_ID_H263,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -161,9 +205,10 @@ AVOutputFormat ff_h263_muxer = {
AVOutputFormat ff_h264_muxer = {
.name = "h264",
.long_name = NULL_IF_CONFIG_SMALL("raw H.264 video"),
- .extensions = "h264",
+ .extensions = "h264,264",
.audio_codec = AV_CODEC_ID_NONE,
.video_codec = AV_CODEC_ID_H264,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -201,6 +246,7 @@ AVOutputFormat ff_mjpeg_muxer = {
.extensions = "mjpg,mjpeg",
.audio_codec = AV_CODEC_ID_NONE,
.video_codec = AV_CODEC_ID_MJPEG,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -213,6 +259,7 @@ AVOutputFormat ff_mlp_muxer = {
.extensions = "mlp",
.audio_codec = AV_CODEC_ID_MLP,
.video_codec = AV_CODEC_ID_NONE,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -226,6 +273,7 @@ AVOutputFormat ff_mpeg1video_muxer = {
.extensions = "mpg,mpeg,m1v",
.audio_codec = AV_CODEC_ID_NONE,
.video_codec = AV_CODEC_ID_MPEG1VIDEO,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -238,6 +286,7 @@ AVOutputFormat ff_mpeg2video_muxer = {
.extensions = "m2v",
.audio_codec = AV_CODEC_ID_NONE,
.video_codec = AV_CODEC_ID_MPEG2VIDEO,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -255,18 +304,6 @@ AVOutputFormat ff_rawvideo_muxer = {
};
#endif
-#if CONFIG_SRT_MUXER
-AVOutputFormat ff_srt_muxer = {
- .name = "srt",
- .long_name = NULL_IF_CONFIG_SMALL("SubRip subtitle"),
- .mime_type = "application/x-subrip",
- .extensions = "srt",
- .write_packet = ff_raw_write_packet,
- .flags = AVFMT_NOTIMESTAMPS,
- .subtitle_codec = AV_CODEC_ID_SRT,
-};
-#endif
-
#if CONFIG_TRUEHD_MUXER
AVOutputFormat ff_truehd_muxer = {
.name = "truehd",
@@ -274,6 +311,20 @@ AVOutputFormat ff_truehd_muxer = {
.extensions = "thd",
.audio_codec = AV_CODEC_ID_TRUEHD,
.video_codec = AV_CODEC_ID_NONE,
+ .write_header = force_one_stream,
+ .write_packet = ff_raw_write_packet,
+ .flags = AVFMT_NOTIMESTAMPS,
+};
+#endif
+
+#if CONFIG_VC1_MUXER
+AVOutputFormat ff_vc1_muxer = {
+ .name = "vc1",
+ .long_name = NULL_IF_CONFIG_SMALL("raw VC-1 video"),
+ .extensions = "vc1",
+ .audio_codec = AV_CODEC_ID_NONE,
+ .video_codec = AV_CODEC_ID_VC1,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
diff --git a/libavformat/rawenc.h b/libavformat/rawenc.h
index daa5489da8..b5523090b8 100644
--- a/libavformat/rawenc.h
+++ b/libavformat/rawenc.h
@@ -2,20 +2,20 @@
* RAW muxers
* Copyright (C) 2007 Aurelien Jacobs <aurel@gnuage.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rawvideodec.c b/libavformat/rawvideodec.c
index 5f372c9a2c..cbcae43c72 100644
--- a/libavformat/rawvideodec.c
+++ b/libavformat/rawvideodec.c
@@ -2,20 +2,20 @@
* RAW video demuxer
* Copyright (c) 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -27,18 +27,16 @@
typedef struct RawVideoDemuxerContext {
const AVClass *class; /**< Class for private options. */
- char *video_size; /**< String describing video size, set by a private option. */
+ int width, height; /**< Integers describing video size, set by a private option. */
char *pixel_format; /**< Set by a private option. */
- char *framerate; /**< String describing framerate, set by a private option. */
+ AVRational framerate; /**< AVRational describing framerate, set by a private option. */
} RawVideoDemuxerContext;
static int rawvideo_read_header(AVFormatContext *ctx)
{
RawVideoDemuxerContext *s = ctx->priv_data;
- int width = 0, height = 0, ret = 0;
enum AVPixelFormat pix_fmt;
- AVRational framerate;
AVStream *st;
st = avformat_new_stream(ctx, NULL);
@@ -49,29 +47,19 @@ static int rawvideo_read_header(AVFormatContext *ctx)
st->codec->codec_id = ctx->iformat->raw_codec_id;
- if (s->video_size &&
- (ret = av_parse_video_size(&width, &height, s->video_size)) < 0) {
- av_log(ctx, AV_LOG_ERROR, "Couldn't parse video size.\n");
- return ret;
- }
-
if ((pix_fmt = av_get_pix_fmt(s->pixel_format)) == AV_PIX_FMT_NONE) {
av_log(ctx, AV_LOG_ERROR, "No such pixel format: %s.\n",
s->pixel_format);
return AVERROR(EINVAL);
}
- if ((ret = av_parse_video_rate(&framerate, s->framerate)) < 0) {
- av_log(ctx, AV_LOG_ERROR, "Could not parse framerate: %s.\n",
- s->framerate);
- return ret;
- }
-
- avpriv_set_pts_info(st, 64, framerate.den, framerate.num);
+ avpriv_set_pts_info(st, 64, s->framerate.den, s->framerate.num);
- st->codec->width = width;
- st->codec->height = height;
+ st->codec->width = s->width;
+ st->codec->height = s->height;
st->codec->pix_fmt = pix_fmt;
+ st->codec->bit_rate = av_rescale_q(avpicture_get_size(st->codec->pix_fmt, s->width, s->height),
+ (AVRational){8,1}, st->time_base);
return 0;
}
@@ -101,9 +89,9 @@ static int rawvideo_read_packet(AVFormatContext *s, AVPacket *pkt)
#define OFFSET(x) offsetof(RawVideoDemuxerContext, x)
#define DEC AV_OPT_FLAG_DECODING_PARAM
static const AVOption rawvideo_options[] = {
- { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC },
- { "pixel_format", "", OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = "yuv420p"}, 0, 0, DEC },
- { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0, DEC },
+ { "video_size", "set frame size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC },
+ { "pixel_format", "set pixel format", OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = "yuv420p"}, 0, 0, DEC },
+ { "framerate", "set frame rate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, DEC },
{ NULL },
};
diff --git a/libavformat/rdt.c b/libavformat/rdt.c
index cc30694756..212e139f31 100644
--- a/libavformat/rdt.c
+++ b/libavformat/rdt.c
@@ -2,20 +2,20 @@
* Realmedia RTSP protocol (RDT) support.
* Copyright (c) 2007 Ronald S. Bultje
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -98,7 +98,7 @@ ff_rdt_calc_response_and_checksum(char response[41], char chksum[9],
unsigned char zres[16],
buf[64] = { 0xa1, 0xe9, 0x14, 0x9d, 0x0e, 0x6b, 0x3b, 0x59 };
#define XOR_TABLE_SIZE 37
- const unsigned char xor_table[XOR_TABLE_SIZE] = {
+ static const unsigned char xor_table[XOR_TABLE_SIZE] = {
0x05, 0x18, 0x74, 0xd0, 0x0d, 0x09, 0x02, 0x53,
0xc0, 0x01, 0x05, 0x05, 0x67, 0x03, 0x19, 0x70,
0x08, 0x27, 0x66, 0x10, 0x10, 0x72, 0x08, 0x09,
@@ -176,7 +176,7 @@ rdt_load_mdpr (PayloadContext *rdt, AVStream *st, int rule_nr)
size = rdt->mlti_data_size;
avio_seek(&pb, 0, SEEK_SET);
}
- if (ff_rm_read_mdpr_codecdata(rdt->rmctx, &pb, st, rdt->rmst[st->index], size) < 0)
+ if (ff_rm_read_mdpr_codecdata(rdt->rmctx, &pb, st, rdt->rmst[st->index], size, NULL) < 0)
return -1;
return 0;
@@ -301,7 +301,7 @@ rdt_parse_packet (AVFormatContext *ctx, PayloadContext *rdt, AVStream *st,
if (rdt->audio_pkt_cnt == 0) {
int pos;
- ffio_init_context(&pb, buf, len, 0, NULL, NULL, NULL, NULL);
+ ffio_init_context(&pb, (uint8_t *)buf, len, 0, NULL, NULL, NULL, NULL);
flags = (flags & RTP_FLAG_KEY) ? 2 : 0;
res = ff_rm_parse_packet (rdt->rmctx, &pb, st, rdt->rmst[st->index], len, pkt,
&seq, flags, *timestamp);
@@ -523,18 +523,27 @@ static PayloadContext *
rdt_new_context (void)
{
PayloadContext *rdt = av_mallocz(sizeof(PayloadContext));
- int ret;
if (!rdt)
return NULL;
- ret = avformat_open_input(&rdt->rmctx, "", &ff_rdt_demuxer, NULL);
- if (ret < 0) {
- av_free(rdt);
- return NULL;
- }
+
+ rdt->rmctx = avformat_alloc_context();
+ if (!rdt->rmctx)
+ av_freep(&rdt);
return rdt;
}
+static int
+rdt_init_context (AVFormatContext *s, int st_index, PayloadContext *rdt)
+{
+ int ret;
+
+ if ((ret = ff_copy_whitelists(rdt->rmctx, s)) < 0)
+ return ret;
+
+ return avformat_open_input(&rdt->rmctx, "", &ff_rdt_demuxer, NULL);
+}
+
static void
rdt_free_context (PayloadContext *rdt)
{
@@ -559,6 +568,7 @@ static RTPDynamicProtocolHandler rdt_ ## n ## _handler = { \
.codec_id = AV_CODEC_ID_NONE, \
.parse_sdp_a_line = rdt_parse_sdp_line, \
.alloc = rdt_new_context, \
+ .init = rdt_init_context, \
.free = rdt_free_context, \
.parse_packet = rdt_parse_packet \
}
diff --git a/libavformat/rdt.h b/libavformat/rdt.h
index bd168903f5..ce6026f49c 100644
--- a/libavformat/rdt.h
+++ b/libavformat/rdt.h
@@ -2,20 +2,20 @@
* Realmedia RTSP (RDT) definitions
* Copyright (c) 2007 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/realtextdec.c b/libavformat/realtextdec.c
new file mode 100644
index 0000000000..fff85d6ba9
--- /dev/null
+++ b/libavformat/realtextdec.c
@@ -0,0 +1,156 @@
+/*
+ * 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)
+{
+ char buf[7];
+ FFTextReader tr;
+ ff_text_init_buf(&tr, p->buf, p->buf_size);
+ ff_text_read(&tr, buf, sizeof(buf));
+
+ return !av_strncasecmp(buf, "<window", 7) ? AVPROBE_SCORE_EXTENSION : 0;
+}
+
+static int read_ts(const char *s)
+{
+ int hh, mm, ss, ms;
+
+ if (sscanf(s, "%u:%u:%u.%u", &hh, &mm, &ss, &ms) == 4) return (hh*3600 + mm*60 + ss) * 100 + ms;
+ if (sscanf(s, "%u:%u:%u" , &hh, &mm, &ss ) == 3) return (hh*3600 + mm*60 + ss) * 100;
+ if (sscanf(s, "%u:%u.%u", &mm, &ss, &ms) == 3) return ( mm*60 + ss) * 100 + ms;
+ if (sscanf(s, "%u:%u" , &mm, &ss ) == 2) return ( mm*60 + ss) * 100;
+ if (sscanf(s, "%u.%u", &ss, &ms) == 2) return ( ss) * 100 + ms;
+ return strtol(s, NULL, 10) * 100;
+}
+
+static int realtext_read_header(AVFormatContext *s)
+{
+ RealTextContext *rt = s->priv_data;
+ AVStream *st = avformat_new_stream(s, NULL);
+ AVBPrint buf;
+ char c = 0;
+ int res = 0, duration = read_ts("60"); // default duration is 60 seconds
+ FFTextReader tr;
+ ff_text_init_avio(s, &tr, s->pb);
+
+ 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 (!ff_text_eof(&tr)) {
+ AVPacket *sub;
+ const int64_t pos = ff_text_pos(&tr) - (c != 0);
+ int n = ff_smil_extract_next_text_chunk(&tr, &buf, &c);
+
+ if (n == 0)
+ break;
+
+ if (!av_strncasecmp(buf.str, "<window", 7)) {
+ /* save header to extradata */
+ const char *p = ff_smil_get_attr_ptr(buf.str, "duration");
+
+ if (p)
+ duration = read_ts(p);
+ st->codec->extradata = av_strdup(buf.str);
+ if (!st->codec->extradata) {
+ res = AVERROR(ENOMEM);
+ goto end;
+ }
+ st->codec->extradata_size = buf.len + 1;
+ } else {
+ /* if we just read a <time> tag, introduce a new event, otherwise merge
+ * with the previous one */
+ int merge = !av_strncasecmp(buf.str, "<time", 5) ? 0 : 1;
+ sub = ff_subtitles_queue_insert(&rt->q, buf.str, buf.len, merge);
+ if (!sub) {
+ res = AVERROR(ENOMEM);
+ goto end;
+ }
+ if (!merge) {
+ const char *begin = ff_smil_get_attr_ptr(buf.str, "begin");
+ const char *end = ff_smil_get_attr_ptr(buf.str, "end");
+
+ sub->pos = pos;
+ sub->pts = begin ? read_ts(begin) : 0;
+ sub->duration = end ? (read_ts(end) - sub->pts) : duration;
+ }
+ }
+ av_bprint_clear(&buf);
+ }
+ ff_subtitles_queue_finalize(&rt->q);
+
+end:
+ av_bprint_finalize(&buf, NULL);
+ return res;
+}
+
+static int realtext_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ RealTextContext *rt = s->priv_data;
+ return ff_subtitles_queue_read_packet(&rt->q, pkt);
+}
+
+static int realtext_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ RealTextContext *rt = s->priv_data;
+ return ff_subtitles_queue_seek(&rt->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int realtext_read_close(AVFormatContext *s)
+{
+ RealTextContext *rt = s->priv_data;
+ ff_subtitles_queue_clean(&rt->q);
+ return 0;
+}
+
+AVInputFormat ff_realtext_demuxer = {
+ .name = "realtext",
+ .long_name = NULL_IF_CONFIG_SMALL("RealText subtitle format"),
+ .priv_data_size = sizeof(RealTextContext),
+ .read_probe = realtext_probe,
+ .read_header = realtext_read_header,
+ .read_packet = realtext_read_packet,
+ .read_seek2 = realtext_read_seek,
+ .read_close = realtext_read_close,
+ .extensions = "rt",
+};
diff --git a/libavformat/redspark.c b/libavformat/redspark.c
new file mode 100644
index 0000000000..13a7b37d25
--- /dev/null
+++ b/libavformat/redspark.c
@@ -0,0 +1,170 @@
+/*
+ * RedSpark demuxer
+ * Copyright (c) 2013 James Almer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavcodec/bytestream.h"
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "avio.h"
+#include "internal.h"
+
+#define HEADER_SIZE 4096
+
+typedef struct RedSparkContext {
+ int samples_count;
+} RedSparkContext;
+
+static int redspark_probe(AVProbeData *p)
+{
+ uint32_t key, data;
+ uint8_t header[8];
+
+ /* Decrypt first 8 bytes of the header */
+ data = AV_RB32(p->buf);
+ data = data ^ (key = data ^ 0x52656453);
+ AV_WB32(header, data);
+ key = (key << 11) | (key >> 21);
+
+ data = AV_RB32(p->buf + 4) ^ (((key << 3) | (key >> 29)) + key);
+ AV_WB32(header + 4, data);
+
+ if (AV_RB64(header) == AV_RB64("RedSpark"))
+ return AVPROBE_SCORE_MAX;
+
+ return 0;
+}
+
+static int redspark_read_header(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ RedSparkContext *redspark = s->priv_data;
+ AVCodecContext *codec;
+ GetByteContext gbc;
+ int i, coef_off, ret = 0;
+ uint32_t key, data;
+ uint8_t *header, *pbc;
+ AVStream *st;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ codec = st->codec;
+
+ header = av_malloc(HEADER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
+ if (!header)
+ return AVERROR(ENOMEM);
+ pbc = header;
+
+ /* Decrypt header */
+ data = avio_rb32(pb);
+ data = data ^ (key = data ^ 0x52656453);
+ bytestream_put_be32(&pbc, data);
+ key = (key << 11) | (key >> 21);
+
+ for (i = 4; i < HEADER_SIZE; i += 4) {
+ data = avio_rb32(pb) ^ (key = ((key << 3) | (key >> 29)) + key);
+ bytestream_put_be32(&pbc, data);
+ }
+
+ codec->codec_id = AV_CODEC_ID_ADPCM_THP;
+ codec->codec_type = AVMEDIA_TYPE_AUDIO;
+
+ bytestream2_init(&gbc, header, HEADER_SIZE);
+ bytestream2_seek(&gbc, 0x3c, SEEK_SET);
+ codec->sample_rate = bytestream2_get_be32u(&gbc);
+ if (codec->sample_rate <= 0 || codec->sample_rate > 96000) {
+ av_log(s, AV_LOG_ERROR, "Invalid sample rate: %d\n", codec->sample_rate);
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ st->duration = bytestream2_get_be32u(&gbc) * 14;
+ redspark->samples_count = 0;
+ bytestream2_skipu(&gbc, 10);
+ codec->channels = bytestream2_get_byteu(&gbc);
+ if (!codec->channels) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ coef_off = 0x54 + codec->channels * 8;
+ if (bytestream2_get_byteu(&gbc)) // Loop flag
+ coef_off += 16;
+
+ if (coef_off + codec->channels * (32 + 14) > HEADER_SIZE) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ if (ff_alloc_extradata(codec, 32 * codec->channels)) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ /* Get the ADPCM table */
+ bytestream2_seek(&gbc, coef_off, SEEK_SET);
+ for (i = 0; i < codec->channels; i++) {
+ if (bytestream2_get_bufferu(&gbc, codec->extradata + i * 32, 32) != 32) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ bytestream2_skipu(&gbc, 14);
+ }
+
+ avpriv_set_pts_info(st, 64, 1, codec->sample_rate);
+
+fail:
+ av_free(header);
+
+ return ret;
+}
+
+static int redspark_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecContext *codec = s->streams[0]->codec;
+ RedSparkContext *redspark = s->priv_data;
+ uint32_t size = 8 * codec->channels;
+ int ret;
+
+ if (avio_feof(s->pb) || redspark->samples_count == s->streams[0]->duration)
+ return AVERROR_EOF;
+
+ ret = av_get_packet(s->pb, pkt, size);
+ if (ret != size) {
+ av_free_packet(pkt);
+ return AVERROR(EIO);
+ }
+
+ pkt->duration = 14;
+ redspark->samples_count += pkt->duration;
+ pkt->stream_index = 0;
+
+ return ret;
+}
+
+AVInputFormat ff_redspark_demuxer = {
+ .name = "redspark",
+ .long_name = NULL_IF_CONFIG_SMALL("RedSpark"),
+ .priv_data_size = sizeof(RedSparkContext),
+ .read_probe = redspark_probe,
+ .read_header = redspark_read_header,
+ .read_packet = redspark_read_packet,
+ .extensions = "rsd",
+};
diff --git a/libavformat/replaygain.c b/libavformat/replaygain.c
index 98e7aad81b..807f851542 100644
--- a/libavformat/replaygain.c
+++ b/libavformat/replaygain.c
@@ -1,18 +1,18 @@
/*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/replaygain.h b/libavformat/replaygain.h
index ea56c17f06..ceacb21421 100644
--- a/libavformat/replaygain.h
+++ b/libavformat/replaygain.h
@@ -1,18 +1,18 @@
/*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/riff.c b/libavformat/riff.c
index 776a25fc05..8d7b1c2c61 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -2,20 +2,20 @@
* RIFF common functions and data
* Copyright (c) 2000 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -25,7 +25,10 @@
#include "riff.h"
/* Note: When encoding, the first matching tag is used, so order is
- * important if multiple tags are possible for a given codec. */
+ * important if multiple tags are possible for a given codec.
+ * Note also that this list is used for more than just riff, other
+ * files use it as well.
+ */
const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_H264, MKTAG('H', '2', '6', '4') },
{ AV_CODEC_ID_H264, MKTAG('h', '2', '6', '4') },
@@ -39,6 +42,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_H264, MKTAG('V', '2', '6', '4') }, /* CCTV recordings */
{ AV_CODEC_ID_H264, MKTAG('G', 'A', 'V', 'C') }, /* GeoVision camera */
{ AV_CODEC_ID_H264, MKTAG('U', 'M', 'S', 'V') },
+ { AV_CODEC_ID_H264, MKTAG('I', 'N', 'M', 'C') },
{ AV_CODEC_ID_H263, MKTAG('H', '2', '6', '3') },
{ AV_CODEC_ID_H263, MKTAG('X', '2', '6', '3') },
{ AV_CODEC_ID_H263, MKTAG('T', '2', '6', '3') },
@@ -50,8 +54,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_H263P, MKTAG('H', '2', '6', '3') },
{ AV_CODEC_ID_H263I, MKTAG('I', '2', '6', '3') }, /* Intel H.263 */
{ AV_CODEC_ID_H261, MKTAG('H', '2', '6', '1') },
- { AV_CODEC_ID_H263P, MKTAG('U', '2', '6', '3') },
- { AV_CODEC_ID_H263P, MKTAG('v', 'i', 'v', '1') },
+ { AV_CODEC_ID_H263, MKTAG('U', '2', '6', '3') },
{ AV_CODEC_ID_MPEG4, MKTAG('F', 'M', 'P', '4') },
{ AV_CODEC_ID_MPEG4, MKTAG('D', 'I', 'V', 'X') },
{ AV_CODEC_ID_MPEG4, MKTAG('D', 'X', '5', '0') },
@@ -90,7 +93,6 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_MPEG4, MKTAG('D', 'M', 'K', '2') },
{ AV_CODEC_ID_MPEG4, MKTAG('D', 'Y', 'M', '4') },
{ AV_CODEC_ID_MPEG4, MKTAG('D', 'I', 'G', 'I') },
- { AV_CODEC_ID_MPEG4, MKTAG('I', 'N', 'M', 'C') },
/* Ephv MPEG-4 */
{ AV_CODEC_ID_MPEG4, MKTAG('E', 'P', 'H', 'V') },
{ AV_CODEC_ID_MPEG4, MKTAG('E', 'M', '4', 'A') },
@@ -178,7 +180,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_MJPEG, MKTAG('M', 'J', 'L', 'S') },
{ AV_CODEC_ID_MJPEG, MKTAG('j', 'p', 'e', 'g') },
{ AV_CODEC_ID_MJPEG, MKTAG('I', 'J', 'P', 'G') },
- { AV_CODEC_ID_MJPEG, MKTAG('A', 'V', 'R', 'n') },
+ { AV_CODEC_ID_AVRN, MKTAG('A', 'V', 'R', 'n') },
{ AV_CODEC_ID_MJPEG, MKTAG('A', 'C', 'D', 'V') },
{ AV_CODEC_ID_MJPEG, MKTAG('Q', 'I', 'V', 'G') },
/* SL M-JPEG */
@@ -238,7 +240,11 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_R10K, MKTAG('R', '1', '0', 'k') },
{ AV_CODEC_ID_R210, MKTAG('r', '2', '1', '0') },
{ AV_CODEC_ID_V210, MKTAG('v', '2', '1', '0') },
+ { AV_CODEC_ID_V308, MKTAG('v', '3', '0', '8') },
+ { AV_CODEC_ID_V408, MKTAG('v', '4', '0', '8') },
+ { AV_CODEC_ID_AYUV, MKTAG('A', 'Y', 'U', 'V') },
{ AV_CODEC_ID_V410, MKTAG('v', '4', '1', '0') },
+ { AV_CODEC_ID_YUV4, MKTAG('y', 'u', 'v', '4') },
{ AV_CODEC_ID_INDEO3, MKTAG('I', 'V', '3', '1') },
{ AV_CODEC_ID_INDEO3, MKTAG('I', 'V', '3', '2') },
{ AV_CODEC_ID_INDEO4, MKTAG('I', 'V', '4', '1') },
@@ -276,6 +282,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_TRUEMOTION1, MKTAG('P', 'V', 'E', 'Z') },
{ AV_CODEC_ID_MSZH, MKTAG('M', 'S', 'Z', 'H') },
{ AV_CODEC_ID_ZLIB, MKTAG('Z', 'L', 'I', 'B') },
+ { AV_CODEC_ID_SNOW, MKTAG('S', 'N', 'O', 'W') },
{ AV_CODEC_ID_4XM, MKTAG('4', 'X', 'M', 'V') },
{ AV_CODEC_ID_FLV1, MKTAG('F', 'L', 'V', '1') },
{ AV_CODEC_ID_FLV1, MKTAG('S', '2', '6', '3') },
@@ -326,16 +333,20 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_DPX, MKTAG('d', 'p', 'x', ' ') },
{ AV_CODEC_ID_KGV1, MKTAG('K', 'G', 'V', '1') },
{ AV_CODEC_ID_LAGARITH, MKTAG('L', 'A', 'G', 'S') },
+ { AV_CODEC_ID_AMV, MKTAG('A', 'M', 'V', 'F') },
{ AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'R', 'A') },
{ AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'R', 'G') },
{ AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'Y', '0') },
{ AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'Y', '2') },
+ /* Ut Video version 13.0.1 BT.709 codecs */
{ AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'H', '0') },
{ AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'H', '2') },
+ { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'Q', '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_MSS1, MKTAG('M', 'S', 'S', '1') },
{ AV_CODEC_ID_MSA1, MKTAG('M', 'S', 'A', '1') },
@@ -344,9 +355,12 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_CLLC, MKTAG('C', 'L', 'L', 'C') },
{ AV_CODEC_ID_MSS2, MKTAG('M', 'S', 'S', '2') },
{ AV_CODEC_ID_SVQ3, MKTAG('S', 'V', 'Q', '3') },
+ { AV_CODEC_ID_012V, MKTAG('0', '1', '2', 'v') },
+ { AV_CODEC_ID_012V, MKTAG('a', '1', '2', 'v') },
{ AV_CODEC_ID_G2M, MKTAG('G', '2', 'M', '2') },
{ AV_CODEC_ID_G2M, MKTAG('G', '2', 'M', '3') },
{ AV_CODEC_ID_G2M, MKTAG('G', '2', 'M', '4') },
+ { AV_CODEC_ID_G2M, MKTAG('G', '2', 'M', '5') },
{ AV_CODEC_ID_FIC, MKTAG('F', 'I', 'C', 'V') },
{ AV_CODEC_ID_PRORES, MKTAG('A', 'P', 'C', 'N') },
{ AV_CODEC_ID_NONE, 0 }
@@ -365,13 +379,15 @@ const AVCodecTag ff_codec_wav_tags[] = {
{ AV_CODEC_ID_PCM_ALAW, 0x0006 },
{ AV_CODEC_ID_PCM_MULAW, 0x0007 },
{ AV_CODEC_ID_WMAVOICE, 0x000A },
+ { AV_CODEC_ID_ADPCM_IMA_OKI, 0x0010 },
{ AV_CODEC_ID_ADPCM_IMA_WAV, 0x0011 },
/* must come after adpcm_ima_wav in this list */
{ AV_CODEC_ID_PCM_ZORK, 0x0011 },
+ { AV_CODEC_ID_ADPCM_IMA_OKI, 0x0017 },
{ AV_CODEC_ID_ADPCM_YAMAHA, 0x0020 },
{ AV_CODEC_ID_TRUESPEECH, 0x0022 },
{ AV_CODEC_ID_GSM_MS, 0x0031 },
- { AV_CODEC_ID_GSM_MS, 0x0032 },
+ { AV_CODEC_ID_GSM_MS, 0x0032 }, /* msn audio */
{ AV_CODEC_ID_AMR_NB, 0x0038 }, /* rogue format number */
{ AV_CODEC_ID_G723_1, 0x0042 },
{ AV_CODEC_ID_ADPCM_G726, 0x0045 },
@@ -387,6 +403,7 @@ const AVCodecTag ff_codec_wav_tags[] = {
{ AV_CODEC_ID_ADPCM_IMA_WAV, 0x0069 },
{ AV_CODEC_ID_METASOUND, 0x0075 },
{ AV_CODEC_ID_AAC, 0x00ff },
+ { AV_CODEC_ID_G723_1, 0x0111 },
{ AV_CODEC_ID_SIPR, 0x0130 },
{ AV_CODEC_ID_WMAV1, 0x0160 },
{ AV_CODEC_ID_WMAV2, 0x0161 },
@@ -405,7 +422,11 @@ const AVCodecTag ff_codec_wav_tags[] = {
{ AV_CODEC_ID_AAC, 0x1600 },
{ AV_CODEC_ID_AAC_LATM, 0x1602 },
{ AV_CODEC_ID_AC3, 0x2000 },
+ /* There is no Microsoft Format Tag for E-AC3, the GUID has to be used */
+ { AV_CODEC_ID_EAC3, 0x2000 },
{ AV_CODEC_ID_DTS, 0x2001 },
+ { AV_CODEC_ID_SONIC, 0x2048 },
+ { AV_CODEC_ID_SONIC_LS, 0x2048 },
{ AV_CODEC_ID_PCM_MULAW, 0x6c75 },
{ AV_CODEC_ID_AAC, 0x706d },
{ AV_CODEC_ID_AAC, 0x4143 },
@@ -446,3 +467,11 @@ const struct AVCodecTag *avformat_get_riff_audio_tags(void)
{
return ff_codec_wav_tags;
}
+
+const AVCodecGuid ff_codec_wav_guids[] = {
+ { AV_CODEC_ID_AC3, { 0x2C, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA } },
+ { AV_CODEC_ID_ATRAC3P, { 0xBF, 0xAA, 0x23, 0xE9, 0x58, 0xCB, 0x71, 0x44, 0xA1, 0x19, 0xFF, 0xFA, 0x01, 0xE4, 0xCE, 0x62 } },
+ { AV_CODEC_ID_EAC3, { 0xAF, 0x87, 0xFB, 0xA7, 0x02, 0x2D, 0xFB, 0x42, 0xA4, 0xD4, 0x05, 0xCD, 0x93, 0x84, 0x3B, 0xDD } },
+ { AV_CODEC_ID_MP2, { 0x2B, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA } },
+ { AV_CODEC_ID_NONE }
+};
diff --git a/libavformat/riff.h b/libavformat/riff.h
index ddfb0fa664..15b07a66e0 100644
--- a/libavformat/riff.h
+++ b/libavformat/riff.h
@@ -2,20 +2,20 @@
* RIFF common functions and data
* copyright (c) 2000 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -43,14 +43,28 @@ void ff_end_tag(AVIOContext *pb, int64_t start);
* bits_per_encoded_sample fields. Does not read extradata.
* @return codec tag
*/
-int ff_get_bmp_header(AVIOContext *pb, AVStream *st);
+int ff_get_bmp_header(AVIOContext *pb, AVStream *st, unsigned *esize);
+
+void ff_put_bmp_header(AVIOContext *pb, AVCodecContext *enc, const AVCodecTag *tags, int for_asf, int ignore_extradata);
+
+/**
+ * Tell ff_put_wav_header() to use WAVEFORMATEX even for PCM codecs.
+ */
+#define FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX 0x00000001
+
+/**
+ * Write WAVEFORMAT header structure.
+ *
+ * @param flags a combination of FF_PUT_WAV_HEADER_* constants
+ *
+ * @return the size or -1 on error
+ */
+int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc, int flags);
-void ff_put_bmp_header(AVIOContext *pb, AVCodecContext *enc, const AVCodecTag *tags, int for_asf);
-int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc);
enum AVCodecID ff_wav_codec_get_id(unsigned int tag, int bps);
-int ff_get_wav_header(AVIOContext *pb, AVCodecContext *codec, int size);
+int ff_get_wav_header(AVIOContext *pb, AVCodecContext *codec, int size, int big_endian);
-extern const AVCodecTag ff_codec_bmp_tags[];
+extern const AVCodecTag ff_codec_bmp_tags[]; // exposed through avformat_get_riff_video_tags()
extern const AVCodecTag ff_codec_wav_tags[];
void ff_parse_specific_params(AVStream *st, int *au_rate, int *au_ssize, int *au_scale);
@@ -77,10 +91,13 @@ typedef struct AVCodecGuid {
extern const AVCodecGuid ff_codec_wav_guids[];
#define FF_PRI_GUID \
- "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
+ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x " \
+ "{%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],\
+ g[3], g[2], g[1], g[0], g[5], g[4], g[7], g[6], \
g[8], g[9], g[10], g[11], g[12], g[13], g[14], g[15]
#define FF_MEDIASUBTYPE_BASE_GUID \
@@ -91,10 +108,9 @@ static av_always_inline int ff_guidcmp(const void *g1, const void *g2)
return memcmp(g1, g2, sizeof(ff_asf_guid));
}
-static av_always_inline int ff_get_guid(AVIOContext *s, ff_asf_guid *g)
-{
- return avio_read(s, *g, sizeof(*g));
-}
+int ff_get_guid(AVIOContext *s, ff_asf_guid *g);
+void ff_put_guid(AVIOContext *s, const ff_asf_guid *g);
+const ff_asf_guid *get_codec_guid(enum AVCodecID id, const AVCodecGuid *av_guid);
enum AVCodecID ff_codec_guid_get_id(const AVCodecGuid *guids, ff_asf_guid guid);
diff --git a/libavformat/riffdec.c b/libavformat/riffdec.c
index 74f93acedf..f44df1e672 100644
--- a/libavformat/riffdec.c
+++ b/libavformat/riffdec.c
@@ -2,20 +2,20 @@
* RIFF demuxing functions and data
* Copyright (c) 2000 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -29,13 +29,15 @@
#include "avio_internal.h"
#include "riff.h"
-const AVCodecGuid ff_codec_wav_guids[] = {
- { AV_CODEC_ID_AC3, { 0x2C, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA } },
- { AV_CODEC_ID_ATRAC3P, { 0xBF, 0xAA, 0x23, 0xE9, 0x58, 0xCB, 0x71, 0x44, 0xA1, 0x19, 0xFF, 0xFA, 0x01, 0xE4, 0xCE, 0x62 } },
- { AV_CODEC_ID_EAC3, { 0xAF, 0x87, 0xFB, 0xA7, 0x02, 0x2D, 0xFB, 0x42, 0xA4, 0xD4, 0x05, 0xCD, 0x93, 0x84, 0x3B, 0xDD } },
- { AV_CODEC_ID_MP2, { 0x2B, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA } },
- { AV_CODEC_ID_NONE }
-};
+int ff_get_guid(AVIOContext *s, ff_asf_guid *g)
+{
+ av_assert0(sizeof(*g) == 16); //compiler will optimize this out
+ if (avio_read(s, *g, sizeof(*g)) < (int)sizeof(*g)) {
+ memset(*g, 0, sizeof(*g));
+ return AVERROR_INVALIDDATA;
+ }
+ return 0;
+}
enum AVCodecID ff_codec_guid_get_id(const AVCodecGuid *guids, ff_asf_guid guid)
{
@@ -57,7 +59,10 @@ enum AVCodecID ff_codec_guid_get_id(const AVCodecGuid *guids, ff_asf_guid guid)
static void parse_waveformatex(AVIOContext *pb, AVCodecContext *c)
{
ff_asf_guid subformat;
- c->bits_per_coded_sample = avio_rl16(pb);
+ int bps = avio_rl16(pb);
+ if (bps)
+ c->bits_per_coded_sample = bps;
+
c->channel_layout = avio_rl32(pb); /* dwChannelMask */
ff_get_guid(pb, &subformat);
@@ -75,20 +80,37 @@ static void parse_waveformatex(AVIOContext *pb, AVCodecContext *c)
}
}
-int ff_get_wav_header(AVIOContext *pb, AVCodecContext *codec, int size)
+/* "big_endian" values are needed for RIFX file format */
+int ff_get_wav_header(AVIOContext *pb, AVCodecContext *codec, int size, int big_endian)
{
int id;
- id = avio_rl16(pb);
+ if (size < 14)
+ avpriv_request_sample(codec, "wav header size < 14");
+
codec->codec_type = AVMEDIA_TYPE_AUDIO;
- codec->channels = avio_rl16(pb);
- codec->sample_rate = avio_rl32(pb);
- codec->bit_rate = avio_rl32(pb) * 8;
- codec->block_align = avio_rl16(pb);
+ if (!big_endian) {
+ id = avio_rl16(pb);
+ codec->channels = avio_rl16(pb);
+ codec->sample_rate = avio_rl32(pb);
+ codec->bit_rate = avio_rl32(pb) * 8;
+ codec->block_align = avio_rl16(pb);
+ } else {
+ id = avio_rb16(pb);
+ codec->channels = avio_rb16(pb);
+ codec->sample_rate = avio_rb32(pb);
+ codec->bit_rate = avio_rb32(pb) * 8;
+ codec->block_align = avio_rb16(pb);
+ }
if (size == 14) { /* We're dealing with plain vanilla WAVEFORMAT */
codec->bits_per_coded_sample = 8;
- } else
- codec->bits_per_coded_sample = avio_rl16(pb);
+ } else {
+ if (!big_endian) {
+ codec->bits_per_coded_sample = avio_rl16(pb);
+ } else {
+ codec->bits_per_coded_sample = avio_rb16(pb);
+ }
+ }
if (id == 0xFFFE) {
codec->codec_tag = 0;
} else {
@@ -98,6 +120,10 @@ int ff_get_wav_header(AVIOContext *pb, AVCodecContext *codec, int size)
}
if (size >= 18) { /* We're obviously dealing with WAVEFORMATEX */
int cbSize = avio_rl16(pb); /* cbSize */
+ if (big_endian) {
+ avpriv_report_missing_feature(codec, "WAVEFORMATEX support for RIFX files\n");
+ return AVERROR_PATCHWELCOME;
+ }
size -= 18;
cbSize = FFMIN(size, cbSize);
if (cbSize >= 22 && id == 0xfffe) { /* WAVEFORMATEXTENSIBLE */
@@ -105,14 +131,10 @@ int ff_get_wav_header(AVIOContext *pb, AVCodecContext *codec, int size)
cbSize -= 22;
size -= 22;
}
- codec->extradata_size = cbSize;
if (cbSize > 0) {
- av_free(codec->extradata);
- codec->extradata = av_mallocz(codec->extradata_size +
- FF_INPUT_BUFFER_PADDING_SIZE);
- if (!codec->extradata)
+ av_freep(&codec->extradata);
+ if (ff_get_extradata(codec, pb, cbSize) < 0)
return AVERROR(ENOMEM);
- avio_read(pb, codec->extradata, codec->extradata_size);
size -= cbSize;
}
@@ -132,7 +154,7 @@ int ff_get_wav_header(AVIOContext *pb, AVCodecContext *codec, int size)
codec->sample_rate = 0;
}
/* override bits_per_coded_sample for G.726 */
- if (codec->codec_id == AV_CODEC_ID_ADPCM_G726)
+ if (codec->codec_id == AV_CODEC_ID_ADPCM_G726 && codec->sample_rate)
codec->bits_per_coded_sample = codec->bit_rate / codec->sample_rate;
return 0;
@@ -155,10 +177,11 @@ enum AVCodecID ff_wav_codec_get_id(unsigned int tag, int bps)
return id;
}
-int ff_get_bmp_header(AVIOContext *pb, AVStream *st)
+int ff_get_bmp_header(AVIOContext *pb, AVStream *st, unsigned *esize)
{
int tag1;
- avio_rl32(pb); /* size */
+ if(esize) *esize = avio_rl32(pb);
+ else avio_rl32(pb);
st->codec->width = avio_rl32(pb);
st->codec->height = (int32_t)avio_rl32(pb);
avio_rl16(pb); /* planes */
@@ -189,12 +212,23 @@ int ff_read_riff_info(AVFormatContext *s, int64_t size)
chunk_code = avio_rl32(pb);
chunk_size = avio_rl32(pb);
-
+ if (avio_feof(pb)) {
+ if (chunk_code || chunk_size) {
+ av_log(s, AV_LOG_WARNING, "INFO subchunk truncated\n");
+ return AVERROR_INVALIDDATA;
+ }
+ return AVERROR_EOF;
+ }
if (chunk_size > end ||
end - chunk_size < cur ||
chunk_size == UINT_MAX) {
- av_log(s, AV_LOG_WARNING, "too big INFO subchunk\n");
- break;
+ avio_seek(pb, -9, SEEK_CUR);
+ chunk_code = avio_rl32(pb);
+ chunk_size = avio_rl32(pb);
+ if (chunk_size > end || end - chunk_size < cur || chunk_size == UINT_MAX) {
+ av_log(s, AV_LOG_WARNING, "too big INFO subchunk\n");
+ return AVERROR_INVALIDDATA;
+ }
}
chunk_size += (chunk_size & 1);
@@ -209,7 +243,7 @@ int ff_read_riff_info(AVFormatContext *s, int64_t size)
continue;
}
- value = av_malloc(chunk_size + 1);
+ value = av_mallocz(chunk_size + 1);
if (!value) {
av_log(s, AV_LOG_ERROR,
"out of memory, unable to read INFO tag\n");
@@ -219,14 +253,10 @@ int ff_read_riff_info(AVFormatContext *s, int64_t size)
AV_WL32(key, chunk_code);
if (avio_read(pb, value, chunk_size) != chunk_size) {
- av_free(value);
av_log(s, AV_LOG_WARNING,
"premature end of file while reading INFO tag\n");
- break;
}
- value[chunk_size] = 0;
-
av_dict_set(&s->metadata, key, value, AV_DICT_DONT_STRDUP_VAL);
}
diff --git a/libavformat/riffenc.c b/libavformat/riffenc.c
index 81b3b2da12..5e858008f5 100644
--- a/libavformat/riffenc.c
+++ b/libavformat/riffenc.c
@@ -2,20 +2,20 @@
* RIFF muxing functions
* Copyright (c) 2000 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -31,7 +31,7 @@
int64_t ff_start_tag(AVIOContext *pb, const char *tag)
{
ffio_wfourcc(pb, tag);
- avio_wl32(pb, 0);
+ avio_wl32(pb, -1);
return avio_tell(pb);
}
@@ -39,18 +39,23 @@ void ff_end_tag(AVIOContext *pb, int64_t start)
{
int64_t pos;
+ av_assert0((start&1) == 0);
+
pos = avio_tell(pb);
+ if (pos & 1)
+ avio_w8(pb, 0);
avio_seek(pb, start - 4, SEEK_SET);
avio_wl32(pb, (uint32_t)(pos - start));
- avio_seek(pb, pos, SEEK_SET);
+ avio_seek(pb, FFALIGN(pos, 2), SEEK_SET);
}
/* WAVEFORMATEX header */
/* returns the size or -1 on error */
-int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc)
+int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc, int flags)
{
int bps, blkalign, bytespersec, frame_size;
- int hdrsize = 18;
+ int hdrsize;
+ int64_t hdrstart = avio_tell(pb);
int waveformatextensible;
uint8_t temp[256];
uint8_t *riff_extradata = temp;
@@ -66,6 +71,7 @@ int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc)
waveformatextensible = (enc->channels > 2 && enc->channel_layout) ||
enc->sample_rate > 48000 ||
+ enc->codec_id == AV_CODEC_ID_EAC3 ||
av_get_bits_per_sample(enc->codec_id) > 16;
if (waveformatextensible)
@@ -75,8 +81,10 @@ int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc)
avio_wl16(pb, enc->channels);
avio_wl32(pb, enc->sample_rate);
- if (enc->codec_id == AV_CODEC_ID_MP2 ||
- enc->codec_id == AV_CODEC_ID_MP3 ||
+ if (enc->codec_id == AV_CODEC_ID_ATRAC3 ||
+ enc->codec_id == AV_CODEC_ID_G723_1 ||
+ enc->codec_id == AV_CODEC_ID_MP2 ||
+ enc->codec_id == AV_CODEC_ID_MP3 ||
enc->codec_id == AV_CODEC_ID_GSM_MS) {
bps = 0;
} else {
@@ -97,9 +105,13 @@ int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc)
if (enc->codec_id == AV_CODEC_ID_MP2) {
blkalign = frame_size;
} else if (enc->codec_id == AV_CODEC_ID_MP3) {
- blkalign = 576 * (enc->sample_rate <= 24000 ? 1 : 2);
+ blkalign = 576 * (enc->sample_rate <= (24000 + 32000)/2 ? 1 : 2);
} 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
@@ -111,6 +123,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;
}
@@ -118,14 +132,12 @@ int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc)
avio_wl16(pb, blkalign); /* block align */
avio_wl16(pb, bps); /* bits per sample */
if (enc->codec_id == AV_CODEC_ID_MP3) {
- hdrsize += 12;
bytestream_put_le16(&riff_extradata, 1); /* wID */
bytestream_put_le32(&riff_extradata, 2); /* fdwFlags */
bytestream_put_le16(&riff_extradata, 1152); /* nBlockSize */
bytestream_put_le16(&riff_extradata, 1); /* nFramesPerBlock */
bytestream_put_le16(&riff_extradata, 1393); /* nCodecDelay */
} else if (enc->codec_id == AV_CODEC_ID_MP2) {
- hdrsize += 22;
/* fwHeadLayer */
bytestream_put_le16(&riff_extradata, 2);
/* dwHeadBitrate */
@@ -142,34 +154,45 @@ int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc)
bytestream_put_le32(&riff_extradata, 0);
/* dwPTSHigh */
bytestream_put_le32(&riff_extradata, 0);
+ } else if (enc->codec_id == AV_CODEC_ID_G723_1) {
+ bytestream_put_le32(&riff_extradata, 0x9ace0002); /* extradata needed for msacm g723.1 codec */
+ bytestream_put_le32(&riff_extradata, 0xaea2f732);
+ bytestream_put_le16(&riff_extradata, 0xacde);
} else if (enc->codec_id == AV_CODEC_ID_GSM_MS ||
enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV) {
- hdrsize += 2;
/* wSamplesPerBlock */
bytestream_put_le16(&riff_extradata, frame_size);
} else if (enc->extradata_size) {
riff_extradata_start = enc->extradata;
riff_extradata = enc->extradata + enc->extradata_size;
- hdrsize += enc->extradata_size;
}
/* write WAVEFORMATEXTENSIBLE extensions */
if (waveformatextensible) {
- hdrsize += 22;
+ int write_channel_mask = enc->strict_std_compliance < FF_COMPLIANCE_NORMAL ||
+ enc->channel_layout < 0x40000;
/* 22 is WAVEFORMATEXTENSIBLE size */
avio_wl16(pb, riff_extradata - riff_extradata_start + 22);
/* ValidBitsPerSample || SamplesPerBlock || Reserved */
avio_wl16(pb, bps);
/* dwChannelMask */
- avio_wl32(pb, enc->channel_layout);
+ avio_wl32(pb, write_channel_mask ? enc->channel_layout : 0);
/* GUID + next 3 */
+ if (enc->codec_id == AV_CODEC_ID_EAC3) {
+ ff_put_guid(pb, get_codec_guid(enc->codec_id, ff_codec_wav_guids));
+ } else {
avio_wl32(pb, enc->codec_tag);
avio_wl32(pb, 0x00100000);
avio_wl32(pb, 0xAA000080);
avio_wl32(pb, 0x719B3800);
- } else {
+ }
+ } else if ((flags & FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX) ||
+ enc->codec_tag != 0x0001 /* PCM */ ||
+ riff_extradata - riff_extradata_start) {
+ /* WAVEFORMATEX */
avio_wl16(pb, riff_extradata - riff_extradata_start); /* cbSize */
- }
+ } /* else PCMWAVEFORMAT */
avio_write(pb, riff_extradata_start, riff_extradata - riff_extradata_start);
+ hdrsize = avio_tell(pb) - hdrstart;
if (hdrsize & 1) {
hdrsize++;
avio_w8(pb, 0);
@@ -180,29 +203,35 @@ int ff_put_wav_header(AVIOContext *pb, AVCodecContext *enc)
/* BITMAPINFOHEADER header */
void ff_put_bmp_header(AVIOContext *pb, AVCodecContext *enc,
- const AVCodecTag *tags, int for_asf)
+ const AVCodecTag *tags, int for_asf, int ignore_extradata)
{
+ int keep_height = enc->extradata_size >= 9 &&
+ !memcmp(enc->extradata + enc->extradata_size - 9, "BottomUp", 9);
+ int extradata_size = enc->extradata_size - 9*keep_height;
+
/* size */
- avio_wl32(pb, 40 + enc->extradata_size);
+ avio_wl32(pb, 40 + (ignore_extradata ? 0 :extradata_size));
avio_wl32(pb, enc->width);
//We always store RGB TopDown
- avio_wl32(pb, enc->codec_tag ? enc->height : -enc->height);
+ avio_wl32(pb, enc->codec_tag || keep_height ? enc->height : -enc->height);
/* planes */
avio_wl16(pb, 1);
/* depth */
avio_wl16(pb, enc->bits_per_coded_sample ? enc->bits_per_coded_sample : 24);
/* compression type */
avio_wl32(pb, enc->codec_tag);
- avio_wl32(pb, enc->width * enc->height * 3);
+ avio_wl32(pb, (enc->width * enc->height * (enc->bits_per_coded_sample ? enc->bits_per_coded_sample : 24)+7) / 8);
avio_wl32(pb, 0);
avio_wl32(pb, 0);
avio_wl32(pb, 0);
avio_wl32(pb, 0);
- avio_write(pb, enc->extradata, enc->extradata_size);
+ if (!ignore_extradata) {
+ avio_write(pb, enc->extradata, extradata_size);
- if (!for_asf && enc->extradata_size & 1)
- avio_w8(pb, 0);
+ if (!for_asf && extradata_size & 1)
+ avio_w8(pb, 0);
+ }
}
void ff_parse_specific_params(AVStream *st, int *au_rate,
@@ -290,3 +319,19 @@ void ff_riff_write_info(AVFormatContext *s)
ff_riff_write_info_tag(s->pb, t->key, t->value);
ff_end_tag(pb, list_pos);
}
+
+void ff_put_guid(AVIOContext *s, const ff_asf_guid *g)
+{
+ av_assert0(sizeof(*g) == 16);
+ avio_write(s, *g, sizeof(*g));
+}
+
+const ff_asf_guid *get_codec_guid(enum AVCodecID id, const AVCodecGuid *av_guid)
+{
+ int i;
+ for (i = 0; av_guid[i].id != AV_CODEC_ID_NONE; i++) {
+ if (id == av_guid[i].id)
+ return &(av_guid[i].guid);
+ }
+ return NULL;
+}
diff --git a/libavformat/rl2.c b/libavformat/rl2.c
index 5d30bf8bd6..d354339ea3 100644
--- a/libavformat/rl2.c
+++ b/libavformat/rl2.c
@@ -2,20 +2,20 @@
* RL2 Format Demuxer
* Copyright (c) 2008 Sascha Sommer (saschasommer@freenet.de)
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -109,10 +109,6 @@ static av_cold int rl2_read_header(AVFormatContext *s)
rate = avio_rl16(pb);
channels = avio_rl16(pb);
def_sound_size = avio_rl16(pb);
- if (!channels || channels > 42) {
- av_log(s, AV_LOG_ERROR, "Invalid number of channels: %d\n", channels);
- return AVERROR_INVALIDDATA;
- }
/** setup video stream */
st = avformat_new_stream(s, NULL);
@@ -131,17 +127,16 @@ static av_cold int rl2_read_header(AVFormatContext *s)
if(signature == RLV3_TAG && back_size > 0)
st->codec->extradata_size += back_size;
- st->codec->extradata = av_mallocz(st->codec->extradata_size +
- FF_INPUT_BUFFER_PADDING_SIZE);
- if(!st->codec->extradata)
+ if(ff_get_extradata(st->codec, pb, st->codec->extradata_size) < 0)
return AVERROR(ENOMEM);
- if(avio_read(pb,st->codec->extradata,st->codec->extradata_size) !=
- st->codec->extradata_size)
- return AVERROR(EIO);
-
/** setup audio stream if present */
if(sound_rate){
+ if (!channels || channels > 42) {
+ av_log(s, AV_LOG_ERROR, "Invalid number of channels: %d\n", channels);
+ return AVERROR_INVALIDDATA;
+ }
+
pts_num = def_sound_size;
pts_den = rate;
@@ -235,7 +230,7 @@ static int rl2_read_packet(AVFormatContext *s,
}
if(stream_id == -1)
- return AVERROR(EIO);
+ return AVERROR_EOF;
++rl2->index_pos[stream_id];
diff --git a/libavformat/rm.c b/libavformat/rm.c
index 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 3aa17732f1..7b080e2a36 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,
- unsigned int codec_data_size);
+ unsigned 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 cae89a9c5a..c0afa7fb6f 100644
--- a/libavformat/rmdec.c
+++ b/libavformat/rmdec.c
@@ -2,31 +2,33 @@
* "Real" compatible demuxer.
* Copyright (c) 2000, 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <inttypes.h>
+#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/internal.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
#include "avformat.h"
+#include "avio_internal.h"
#include "internal.h"
#include "rmsipr.h"
#include "rm.h"
@@ -63,6 +65,8 @@ typedef struct RMDemuxContext {
int audio_pkt_cnt; ///< Output packet counter
} RMDemuxContext;
+static int rm_read_close(AVFormatContext *s);
+
static inline void get_strl(AVIOContext *pb, char *buf, int buf_size, int len)
{
int i;
@@ -84,14 +88,12 @@ static void get_str8(AVIOContext *pb, char *buf, int buf_size)
static int rm_read_extradata(AVIOContext *pb, AVCodecContext *avctx, unsigned size)
{
- if (size >= 1<<24)
+ if (size >= 1<<24) {
+ av_log(avctx, AV_LOG_ERROR, "extradata size %u too large\n", size);
return -1;
- avctx->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!avctx->extradata)
+ }
+ if (ff_get_extradata(avctx, pb, size) < 0)
return AVERROR(ENOMEM);
- avctx->extradata_size = avio_read(pb, avctx->extradata, size);
- if (avctx->extradata_size != size)
- return AVERROR(EIO);
return 0;
}
@@ -99,6 +101,7 @@ static void rm_read_metadata(AVFormatContext *s, AVIOContext *pb, int wide)
{
char buf[1024];
int i;
+
for (i=0; i<FF_ARRAY_ELEMS(ff_rm_metadata); i++) {
int len = wide ? avio_rb16(pb) : avio_r8(pb);
get_strl(pb, buf, sizeof(buf), len);
@@ -128,9 +131,12 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb,
/* ra type header */
version = avio_rb16(pb); /* version */
if (version == 3) {
+ unsigned bytes_per_minute;
int header_size = avio_rb16(pb);
int64_t startpos = avio_tell(pb);
- avio_skip(pb, 14);
+ avio_skip(pb, 8);
+ bytes_per_minute = avio_rb16(pb);
+ avio_skip(pb, 4);
rm_read_metadata(s, pb, 0);
if ((startpos + header_size) >= avio_tell(pb) + 2) {
// fourcc (should always be "lpcJ")
@@ -140,6 +146,8 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb,
// Skip extra header crap (this should never happen)
if ((startpos + header_size) > avio_tell(pb))
avio_skip(pb, header_size + startpos - avio_tell(pb));
+ if (bytes_per_minute)
+ st->codec->bit_rate = 8LL * bytes_per_minute / 60;
st->codec->sample_rate = 8000;
st->codec->channels = 1;
st->codec->channel_layout = AV_CH_LAYOUT_MONO;
@@ -149,6 +157,7 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb,
} else {
int flavor, sub_packet_h, coded_framesize, sub_packet_size;
int codecdata_length;
+ unsigned bytes_per_minute;
/* old version (4) */
avio_skip(pb, 2); /* unused */
avio_rb32(pb); /* .ra4 */
@@ -158,7 +167,11 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb,
flavor= avio_rb16(pb); /* add codec info / flavor */
ast->coded_framesize = coded_framesize = avio_rb32(pb); /* coded frame size */
avio_rb32(pb); /* ??? */
- avio_rb32(pb); /* ??? */
+ bytes_per_minute = avio_rb32(pb);
+ if (version == 4) {
+ if (bytes_per_minute)
+ st->codec->bit_rate = 8LL * bytes_per_minute / 60;
+ }
avio_rb32(pb); /* ??? */
ast->sub_packet_h = sub_packet_h = avio_rb16(pb); /* 1 */
st->codec->block_align= avio_rb16(pb); /* frame size */
@@ -175,6 +188,7 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb,
avio_read(pb, buf, 4);
buf[4] = 0;
} else {
+ AV_WL32(buf, 0);
get_str8(pb, buf, sizeof(buf)); /* desc */
ast->deint_id = AV_RL32(buf);
get_str8(pb, buf, sizeof(buf)); /* desc */
@@ -197,13 +211,17 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb,
st->need_parsing = AVSTREAM_PARSE_HEADERS;
case AV_CODEC_ID_ATRAC3:
case AV_CODEC_ID_SIPR:
- avio_rb16(pb); avio_r8(pb);
- if (version == 5)
- avio_r8(pb);
- codecdata_length = avio_rb32(pb);
- if(codecdata_length + FF_INPUT_BUFFER_PADDING_SIZE <= (unsigned)codecdata_length){
- av_log(s, AV_LOG_ERROR, "codecdata_length too large\n");
- return -1;
+ if (read_all) {
+ codecdata_length = 0;
+ } else {
+ avio_rb16(pb); avio_r8(pb);
+ if (version == 5)
+ avio_r8(pb);
+ codecdata_length = avio_rb32(pb);
+ if(codecdata_length + FF_INPUT_BUFFER_PADDING_SIZE <= (unsigned)codecdata_length){
+ av_log(s, AV_LOG_ERROR, "codecdata_length too large\n");
+ return -1;
+ }
}
ast->audio_framesize = st->codec->block_align;
@@ -223,6 +241,7 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb,
}
if ((ret = rm_read_extradata(pb, st->codec, codecdata_length)) < 0)
return ret;
+
break;
case AV_CODEC_ID_AAC:
avio_rb16(pb); avio_r8(pb);
@@ -240,27 +259,23 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb,
}
break;
}
- if (ast->deint_id == DEINT_ID_INT4 ||
- ast->deint_id == DEINT_ID_GENR ||
- ast->deint_id == DEINT_ID_SIPR) {
- if (st->codec->block_align <= 0 ||
- ast->audio_framesize * sub_packet_h > (unsigned)INT_MAX ||
- ast->audio_framesize * sub_packet_h < st->codec->block_align)
- return AVERROR_INVALIDDATA;
- if (av_new_packet(&ast->pkt, ast->audio_framesize * sub_packet_h) < 0)
- return AVERROR(ENOMEM);
- }
switch (ast->deint_id) {
case DEINT_ID_INT4:
if (ast->coded_framesize > ast->audio_framesize ||
sub_packet_h <= 1 ||
ast->coded_framesize * sub_packet_h > (2 + (sub_packet_h & 1)) * ast->audio_framesize)
return AVERROR_INVALIDDATA;
+ if (ast->coded_framesize * sub_packet_h != 2*ast->audio_framesize) {
+ avpriv_request_sample(s, "mismatching interleaver parameters");
+ return AVERROR_INVALIDDATA;
+ }
break;
case DEINT_ID_GENR:
if (ast->sub_packet_size <= 0 ||
ast->sub_packet_size > ast->audio_framesize)
return AVERROR_INVALIDDATA;
+ if (ast->audio_framesize % ast->sub_packet_size)
+ return AVERROR_INVALIDDATA;
break;
case DEINT_ID_SIPR:
case DEINT_ID_INT0:
@@ -268,9 +283,19 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb,
case DEINT_ID_VBRF:
break;
default:
- av_log(NULL, 0 ,"Unknown interleaver %"PRIX32"\n", ast->deint_id);
+ av_log(s, AV_LOG_ERROR ,"Unknown interleaver %"PRIX32"\n", ast->deint_id);
return AVERROR_INVALIDDATA;
}
+ if (ast->deint_id == DEINT_ID_INT4 ||
+ ast->deint_id == DEINT_ID_GENR ||
+ ast->deint_id == DEINT_ID_SIPR) {
+ if (st->codec->block_align <= 0 ||
+ ast->audio_framesize * sub_packet_h > (unsigned)INT_MAX ||
+ ast->audio_framesize * sub_packet_h < st->codec->block_align)
+ return AVERROR_INVALIDDATA;
+ if (av_new_packet(&ast->pkt, ast->audio_framesize * sub_packet_h) < 0)
+ return AVERROR(ENOMEM);
+ }
if (read_all) {
avio_r8(pb);
@@ -284,16 +309,34 @@ 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,
- unsigned int codec_data_size)
+ unsigned int codec_data_size, const uint8_t *mime)
{
unsigned int v;
int size;
int64_t codec_pos;
int ret;
+ if (codec_data_size > INT_MAX)
+ return AVERROR_INVALIDDATA;
+
avpriv_set_pts_info(st, 64, 1, 1000);
codec_pos = avio_tell(pb);
v = avio_rb32(pb);
+
+ if (v == MKBETAG('M', 'L', 'T', 'I')) {
+ int number_of_streams = avio_rb16(pb);
+ int number_of_mdpr;
+ int i;
+ for (i = 0; i<number_of_streams; i++)
+ avio_rb16(pb);
+ number_of_mdpr = avio_rb16(pb);
+ if (number_of_mdpr != 1) {
+ avpriv_request_sample(s, "MLTI with multiple MDPR");
+ }
+ avio_rb32(pb);
+ v = avio_rb32(pb);
+ }
+
if (v == MKTAG(0xfd, 'a', 'r', '.')) {
/* ra type header */
if (rm_read_audio_stream_info(s, pb, st, rst, 0))
@@ -307,11 +350,38 @@ int ff_rm_read_mdpr_codecdata(AVFormatContext *s, AVIOContext *pb,
st->codec->codec_tag = AV_RL32(st->codec->extradata);
st->codec->codec_id = ff_codec_get_id(ff_rm_codec_tags,
st->codec->codec_tag);
+ } else if(mime && !strcmp(mime, "logical-fileinfo")){
+ int stream_count, rule_count, property_count, i;
+ ff_free_stream(s, st);
+ if (avio_rb16(pb) != 0) {
+ av_log(s, AV_LOG_WARNING, "Unsupported version\n");
+ goto skip;
+ }
+ stream_count = avio_rb16(pb);
+ avio_skip(pb, 6*stream_count);
+ rule_count = avio_rb16(pb);
+ avio_skip(pb, 2*rule_count);
+ property_count = avio_rb16(pb);
+ for(i=0; i<property_count; i++){
+ uint8_t name[128], val[128];
+ avio_rb32(pb);
+ if (avio_rb16(pb) != 0) {
+ av_log(s, AV_LOG_WARNING, "Unsupported Name value property version\n");
+ goto skip; //FIXME skip just this one
+ }
+ get_str8(pb, name, sizeof(name));
+ switch(avio_rb32(pb)) {
+ case 2: get_strl(pb, val, sizeof(val), avio_rb16(pb));
+ av_dict_set(&s->metadata, name, val, 0);
+ break;
+ default: avio_skip(pb, avio_rb16(pb));
+ }
+ }
} else {
int fps;
if (avio_rl32(pb) != MKTAG('V', 'I', 'D', 'O')) {
fail1:
- av_log(st->codec, AV_LOG_ERROR, "Unsupported video codec\n");
+ av_log(s, AV_LOG_WARNING, "Unsupported stream type %08x\n", v);
goto skip;
}
st->codec->codec_tag = avio_rl32(pb);
@@ -334,6 +404,9 @@ int ff_rm_read_mdpr_codecdata(AVFormatContext *s, AVIOContext *pb,
if (fps > 0) {
av_reduce(&st->avg_frame_rate.den, &st->avg_frame_rate.num,
0x10000, fps, (1 << 30) - 1);
+#if FF_API_R_FRAME_RATE
+ st->r_frame_rate = st->avg_frame_rate;
+#endif
} else if (s->error_recognition & AV_EF_EXPLODE) {
av_log(s, AV_LOG_ERROR, "Invalid framerate\n");
return AVERROR_INVALIDDATA;
@@ -343,7 +416,11 @@ int ff_rm_read_mdpr_codecdata(AVFormatContext *s, AVIOContext *pb,
skip:
/* skip codec info */
size = avio_tell(pb) - codec_pos;
- avio_skip(pb, codec_data_size - size);
+ if (codec_data_size >= size) {
+ avio_skip(pb, codec_data_size - size);
+ } else {
+ av_log(s, AV_LOG_WARNING, "codec_data_size %u < size %d\n", codec_data_size, size);
+ }
return 0;
}
@@ -428,8 +505,9 @@ 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;
+ int ret = -1;
tag = avio_rl32(pb);
if (tag == MKTAG('.', 'r', 'a', 0xfd)) {
@@ -443,8 +521,8 @@ static int rm_read_header(AVFormatContext *s)
avio_skip(pb, tag_size - 8);
for(;;) {
- if (pb->eof_reached)
- return -1;
+ if (avio_feof(pb))
+ goto fail;
tag = avio_rl32(pb);
tag_size = avio_rb32(pb);
avio_rb16(pb);
@@ -456,7 +534,7 @@ static int rm_read_header(AVFormatContext *s)
tag,
tag_size);
if (tag_size < 10 && tag != MKTAG('D', 'A', 'T', 'A'))
- return -1;
+ goto fail;
switch(tag) {
case MKTAG('P', 'R', 'O', 'P'):
/* file header */
@@ -465,7 +543,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 */
@@ -477,8 +556,10 @@ static int rm_read_header(AVFormatContext *s)
break;
case MKTAG('M', 'D', 'P', 'R'):
st = avformat_new_stream(s, NULL);
- if (!st)
- return AVERROR(ENOMEM);
+ if (!st) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
st->id = avio_rb16(pb);
avio_rb32(pb); /* max bit rate */
st->codec->bit_rate = avio_rb32(pb); /* bit rate */
@@ -489,13 +570,15 @@ 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)
- return -1;
+ avio_rb32(pb), mime) < 0)
+ goto fail;
break;
case MKTAG('D', 'A', 'T', 'A'):
goto header_end;
@@ -520,6 +603,10 @@ static int rm_read_header(AVFormatContext *s)
}
return 0;
+
+fail:
+ rm_read_close(s);
+ return ret;
}
static int get_num(AVIOContext *pb, int *len)
@@ -541,13 +628,13 @@ static int get_num(AVIOContext *pb, int *len)
/* multiple of 20 bytes for ra144 (ugly) */
#define RAW_PACKET_SIZE 1000
-static int sync(AVFormatContext *s, int64_t *timestamp, int *flags, int *stream_index, int64_t *pos){
+static int rm_sync(AVFormatContext *s, int64_t *timestamp, int *flags, int *stream_index, int64_t *pos){
RMDemuxContext *rm = s->priv_data;
AVIOContext *pb = s->pb;
AVStream *st;
uint32_t state=0xFFFFFFFF;
- while(!pb->eof_reached){
+ while(!avio_feof(pb)){
int len, num, i;
*pos= avio_tell(pb) - 3;
if(rm->remaining_len > 0){
@@ -614,8 +701,10 @@ static int rm_assemble_video_frame(AVFormatContext *s, AVIOContext *pb,
AVPacket *pkt, int len, int *pseq,
int64_t *timestamp)
{
- int hdr, seq, pic_num, len2, pos;
+ int hdr;
+ int seq = 0, pic_num = 0, len2 = 0, pos = 0; //init to silcense compiler warning
int type;
+ int ret;
hdr = avio_r8(pb); len--;
type = hdr >> 6;
@@ -628,34 +717,47 @@ static int rm_assemble_video_frame(AVFormatContext *s, AVIOContext *pb,
pos = get_num(pb, &len);
pic_num = avio_r8(pb); len--;
}
- if(len<0)
+ if(len<0) {
+ av_log(s, AV_LOG_ERROR, "Insufficient data\n");
return -1;
+ }
rm->remaining_len = len;
if(type&1){ // frame, not slice
if(type == 3){ // frame as a part of packet
len= len2;
*timestamp = pos;
}
- if(rm->remaining_len < len)
+ if(rm->remaining_len < len) {
+ av_log(s, AV_LOG_ERROR, "Insufficient remaining len\n");
return -1;
+ }
rm->remaining_len -= len;
if(av_new_packet(pkt, len + 9) < 0)
return AVERROR(EIO);
pkt->data[0] = 0;
AV_WL32(pkt->data + 1, 1);
AV_WL32(pkt->data + 5, 0);
- avio_read(pb, pkt->data + 9, len);
+ if ((ret = avio_read(pb, pkt->data + 9, len)) != len) {
+ av_free_packet(pkt);
+ av_log(s, AV_LOG_ERROR, "Failed to read %d bytes\n", len);
+ return ret < 0 ? ret : AVERROR(EIO);
+ }
return 0;
}
//now we have to deal with single slice
*pseq = seq;
if((seq & 0x7F) == 1 || vst->curpic_num != pic_num){
+ if (len2 > ffio_limit(pb, len2)) {
+ av_log(s, AV_LOG_ERROR, "Impossibly sized packet\n");
+ return AVERROR_INVALIDDATA;
+ }
vst->slices = ((hdr & 0x3F) << 1) + 1;
vst->videobufsize = len2 + 8*vst->slices + 1;
av_free_packet(&vst->pkt); //FIXME this should be output.
if(av_new_packet(&vst->pkt, vst->videobufsize) < 0)
return AVERROR(ENOMEM);
+ memset(vst->pkt.data, 0, vst->pkt.size);
vst->videobufpos = 8*vst->slices + 1;
vst->cur_slice = 0;
vst->curpic_num = pic_num;
@@ -664,12 +766,18 @@ static int rm_assemble_video_frame(AVFormatContext *s, AVIOContext *pb,
if(type == 2)
len = FFMIN(len, pos);
- if(++vst->cur_slice > vst->slices)
+ if(++vst->cur_slice > vst->slices) {
+ av_log(s, AV_LOG_ERROR, "cur slice %d, too large\n", vst->cur_slice);
return 1;
+ }
+ if(!vst->pkt.data)
+ return AVERROR(ENOMEM);
AV_WL32(vst->pkt.data - 7 + 8*vst->cur_slice, 1);
AV_WL32(vst->pkt.data - 3 + 8*vst->cur_slice, vst->videobufpos - 8*vst->slices - 1);
- if(vst->videobufpos + len > vst->videobufsize)
+ if(vst->videobufpos + len > vst->videobufsize) {
+ av_log(s, AV_LOG_ERROR, "outside videobufsize\n");
return 1;
+ }
if (avio_read(pb, vst->pkt.data + vst->videobufpos, len) != len)
return AVERROR(EIO);
vst->videobufpos += len;
@@ -714,17 +822,29 @@ rm_ac3_swap_bytes (AVStream *st, AVPacket *pkt)
}
}
+static int readfull(AVFormatContext *s, AVIOContext *pb, uint8_t *dst, int n) {
+ int ret = avio_read(pb, dst, n);
+ if (ret != n) {
+ if (ret >= 0) memset(dst + ret, 0, n - ret);
+ else memset(dst , 0, n);
+ av_log(s, AV_LOG_ERROR, "Failed to fully read block\n");
+ }
+ return ret;
+}
+
int
ff_rm_parse_packet (AVFormatContext *s, AVIOContext *pb,
AVStream *st, RMStream *ast, int len, AVPacket *pkt,
int *seq, int flags, int64_t timestamp)
{
RMDemuxContext *rm = s->priv_data;
+ int ret;
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
rm->current_stream= st->id;
- if(rm_assemble_video_frame(s, pb, rm, ast, pkt, len, seq, &timestamp))
- return -1; //got partial frame
+ ret = rm_assemble_video_frame(s, pb, rm, ast, pkt, len, seq, &timestamp);
+ if(ret)
+ return ret < 0 ? ret : -1; //got partial frame or error
} else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
if ((ast->deint_id == DEINT_ID_GENR) ||
(ast->deint_id == DEINT_ID_INT4) ||
@@ -744,14 +864,14 @@ ff_rm_parse_packet (AVFormatContext *s, AVIOContext *pb,
switch (ast->deint_id) {
case DEINT_ID_INT4:
for (x = 0; x < h/2; x++)
- avio_read(pb, ast->pkt.data+x*2*w+y*cfs, cfs);
+ readfull(s, pb, ast->pkt.data+x*2*w+y*cfs, cfs);
break;
case DEINT_ID_GENR:
for (x = 0; x < w/sps; x++)
- avio_read(pb, ast->pkt.data+sps*(h*x+((h+1)/2)*(y&1)+(y>>1)), sps);
+ readfull(s, pb, ast->pkt.data+sps*(h*x+((h+1)/2)*(y&1)+(y>>1)), sps);
break;
case DEINT_ID_SIPR:
- avio_read(pb, ast->pkt.data + y * w, w);
+ readfull(s, pb, ast->pkt.data + y * w, w);
break;
}
@@ -776,11 +896,14 @@ ff_rm_parse_packet (AVFormatContext *s, AVIOContext *pb,
} else
return -1;
} else {
- av_get_packet(pb, pkt, len);
+ if ((ret = av_get_packet(pb, pkt, len)) < 0)
+ return ret;
rm_ac3_swap_bytes(st, pkt);
}
- } else
- av_get_packet(pb, pkt, len);
+ } else {
+ if ((ret = av_get_packet(pb, pkt, len)) < 0)
+ return ret;
+ }
pkt->stream_index = st->index;
@@ -810,12 +933,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 {
+ ast->deint_id == DEINT_ID_VBRS) {
+ int ret = av_get_packet(pb, pkt, ast->sub_packet_lengths[ast->sub_packet_cnt - rm->audio_pkt_cnt]);
+ if (ret < 0)
+ return ret;
+ } else {
int ret = av_new_packet(pkt, st->codec->block_align);
if (ret < 0)
return ret;
@@ -837,7 +962,7 @@ ff_rm_retrieve_cache (AVFormatContext *s, AVIOContext *pb,
static int rm_read_packet(AVFormatContext *s, AVPacket *pkt)
{
RMDemuxContext *rm = s->priv_data;
- AVStream *st;
+ AVStream *st = NULL; // init to silence compiler warning
int i, len, res, seq = 1;
int64_t timestamp, pos;
int flags;
@@ -846,7 +971,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) {
@@ -860,16 +987,18 @@ static int rm_read_packet(AVFormatContext *s, AVPacket *pkt)
flags = (seq++ == 1) ? 2 : 0;
pos = avio_tell(s->pb);
} else {
- len=sync(s, &timestamp, &flags, &i, &pos);
+ len = rm_sync(s, &timestamp, &flags, &i, &pos);
if (len > 0)
st = s->streams[i];
}
- if (len <= 0 || s->pb->eof_reached)
+ if (len <= 0 || avio_feof(s->pb))
return AVERROR(EIO);
res = ff_rm_parse_packet (s, s->pb, st, st->priv_data, len, pkt,
&seq, flags, timestamp);
+ if (res < -1)
+ return res;
if((flags&2) && (seq&0x7F) == 1)
av_add_index_entry(st, pos, timestamp, 0, 0, AVINDEX_KEYFRAME);
if (res)
@@ -921,13 +1050,15 @@ 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;
AVStream *st;
- len=sync(s, &dts, &flags, &stream_index2, &pos);
+ len = rm_sync(s, &dts, &flags, &stream_index2, &pos);
if(len<0)
return AV_NOPTS_VALUE;
@@ -953,6 +1084,18 @@ static int64_t rm_read_dts(AVFormatContext *s, int stream_index,
return dts;
}
+static int rm_read_seek(AVFormatContext *s, int stream_index,
+ int64_t pts, int flags)
+{
+ RMDemuxContext *rm = s->priv_data;
+
+ if (ff_seek_frame_binary(s, stream_index, pts, flags) < 0)
+ return -1;
+ rm->audio_pkt_cnt = 0;
+ return 0;
+}
+
+
AVInputFormat ff_rm_demuxer = {
.name = "rm",
.long_name = NULL_IF_CONFIG_SMALL("RealMedia"),
@@ -962,6 +1105,7 @@ AVInputFormat ff_rm_demuxer = {
.read_packet = rm_read_packet,
.read_close = rm_read_close,
.read_timestamp = rm_read_dts,
+ .read_seek = rm_read_seek,
};
AVInputFormat ff_rdt_demuxer = {
diff --git a/libavformat/rmenc.c b/libavformat/rmenc.c
index 6d8fa43467..d019e1beab 100644
--- a/libavformat/rmenc.c
+++ b/libavformat/rmenc.c
@@ -2,20 +2,20 @@
* "Real" compatible muxer.
* Copyright (c) 2000, 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "avformat.h"
@@ -217,8 +217,8 @@ static int rv10_write_header(AVFormatContext *ctx,
coded_frame_size--;
avio_wb32(s, coded_frame_size); /* frame length */
avio_wb32(s, 0x51540); /* unknown */
- avio_wb32(s, 0x249f0); /* unknown */
- avio_wb32(s, 0x249f0); /* unknown */
+ avio_wb32(s, stream->enc->bit_rate / 8 * 60); /* bytes per minute */
+ avio_wb32(s, stream->enc->bit_rate / 8 * 60); /* bytes per minute */
avio_wb16(s, 0x01);
/* frame length : seems to be very important */
avio_wb16(s, coded_frame_size);
@@ -309,6 +309,11 @@ static int rm_write_header(AVFormatContext *s)
int n;
AVCodecContext *codec;
+ if (s->nb_streams > 2) {
+ av_log(s, AV_LOG_ERROR, "At most 2 streams are currently supported for muxing in RM\n");
+ return AVERROR_PATCHWELCOME;
+ }
+
for(n=0;n<s->nb_streams;n++) {
AVStream *st = s->streams[n];
diff --git a/libavformat/rmsipr.c b/libavformat/rmsipr.c
index 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 b72b8d0691..04cb917aef 100644
--- a/libavformat/rpl.c
+++ b/libavformat/rpl.c
@@ -2,20 +2,20 @@
* ARMovie/RPL demuxer
* Copyright (c) 2007 Christian Ohm, 2008 Eli Friedman
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -60,7 +60,7 @@ static int read_line(AVIOContext * pb, char* line, int bufsize)
break;
if (b == '\n') {
line[i] = '\0';
- return 0;
+ return avio_feof(pb) ? -1 : 0;
}
line[i] = b;
}
@@ -170,9 +170,8 @@ static int rpl_read_header(AVFormatContext *s)
vst->codec->codec_id = AV_CODEC_ID_ESCAPE130;
break;
default:
- av_log(s, AV_LOG_WARNING,
- "RPL video format %i not supported yet!\n",
- vst->codec->codec_tag);
+ avpriv_report_missing_feature(s, "Video format %i",
+ vst->codec->codec_tag);
vst->codec->codec_id = AV_CODEC_ID_NONE;
}
@@ -222,11 +221,9 @@ static int rpl_read_header(AVFormatContext *s)
}
break;
}
- if (ast->codec->codec_id == AV_CODEC_ID_NONE) {
- av_log(s, AV_LOG_WARNING,
- "RPL audio format %"PRId32" not supported yet!\n",
- audio_format);
- }
+ if (ast->codec->codec_id == AV_CODEC_ID_NONE)
+ avpriv_request_sample(s, "Audio format %"PRId32,
+ audio_format);
avpriv_set_pts_info(ast, 32, 1, ast->codec->bit_rate);
} else {
for (i = 0; i < 3; i++)
@@ -254,12 +251,14 @@ static int rpl_read_header(AVFormatContext *s)
// Read the index
avio_seek(pb, chunk_catalog_offset, SEEK_SET);
total_audio_size = 0;
- for (i = 0; i < number_of_chunks; i++) {
+ for (i = 0; !error && i < number_of_chunks; i++) {
int64_t offset, video_size, audio_size;
error |= read_line(pb, line, sizeof(line));
- if (3 != sscanf(line, "%"PRId64" , %"PRId64" ; %"PRId64,
- &offset, &video_size, &audio_size))
+ if (3 != sscanf(line, "%"SCNd64" , %"SCNd64" ; %"SCNd64,
+ &offset, &video_size, &audio_size)) {
error = -1;
+ continue;
+ }
av_add_index_entry(vst, offset, i * rpl->frames_per_chunk,
video_size, rpl->frames_per_chunk, 0);
if (ast)
@@ -279,7 +278,7 @@ static int rpl_read_packet(AVFormatContext *s, AVPacket *pkt)
AVIOContext *pb = s->pb;
AVStream* stream;
AVIndexEntry* index_entry;
- uint32_t ret;
+ int ret;
if (rpl->chunk_part == s->nb_streams) {
rpl->chunk_number++;
@@ -289,7 +288,7 @@ static int rpl_read_packet(AVFormatContext *s, AVPacket *pkt)
stream = s->streams[rpl->chunk_part];
if (rpl->chunk_number >= stream->nb_index_entries)
- return -1;
+ return AVERROR_EOF;
index_entry = &stream->index_entries[rpl->chunk_number];
@@ -309,6 +308,8 @@ static int rpl_read_packet(AVFormatContext *s, AVPacket *pkt)
return AVERROR(EIO);
ret = av_get_packet(pb, pkt, frame_size);
+ if (ret < 0)
+ return ret;
if (ret != frame_size) {
av_free_packet(pkt);
return AVERROR(EIO);
@@ -324,6 +325,8 @@ static int rpl_read_packet(AVFormatContext *s, AVPacket *pkt)
}
} else {
ret = av_get_packet(pb, pkt, index_entry->size);
+ if (ret < 0)
+ return ret;
if (ret != index_entry->size) {
av_free_packet(pkt);
return AVERROR(EIO);
diff --git a/libavformat/rsd.c b/libavformat/rsd.c
new file mode 100644
index 0000000000..1eff5de7e6
--- /dev/null
+++ b/libavformat/rsd.c
@@ -0,0 +1,168 @@
+/*
+ * RSD demuxer
+ * Copyright (c) 2013 James Almer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavcodec/bytestream.h"
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "avio.h"
+#include "internal.h"
+
+static const AVCodecTag rsd_tags[] = {
+ { AV_CODEC_ID_ADPCM_THP, MKTAG('G','A','D','P') },
+ { AV_CODEC_ID_ADPCM_IMA_RAD, MKTAG('R','A','D','P') },
+ { AV_CODEC_ID_PCM_S16BE, MKTAG('P','C','M','B') },
+ { AV_CODEC_ID_PCM_S16LE, MKTAG('P','C','M',' ') },
+ { AV_CODEC_ID_NONE, 0 },
+};
+
+static const uint32_t rsd_unsupported_tags[] = {
+ MKTAG('O','G','G',' '),
+ MKTAG('V','A','G',' '),
+ MKTAG('W','A','D','P'),
+ MKTAG('X','A','D','P'),
+ MKTAG('X','M','A',' '),
+};
+
+static int rsd_probe(AVProbeData *p)
+{
+ if (memcmp(p->buf, "RSD", 3) || p->buf[3] - '0' < 2 || p->buf[3] - '0' > 6)
+ return 0;
+ if (AV_RL32(p->buf + 8) > 256 || !AV_RL32(p->buf + 8))
+ return AVPROBE_SCORE_MAX / 8;
+ if (AV_RL32(p->buf + 16) > 8*48000 || !AV_RL32(p->buf + 16))
+ return AVPROBE_SCORE_MAX / 8;
+ return AVPROBE_SCORE_MAX;
+}
+
+static int rsd_read_header(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ int i, version, start = 0x800;
+ AVCodecContext *codec;
+ AVStream *st = avformat_new_stream(s, NULL);
+
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avio_skip(pb, 3); // "RSD"
+ version = avio_r8(pb) - '0';
+
+ codec = st->codec;
+ codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ codec->codec_tag = avio_rl32(pb);
+ codec->codec_id = ff_codec_get_id(rsd_tags, codec->codec_tag);
+ if (!codec->codec_id) {
+ char tag_buf[32];
+
+ av_get_codec_tag_string(tag_buf, sizeof(tag_buf), codec->codec_tag);
+ for (i=0; i < FF_ARRAY_ELEMS(rsd_unsupported_tags); i++) {
+ if (codec->codec_tag == rsd_unsupported_tags[i]) {
+ avpriv_request_sample(s, "Codec tag: %s", tag_buf);
+ return AVERROR_PATCHWELCOME;
+ }
+ }
+ av_log(s, AV_LOG_ERROR, "Unknown codec tag: %s\n", tag_buf);
+ return AVERROR_INVALIDDATA;
+ }
+
+ codec->channels = avio_rl32(pb);
+ if (!codec->channels)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(pb, 4); // Bit depth
+ codec->sample_rate = avio_rl32(pb);
+ if (!codec->sample_rate)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(pb, 4); // Unknown
+
+ switch (codec->codec_id) {
+ case AV_CODEC_ID_ADPCM_IMA_RAD:
+ codec->block_align = 20 * codec->channels;
+ if (pb->seekable)
+ st->duration = av_get_audio_frame_duration(codec, avio_size(pb) - start);
+ break;
+ case AV_CODEC_ID_ADPCM_THP:
+ /* RSD3GADP is mono, so only alloc enough memory
+ to store the coeff table for a single channel. */
+
+ start = avio_rl32(pb);
+
+ if (ff_get_extradata(codec, s->pb, 32) < 0)
+ return AVERROR(ENOMEM);
+
+ for (i = 0; i < 16; i++)
+ AV_WB16(codec->extradata + i * 2, AV_RL16(codec->extradata + i * 2));
+
+ if (pb->seekable)
+ st->duration = (avio_size(pb) - start) / 8 * 14;
+ break;
+ case AV_CODEC_ID_PCM_S16LE:
+ case AV_CODEC_ID_PCM_S16BE:
+ if (version != 4)
+ start = avio_rl32(pb);
+
+ if (pb->seekable)
+ st->duration = (avio_size(pb) - start) / 2 / codec->channels;
+ break;
+ }
+
+ avio_skip(pb, start - avio_tell(pb));
+
+ avpriv_set_pts_info(st, 64, 1, codec->sample_rate);
+
+ return 0;
+}
+
+static int rsd_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecContext *codec = s->streams[0]->codec;
+ int ret, size = 1024;
+
+ if (avio_feof(s->pb))
+ return AVERROR_EOF;
+
+ if (codec->codec_id == AV_CODEC_ID_ADPCM_IMA_RAD)
+ ret = av_get_packet(s->pb, pkt, codec->block_align);
+ else
+ ret = av_get_packet(s->pb, pkt, size);
+
+ if (ret != size) {
+ if (ret < 0) {
+ av_free_packet(pkt);
+ return ret;
+ }
+ av_shrink_packet(pkt, ret);
+ }
+ pkt->stream_index = 0;
+
+ return ret;
+}
+
+AVInputFormat ff_rsd_demuxer = {
+ .name = "rsd",
+ .long_name = NULL_IF_CONFIG_SMALL("GameCube RSD"),
+ .read_probe = rsd_probe,
+ .read_header = rsd_read_header,
+ .read_packet = rsd_read_packet,
+ .extensions = "rsd",
+ .codec_tag = (const AVCodecTag* const []){rsd_tags, 0},
+};
diff --git a/libavformat/rso.c b/libavformat/rso.c
index 4ce61e7a10..7d88f0f5bc 100644
--- a/libavformat/rso.c
+++ b/libavformat/rso.c
@@ -2,20 +2,20 @@
* RSO format common data
* Copyright (c) 2010 Rafael Carre
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rso.h b/libavformat/rso.h
index e3e88ea644..1f65dd90c7 100644
--- a/libavformat/rso.h
+++ b/libavformat/rso.h
@@ -2,20 +2,20 @@
* RSO format common data
* Copyright (c) 2010 Rafael Carre
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rsodec.c b/libavformat/rsodec.c
index 1612572b56..6e3ea0a153 100644
--- a/libavformat/rsodec.c
+++ b/libavformat/rsodec.c
@@ -3,20 +3,20 @@
* Copyright (c) 2001 Fabrice Bellard (original AU code)
* Copyright (c) 2010 Rafael Carre
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -65,36 +65,19 @@ static int rso_read_header(AVFormatContext *s)
st->codec->channels = 1;
st->codec->channel_layout = AV_CH_LAYOUT_MONO;
st->codec->sample_rate = rate;
+ st->codec->block_align = 1;
avpriv_set_pts_info(st, 64, 1, rate);
return 0;
}
-#define BLOCK_SIZE 1024 /* in samples */
-
-static int rso_read_packet(AVFormatContext *s, AVPacket *pkt)
-{
- int bps = av_get_bits_per_sample(s->streams[0]->codec->codec_id);
- int ret = av_get_packet(s->pb, pkt, BLOCK_SIZE * bps >> 3);
-
- if (ret < 0)
- return ret;
-
- pkt->stream_index = 0;
-
- /* note: we need to modify the packet size here to handle the last packet */
- pkt->size = ret;
-
- return 0;
-}
-
AVInputFormat ff_rso_demuxer = {
.name = "rso",
.long_name = NULL_IF_CONFIG_SMALL("Lego Mindstorms RSO"),
.extensions = "rso",
.read_header = rso_read_header,
- .read_packet = rso_read_packet,
+ .read_packet = ff_pcm_read_packet,
.read_seek = ff_pcm_read_seek,
.codec_tag = (const AVCodecTag* const []){ff_codec_rso_tags, 0},
};
diff --git a/libavformat/rsoenc.c b/libavformat/rsoenc.c
index 8ebcf8193b..6da9594fcb 100644
--- a/libavformat/rsoenc.c
+++ b/libavformat/rsoenc.c
@@ -3,20 +3,20 @@
* Copyright (c) 2001 Fabrice Bellard (original AU code)
* Copyright (c) 2010 Rafael Carre
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtmp.h b/libavformat/rtmp.h
index dc7ba5bdeb..8fc8040d89 100644
--- a/libavformat/rtmp.h
+++ b/libavformat/rtmp.h
@@ -2,20 +2,20 @@
* RTMP definitions
* Copyright (c) 2009 Konstantin Shishkov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtmpcrypt.c b/libavformat/rtmpcrypt.c
index dfdd029a53..2312527d37 100644
--- a/libavformat/rtmpcrypt.c
+++ b/libavformat/rtmpcrypt.c
@@ -4,20 +4,20 @@
* Copyright (c) 2009-2010 Howard Chu
* Copyright (c) 2012 Samuel Pitoiset
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtmpcrypt.h b/libavformat/rtmpcrypt.h
index 27994333df..590a8c81b8 100644
--- a/libavformat/rtmpcrypt.h
+++ b/libavformat/rtmpcrypt.h
@@ -2,20 +2,20 @@
* RTMPE encryption utilities
* Copyright (c) 2012 Samuel Pitoiset
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtmpdh.c b/libavformat/rtmpdh.c
index 38c2f3df63..7d6734daac 100644
--- a/libavformat/rtmpdh.c
+++ b/libavformat/rtmpdh.c
@@ -4,20 +4,20 @@
* Copyright (c) 2009-2010 Howard Chu
* Copyright (c) 2012 Samuel Pitoiset
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtmpdh.h b/libavformat/rtmpdh.h
index 5de8bde821..cf262fc245 100644
--- a/libavformat/rtmpdh.h
+++ b/libavformat/rtmpdh.h
@@ -2,20 +2,20 @@
* RTMP Diffie-Hellmann utilities
* Copyright (c) 2012 Samuel Pitoiset
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtmphttp.c b/libavformat/rtmphttp.c
index 34c68fb437..0334ba558c 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 f8c51d01fa..c474fb3d58 100644
--- a/libavformat/rtmppkt.c
+++ b/libavformat/rtmppkt.c
@@ -2,20 +2,20 @@
* RTMP input format
* Copyright (c) 2009 Konstantin Shishkov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -279,6 +279,7 @@ static int rtmp_packet_read_one_chunk(URLContext *h, RTMPPacket *p,
prev->data = p->data;
prev->read = p->read;
prev->offset = p->offset;
+ p->data = NULL;
return AVERROR(EAGAIN);
}
diff --git a/libavformat/rtmppkt.h b/libavformat/rtmppkt.h
index 149c153cad..a082b45f98 100644
--- a/libavformat/rtmppkt.h
+++ b/libavformat/rtmppkt.h
@@ -2,20 +2,20 @@
* RTMP packet utilities
* Copyright (c) 2009 Konstantin Shishkov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
index be87b4d4c1..3d7cdaab2d 100644
--- a/libavformat/rtmpproto.c
+++ b/libavformat/rtmpproto.c
@@ -2,20 +2,20 @@
* RTMP network protocol
* Copyright (c) 2009 Konstantin Shishkov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -48,7 +48,7 @@
#include <zlib.h>
#endif
-#define APP_MAX_LENGTH 128
+#define APP_MAX_LENGTH 1024
#define PLAYPATH_MAX_LENGTH 256
#define TCURL_MAX_LENGTH 512
#define FLASHVER_MAX_LENGTH 64
@@ -217,9 +217,8 @@ static void free_tracked_methods(RTMPContext *rt)
int i;
for (i = 0; i < rt->nb_tracked_methods; i ++)
- av_free(rt->tracked_methods[i].name);
- av_free(rt->tracked_methods);
- rt->tracked_methods = NULL;
+ av_freep(&rt->tracked_methods[i].name);
+ av_freep(&rt->tracked_methods);
rt->tracked_methods_size = 0;
rt->nb_tracked_methods = 0;
}
@@ -322,7 +321,7 @@ static int gen_connect(URLContext *s, RTMPContext *rt)
int ret;
if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
- 0, 4096)) < 0)
+ 0, 4096 + APP_MAX_LENGTH)) < 0)
return ret;
p = pkt.data;
@@ -419,6 +418,7 @@ static int read_connect(URLContext *s, RTMPContext *rt)
if (pkt.type == RTMP_PT_CHUNK_SIZE) {
if ((ret = handle_chunk_size(s, &pkt)) < 0)
return ret;
+
ff_rtmp_packet_destroy(&pkt);
if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->in_chunk_size,
&rt->prev_pkt[0], &rt->nb_prev_pkt[0])) < 0)
@@ -1767,18 +1767,23 @@ static int handle_connect_error(URLContext *s, const char *desc)
char *value = strchr(ptr, '=');
if (next)
*next++ = '\0';
- if (value)
+ if (value) {
*value++ = '\0';
- if (!strcmp(ptr, "user")) {
- user = value;
- } else if (!strcmp(ptr, "salt")) {
- salt = value;
- } else if (!strcmp(ptr, "opaque")) {
- opaque = value;
- } else if (!strcmp(ptr, "challenge")) {
- challenge = value;
- } else if (!strcmp(ptr, "nonce")) {
- nonce = value;
+ if (!strcmp(ptr, "user")) {
+ user = value;
+ } else if (!strcmp(ptr, "salt")) {
+ salt = value;
+ } else if (!strcmp(ptr, "opaque")) {
+ opaque = value;
+ } else if (!strcmp(ptr, "challenge")) {
+ challenge = value;
+ } else if (!strcmp(ptr, "nonce")) {
+ nonce = value;
+ } else {
+ av_log(s, AV_LOG_INFO, "Ignoring unsupported var %s\n", ptr);
+ }
+ } else {
+ av_log(s, AV_LOG_WARNING, "Variable %s has NULL value\n", ptr);
}
ptr = next;
}
@@ -2429,7 +2434,7 @@ static int get_packet(URLContext *s, int for_header)
rt->last_timestamp = rpkt.timestamp;
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;
@@ -2546,7 +2551,7 @@ static int inject_fake_duration_metadata(RTMPContext *rt)
// Increase the size by the injected packet
rt->flv_size += 55;
// Delete the old FLV data
- av_free(old_flv_data);
+ av_freep(&old_flv_data);
p = rt->flv_data + 13;
bytestream_put_byte(&p, FLV_TAG_TYPE_META);
@@ -2591,7 +2596,7 @@ static int rtmp_open(URLContext *s, const char *uri, int flags)
{
RTMPContext *rt = s->priv_data;
char proto[8], hostname[256], path[1024], auth[100], *fname;
- char *old_app, *qmark, fname_buffer[1024];
+ char *old_app, *qmark, *n, fname_buffer[1024];
uint8_t buf[2048];
int port;
AVDictionary *opts = NULL;
@@ -2606,11 +2611,13 @@ static int rtmp_open(URLContext *s, const char *uri, int flags)
hostname, sizeof(hostname), &port,
path, sizeof(path), s->filename);
- if (strchr(path, ' ')) {
+ n = strchr(path, ' ');
+ if (n) {
av_log(s, AV_LOG_WARNING,
"Detected librtmp style URL parameters, these aren't supported "
"by the libavformat internal RTMP handler currently enabled. "
"See the documentation for the correct way to pass parameters.\n");
+ *n = '\0'; // Trim not supported part
}
if (auth[0]) {
@@ -2709,8 +2716,14 @@ reconnect:
char *next = *path ? path + 1 : path;
char *p = strchr(next, '/');
if (!p) {
- fname = next;
- rt->app[0] = '\0';
+ if (old_app) {
+ // If name of application has been defined by the user, assume that
+ // playpath is provided in the URL
+ fname = next;
+ } else {
+ fname = NULL;
+ av_strlcpy(rt->app, next, APP_MAX_LENGTH);
+ }
} else {
// make sure we do not mismatch a playpath for an application instance
char *c = strchr(p + 1, ':');
@@ -2727,29 +2740,36 @@ reconnect:
if (old_app) {
// The name of application has been defined by the user, override it.
+ if (strlen(old_app) >= APP_MAX_LENGTH) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
av_free(rt->app);
rt->app = old_app;
}
if (!rt->playpath) {
- int len = strlen(fname);
-
rt->playpath = av_malloc(PLAYPATH_MAX_LENGTH);
if (!rt->playpath) {
ret = AVERROR(ENOMEM);
goto fail;
}
- if (!strchr(fname, ':') && len >= 4 &&
- (!strcmp(fname + len - 4, ".f4v") ||
- !strcmp(fname + len - 4, ".mp4"))) {
- memcpy(rt->playpath, "mp4:", 5);
+ if (fname) {
+ int len = strlen(fname);
+ if (!strchr(fname, ':') && len >= 4 &&
+ (!strcmp(fname + len - 4, ".f4v") ||
+ !strcmp(fname + len - 4, ".mp4"))) {
+ memcpy(rt->playpath, "mp4:", 5);
+ } else {
+ if (len >= 4 && !strcmp(fname + len - 4, ".flv"))
+ fname[len - 4] = '\0';
+ rt->playpath[0] = 0;
+ }
+ av_strlcat(rt->playpath, fname, PLAYPATH_MAX_LENGTH);
} else {
- if (len >= 4 && !strcmp(fname + len - 4, ".flv"))
- fname[len - 4] = '\0';
- rt->playpath[0] = 0;
+ rt->playpath[0] = '\0';
}
- av_strlcat(rt->playpath, fname, PLAYPATH_MAX_LENGTH);
}
if (!rt->tcurl) {
@@ -2951,6 +2971,7 @@ static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
if (rt->flv_header_bytes < RTMP_HEADER) {
const uint8_t *header = rt->flv_header;
int channel = RTMP_AUDIO_CHANNEL;
+
copy = FFMIN(RTMP_HEADER - rt->flv_header_bytes, size_temp);
bytestream_get_buffer(&buf_temp, rt->flv_header + rt->flv_header_bytes, copy);
rt->flv_header_bytes += copy;
diff --git a/libavformat/rtp.c b/libavformat/rtp.c
index 0a3c411f51..4d41350f3c 100644
--- a/libavformat/rtp.c
+++ b/libavformat/rtp.c
@@ -2,23 +2,24 @@
* RTP input/output format
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/avstring.h"
#include "libavutil/opt.h"
#include "avformat.h"
@@ -144,7 +145,7 @@ enum AVCodecID ff_rtp_codec_id(const char *buf, enum AVMediaType codec_type)
int i;
for (i = 0; rtp_payload_types[i].pt >= 0; i++)
- if (!strcmp(buf, rtp_payload_types[i].enc_name) && (codec_type == rtp_payload_types[i].codec_type))
+ if (!av_strcasecmp(buf, rtp_payload_types[i].enc_name) && (codec_type == rtp_payload_types[i].codec_type))
return rtp_payload_types[i].codec_id;
return AV_CODEC_ID_NONE;
diff --git a/libavformat/rtp.h b/libavformat/rtp.h
index feaf167965..66a4f3afbd 100644
--- a/libavformat/rtp.h
+++ b/libavformat/rtp.h
@@ -2,20 +2,20 @@
* RTP definitions
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef AVFORMAT_RTP_H
diff --git a/libavformat/rtpdec.c b/libavformat/rtpdec.c
index 97f142815a..550da4a282 100644
--- a/libavformat/rtpdec.c
+++ b/libavformat/rtpdec.c
@@ -2,20 +2,20 @@
* RTP input format
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -32,6 +32,12 @@
#define MIN_FEEDBACK_INTERVAL 200000 /* 200 ms in us */
+static RTPDynamicProtocolHandler gsm_dynamic_handler = {
+ .enc_name = "GSM",
+ .codec_type = AVMEDIA_TYPE_AUDIO,
+ .codec_id = AV_CODEC_ID_GSM,
+};
+
static RTPDynamicProtocolHandler realmedia_mp3_dynamic_handler = {
.enc_name = "X-MP3-draft-00",
.codec_type = AVMEDIA_TYPE_AUDIO,
@@ -50,6 +56,12 @@ static RTPDynamicProtocolHandler opus_dynamic_handler = {
.codec_id = AV_CODEC_ID_OPUS,
};
+static RTPDynamicProtocolHandler ff_t140_dynamic_handler = {
+ .enc_name = "t140",
+ .codec_type = AVMEDIA_TYPE_SUBTITLE,
+ .codec_id = AV_CODEC_ID_SUBRIP,
+};
+
static RTPDynamicProtocolHandler *rtp_first_dynamic_payload_handler = NULL;
void ff_register_dynamic_payload_handler(RTPDynamicProtocolHandler *handler)
@@ -60,6 +72,7 @@ void ff_register_dynamic_payload_handler(RTPDynamicProtocolHandler *handler)
void ff_register_rtp_dynamic_payload_handlers(void)
{
+ ff_register_dynamic_payload_handler(&ff_ac3_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_amr_nb_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_amr_wb_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_g726_16_dynamic_handler);
@@ -77,6 +90,7 @@ void ff_register_rtp_dynamic_payload_handlers(void)
ff_register_dynamic_payload_handler(&ff_mp4a_latm_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_mp4v_es_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_mpeg_audio_dynamic_handler);
+ ff_register_dynamic_payload_handler(&ff_mpeg_audio_robust_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_mpeg_video_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_mpeg4_generic_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_mpegts_dynamic_handler);
@@ -89,9 +103,11 @@ void ff_register_rtp_dynamic_payload_handlers(void)
ff_register_dynamic_payload_handler(&ff_quicktime_rtp_aud_handler);
ff_register_dynamic_payload_handler(&ff_quicktime_rtp_vid_handler);
ff_register_dynamic_payload_handler(&ff_svq3_dynamic_handler);
+ ff_register_dynamic_payload_handler(&ff_t140_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_theora_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_vorbis_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_vp8_dynamic_handler);
+ ff_register_dynamic_payload_handler(&gsm_dynamic_handler);
ff_register_dynamic_payload_handler(&opus_dynamic_handler);
ff_register_dynamic_payload_handler(&realmedia_mp3_dynamic_handler);
ff_register_dynamic_payload_handler(&speex_dynamic_handler);
@@ -659,8 +675,8 @@ void ff_rtp_reset_packet_queue(RTPDemuxContext *s)
{
while (s->queue) {
RTPPacket *next = s->queue->next;
- av_free(s->queue->buf);
- av_free(s->queue);
+ av_freep(&s->queue->buf);
+ av_freep(&s->queue);
s->queue = next;
}
s->seq = 0;
@@ -718,8 +734,8 @@ static int rtp_parse_queued_packet(RTPDemuxContext *s, AVPacket *pkt)
/* Parse the first packet in the queue, and dequeue it */
rv = rtp_parse_packet_internal(s, pkt, s->queue->buf, s->queue->len);
next = s->queue->next;
- av_free(s->queue->buf);
- av_free(s->queue);
+ av_freep(&s->queue->buf);
+ av_freep(&s->queue);
s->queue = next;
s->queue_len--;
return rv;
@@ -841,7 +857,7 @@ int ff_parse_fmtp(AVFormatContext *s,
int value_size = strlen(p) + 1;
if (!(value = av_malloc(value_size))) {
- av_log(NULL, AV_LOG_ERROR, "Failed to allocate data for FMTP.");
+ av_log(NULL, AV_LOG_ERROR, "Failed to allocate data for FMTP.\n");
return AVERROR(ENOMEM);
}
diff --git a/libavformat/rtpdec.h b/libavformat/rtpdec.h
index 46b08ce925..7b41cc4b5a 100644
--- a/libavformat/rtpdec.h
+++ b/libavformat/rtpdec.h
@@ -3,20 +3,20 @@
* Copyright (c) 2002 Fabrice Bellard
* Copyright (c) 2006 Ryan Martell <rdm4@martellventures.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -173,9 +173,9 @@ struct RTPDemuxContext {
/*@}*/
/* rtcp sender statistics receive */
- int64_t last_rtcp_ntp_time;
+ uint64_t last_rtcp_ntp_time;
int64_t last_rtcp_reception_time;
- int64_t first_rtcp_ntp_time;
+ uint64_t first_rtcp_ntp_time;
uint32_t last_rtcp_timestamp;
int64_t rtcp_ts_offset;
diff --git a/libavformat/rtpdec_ac3.c b/libavformat/rtpdec_ac3.c
new file mode 100644
index 0000000000..7454a9af13
--- /dev/null
+++ b/libavformat/rtpdec_ac3.c
@@ -0,0 +1,157 @@
+/*
+ * RTP parser for AC3 payload format (RFC 4184)
+ * Copyright (c) 2015 Gilles Chanteperdrix <gch@xenomai.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 "rtpdec_formats.h"
+
+#define RTP_AC3_PAYLOAD_HEADER_SIZE 2
+
+struct PayloadContext {
+ unsigned nr_frames;
+ unsigned last_frame;
+ uint32_t timestamp;
+ AVIOContext *fragment;
+};
+
+static av_cold int ac3_init(AVFormatContext *s, int st_index,
+ PayloadContext *data)
+{
+ if (st_index < 0)
+ return 0;
+ s->streams[st_index]->need_parsing = AVSTREAM_PARSE_FULL;
+ return 0;
+}
+
+static PayloadContext *ac3_new_context(void)
+{
+ return av_mallocz(sizeof(PayloadContext));
+}
+
+static inline void free_fragment_if_needed(PayloadContext *data)
+{
+ if (data->fragment) {
+ uint8_t *p;
+ avio_close_dyn_buf(data->fragment, &p);
+ av_free(p);
+ data->fragment = NULL;
+ }
+}
+
+static void ac3_free_context(PayloadContext *data)
+{
+ free_fragment_if_needed(data);
+ av_free(data);
+}
+
+static int ac3_handle_packet(AVFormatContext *ctx, PayloadContext *data,
+ AVStream *st, AVPacket *pkt, uint32_t *timestamp,
+ const uint8_t *buf, int len, uint16_t seq,
+ int flags)
+{
+ unsigned frame_type;
+ unsigned nr_frames;
+ int err;
+
+ if (len < RTP_AC3_PAYLOAD_HEADER_SIZE + 1) {
+ av_log(ctx, AV_LOG_ERROR, "Invalid %d bytes packet\n", len);
+ return AVERROR_INVALIDDATA;
+ }
+
+ frame_type = buf[0] & 0x3;
+ nr_frames = buf[1];
+ buf += RTP_AC3_PAYLOAD_HEADER_SIZE;
+ len -= RTP_AC3_PAYLOAD_HEADER_SIZE;
+
+ switch (frame_type) {
+ case 0: /* One or more complete frames */
+ if (!nr_frames) {
+ av_log(ctx, AV_LOG_ERROR, "Invalid AC3 packet data\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if (av_new_packet(pkt, len)) {
+ av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
+ return AVERROR(ENOMEM);
+ }
+
+ pkt->stream_index = st->index;
+ memcpy(pkt->data, buf, len);
+ return 0;
+
+ case 1:
+ case 2: /* First fragment */
+ free_fragment_if_needed(data);
+
+ data->last_frame = 1;
+ data->nr_frames = nr_frames;
+ err = avio_open_dyn_buf(&data->fragment);
+ if (err < 0)
+ return err;
+
+ avio_write(data->fragment, buf, len);
+ data->timestamp = *timestamp;
+ return AVERROR(EAGAIN);
+
+ case 3: /* Fragment other than first */
+ if (!data->fragment) {
+ av_log(ctx, AV_LOG_WARNING,
+ "Received packet without a start fragment; dropping.\n");
+ return AVERROR(EAGAIN);
+ }
+ if (nr_frames != data->nr_frames ||
+ data->timestamp != *timestamp) {
+ free_fragment_if_needed(data);
+ av_log(ctx, AV_LOG_ERROR, "Invalid packet received\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ avio_write(data->fragment, buf, len);
+ data->last_frame++;
+ }
+
+ if (!(flags & RTP_FLAG_MARKER))
+ return AVERROR(EAGAIN);
+
+ if (data->last_frame != data->nr_frames) {
+ free_fragment_if_needed(data);
+ av_log(ctx, AV_LOG_ERROR, "Missed %d packets\n",
+ data->nr_frames - data->last_frame);
+ return AVERROR_INVALIDDATA;
+ }
+
+ err = ff_rtp_finalize_packet(pkt, &data->fragment, st->index);
+ if (err < 0) {
+ av_log(ctx, AV_LOG_ERROR,
+ "Error occurred when getting fragment buffer.");
+ return err;
+ }
+
+ return 0;
+}
+
+RTPDynamicProtocolHandler ff_ac3_dynamic_handler = {
+ .enc_name = "ac3",
+ .codec_type = AVMEDIA_TYPE_AUDIO,
+ .codec_id = AV_CODEC_ID_AC3,
+ .init = ac3_init,
+ .alloc = ac3_new_context,
+ .free = ac3_free_context,
+ .parse_packet = ac3_handle_packet,
+};
diff --git a/libavformat/rtpdec_amr.c b/libavformat/rtpdec_amr.c
index 86348bb0e8..31acfe7edc 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 61b14197ab..44c0a243bd 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
*/
@@ -25,6 +25,7 @@
* @author Ronald S. Bultje <rbultje@ronald.bitfreak.net>
*/
+#include "libavutil/avassert.h"
#include "libavutil/base64.h"
#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
@@ -102,6 +103,8 @@ int ff_wms_parse_sdp_a_line(AVFormatContext *s, const char *p)
AVDictionary *opts = NULL;
int len = strlen(p) * 6 / 8;
char *buf = av_mallocz(len);
+ AVInputFormat *iformat;
+
av_base64_decode(buf, p, len);
if (rtp_asf_fix_header(buf, len) < 0)
@@ -111,11 +114,19 @@ int ff_wms_parse_sdp_a_line(AVFormatContext *s, const char *p)
if (rt->asf_ctx) {
avformat_close_input(&rt->asf_ctx);
}
+ if (!(iformat = av_find_input_format("asf")))
+ return AVERROR_DEMUXER_NOT_FOUND;
if (!(rt->asf_ctx = avformat_alloc_context()))
return AVERROR(ENOMEM);
rt->asf_ctx->pb = &pb;
av_dict_set(&opts, "no_resync_search", "1", 0);
- ret = avformat_open_input(&rt->asf_ctx, "", &ff_asf_demuxer, &opts);
+
+ if ((ret = ff_copy_whitelists(rt->asf_ctx, s)) < 0) {
+ av_dict_free(&opts);
+ return ret;
+ }
+
+ ret = avformat_open_input(&rt->asf_ctx, "", iformat, &opts);
av_dict_free(&opts);
if (ret < 0)
return ret;
@@ -188,7 +199,7 @@ static int asfrtp_parse_packet(AVFormatContext *s, PayloadContext *asf,
av_freep(&asf->buf);
- ffio_init_context(pb, buf, len, 0, NULL, NULL, NULL, NULL);
+ ffio_init_context(pb, (uint8_t *)buf, len, 0, NULL, NULL, NULL, NULL);
while (avio_tell(pb) + 4 < len) {
int start_off = avio_tell(pb);
diff --git a/libavformat/rtpdec_formats.h b/libavformat/rtpdec_formats.h
index 0c9f49cfbc..b38cf1bf5c 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
*/
@@ -35,6 +35,7 @@ int ff_h263_handle_packet(AVFormatContext *ctx, PayloadContext *data,
AVStream *st, AVPacket *pkt, uint32_t *timestamp,
const uint8_t *buf, int len, uint16_t seq, int flags);
+extern RTPDynamicProtocolHandler ff_ac3_dynamic_handler;
extern RTPDynamicProtocolHandler ff_amr_nb_dynamic_handler;
extern RTPDynamicProtocolHandler ff_amr_wb_dynamic_handler;
extern RTPDynamicProtocolHandler ff_g726_16_dynamic_handler;
@@ -52,6 +53,7 @@ extern RTPDynamicProtocolHandler ff_jpeg_dynamic_handler;
extern RTPDynamicProtocolHandler ff_mp4a_latm_dynamic_handler;
extern RTPDynamicProtocolHandler ff_mp4v_es_dynamic_handler;
extern RTPDynamicProtocolHandler ff_mpeg_audio_dynamic_handler;
+extern RTPDynamicProtocolHandler ff_mpeg_audio_robust_dynamic_handler;
extern RTPDynamicProtocolHandler ff_mpeg_video_dynamic_handler;
extern RTPDynamicProtocolHandler ff_mpeg4_generic_dynamic_handler;
extern RTPDynamicProtocolHandler ff_mpegts_dynamic_handler;
diff --git a/libavformat/rtpdec_g726.c b/libavformat/rtpdec_g726.c
index 7b3f6cbb89..7e9ef56e06 100644
--- a/libavformat/rtpdec_g726.c
+++ b/libavformat/rtpdec_g726.c
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2011 Miroslav Slugeň <Thunder.m@seznam.cz>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpdec_h261.c b/libavformat/rtpdec_h261.c
index 4988300831..9f3488931b 100644
--- a/libavformat/rtpdec_h261.c
+++ b/libavformat/rtpdec_h261.c
@@ -2,20 +2,20 @@
* RTP parser for H.261 payload format (RFC 4587)
* Copyright (c) 2014 Thomas Volkert <thomas@homer-conferencing.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpdec_h263.c b/libavformat/rtpdec_h263.c
index b3714910eb..ae4cc27040 100644
--- a/libavformat/rtpdec_h263.c
+++ b/libavformat/rtpdec_h263.c
@@ -2,20 +2,20 @@
* RTP H.263 Depacketizer, RFC 4629
* Copyright (c) 2010 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpdec_h263_rfc2190.c b/libavformat/rtpdec_h263_rfc2190.c
index d507ef7ef3..8a36e5334c 100644
--- a/libavformat/rtpdec_h263_rfc2190.c
+++ b/libavformat/rtpdec_h263_rfc2190.c
@@ -8,20 +8,20 @@
* Copyright 2007 Collabora Ltd, Philippe Kalaf
* Copyright 2010 Mark Nauwelaerts
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpdec_h264.c b/libavformat/rtpdec_h264.c
index abf1f39279..4dbbae770b 100644
--- a/libavformat/rtpdec_h264.c
+++ b/libavformat/rtpdec_h264.c
@@ -2,20 +2,20 @@
* RTP H264 Protocol (RFC3984)
* Copyright (c) 2006 Ryan Martell
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -71,7 +71,7 @@ static int sdp_parse_fmtp_config_h264(AVFormatContext *s,
{
AVCodecContext *codec = stream->codec;
assert(codec->codec_id == AV_CODEC_ID_H264);
- assert(h264_data != NULL);
+ assert(h264_data);
if (!strcmp(attr, "packetization-mode")) {
av_log(s, AV_LOG_DEBUG, "RTP Packetization Mode: %d\n", atoi(value));
diff --git a/libavformat/rtpdec_hevc.c b/libavformat/rtpdec_hevc.c
index 3ed7cac8f6..471823cfb0 100644
--- a/libavformat/rtpdec_hevc.c
+++ b/libavformat/rtpdec_hevc.c
@@ -2,34 +2,38 @@
* RTP parser for HEVC/H.265 payload format (draft version 6)
* Copyright (c) 2014 Thomas Volkert <thomas@homer-conferencing.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free 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/base64.h"
+#include "libavcodec/get_bits.h"
#include "avformat.h"
#include "rtpdec.h"
-#define RTP_HEVC_PAYLOAD_HEADER_SIZE 2
-#define RTP_HEVC_FU_HEADER_SIZE 1
-#define RTP_HEVC_DONL_FIELD_SIZE 2
-#define HEVC_SPECIFIED_NAL_UNIT_TYPES 48
+#define RTP_HEVC_PAYLOAD_HEADER_SIZE 2
+#define RTP_HEVC_FU_HEADER_SIZE 1
+#define RTP_HEVC_DONL_FIELD_SIZE 2
+#define RTP_HEVC_DOND_FIELD_SIZE 1
+#define RTP_HEVC_AP_NALU_LENGTH_FIELD_SIZE 2
+#define HEVC_SPECIFIED_NAL_UNIT_TYPES 48
/* SDP out-of-band signaling data */
struct PayloadContext {
@@ -104,7 +108,8 @@ static av_cold int hevc_sdp_parse_fmtp_config(AVFormatContext *s,
} else if (!strcmp(attr, "sprop-sei")) {
data_ptr = &hevc_data->sei;
size_ptr = &hevc_data->sei_size;
- }
+ } else
+ av_assert0(0);
while (*value) {
char base64packet[1024];
@@ -315,19 +320,6 @@ static int hevc_handle_packet(AVFormatContext *ctx, PayloadContext *rtp_hevc_ctx
}
switch (nal_type) {
- /* aggregated packets (AP) */
- case 48:
- /* pass the HEVC payload header */
- buf += RTP_HEVC_PAYLOAD_HEADER_SIZE;
- len -= RTP_HEVC_PAYLOAD_HEADER_SIZE;
-
- /* pass the HEVC DONL field */
- if (rtp_hevc_ctx->using_donl_field) {
- buf += RTP_HEVC_DONL_FIELD_SIZE;
- len -= RTP_HEVC_DONL_FIELD_SIZE;
- }
-
- /* fall-through */
/* video parameter set (VPS) */
case 32:
/* sequence parameter set (SPS) */
@@ -355,6 +347,73 @@ static int hevc_handle_packet(AVFormatContext *ctx, PayloadContext *rtp_hevc_ctx
memcpy(pkt->data + sizeof(start_sequence), buf, len);
break;
+ /* aggregated packet (AP) - with two or more NAL units */
+ case 48:
+ /* pass the HEVC payload header */
+ buf += RTP_HEVC_PAYLOAD_HEADER_SIZE;
+ len -= RTP_HEVC_PAYLOAD_HEADER_SIZE;
+
+ /* pass the HEVC DONL field */
+ if (rtp_hevc_ctx->using_donl_field) {
+ buf += RTP_HEVC_DONL_FIELD_SIZE;
+ len -= RTP_HEVC_DONL_FIELD_SIZE;
+ }
+
+ /*
+ * pass 0: determine overall size of the A/V packet
+ * pass 1: create resulting A/V packet
+ */
+ {
+ int pass = 0;
+ int pkt_size = 0;
+ uint8_t *pkt_data = 0;
+
+ for (pass = 0; pass < 2; pass++) {
+ const uint8_t *buf1 = buf;
+ int len1 = len;
+
+ while (len1 > RTP_HEVC_AP_NALU_LENGTH_FIELD_SIZE) {
+ uint16_t nalu_size = AV_RB16(buf1);
+
+ /* pass the NALU length field */
+ buf1 += RTP_HEVC_AP_NALU_LENGTH_FIELD_SIZE;
+ len1 -= RTP_HEVC_AP_NALU_LENGTH_FIELD_SIZE;
+
+ if (nalu_size > 0 && nalu_size <= len1) {
+ if (pass == 0) {
+ pkt_size += sizeof(start_sequence) + nalu_size;
+ } else {
+ /* A/V packet: copy start sequence */
+ memcpy(pkt_data, start_sequence, sizeof(start_sequence));
+ /* A/V packet: copy NAL unit data */
+ memcpy(pkt_data + sizeof(start_sequence), buf1, nalu_size);
+ /* shift pointer beyond the current NAL unit */
+ pkt_data += sizeof(start_sequence) + nalu_size;
+ }
+ }
+
+ /* pass the current NAL unit */
+ buf1 += nalu_size;
+ len1 -= nalu_size;
+
+ /* pass the HEVC DOND field */
+ if (rtp_hevc_ctx->using_donl_field) {
+ buf1 += RTP_HEVC_DOND_FIELD_SIZE;
+ len1 -= RTP_HEVC_DOND_FIELD_SIZE;
+ }
+ }
+
+ /* create A/V packet */
+ if (pass == 0) {
+ if ((res = av_new_packet(pkt, pkt_size)) < 0)
+ return res;
+
+ pkt_data = pkt->data;
+ }
+ }
+ }
+
+ break;
/* fragmentation unit (FU) */
case 49:
/* pass the HEVC payload header */
@@ -389,6 +448,7 @@ static int hevc_handle_packet(AVFormatContext *ctx, PayloadContext *rtp_hevc_ctx
av_dlog(ctx, " FU type %d with %d bytes\n", fu_type, len);
+ /* sanity check for size of input packet: 1 byte payload at least */
if (len > 0) {
new_nal_header[0] = (rtp_pl[0] & 0x81) | (fu_type << 1);
new_nal_header[1] = rtp_pl[1];
@@ -417,11 +477,14 @@ static int hevc_handle_packet(AVFormatContext *ctx, PayloadContext *rtp_hevc_ctx
memcpy(pkt->data, buf, len);
}
} else {
- /* sanity check for size of input packet: 1 byte payload at least */
- av_log(ctx, AV_LOG_ERROR,
- "Too short RTP/HEVC packet, got %d bytes of NAL unit type %d\n",
- len, nal_type);
- res = AVERROR_INVALIDDATA;
+ if (len < 0) {
+ av_log(ctx, AV_LOG_ERROR,
+ "Too short RTP/HEVC packet, got %d bytes of NAL unit type %d\n",
+ len, nal_type);
+ res = AVERROR_INVALIDDATA;
+ } else {
+ res = AVERROR(EAGAIN);
+ }
}
break;
diff --git a/libavformat/rtpdec_ilbc.c b/libavformat/rtpdec_ilbc.c
index bff47c7ac2..aa1579fa63 100644
--- a/libavformat/rtpdec_ilbc.c
+++ b/libavformat/rtpdec_ilbc.c
@@ -2,20 +2,20 @@
* RTP iLBC Depacketizer, RFC 3952
* Copyright (c) 2012 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpdec_jpeg.c b/libavformat/rtpdec_jpeg.c
index ed9c86ceb9..ccd80ad7e8 100644
--- a/libavformat/rtpdec_jpeg.c
+++ b/libavformat/rtpdec_jpeg.c
@@ -2,20 +2,20 @@
* RTP JPEG-compressed Video Depacketizer, RFC 2435
* Copyright (c) 2012 Samuel Pitoiset
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -106,7 +106,8 @@ static void jpeg_put_marker(PutByteContext *pbc, int code)
}
static int jpeg_create_header(uint8_t *buf, int size, uint32_t type, uint32_t w,
- uint32_t h, const uint8_t *qtable, int nb_qtable)
+ uint32_t h, const uint8_t *qtable, int nb_qtable,
+ int dri)
{
PutByteContext pbc;
uint8_t *dht_size_ptr;
@@ -132,6 +133,12 @@ static int jpeg_create_header(uint8_t *buf, int size, uint32_t type, uint32_t w,
bytestream2_put_byte(&pbc, 0);
bytestream2_put_byte(&pbc, 0);
+ if (dri) {
+ jpeg_put_marker(&pbc, DRI);
+ bytestream2_put_be16(&pbc, 4);
+ bytestream2_put_be16(&pbc, dri);
+ }
+
/* DQT */
jpeg_put_marker(&pbc, DQT);
bytestream2_put_be16(&pbc, 2 + nb_qtable * (1 + 64));
@@ -226,7 +233,7 @@ static int jpeg_parse_packet(AVFormatContext *ctx, PayloadContext *jpeg,
const uint8_t *qtables = NULL;
uint16_t qtable_len;
uint32_t off;
- int ret;
+ int ret, dri = 0;
if (len < 8) {
av_log(ctx, AV_LOG_ERROR, "Too short RTP/JPEG packet.\n");
@@ -242,6 +249,16 @@ static int jpeg_parse_packet(AVFormatContext *ctx, PayloadContext *jpeg,
buf += 8;
len -= 8;
+ if (type & 0x40) {
+ if (len < 4) {
+ av_log(ctx, AV_LOG_ERROR, "Too short RTP/JPEG packet.\n");
+ return AVERROR_INVALIDDATA;
+ }
+ dri = AV_RB16(buf);
+ buf += 4;
+ len -= 4;
+ type &= ~0x40;
+ }
/* Parse the restart marker header. */
if (type > 63) {
av_log(ctx, AV_LOG_ERROR,
@@ -332,7 +349,7 @@ static int jpeg_parse_packet(AVFormatContext *ctx, PayloadContext *jpeg,
* interchange format. */
jpeg->hdr_size = jpeg_create_header(hdr, sizeof(hdr), type, width,
height, qtables,
- qtable_len / 64);
+ qtable_len / 64, dri);
/* Copy JPEG header to frame buffer. */
avio_write(jpeg->frame, hdr, jpeg->hdr_size);
diff --git a/libavformat/rtpdec_latm.c b/libavformat/rtpdec_latm.c
index 6ab3f783d7..c3cb2f28d0 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
*/
@@ -45,7 +45,7 @@ static void latm_free_context(PayloadContext *data)
avio_close_dyn_buf(data->dyn_buf, &p);
av_free(p);
}
- av_free(data->buf);
+ av_freep(&data->buf);
av_free(data);
}
@@ -72,7 +72,7 @@ static int latm_parse_packet(AVFormatContext *ctx, PayloadContext *data,
if (!(flags & RTP_FLAG_MARKER))
return AVERROR(EAGAIN);
- av_free(data->buf);
+ av_freep(&data->buf);
data->len = avio_close_dyn_buf(data->dyn_buf, &data->buf);
data->dyn_buf = NULL;
data->pos = 0;
@@ -130,10 +130,7 @@ static int parse_fmtp_config(AVStream *st, char *value)
goto end;
}
av_freep(&st->codec->extradata);
- st->codec->extradata_size = (get_bits_left(&gb) + 7)/8;
- st->codec->extradata = av_mallocz(st->codec->extradata_size +
- FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata) {
+ if (ff_alloc_extradata(st->codec, (get_bits_left(&gb) + 7)/8)) {
ret = AVERROR(ENOMEM);
goto end;
}
diff --git a/libavformat/rtpdec_mpeg12.c b/libavformat/rtpdec_mpeg12.c
index b059fcfa5b..d99c507716 100644
--- a/libavformat/rtpdec_mpeg12.c
+++ b/libavformat/rtpdec_mpeg12.c
@@ -2,20 +2,23 @@
* Common code for the RTP depacketization of MPEG-1/2 formats.
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * RTP parser for loss tolerant payload format for MP3 audio (RFC 5219)
+ * Copyright (c) 2015 Gilles Chanteperdrix <gch@xenomai.org>
*
- * Libav is free software; you can redistribute it and/or
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -23,6 +26,8 @@
#include "libavutil/intreadwrite.h"
#include "rtpdec_formats.h"
+#define RTP_MPA_PAYLOAD_HEADER_SIZE 1
+
static av_cold int mpeg_init(AVFormatContext *ctx, int st_index, PayloadContext *data)
{
if (st_index < 0)
@@ -71,3 +76,202 @@ RTPDynamicProtocolHandler ff_mpeg_video_dynamic_handler = {
.parse_packet = mpeg_parse_packet,
.static_payload_id = 32,
};
+
+/* MPA-ROBUST, RFC 5219 */
+struct PayloadContext {
+ unsigned adu_size;
+ unsigned cur_size;
+ uint32_t timestamp;
+ uint8_t *split_buf;
+ int split_pos, split_buf_size, split_pkts;
+ AVIOContext *fragment;
+};
+
+static av_cold int mpa_robust_init(AVFormatContext *ctx, int st_index,
+ PayloadContext *data)
+{
+ if (st_index < 0)
+ return 0;
+ ctx->streams[st_index]->need_parsing = AVSTREAM_PARSE_HEADERS;
+ return 0;
+}
+
+static PayloadContext *mpa_robust_new_context(void)
+{
+ return av_mallocz(sizeof(PayloadContext));
+}
+
+static inline void free_fragment_if_needed(PayloadContext *data)
+{
+ if (data->fragment) {
+ uint8_t *p;
+ avio_close_dyn_buf(data->fragment, &p);
+ av_free(p);
+ data->fragment = NULL;
+ }
+}
+
+static void mpa_robust_free_context(PayloadContext *data)
+{
+ free_fragment_if_needed(data);
+ av_free(data);
+}
+
+static int mpa_robust_parse_rtp_header(AVFormatContext *ctx,
+ const uint8_t *buf, int len,
+ unsigned *adu_size, unsigned *cont)
+{
+ unsigned header_size;
+
+ if (len < RTP_MPA_PAYLOAD_HEADER_SIZE + 1) {
+ av_log(ctx, AV_LOG_ERROR, "Invalid %d bytes packet\n", len);
+ return AVERROR_INVALIDDATA;
+ }
+
+ *cont = !!(buf[0] & 0x80);
+ if (!(buf[0] & 0x40)) {
+ header_size = 1;
+ *adu_size = buf[0] & ~0xc0;
+ } else {
+ header_size = 2;
+ *adu_size = AV_RB16(buf) & ~0xc000;
+ }
+
+ return header_size;
+}
+
+static int mpa_robust_parse_packet(AVFormatContext *ctx, PayloadContext *data,
+ AVStream *st, AVPacket *pkt,
+ uint32_t *timestamp, const uint8_t *buf,
+ int len, uint16_t seq, int flags)
+{
+ unsigned adu_size, continuation;
+ int err, header_size;
+
+ if (!buf) {
+ buf = &data->split_buf[data->split_pos];
+ len = data->split_buf_size - data->split_pos;
+
+ header_size = mpa_robust_parse_rtp_header(ctx, buf, len, &adu_size,
+ &continuation);
+ if (header_size < 0) {
+ av_freep(&data->split_buf);
+ return header_size;
+ }
+ buf += header_size;
+ len -= header_size;
+
+ if (continuation || adu_size > len) {
+ av_freep(&data->split_buf);
+ av_log(ctx, AV_LOG_ERROR, "Invalid frame\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (av_new_packet(pkt, adu_size)) {
+ av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
+ return AVERROR(ENOMEM);
+ }
+
+ pkt->stream_index = st->index;
+ memcpy(pkt->data, buf, adu_size);
+
+ data->split_pos = (buf - data->split_buf) + adu_size;
+
+ if (data->split_pos == data->split_buf_size) {
+ av_freep(&data->split_buf);
+ return 0;
+ }
+
+ return 1;
+ }
+
+
+ header_size = mpa_robust_parse_rtp_header(ctx, buf, len, &adu_size,
+ &continuation);
+ if (header_size < 0)
+ return header_size;
+
+ buf += header_size;
+ len -= header_size;
+
+ if (!continuation && adu_size <= len) {
+ /* One or more complete frames */
+
+ if (av_new_packet(pkt, adu_size)) {
+ av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
+ return AVERROR(ENOMEM);
+ }
+
+ pkt->stream_index = st->index;
+ memcpy(pkt->data, buf, adu_size);
+
+ buf += adu_size;
+ len -= adu_size;
+ if (len) {
+ data->split_buf_size = len;
+ data->split_buf = av_malloc(data->split_buf_size);
+ data->split_pos = 0;
+ if (!data->split_buf) {
+ av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
+ av_free_packet(pkt);
+ return AVERROR(ENOMEM);
+ }
+ memcpy(data->split_buf, buf, data->split_buf_size);
+ return 1;
+ }
+ return 0;
+ } else if (!continuation) { /* && adu_size > len */
+ /* First fragment */
+ free_fragment_if_needed(data);
+
+ data->adu_size = adu_size;
+ data->cur_size = len;
+ data->timestamp = *timestamp;
+
+ err = avio_open_dyn_buf(&data->fragment);
+ if (err < 0)
+ return err;
+
+ avio_write(data->fragment, buf, len);
+ return AVERROR(EAGAIN);
+ }
+ /* else continuation == 1 */
+
+ /* Fragment other than first */
+ if (!data->fragment) {
+ av_log(ctx, AV_LOG_WARNING,
+ "Received packet without a start fragment; dropping.\n");
+ return AVERROR(EAGAIN);
+ }
+ if (adu_size = data->adu_size ||
+ data->timestamp != *timestamp) {
+ free_fragment_if_needed(data);
+ av_log(ctx, AV_LOG_ERROR, "Invalid packet received\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ avio_write(data->fragment, buf, len);
+ data->cur_size += len;
+
+ if (data->cur_size < data->adu_size)
+ return AVERROR(EAGAIN);
+
+ err = ff_rtp_finalize_packet(pkt, &data->fragment, st->index);
+ if (err < 0) {
+ av_log(ctx, AV_LOG_ERROR,
+ "Error occurred when getting fragment buffer.");
+ return err;
+ }
+
+ return 0;
+}
+
+RTPDynamicProtocolHandler ff_mpeg_audio_robust_dynamic_handler = {
+ .codec_type = AVMEDIA_TYPE_AUDIO,
+ .codec_id = AV_CODEC_ID_MP3ADU,
+ .init = mpa_robust_init,
+ .alloc = mpa_robust_new_context,
+ .free = mpa_robust_free_context,
+ .parse_packet = mpa_robust_parse_packet,
+ .enc_name = "mpa-robust",
+};
diff --git a/libavformat/rtpdec_mpeg4.c b/libavformat/rtpdec_mpeg4.c
index 0c0d336fb1..f22086d5a8 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
*/
@@ -95,20 +95,18 @@ static PayloadContext *new_context(void)
static void free_context(PayloadContext *data)
{
- av_free(data->au_headers);
- av_free(data->mode);
- av_free(data);
+ av_freep(&data->au_headers);
+ av_freep(&data->mode);
+ av_freep(&data);
}
static int parse_fmtp_config(AVCodecContext *codec, char *value)
{
/* decode the hexa encoded parameter */
int len = ff_hex_to_data(NULL, value);
- av_free(codec->extradata);
- codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!codec->extradata)
+ av_freep(&codec->extradata);
+ if (ff_alloc_extradata(codec, len))
return AVERROR(ENOMEM);
- codec->extradata_size = len;
ff_hex_to_data(codec->extradata, value);
return 0;
}
@@ -139,7 +137,7 @@ static int rtp_parse_mp4_au(PayloadContext *data, const uint8_t *buf, int len)
init_get_bits(&getbitcontext, buf, data->au_headers_length_bytes * 8);
- /* XXX: Wrong if optionnal additional sections are present (cts, dts etc...) */
+ /* XXX: Wrong if optional additional sections are present (cts, dts etc...) */
au_header_size = data->sizelength + data->indexlength;
if (au_header_size <= 0 || (au_headers_length % au_header_size != 0))
return -1;
diff --git a/libavformat/rtpdec_mpegts.c b/libavformat/rtpdec_mpegts.c
index b3ef261336..0c2b71745e 100644
--- a/libavformat/rtpdec_mpegts.c
+++ b/libavformat/rtpdec_mpegts.c
@@ -2,20 +2,20 @@
* RTP MPEG2TS depacketizer
* Copyright (c) 2003 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -40,14 +40,14 @@ static void mpegts_free_context(PayloadContext *data)
if (!data)
return;
if (data->ts)
- ff_mpegts_parse_close(data->ts);
+ avpriv_mpegts_parse_close(data->ts);
av_free(data);
}
static av_cold int mpegts_init(AVFormatContext *ctx, int st_index,
PayloadContext *data)
{
- data->ts = ff_mpegts_parse_open(ctx);
+ data->ts = avpriv_mpegts_parse_open(ctx);
if (!data->ts)
return AVERROR(ENOMEM);
return 0;
@@ -72,8 +72,8 @@ static int mpegts_handle_packet(AVFormatContext *ctx, PayloadContext *data,
if (!buf) {
if (data->read_buf_index >= data->read_buf_size)
return AVERROR(EAGAIN);
- ret = ff_mpegts_parse_packet(data->ts, pkt, data->buf + data->read_buf_index,
- data->read_buf_size - data->read_buf_index);
+ ret = avpriv_mpegts_parse_packet(data->ts, pkt, data->buf + data->read_buf_index,
+ data->read_buf_size - data->read_buf_index);
if (ret < 0)
return AVERROR(EAGAIN);
data->read_buf_index += ret;
@@ -83,8 +83,8 @@ static int mpegts_handle_packet(AVFormatContext *ctx, PayloadContext *data,
return 0;
}
- ret = ff_mpegts_parse_packet(data->ts, pkt, buf, len);
- /* The only error that can be returned from ff_mpegts_parse_packet
+ ret = avpriv_mpegts_parse_packet(data->ts, pkt, buf, len);
+ /* The only error that can be returned from avpriv_mpegts_parse_packet
* is "no more data to return from the provided buffer", so return
* AVERROR(EAGAIN) for all errors */
if (ret < 0)
diff --git a/libavformat/rtpdec_qcelp.c b/libavformat/rtpdec_qcelp.c
index 9c48314846..f2893499d5 100644
--- a/libavformat/rtpdec_qcelp.c
+++ b/libavformat/rtpdec_qcelp.c
@@ -2,20 +2,20 @@
* RTP Depacketization of QCELP/PureVoice, RFC 2658
* Copyright (c) 2010 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpdec_qdm2.c b/libavformat/rtpdec_qdm2.c
index 0d7b5bb87a..edd9490051 100644
--- a/libavformat/rtpdec_qdm2.c
+++ b/libavformat/rtpdec_qdm2.c
@@ -2,20 +2,20 @@
* QDesign Music 2 (QDM2) payload for RTP
* Copyright (c) 2010 Ronald S. Bultje
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -26,8 +26,10 @@
*/
#include <string.h>
+#include "libavutil/avassert.h"
#include "libavutil/intreadwrite.h"
#include "libavcodec/avcodec.h"
+#include "internal.h"
#include "rtp.h"
#include "rtpdec.h"
#include "rtpdec_formats.h"
@@ -103,9 +105,7 @@ static int qdm2_parse_config(PayloadContext *qdm, AVStream *st,
if (item_len < 30)
return AVERROR_INVALIDDATA;
av_freep(&st->codec->extradata);
- st->codec->extradata_size = 26 + item_len;
- if (!(st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE))) {
- st->codec->extradata_size = 0;
+ if (ff_alloc_extradata(st->codec, 26 + item_len)) {
return AVERROR(ENOMEM);
}
AV_WB32(st->codec->extradata, 12);
@@ -189,11 +189,11 @@ static int qdm2_restore_block(PayloadContext *qdm, AVStream *st, AVPacket *pkt)
uint8_t *p, *csum_pos = NULL;
/* create packet to hold subpkts into a superblock */
- assert(qdm->cache > 0);
+ av_assert0(qdm->cache > 0);
for (n = 0; n < 0x80; n++)
if (qdm->len[n] > 0)
break;
- assert(n < 0x80);
+ av_assert0(n < 0x80);
if ((res = av_new_packet(pkt, qdm->block_size)) < 0)
return res;
diff --git a/libavformat/rtpdec_qt.c b/libavformat/rtpdec_qt.c
index 2d9c603fcd..58253b6651 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
*/
@@ -71,7 +71,7 @@ static int qt_rtp_parse_packet(AVFormatContext *s, PayloadContext *qt,
* http://developer.apple.com/quicktime/icefloe/dispatch026.html
*/
init_get_bits(&gb, buf, len << 3);
- ffio_init_context(&pb, buf, len, 0, NULL, NULL, NULL, NULL);
+ ffio_init_context(&pb, (uint8_t*)buf, len, 0, NULL, NULL, NULL, NULL);
if (len < 4)
return AVERROR_INVALIDDATA;
diff --git a/libavformat/rtpdec_svq3.c b/libavformat/rtpdec_svq3.c
index aa880e5954..98e8b58673 100644
--- a/libavformat/rtpdec_svq3.c
+++ b/libavformat/rtpdec_svq3.c
@@ -2,20 +2,20 @@
* Sorenson-3 (SVQ3/SV3V) payload for RTP
* Copyright (c) 2010 Ronald S. Bultje
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -28,6 +28,7 @@
#include <string.h>
#include "libavutil/intreadwrite.h"
+#include "internal.h"
#include "rtp.h"
#include "rtpdec.h"
#include "rtpdec_formats.h"
@@ -60,11 +61,9 @@ static int svq3_parse_packet (AVFormatContext *s, PayloadContext *sv,
av_freep(&st->codec->extradata);
st->codec->extradata_size = 0;
- if (len < 2 || !(st->codec->extradata =
- av_malloc(len + 8 + FF_INPUT_BUFFER_PADDING_SIZE)))
+ if (len < 2 || ff_alloc_extradata(st->codec, len + 8))
return AVERROR_INVALIDDATA;
- st->codec->extradata_size = len + 8;
memcpy(st->codec->extradata, "SEQH", 4);
AV_WB32(st->codec->extradata + 4, len);
memcpy(st->codec->extradata + 8, buf, len);
diff --git a/libavformat/rtpdec_vp8.c b/libavformat/rtpdec_vp8.c
index fc86ac1dd3..b4b4f2207d 100644
--- a/libavformat/rtpdec_vp8.c
+++ b/libavformat/rtpdec_vp8.c
@@ -3,20 +3,20 @@
* Copyright (c) 2010 Josh Allmann
* Copyright (c) 2012 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpdec_xiph.c b/libavformat/rtpdec_xiph.c
index 9e4fbc52f2..53b4975278 100644
--- a/libavformat/rtpdec_xiph.c
+++ b/libavformat/rtpdec_xiph.c
@@ -3,20 +3,20 @@
* Copyright (c) 2009 Colin McQuillian
* Copyright (c) 2010 Josh Allmann
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -28,12 +28,12 @@
*/
#include "libavutil/attributes.h"
+#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/base64.h"
#include "libavcodec/bytestream.h"
-#include <assert.h>
-
+#include "internal.h"
#include "rtpdec.h"
#include "rtpdec_formats.h"
@@ -67,8 +67,8 @@ static inline void free_fragment_if_needed(PayloadContext * data)
static void xiph_free_context(PayloadContext * data)
{
free_fragment_if_needed(data);
- av_free(data->split_buf);
- av_free(data);
+ av_freep(&data->split_buf);
+ av_freep(&data);
}
static av_cold int xiph_vorbis_init(AVFormatContext *ctx, int st_index,
@@ -192,7 +192,7 @@ static int xiph_handle_packet(AVFormatContext *ctx, PayloadContext *data,
data->timestamp = *timestamp;
} else {
- assert(fragmented < 4);
+ av_assert1(fragmented < 4);
if (data->timestamp != *timestamp) {
// skip if fragmented timestamp is incorrect;
// a start packet has been lost somewhere
@@ -256,7 +256,7 @@ parse_packed_headers(const uint8_t * packed_headers,
if (packed_headers_end - packed_headers < 9) {
av_log(codec, AV_LOG_ERROR,
- "Invalid %td byte packed header.",
+ "Invalid %"PTRDIFF_SPECIFIER" byte packed header.",
packed_headers_end - packed_headers);
return AVERROR_INVALIDDATA;
}
@@ -278,7 +278,7 @@ parse_packed_headers(const uint8_t * packed_headers,
if (packed_headers_end - packed_headers != length ||
length1 > length || length2 > length - length1) {
av_log(codec, AV_LOG_ERROR,
- "Bad packed header lengths (%d,%d,%td,%d)\n", length1,
+ "Bad packed header lengths (%d,%d,%"PTRDIFF_SPECIFIER",%d)\n", length1,
length2, packed_headers_end - packed_headers, length);
return AVERROR_INVALIDDATA;
}
@@ -289,11 +289,11 @@ parse_packed_headers(const uint8_t * packed_headers,
* -- FF_INPUT_BUFFER_PADDING_SIZE required */
extradata_alloc = length + length/255 + 3 + FF_INPUT_BUFFER_PADDING_SIZE;
- ptr = codec->extradata = av_malloc(extradata_alloc);
- if (!ptr) {
+ if (ff_alloc_extradata(codec, extradata_alloc)) {
av_log(codec, AV_LOG_ERROR, "Out of memory\n");
return AVERROR(ENOMEM);
}
+ ptr = codec->extradata;
*ptr++ = 2;
ptr += av_xiphlacing(ptr, length1);
ptr += av_xiphlacing(ptr, length2);
diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c
index bf82da0969..c4c4d3e416 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
*/
@@ -98,7 +98,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;
}
@@ -121,16 +121,19 @@ static int rtp_write_header(AVFormatContext *s1)
s->ssrc = av_get_random_seed();
s->first_packet = 1;
s->first_rtcp_ntp_time = ff_ntp_time();
- if (s1->start_time_realtime)
+ if (s1->start_time_realtime != 0 && s1->start_time_realtime != AV_NOPTS_VALUE)
/* Round the NTP time to whole milliseconds. */
s->first_rtcp_ntp_time = (s1->start_time_realtime / 1000) * 1000 +
NTP_OFFSET_US;
// Pick a random sequence start number, but in the lower end of the
// available range, so that any wraparound doesn't happen immediately.
// (Immediate wraparound would be an issue for SRTP.)
- if (s->seq < 0)
- s->seq = av_get_random_seed() & 0x0fff;
- else
+ if (s->seq < 0) {
+ if (s1->flags & AVFMT_FLAG_BITEXACT) {
+ s->seq = 0;
+ } else
+ s->seq = av_get_random_seed() & 0x0fff;
+ } else
s->seq &= 0xffff; // Use the given parameter, wrapped to the right interval
if (s1->packet_size) {
@@ -588,6 +591,10 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt)
const uint8_t *mb_info =
av_packet_get_side_data(pkt, AV_PKT_DATA_H263_MB_INFO,
&mb_info_size);
+ if (!mb_info) {
+ av_log(s1, AV_LOG_ERROR, "failed to allocate side data\n");
+ return AVERROR(ENOMEM);
+ }
ff_rtp_send_h263_rfc2190(s1, pkt->data, size, mb_info, mb_info_size);
break;
}
diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h
index f2ac0fa9ad..4eb98d8c3e 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
@@ -94,7 +94,7 @@ void ff_rtp_send_xiph(AVFormatContext *s1, const uint8_t *buff, int size);
void ff_rtp_send_vp8(AVFormatContext *s1, const uint8_t *buff, int size);
void ff_rtp_send_jpeg(AVFormatContext *s1, const uint8_t *buff, int size);
-const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *restrict start,
- const uint8_t *restrict end);
+const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *av_restrict start,
+ const uint8_t *av_restrict end);
#endif /* AVFORMAT_RTPENC_H */
diff --git a/libavformat/rtpenc_aac.c b/libavformat/rtpenc_aac.c
index 1b2fa0a78c..be8f43ae69 100644
--- a/libavformat/rtpenc_aac.c
+++ b/libavformat/rtpenc_aac.c
@@ -1,20 +1,20 @@
/*
* copyright (c) 2007 Luca Abeni
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpenc_amr.c b/libavformat/rtpenc_amr.c
index 73da8c8251..bd1c197101 100644
--- a/libavformat/rtpenc_amr.c
+++ b/libavformat/rtpenc_amr.c
@@ -3,20 +3,20 @@
* Copyright (c) 2007 Luca Abeni
* Copyright (c) 2009 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpenc_chain.c b/libavformat/rtpenc_chain.c
index 773a7dbbc8..302c14c11a 100644
--- a/libavformat/rtpenc_chain.c
+++ b/libavformat/rtpenc_chain.c
@@ -2,27 +2,26 @@
* RTP muxer chaining code
* Copyright (c) 2010 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "avformat.h"
#include "avio_internal.h"
#include "rtpenc_chain.h"
-#include "avio_internal.h"
#include "rtp.h"
#include "libavutil/opt.h"
@@ -59,6 +58,8 @@ int ff_rtp_chain_mux_open(AVFormatContext **out, AVFormatContext *s,
rtpctx->max_delay = s->max_delay;
/* Copy other stream parameters. */
rtpctx->streams[0]->sample_aspect_ratio = st->sample_aspect_ratio;
+ rtpctx->flags |= s->flags & AVFMT_FLAG_MP4A_LATM;
+
/* Get the payload type from the codec */
if (st->id < RTP_PT_PRIVATE)
rtpctx->streams[0]->id =
@@ -66,6 +67,7 @@ int ff_rtp_chain_mux_open(AVFormatContext **out, AVFormatContext *s,
else
rtpctx->streams[0]->id = st->id;
+
if (av_opt_get(s, "rtpflags", AV_OPT_SEARCH_CHILDREN, &rtpflags) >= 0)
av_dict_set(&opts, "rtpflags", rtpflags, AV_DICT_DONT_STRDUP_VAL);
@@ -87,7 +89,7 @@ int ff_rtp_chain_mux_open(AVFormatContext **out, AVFormatContext *s,
if (ret) {
if (handle && rtpctx->pb) {
- avio_close(rtpctx->pb);
+ avio_closep(&rtpctx->pb);
} else if (rtpctx->pb) {
uint8_t *ptr;
avio_close_dyn_buf(rtpctx->pb, &ptr);
diff --git a/libavformat/rtpenc_chain.h b/libavformat/rtpenc_chain.h
index 4117239402..21c27c145d 100644
--- a/libavformat/rtpenc_chain.h
+++ b/libavformat/rtpenc_chain.h
@@ -2,20 +2,20 @@
* RTP muxer chaining code
* Copyright (c) 2010 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpenc_h261.c b/libavformat/rtpenc_h261.c
index 514e7bf9ca..fc50285f5e 100644
--- a/libavformat/rtpenc_h261.c
+++ b/libavformat/rtpenc_h261.c
@@ -2,20 +2,20 @@
* RTP packetization for H.261 video (RFC 4587)
* Copyright (c) 2014 Thomas Volkert <thomas@homer-conferencing.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -24,8 +24,8 @@
#define RTP_H261_HEADER_SIZE 4
-static const uint8_t *find_resync_marker_reverse(const uint8_t *restrict start,
- const uint8_t *restrict end)
+static const uint8_t *find_resync_marker_reverse(const uint8_t *av_restrict start,
+ const uint8_t *av_restrict end)
{
const uint8_t *p = end - 1;
start += 1; /* Make sure we never return the original start. */
diff --git a/libavformat/rtpenc_h263.c b/libavformat/rtpenc_h263.c
index 87f0bd7981..9cea013013 100644
--- a/libavformat/rtpenc_h263.c
+++ b/libavformat/rtpenc_h263.c
@@ -3,28 +3,28 @@
* Copyright (c) 2009 Luca Abeni
* Copyright (c) 2009 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "avformat.h"
#include "rtpenc.h"
-const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *restrict start,
- const uint8_t *restrict end)
+const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *av_restrict start,
+ const uint8_t *av_restrict end)
{
const uint8_t *p = end - 1;
start += 1; /* Make sure we never return the original start. */
diff --git a/libavformat/rtpenc_h263_rfc2190.c b/libavformat/rtpenc_h263_rfc2190.c
index f714d010f4..34b39065b8 100644
--- a/libavformat/rtpenc_h263_rfc2190.c
+++ b/libavformat/rtpenc_h263_rfc2190.c
@@ -2,20 +2,20 @@
* RTP packetization for H.263 video
* Copyright (c) 2012 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -150,9 +150,12 @@ void ff_rtp_send_h263_rfc2190(AVFormatContext *s1, const uint8_t *buf, int size,
}
if (mb_info_pos < mb_info_count) {
const uint8_t *ptr = &mb_info[12*mb_info_pos];
+ /* get position in bits in the input packet at which the next info block should be used */
uint32_t bit_pos = AV_RL32(ptr);
- uint32_t pos = (bit_pos + 7)/8;
- if (pos <= end - buf_base) {
+ /* get position in bytes */
+ uint32_t pos_next_mb_info = (bit_pos + 7)/8;
+ /* check if data from the next MB info block should be used */
+ if (pos_next_mb_info <= end - buf_base) {
state.quant = ptr[4];
state.gobn = ptr[5];
state.mba = AV_RL16(&ptr[6]);
@@ -160,13 +163,9 @@ void ff_rtp_send_h263_rfc2190(AVFormatContext *s1, const uint8_t *buf, int size,
state.vmv1 = (int8_t) ptr[9];
state.hmv2 = (int8_t) ptr[10];
state.vmv2 = (int8_t) ptr[11];
- ebits = 8 * pos - bit_pos;
- len = pos - (buf - buf_base);
+ ebits = 8 * pos_next_mb_info - bit_pos;
+ len = pos_next_mb_info - (buf - buf_base);
mb_info_pos++;
- } else {
- av_log(s1, AV_LOG_ERROR,
- "Unable to split H263 packet, use -mb_info %d "
- "or lower.\n", s->max_payload_size - 8);
}
} else {
av_log(s1, AV_LOG_ERROR, "Unable to split H263 packet, "
diff --git a/libavformat/rtpenc_h264.c b/libavformat/rtpenc_h264.c
index 395c3a42e9..d2b2ed81b7 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_hevc.c b/libavformat/rtpenc_hevc.c
index c6474dcdaa..ce661fa1a8 100644
--- a/libavformat/rtpenc_hevc.c
+++ b/libavformat/rtpenc_hevc.c
@@ -2,20 +2,20 @@
* RTP packetizer for HEVC/H.265 payload format (draft version 6)
* Copyright (c) 2014 Thomas Volkert <thomas@homer-conferencing.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpenc_jpeg.c b/libavformat/rtpenc_jpeg.c
index 9d0915b04b..c35332953c 100644
--- a/libavformat/rtpenc_jpeg.c
+++ b/libavformat/rtpenc_jpeg.c
@@ -2,20 +2,20 @@
* RTP JPEG-compressed video Packetizer, RFC 2435
* Copyright (c) 2012 Samuel Pitoiset
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpenc_latm.c b/libavformat/rtpenc_latm.c
index 64676771a7..4430c44afe 100644
--- a/libavformat/rtpenc_latm.c
+++ b/libavformat/rtpenc_latm.c
@@ -2,20 +2,20 @@
* RTP Packetization of MPEG-4 Audio (RFC 3016)
* Copyright (c) 2011 Juan Carlos Rodriguez <ing.juancarlosrodriguez@hotmail.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpenc_mpegts.c b/libavformat/rtpenc_mpegts.c
index f170f97934..e0339bd58d 100644
--- a/libavformat/rtpenc_mpegts.c
+++ b/libavformat/rtpenc_mpegts.c
@@ -2,20 +2,20 @@
* RTP/mpegts muxer
* Copyright (c) 2011 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpenc_mpv.c b/libavformat/rtpenc_mpv.c
index 70248c408a..4b45f51772 100644
--- a/libavformat/rtpenc_mpv.c
+++ b/libavformat/rtpenc_mpv.c
@@ -3,20 +3,20 @@
* Copyright (c) 2002 Fabrice Bellard
* Copyright (c) 2007 Luca Abeni
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpenc_vp8.c b/libavformat/rtpenc_vp8.c
index d24c8bc40d..671d245758 100644
--- a/libavformat/rtpenc_vp8.c
+++ b/libavformat/rtpenc_vp8.c
@@ -2,20 +2,20 @@
* RTP VP8 Packetizer
* Copyright (c) 2010 Josh Allmann
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpenc_xiph.c b/libavformat/rtpenc_xiph.c
index 07086b1a12..bc40cf88f8 100644
--- a/libavformat/rtpenc_xiph.c
+++ b/libavformat/rtpenc_xiph.c
@@ -2,23 +2,24 @@
* RTP packetization for Xiph audio and video
* Copyright (c) 2010 Josh Allmann
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/avassert.h"
#include "avformat.h"
#include "rtpenc.h"
@@ -72,7 +73,7 @@ void ff_rtp_send_xiph(AVFormatContext *s1, const uint8_t *buff, int size)
uint8_t *ptr = s->buf_ptr + 2 + size; // what we're going to write
int remaining = end_ptr - ptr;
- assert(s->num_frames <= s->max_frames_per_packet);
+ av_assert1(s->num_frames <= s->max_frames_per_packet);
if ((s->num_frames > 0 && remaining < 0) ||
s->num_frames == s->max_frames_per_packet) {
// send previous packets now; no room for new data
diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c
index 5bff00ed7a..b6b89247e7 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
*/
@@ -192,6 +192,7 @@ static void build_udp_url(char *buf, int buf_size,
const char *hostname, int port,
int local_port, int ttl,
int max_packet_size, int connect,
+ int dscp,
const char *include_sources,
const char *exclude_sources)
{
@@ -204,6 +205,9 @@ 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");
+ if (dscp >= 0)
+ url_add_option(buf, buf_size, "dscp=%d", dscp);
+ url_add_option(buf, buf_size, "fifo_size=0");
if (include_sources && include_sources[0])
url_add_option(buf, buf_size, "sources=%s", include_sources);
if (exclude_sources && exclude_sources[0])
@@ -263,6 +267,7 @@ static void rtp_parse_addr_list(URLContext *h, char *buf,
* 'sources=ip[,ip]' : list allowed source IP addresses
* 'block=ip[,ip]' : list disallowed source IP addresses
* 'write_to_source=0/1' : send packets to the source address of the latest received packet
+ * 'dscp=n' : set DSCP value to n (QoS)
* deprecated option:
* 'localport=n' : set the local port to n
*
@@ -277,11 +282,12 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
RTPContext *s = h->priv_data;
int rtp_port, rtcp_port,
ttl, connect,
- local_rtp_port, local_rtcp_port, max_packet_size;
+ local_rtp_port, local_rtcp_port, max_packet_size, dscp;
char hostname[256], include_sources[1024] = "", exclude_sources[1024] = "";
char buf[1024];
char path[1024];
const char *p;
+ int i, max_retry_count = 3;
av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &rtp_port,
path, sizeof(path), uri);
@@ -292,6 +298,7 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
local_rtcp_port = -1;
max_packet_size = -1;
connect = 0;
+ dscp = -1;
p = strchr(uri, '?');
if (p) {
@@ -319,6 +326,9 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
if (av_find_info_tag(buf, sizeof(buf), "write_to_source", p)) {
s->write_to_source = strtol(buf, NULL, 10);
}
+ if (av_find_info_tag(buf, sizeof(buf), "dscp", p)) {
+ dscp = strtol(buf, NULL, 10);
+ }
if (av_find_info_tag(buf, sizeof(buf), "sources", p)) {
av_strlcpy(include_sources, buf, sizeof(include_sources));
rtp_parse_addr_list(h, buf, &s->ssm_include_addrs, &s->nb_ssm_include_addrs);
@@ -329,19 +339,35 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
}
}
- build_udp_url(buf, sizeof(buf),
- hostname, rtp_port, local_rtp_port, ttl, max_packet_size,
- connect, include_sources, exclude_sources);
- if (ffurl_open(&s->rtp_hd, buf, flags, &h->interrupt_callback, NULL) < 0)
- goto fail;
- if (local_rtp_port>=0 && local_rtcp_port<0)
- local_rtcp_port = ff_udp_get_local_port(s->rtp_hd) + 1;
-
- build_udp_url(buf, sizeof(buf),
- hostname, rtcp_port, local_rtcp_port, ttl, max_packet_size,
- connect, include_sources, exclude_sources);
- if (ffurl_open(&s->rtcp_hd, buf, flags, &h->interrupt_callback, NULL) < 0)
- goto fail;
+ for (i = 0;i < max_retry_count;i++) {
+ build_udp_url(buf, sizeof(buf),
+ hostname, rtp_port, local_rtp_port, ttl, max_packet_size,
+ connect, dscp, include_sources, exclude_sources);
+ if (ffurl_open(&s->rtp_hd, buf, flags, &h->interrupt_callback, NULL) < 0)
+ goto fail;
+ local_rtp_port = ff_udp_get_local_port(s->rtp_hd);
+ if(local_rtp_port == 65535) {
+ local_rtp_port = -1;
+ continue;
+ }
+ if (local_rtcp_port<0) {
+ local_rtcp_port = local_rtp_port + 1;
+ build_udp_url(buf, sizeof(buf),
+ hostname, rtcp_port, local_rtcp_port, ttl, max_packet_size,
+ connect, dscp, include_sources, exclude_sources);
+ if (ffurl_open(&s->rtcp_hd, buf, flags, &h->interrupt_callback, NULL) < 0) {
+ local_rtp_port = local_rtcp_port = -1;
+ continue;
+ }
+ break;
+ }
+ build_udp_url(buf, sizeof(buf),
+ hostname, rtcp_port, local_rtcp_port, ttl, max_packet_size,
+ connect, dscp, include_sources, exclude_sources);
+ if (ffurl_open(&s->rtcp_hd, buf, flags, &h->interrupt_callback, NULL) < 0)
+ goto fail;
+ break;
+ }
/* just to ease handle access. XXX: need to suppress direct handle
access */
@@ -484,10 +510,10 @@ static int rtp_close(URLContext *h)
int i;
for (i = 0; i < s->nb_ssm_include_addrs; i++)
- av_free(s->ssm_include_addrs[i]);
+ av_freep(&s->ssm_include_addrs[i]);
av_freep(&s->ssm_include_addrs);
for (i = 0; i < s->nb_ssm_exclude_addrs; i++)
- av_free(s->ssm_exclude_addrs[i]);
+ av_freep(&s->ssm_exclude_addrs[i]);
av_freep(&s->ssm_exclude_addrs);
ffurl_close(s->rtp_hd);
diff --git a/libavformat/rtpproto.h b/libavformat/rtpproto.h
index 7acc80ef8e..5b243fb248 100644
--- a/libavformat/rtpproto.h
+++ b/libavformat/rtpproto.h
@@ -1,20 +1,20 @@
/*
* RTP network protocol
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c
index 2200f6ec07..2197671782 100644
--- a/libavformat/rtsp.c
+++ b/libavformat/rtsp.c
@@ -2,23 +2,24 @@
* RTSP/SDP client
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/avassert.h"
#include "libavutil/base64.h"
#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
@@ -64,46 +65,50 @@
#define RTSP_FLAG_OPTS(name, longname) \
{ name, longname, OFFSET(rtsp_flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, DEC, "rtsp_flags" }, \
- { "filter_src", "Only receive packets from the negotiated peer IP", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_FILTER_SRC}, 0, 0, DEC, "rtsp_flags" }
+ { "filter_src", "only receive packets from the negotiated peer IP", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_FILTER_SRC}, 0, 0, DEC, "rtsp_flags" }
#define RTSP_MEDIATYPE_OPTS(name, longname) \
- { name, longname, OFFSET(media_type_mask), AV_OPT_TYPE_FLAGS, { .i64 = (1 << (AVMEDIA_TYPE_DATA+1)) - 1 }, INT_MIN, INT_MAX, DEC, "allowed_media_types" }, \
+ { name, longname, OFFSET(media_type_mask), AV_OPT_TYPE_FLAGS, { .i64 = (1 << (AVMEDIA_TYPE_SUBTITLE+1)) - 1 }, INT_MIN, INT_MAX, DEC, "allowed_media_types" }, \
{ "video", "Video", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << AVMEDIA_TYPE_VIDEO}, 0, 0, DEC, "allowed_media_types" }, \
{ "audio", "Audio", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << AVMEDIA_TYPE_AUDIO}, 0, 0, DEC, "allowed_media_types" }, \
- { "data", "Data", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << AVMEDIA_TYPE_DATA}, 0, 0, DEC, "allowed_media_types" }
+ { "data", "Data", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << AVMEDIA_TYPE_DATA}, 0, 0, DEC, "allowed_media_types" }, \
+ { "subtitle", "Subtitle", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << AVMEDIA_TYPE_SUBTITLE}, 0, 0, DEC, "allowed_media_types" }
#define RTSP_REORDERING_OPTS() \
- { "reorder_queue_size", "Number of packets to buffer for handling of reordered packets", OFFSET(reordering_queue_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, DEC }
+ { "reorder_queue_size", "set number of packets to buffer for handling of reordered packets", OFFSET(reordering_queue_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, DEC }
const AVOption ff_rtsp_options[] = {
- { "initial_pause", "Don't start playing the stream immediately", OFFSET(initial_pause), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC },
+ { "initial_pause", "do not start playing the stream immediately", OFFSET(initial_pause), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC },
FF_RTP_FLAG_OPTS(RTSPState, rtp_muxer_flags),
- { "rtsp_transport", "RTSP transport protocols", OFFSET(lower_transport_mask), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, DEC|ENC, "rtsp_transport" }, \
+ { "rtsp_transport", "set RTSP transport protocols", OFFSET(lower_transport_mask), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, DEC|ENC, "rtsp_transport" }, \
{ "udp", "UDP", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_UDP}, 0, 0, DEC|ENC, "rtsp_transport" }, \
{ "tcp", "TCP", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_TCP}, 0, 0, DEC|ENC, "rtsp_transport" }, \
{ "udp_multicast", "UDP multicast", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_UDP_MULTICAST}, 0, 0, DEC, "rtsp_transport" },
{ "http", "HTTP tunneling", 0, AV_OPT_TYPE_CONST, {.i64 = (1 << RTSP_LOWER_TRANSPORT_HTTP)}, 0, 0, DEC, "rtsp_transport" },
- RTSP_FLAG_OPTS("rtsp_flags", "RTSP flags"),
- { "listen", "Wait for incoming connections", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_LISTEN}, 0, 0, DEC, "rtsp_flags" },
- RTSP_MEDIATYPE_OPTS("allowed_media_types", "Media types to accept from the server"),
- { "min_port", "Minimum local UDP port", OFFSET(rtp_port_min), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MIN}, 0, 65535, DEC|ENC },
- { "max_port", "Maximum local UDP port", OFFSET(rtp_port_max), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MAX}, 0, 65535, DEC|ENC },
- { "timeout", "Maximum timeout (in seconds) to wait for incoming connections. -1 is infinite. Implies flag listen", OFFSET(initial_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, DEC },
+ RTSP_FLAG_OPTS("rtsp_flags", "set RTSP flags"),
+ { "listen", "wait for incoming connections", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_LISTEN}, 0, 0, DEC, "rtsp_flags" },
+ { "prefer_tcp", "try RTP via TCP first, if available", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_PREFER_TCP}, 0, 0, DEC|ENC, "rtsp_flags" },
+ RTSP_MEDIATYPE_OPTS("allowed_media_types", "set media types to accept from the server"),
+ { "min_port", "set minimum local UDP port", OFFSET(rtp_port_min), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MIN}, 0, 65535, DEC|ENC },
+ { "max_port", "set maximum local UDP port", OFFSET(rtp_port_max), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MAX}, 0, 65535, DEC|ENC },
+ { "timeout", "set maximum timeout (in seconds) to wait for incoming connections (-1 is infinite, imply flag listen)", OFFSET(initial_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, DEC },
+ { "stimeout", "set timeout (in microseconds) of socket TCP I/O operations", OFFSET(stimeout), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC },
RTSP_REORDERING_OPTS(),
+ { "user-agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC },
{ NULL },
};
static const AVOption sdp_options[] = {
RTSP_FLAG_OPTS("sdp_flags", "SDP flags"),
- { "custom_io", "Use custom IO", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_CUSTOM_IO}, 0, 0, DEC, "rtsp_flags" },
- { "rtcp_to_source", "Send RTCP packets to the source address of received packets", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_RTCP_TO_SOURCE}, 0, 0, DEC, "rtsp_flags" },
- RTSP_MEDIATYPE_OPTS("allowed_media_types", "Media types to accept from the server"),
+ { "custom_io", "use custom I/O", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_CUSTOM_IO}, 0, 0, DEC, "rtsp_flags" },
+ { "rtcp_to_source", "send RTCP packets to the source address of received packets", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_RTCP_TO_SOURCE}, 0, 0, DEC, "rtsp_flags" },
+ RTSP_MEDIATYPE_OPTS("allowed_media_types", "set media types to accept from the server"),
RTSP_REORDERING_OPTS(),
{ NULL },
};
static const AVOption rtp_options[] = {
- RTSP_FLAG_OPTS("rtp_flags", "RTP flags"),
+ RTSP_FLAG_OPTS("rtp_flags", "set RTP flags"),
RTSP_REORDERING_OPTS(),
{ NULL },
};
@@ -395,6 +400,8 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
codec_type = AVMEDIA_TYPE_VIDEO;
} else if (!strcmp(st_type, "application")) {
codec_type = AVMEDIA_TYPE_DATA;
+ } else if (!strcmp(st_type, "text")) {
+ codec_type = AVMEDIA_TYPE_SUBTITLE;
}
if (codec_type == AVMEDIA_TYPE_UNKNOWN || !(rt->media_type_mask & (1 << codec_type))) {
s1->skip_media = 1;
@@ -435,7 +442,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
/* no corresponding stream */
if (rt->transport == RTSP_TRANSPORT_RAW) {
if (CONFIG_RTPDEC && !rt->ts)
- rt->ts = ff_mpegts_parse_open(s);
+ rt->ts = avpriv_mpegts_parse_open(s);
} else {
RTPDynamicProtocolHandler *handler;
handler = ff_rtp_handler_find_by_id(
@@ -470,6 +477,8 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
handler->init(s, st->index,
rtsp_st->dynamic_protocol_context);
}
+ if (rt->default_lang[0])
+ av_dict_set(&st->metadata, "language", rt->default_lang, 0);
}
/* put a default control url */
av_strlcpy(rtsp_st->control_url, rt->control_uri,
@@ -533,6 +542,16 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
/* AV_NOPTS_VALUE means live broadcast (and can't seek) */
s->duration = (end == AV_NOPTS_VALUE) ?
AV_NOPTS_VALUE : end - start;
+ } else if (av_strstart(p, "lang:", &p)) {
+ if (s->nb_streams > 0) {
+ get_word(buf1, sizeof(buf1), &p);
+ rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1];
+ if (rtsp_st->stream_index >= 0) {
+ st = s->streams[rtsp_st->stream_index];
+ av_dict_set(&st->metadata, "language", buf1, 0);
+ }
+ } else
+ get_word(rt->default_lang, sizeof(rt->default_lang), &p);
} else if (av_strstart(p, "IsRealDataType:integer;",&p)) {
if (atoi(p) == 1)
rt->transport = RTSP_TRANSPORT_RDT;
@@ -648,13 +667,13 @@ int ff_sdp_parse(AVFormatContext *s, const char *content)
}
for (i = 0; i < s1->nb_default_include_source_addrs; i++)
- av_free(s1->default_include_source_addrs[i]);
+ av_freep(&s1->default_include_source_addrs[i]);
av_freep(&s1->default_include_source_addrs);
for (i = 0; i < s1->nb_default_exclude_source_addrs; i++)
- av_free(s1->default_exclude_source_addrs[i]);
+ av_freep(&s1->default_exclude_source_addrs[i]);
av_freep(&s1->default_exclude_source_addrs);
- rt->p = av_malloc(sizeof(struct pollfd)*2*(rt->nb_rtsp_streams+1));
+ rt->p = av_malloc_array(rt->nb_rtsp_streams + 1, sizeof(struct pollfd) * 2);
if (!rt->p) return AVERROR(ENOMEM);
return 0;
}
@@ -680,7 +699,7 @@ void ff_rtsp_undo_setup(AVFormatContext *s, int send_packets)
avio_close_dyn_buf(rtpctx->pb, &ptr);
av_free(ptr);
} else {
- avio_close(rtpctx->pb);
+ avio_closep(&rtpctx->pb);
}
avformat_free_context(rtpctx);
} else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RDT)
@@ -710,23 +729,23 @@ void ff_rtsp_close_streams(AVFormatContext *s)
rtsp_st->dynamic_handler->free(
rtsp_st->dynamic_protocol_context);
for (j = 0; j < rtsp_st->nb_include_source_addrs; j++)
- av_free(rtsp_st->include_source_addrs[j]);
+ av_freep(&rtsp_st->include_source_addrs[j]);
av_freep(&rtsp_st->include_source_addrs);
for (j = 0; j < rtsp_st->nb_exclude_source_addrs; j++)
- av_free(rtsp_st->exclude_source_addrs[j]);
+ av_freep(&rtsp_st->exclude_source_addrs[j]);
av_freep(&rtsp_st->exclude_source_addrs);
- av_free(rtsp_st);
+ av_freep(&rtsp_st);
}
}
- av_free(rt->rtsp_streams);
+ av_freep(&rt->rtsp_streams);
if (rt->asf_ctx) {
avformat_close_input(&rt->asf_ctx);
}
if (CONFIG_RTPDEC && rt->ts)
- ff_mpegts_parse_close(rt->ts);
- av_free(rt->p);
- av_free(rt->recvbuf);
+ avpriv_mpegts_parse_close(rt->ts);
+ av_freep(&rt->p);
+ av_freep(&rt->recvbuf);
}
int ff_rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st)
@@ -1154,7 +1173,7 @@ start:
if (content_ptr)
*content_ptr = content;
else
- av_free(content);
+ av_freep(&content);
if (request) {
char buf[1024];
@@ -1243,7 +1262,7 @@ static int rtsp_send_cmd_with_content_async(AVFormatContext *s,
if (headers)
av_strlcat(buf, headers, sizeof(buf));
av_strlcatf(buf, sizeof(buf), "CSeq: %d\r\n", rt->seq);
- av_strlcatf(buf, sizeof(buf), "User-Agent: %s\r\n", LIBAVFORMAT_IDENT);
+ av_strlcatf(buf, sizeof(buf), "User-Agent: %s\r\n", rt->user_agent);
if (rt->session_id[0] != '\0' && (!headers ||
!strstr(headers, "\nIf-Match:"))) {
av_strlcatf(buf, sizeof(buf), "Session: %s\r\n", rt->session_id);
@@ -1354,10 +1373,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. */
@@ -1411,7 +1426,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;
@@ -1479,7 +1493,7 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port,
goto fail;
} else if (reply->status_code != RTSP_STATUS_OK ||
reply->nb_transports != 1) {
- err = AVERROR_INVALIDDATA;
+ err = ff_rtsp_averror(reply->status_code, AVERROR_INVALIDDATA);
goto fail;
}
@@ -1738,12 +1752,14 @@ redirect:
goto fail;
}
} else {
+ int ret;
/* open the tcp connection */
ff_url_join(tcpname, sizeof(tcpname), lower_rtsp_proto, NULL,
- host, port, NULL);
- if (ffurl_open(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, NULL) < 0) {
- err = AVERROR(EIO);
+ host, port,
+ "?timeout=%d", rt->stimeout);
+ if ((ret = ffurl_open(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
+ &s->interrupt_callback, NULL)) < 0) {
+ err = ret;
goto fail;
}
rt->rtsp_hd_out = rt->rtsp_hd;
@@ -1782,7 +1798,7 @@ redirect:
sizeof(cmd));
ff_rtsp_send_cmd(s, "OPTIONS", rt->control_uri, cmd, reply, NULL);
if (reply->status_code != RTSP_STATUS_OK) {
- err = AVERROR_INVALIDDATA;
+ err = ff_rtsp_averror(reply->status_code, AVERROR_INVALIDDATA);
goto fail;
}
@@ -1801,6 +1817,8 @@ redirect:
err = ff_rtsp_setup_input_streams(s, reply);
else if (CONFIG_RTSP_MUXER)
err = ff_rtsp_setup_output_streams(s, host);
+ else
+ av_assert0(0);
if (err)
goto fail;
@@ -1808,6 +1826,10 @@ redirect:
int lower_transport = ff_log2_tab[lower_transport_mask &
~(lower_transport_mask - 1)];
+ if ((lower_transport_mask & (1 << RTSP_LOWER_TRANSPORT_TCP))
+ && (rt->rtsp_flags & RTSP_FLAG_PREFER_TCP))
+ lower_transport = RTSP_LOWER_TRANSPORT_TCP;
+
err = ff_rtsp_make_setup_request(s, host, port, lower_transport,
rt->server_type == RTSP_SERVER_REAL ?
real_challenge : NULL);
@@ -1882,7 +1904,7 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
p[max_p].fd = fds[fdsidx];
p[max_p++].events = POLLIN;
}
- av_free(fds);
+ av_freep(&fds);
}
}
n = poll(p, max_p, POLL_TIMEOUT_MS);
@@ -1992,7 +2014,7 @@ int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt)
} else if (rt->transport == RTSP_TRANSPORT_RTP) {
ret = ff_rtp_parse_packet(rt->cur_transport_priv, pkt, NULL, 0);
} else if (CONFIG_RTPDEC && rt->ts) {
- ret = ff_mpegts_parse_packet(rt->ts, pkt, rt->recvbuf + rt->recvbuf_pos, rt->recvbuf_len - rt->recvbuf_pos);
+ ret = avpriv_mpegts_parse_packet(rt->ts, pkt, rt->recvbuf + rt->recvbuf_pos, rt->recvbuf_len - rt->recvbuf_pos);
if (ret >= 0) {
rt->recvbuf_pos += ret;
ret = rt->recvbuf_pos < rt->recvbuf_len;
@@ -2109,6 +2131,16 @@ redo:
st2->time_base);
}
}
+ // Make real NTP start time available in AVFormatContext
+ if (s->start_time_realtime == AV_NOPTS_VALUE) {
+ s->start_time_realtime = av_rescale (rtpctx->first_rtcp_ntp_time - (NTP_OFFSET << 32), 1000000, 1LL << 32);
+ if (rtpctx->st) {
+ s->start_time_realtime -=
+ av_rescale (rtpctx->rtcp_ts_offset,
+ (uint64_t) rtpctx->st->time_base.num * 1000000,
+ rtpctx->st->time_base.den);
+ }
+ }
}
if (ret == -RTCP_BYE) {
rt->nb_byes++;
@@ -2121,7 +2153,7 @@ redo:
}
}
} else if (CONFIG_RTPDEC && rt->ts) {
- ret = ff_mpegts_parse_packet(rt->ts, pkt, rt->recvbuf, len);
+ ret = avpriv_mpegts_parse_packet(rt->ts, pkt, rt->recvbuf, len);
if (ret >= 0) {
if (ret < len) {
rt->recvbuf_len = len;
@@ -2204,7 +2236,7 @@ static int sdp_read_header(AVFormatContext *s)
content[size] ='\0';
err = ff_sdp_parse(s, content);
- av_free(content);
+ av_freep(&content);
if (err) goto fail;
/* open each RTP stream */
@@ -2355,7 +2387,7 @@ static int rtp_read_header(AVFormatContext *s)
/* sdp_read_header initializes this again */
ff_network_close();
- rt->media_type_mask = (1 << (AVMEDIA_TYPE_DATA+1)) - 1;
+ rt->media_type_mask = (1 << (AVMEDIA_TYPE_SUBTITLE+1)) - 1;
ret = sdp_read_header(s);
s->pb = NULL;
diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h
index e83ad54909..3cfcfb6d00 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
@@ -76,7 +76,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
@@ -392,9 +392,21 @@ typedef struct RTSPState {
int initial_timeout;
/**
+ * timeout of socket i/o operations.
+ */
+ int stimeout;
+
+ /**
* Size of RTP packet reordering queue.
*/
int reordering_queue_size;
+
+ /**
+ * User-Agent string
+ */
+ char *user_agent;
+
+ char default_lang[4];
} RTSPState;
#define RTSP_FLAG_FILTER_SRC 0x1 /**< Filter incoming UDP packets -
@@ -404,6 +416,7 @@ typedef struct RTSPState {
#define RTSP_FLAG_CUSTOM_IO 0x4 /**< Do all IO via the AVIOContext. */
#define RTSP_FLAG_RTCP_TO_SOURCE 0x8 /**< Send RTCP packets to the source
address of received packets. */
+#define RTSP_FLAG_PREFER_TCP 0x10 /**< Try RTP via TCP first if possible. */
typedef struct RTSPSource {
char addr[128]; /**< Source-specific multicast include source IP address (from SDP content) */
diff --git a/libavformat/rtspcodes.h b/libavformat/rtspcodes.h
index 31ab33699c..0ae490a42e 100644
--- a/libavformat/rtspcodes.h
+++ b/libavformat/rtspcodes.h
@@ -1,42 +1,131 @@
/*
* RTSP definitions
* copyright (c) 2002 Fabrice Bellard
+ * copyright (c) 2014 Samsung Electronics. All rights reserved.
+ * @Author: Reynaldo H. Verdejo Pinochet <r.verdejo@sisa.samsung.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef AVFORMAT_RTSPCODES_H
#define AVFORMAT_RTSPCODES_H
+#include "libavutil/common.h"
+#include "libavformat/http.h"
+
/** RTSP handling */
enum RTSPStatusCode {
-RTSP_STATUS_OK =200, /**< OK */
-RTSP_STATUS_METHOD =405, /**< Method Not Allowed */
-RTSP_STATUS_BANDWIDTH =453, /**< Not Enough Bandwidth */
-RTSP_STATUS_SESSION =454, /**< Session Not Found */
-RTSP_STATUS_STATE =455, /**< Method Not Valid in This State */
-RTSP_STATUS_AGGREGATE =459, /**< Aggregate operation not allowed */
-RTSP_STATUS_ONLY_AGGREGATE =460, /**< Only aggregate operation allowed */
-RTSP_STATUS_TRANSPORT =461, /**< Unsupported transport */
-RTSP_STATUS_INTERNAL =500, /**< Internal Server Error */
-RTSP_STATUS_SERVICE =503, /**< Service Unavailable */
-RTSP_STATUS_VERSION =505, /**< RTSP Version not supported */
+RTSP_STATUS_CONTINUE =100,
+RTSP_STATUS_OK =200,
+RTSP_STATUS_CREATED =201,
+RTSP_STATUS_LOW_ON_STORAGE_SPACE =250,
+RTSP_STATUS_MULTIPLE_CHOICES =300,
+RTSP_STATUS_MOVED_PERMANENTLY =301,
+RTSP_STATUS_MOVED_TEMPORARILY =302,
+RTSP_STATUS_SEE_OTHER =303,
+RTSP_STATUS_NOT_MODIFIED =304,
+RTSP_STATUS_USE_PROXY =305,
+RTSP_STATUS_BAD_REQUEST =400,
+RTSP_STATUS_UNAUTHORIZED =401,
+RTSP_STATUS_PAYMENT_REQUIRED =402,
+RTSP_STATUS_FORBIDDEN =403,
+RTSP_STATUS_NOT_FOUND =404,
+RTSP_STATUS_METHOD =405,
+RTSP_STATUS_NOT_ACCEPTABLE =406,
+RTSP_STATUS_PROXY_AUTH_REQUIRED =407,
+RTSP_STATUS_REQ_TIME_OUT =408,
+RTSP_STATUS_GONE =410,
+RTSP_STATUS_LENGTH_REQUIRED =411,
+RTSP_STATUS_PRECONDITION_FAILED =412,
+RTSP_STATUS_REQ_ENTITY_2LARGE =413,
+RTSP_STATUS_REQ_URI_2LARGE =414,
+RTSP_STATUS_UNSUPPORTED_MTYPE =415,
+RTSP_STATUS_PARAM_NOT_UNDERSTOOD =451,
+RTSP_STATUS_CONFERENCE_NOT_FOUND =452,
+RTSP_STATUS_BANDWIDTH =453,
+RTSP_STATUS_SESSION =454,
+RTSP_STATUS_STATE =455,
+RTSP_STATUS_INVALID_HEADER_FIELD =456,
+RTSP_STATUS_INVALID_RANGE =457,
+RTSP_STATUS_RONLY_PARAMETER =458,
+RTSP_STATUS_AGGREGATE =459,
+RTSP_STATUS_ONLY_AGGREGATE =460,
+RTSP_STATUS_TRANSPORT =461,
+RTSP_STATUS_UNREACHABLE =462,
+RTSP_STATUS_INTERNAL =500,
+RTSP_STATUS_NOT_IMPLEMENTED =501,
+RTSP_STATUS_BAD_GATEWAY =502,
+RTSP_STATUS_SERVICE =503,
+RTSP_STATUS_GATEWAY_TIME_OUT =504,
+RTSP_STATUS_VERSION =505,
+RTSP_STATUS_UNSUPPORTED_OPTION =551,
+};
+
+static const av_unused char * const rtsp_status_strings[] = {
+[RTSP_STATUS_CONTINUE] ="Continue",
+[RTSP_STATUS_OK] ="OK",
+[RTSP_STATUS_CREATED] ="Created",
+[RTSP_STATUS_LOW_ON_STORAGE_SPACE] ="Low on Storage Space",
+[RTSP_STATUS_MULTIPLE_CHOICES] ="Multiple Choices",
+[RTSP_STATUS_MOVED_PERMANENTLY] ="Moved Permanently",
+[RTSP_STATUS_MOVED_TEMPORARILY] ="Moved Temporarily",
+[RTSP_STATUS_SEE_OTHER] ="See Other",
+[RTSP_STATUS_NOT_MODIFIED] ="Not Modified",
+[RTSP_STATUS_USE_PROXY] ="Use Proxy",
+[RTSP_STATUS_BAD_REQUEST] ="Bad Request",
+[RTSP_STATUS_UNAUTHORIZED] ="Unauthorized",
+[RTSP_STATUS_PAYMENT_REQUIRED] ="Payment Required",
+[RTSP_STATUS_FORBIDDEN] ="Forbidden",
+[RTSP_STATUS_NOT_FOUND] ="Not Found",
+[RTSP_STATUS_METHOD] ="Method Not Allowed",
+[RTSP_STATUS_NOT_ACCEPTABLE] ="Not Acceptable",
+[RTSP_STATUS_PROXY_AUTH_REQUIRED] ="Proxy Authentication Required",
+[RTSP_STATUS_REQ_TIME_OUT] ="Request Time-out",
+[RTSP_STATUS_GONE] ="Gone",
+[RTSP_STATUS_LENGTH_REQUIRED] ="Length Required",
+[RTSP_STATUS_PRECONDITION_FAILED] ="Precondition Failed",
+[RTSP_STATUS_REQ_ENTITY_2LARGE] ="Request Entity Too Large",
+[RTSP_STATUS_REQ_URI_2LARGE] ="Request URI Too Large",
+[RTSP_STATUS_UNSUPPORTED_MTYPE] ="Unsupported Media Type",
+[RTSP_STATUS_PARAM_NOT_UNDERSTOOD] ="Parameter Not Understood",
+[RTSP_STATUS_CONFERENCE_NOT_FOUND] ="Conference Not Found",
+[RTSP_STATUS_BANDWIDTH] ="Not Enough Bandwidth",
+[RTSP_STATUS_SESSION] ="Session Not Found",
+[RTSP_STATUS_STATE] ="Method Not Valid in This State",
+[RTSP_STATUS_INVALID_HEADER_FIELD] ="Header Field Not Valid for Resource",
+[RTSP_STATUS_INVALID_RANGE] ="Invalid Range",
+[RTSP_STATUS_RONLY_PARAMETER] ="Parameter Is Read-Only",
+[RTSP_STATUS_AGGREGATE] ="Aggregate Operation no Allowed",
+[RTSP_STATUS_ONLY_AGGREGATE] ="Only Aggregate Operation Allowed",
+[RTSP_STATUS_TRANSPORT] ="Unsupported Transport",
+[RTSP_STATUS_UNREACHABLE] ="Destination Unreachable",
+[RTSP_STATUS_INTERNAL] ="Internal Server Error",
+[RTSP_STATUS_NOT_IMPLEMENTED] ="Not Implemented",
+[RTSP_STATUS_BAD_GATEWAY] ="Bad Gateway",
+[RTSP_STATUS_SERVICE] ="Service Unavailable",
+[RTSP_STATUS_GATEWAY_TIME_OUT] ="Gateway Time-out",
+[RTSP_STATUS_VERSION] ="RTSP Version not Supported",
+[RTSP_STATUS_UNSUPPORTED_OPTION] ="Option not supported",
};
+#define RTSP_STATUS_CODE2STRING(x) (\
+x >= 100 && x < FF_ARRAY_ELEMS(rtsp_status_strings) && rtsp_status_strings[x] \
+)? rtsp_status_strings[x] : NULL
+
enum RTSPMethod {
DESCRIBE,
ANNOUNCE,
@@ -51,4 +140,10 @@ enum RTSPMethod {
RECORD,
UNKNOWN = -1,
};
+
+static inline int ff_rtsp_averror(enum RTSPStatusCode status_code, int default_averror)
+{
+ return ff_http_averror(status_code, default_averror);
+}
+
#endif /* AVFORMAT_RTSPCODES_H */
diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c
index 418f3837b8..c8994988d5 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
*/
@@ -361,8 +361,10 @@ static inline int parse_command_line(AVFormatContext *s, const char *line,
const char *linept, *searchlinept;
linept = strchr(line, ' ');
- if (!linept)
+ if (!linept) {
+ av_log(s, AV_LOG_ERROR, "Error parsing method string\n");
return AVERROR_INVALIDDATA;
+ }
if (linept - line > methodsize - 1) {
av_log(s, AV_LOG_ERROR, "Method string too long\n");
@@ -530,7 +532,7 @@ static int rtsp_read_play(AVFormatContext *s)
}
ff_rtsp_send_cmd(s, "PLAY", rt->control_uri, cmd, reply, NULL);
if (reply->status_code != RTSP_STATUS_OK) {
- return -1;
+ return ff_rtsp_averror(reply->status_code, -1);
}
if (rt->transport == RTSP_TRANSPORT_RTP &&
reply->range_start != AV_NOPTS_VALUE) {
@@ -562,7 +564,7 @@ static int rtsp_read_pause(AVFormatContext *s)
else if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) {
ff_rtsp_send_cmd(s, "PAUSE", rt->control_uri, NULL, reply, NULL);
if (reply->status_code != RTSP_STATUS_OK) {
- return -1;
+ return ff_rtsp_averror(reply->status_code, -1);
}
}
rt->state = RTSP_STATE_PAUSED;
@@ -589,12 +591,12 @@ int ff_rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply)
sizeof(cmd));
}
ff_rtsp_send_cmd(s, "DESCRIBE", rt->control_uri, cmd, reply, &content);
- if (!content)
- return AVERROR_INVALIDDATA;
if (reply->status_code != RTSP_STATUS_OK) {
av_freep(&content);
- return AVERROR_INVALIDDATA;
+ return ff_rtsp_averror(reply->status_code, AVERROR_INVALIDDATA);
}
+ if (!content)
+ return AVERROR_INVALIDDATA;
av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", content);
/* now we got the SDP description, we parse it */
@@ -707,7 +709,7 @@ static int rtsp_read_header(AVFormatContext *s)
return ret;
rt->real_setup_cache = !s->nb_streams ? NULL :
- av_mallocz(2 * s->nb_streams * sizeof(*rt->real_setup_cache));
+ av_mallocz_array(s->nb_streams, 2 * sizeof(*rt->real_setup_cache));
if (!rt->real_setup_cache && s->nb_streams)
return AVERROR(ENOMEM);
rt->real_setup = rt->real_setup_cache + s->nb_streams;
@@ -715,10 +717,10 @@ static int rtsp_read_header(AVFormatContext *s)
if (rt->initial_pause) {
/* do not start immediately */
} else {
- if (rtsp_read_play(s) < 0) {
+ if ((ret = rtsp_read_play(s)) < 0) {
ff_rtsp_close_streams(s);
ff_rtsp_close_connections(s);
- return AVERROR_INVALIDDATA;
+ return ret;
}
}
}
@@ -753,7 +755,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);
@@ -812,7 +814,7 @@ retry:
ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri,
cmd, reply, NULL);
if (reply->status_code != RTSP_STATUS_OK)
- return AVERROR_INVALIDDATA;
+ return ff_rtsp_averror(reply->status_code, AVERROR_INVALIDDATA);
rt->need_subscription = 1;
}
}
@@ -847,7 +849,7 @@ retry:
ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri,
cmd, reply, NULL);
if (reply->status_code != RTSP_STATUS_OK)
- return AVERROR_INVALIDDATA;
+ return ff_rtsp_averror(reply->status_code, AVERROR_INVALIDDATA);
rt->need_subscription = 0;
if (rt->state == RTSP_STATE_STREAMING)
@@ -908,6 +910,7 @@ static int rtsp_read_seek(AVFormatContext *s, int stream_index,
int64_t timestamp, int flags)
{
RTSPState *rt = s->priv_data;
+ int ret;
rt->seek_timestamp = av_rescale_q(timestamp,
s->streams[stream_index]->time_base,
@@ -917,11 +920,11 @@ static int rtsp_read_seek(AVFormatContext *s, int stream_index,
case RTSP_STATE_IDLE:
break;
case RTSP_STATE_STREAMING:
- if (rtsp_read_pause(s) != 0)
- return -1;
+ if ((ret = rtsp_read_pause(s)) != 0)
+ return ret;
rt->state = RTSP_STATE_SEEKING;
- if (rtsp_read_play(s) != 0)
- return -1;
+ if ((ret = rtsp_read_play(s)) != 0)
+ return ret;
break;
case RTSP_STATE_PAUSED:
rt->state = RTSP_STATE_IDLE;
diff --git a/libavformat/rtspenc.c b/libavformat/rtspenc.c
index 3db53aca07..e7707bb5fb 100644
--- a/libavformat/rtspenc.c
+++ b/libavformat/rtspenc.c
@@ -2,20 +2,20 @@
* RTSP muxer
* Copyright (c) 2010 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -51,7 +51,8 @@ int ff_rtsp_setup_output_streams(AVFormatContext *s, const char *addr)
char *sdp;
AVFormatContext sdp_ctx, *ctx_array[1];
- s->start_time_realtime = av_gettime();
+ if (s->start_time_realtime == 0 || s->start_time_realtime == AV_NOPTS_VALUE)
+ s->start_time_realtime = av_gettime();
/* Announce the stream */
sdp = av_mallocz(SDP_MAX_SIZE);
@@ -83,7 +84,7 @@ int ff_rtsp_setup_output_streams(AVFormatContext *s, const char *addr)
reply, NULL, sdp, strlen(sdp));
av_free(sdp);
if (reply->status_code != RTSP_STATUS_OK)
- return AVERROR_INVALIDDATA;
+ return ff_rtsp_averror(reply->status_code, AVERROR_INVALIDDATA);
/* Set up the RTSPStreams for each AVStream */
for (i = 0; i < s->nb_streams; i++) {
@@ -115,7 +116,7 @@ static int rtsp_write_record(AVFormatContext *s)
"Range: npt=0.000-\r\n");
ff_rtsp_send_cmd(s, "RECORD", rt->control_uri, cmd, reply, NULL);
if (reply->status_code != RTSP_STATUS_OK)
- return -1;
+ return ff_rtsp_averror(reply->status_code, -1);
rt->state = RTSP_STATE_STREAMING;
return 0;
}
@@ -211,7 +212,7 @@ static int rtsp_write_packet(AVFormatContext *s, AVPacket *pkt)
rtsp_st = rt->rtsp_streams[pkt->stream_index];
rtpctx = rtsp_st->transport_priv;
- ret = ff_write_chained(rtpctx, 0, pkt, s);
+ ret = ff_write_chained(rtpctx, 0, pkt, s, 0);
/* ff_write_chained does all the RTP packetization. If using TCP as
* transport, rtpctx->pb is only a dyn_packet_buf that queues up the
* packets, so we need to send them out on the TCP connection separately.
diff --git a/libavformat/samidec.c b/libavformat/samidec.c
new file mode 100644
index 0000000000..948e1ed8b1
--- /dev/null
+++ b/libavformat/samidec.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2012 Clément Bœsch
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * SAMI subtitle demuxer
+ * @see http://msdn.microsoft.com/en-us/library/ms971327.aspx
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "subtitles.h"
+#include "libavcodec/internal.h"
+#include "libavutil/avstring.h"
+#include "libavutil/bprint.h"
+#include "libavutil/intreadwrite.h"
+
+typedef struct {
+ FFDemuxSubtitlesQueue q;
+} SAMIContext;
+
+static int sami_probe(AVProbeData *p)
+{
+ char buf[6];
+ FFTextReader tr;
+ ff_text_init_buf(&tr, p->buf, p->buf_size);
+ ff_text_read(&tr, buf, sizeof(buf));
+
+ return !strncmp(buf, "<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;
+ FFTextReader tr;
+ ff_text_init_avio(s, &tr, s->pb);
+
+ 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 (!ff_text_eof(&tr)) {
+ AVPacket *sub;
+ const int64_t pos = ff_text_pos(&tr) - (c != 0);
+ int is_sync, n = ff_smil_extract_next_text_chunk(&tr, &buf, &c);
+
+ if (n == 0)
+ break;
+
+ is_sync = !av_strncasecmp(buf.str, "<SYNC", 5);
+ if (is_sync)
+ got_first_sync_point = 1;
+
+ if (!got_first_sync_point) {
+ av_bprintf(&hdr_buf, "%s", buf.str);
+ } else {
+ sub = ff_subtitles_queue_insert(&sami->q, buf.str, buf.len, !is_sync);
+ if (!sub) {
+ res = AVERROR(ENOMEM);
+ goto end;
+ }
+ if (is_sync) {
+ const char *p = ff_smil_get_attr_ptr(buf.str, "Start");
+ sub->pos = pos;
+ sub->pts = p ? strtol(p, NULL, 10) : 0;
+ sub->duration = -1;
+ }
+ }
+ av_bprint_clear(&buf);
+ }
+
+ res = avpriv_bprint_to_extradata(st->codec, &hdr_buf);
+ if (res < 0)
+ goto end;
+
+ ff_subtitles_queue_finalize(&sami->q);
+
+end:
+ av_bprint_finalize(&buf, NULL);
+ return res;
+}
+
+static int sami_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ SAMIContext *sami = s->priv_data;
+ return ff_subtitles_queue_read_packet(&sami->q, pkt);
+}
+
+static int sami_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ SAMIContext *sami = s->priv_data;
+ return ff_subtitles_queue_seek(&sami->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int sami_read_close(AVFormatContext *s)
+{
+ SAMIContext *sami = s->priv_data;
+ ff_subtitles_queue_clean(&sami->q);
+ return 0;
+}
+
+AVInputFormat ff_sami_demuxer = {
+ .name = "sami",
+ .long_name = NULL_IF_CONFIG_SMALL("SAMI subtitle format"),
+ .priv_data_size = sizeof(SAMIContext),
+ .read_probe = sami_probe,
+ .read_header = sami_read_header,
+ .read_packet = sami_read_packet,
+ .read_seek2 = sami_read_seek,
+ .read_close = sami_read_close,
+ .extensions = "smi,sami",
+};
diff --git a/libavformat/sapdec.c b/libavformat/sapdec.c
index 0280cead9c..2dd8524bce 100644
--- a/libavformat/sapdec.c
+++ b/libavformat/sapdec.c
@@ -2,24 +2,25 @@
* 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
*/
#include "avformat.h"
+#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
#include "network.h"
@@ -158,6 +159,10 @@ static int sap_read_header(AVFormatContext *s)
sap->sdp_ctx->max_delay = s->max_delay;
sap->sdp_ctx->pb = &sap->sdp_pb;
sap->sdp_ctx->interrupt_callback = s->interrupt_callback;
+
+ if ((ret = ff_copy_whitelists(sap->sdp_ctx, s)) < 0)
+ goto fail;
+
ret = avformat_open_input(&sap->sdp_ctx, "temp.sdp", infmt, NULL);
if (ret < 0)
goto fail;
diff --git a/libavformat/sapenc.c b/libavformat/sapenc.c
index 89f7f99ecc..07fbf48e97 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
*/
@@ -49,7 +49,7 @@ static int sap_write_close(AVFormatContext *s)
if (!rtpctx)
continue;
av_write_trailer(rtpctx);
- avio_close(rtpctx->pb);
+ avio_closep(&rtpctx->pb);
avformat_free_context(rtpctx);
s->streams[i]->priv_data = NULL;
}
@@ -134,13 +134,14 @@ static int sap_write_header(AVFormatContext *s)
freeaddrinfo(ai);
}
- contexts = av_mallocz(sizeof(AVFormatContext*) * s->nb_streams);
+ contexts = av_mallocz_array(s->nb_streams, sizeof(AVFormatContext*));
if (!contexts) {
ret = AVERROR(ENOMEM);
goto fail;
}
- s->start_time_realtime = av_gettime();
+ if (s->start_time_realtime == 0 || s->start_time_realtime == AV_NOPTS_VALUE)
+ s->start_time_realtime = av_gettime();
for (i = 0; i < s->nb_streams; i++) {
URLContext *fd;
@@ -255,7 +256,7 @@ static int sap_write_packet(AVFormatContext *s, AVPacket *pkt)
sap->last_time = now;
}
rtpctx = s->streams[pkt->stream_index]->priv_data;
- return ff_write_chained(rtpctx, 0, pkt, s);
+ return ff_write_chained(rtpctx, 0, pkt, s, 0);
}
AVOutputFormat ff_sap_muxer = {
diff --git a/libavformat/sauce.c b/libavformat/sauce.c
index a125241335..a2347b0197 100644
--- a/libavformat/sauce.c
+++ b/libavformat/sauce.c
@@ -2,20 +2,20 @@
* SAUCE header parser
* Copyright (c) 2010 Peter Ross <pross@xvid.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -71,7 +71,7 @@ int ff_sauce_read(AVFormatContext *avctx, uint64_t *fsize, int *got_width, int g
if (get_height && t2)
avctx->streams[0]->codec->height = t2<<4;
} else if (datatype == 5) {
- if (filetype > 1) {
+ if (filetype) {
avctx->streams[0]->codec->width = (filetype == 1 ? t1 : filetype) << 4;
*got_width = 1;
}
diff --git a/libavformat/sauce.h b/libavformat/sauce.h
index 62d8e688a4..0ba9ae5b4a 100644
--- a/libavformat/sauce.h
+++ b/libavformat/sauce.h
@@ -2,20 +2,20 @@
* SAUCE header parser
* Copyright (c) 2010 Peter Ross <pross@xvid.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/sbgdec.c b/libavformat/sbgdec.c
new file mode 100644
index 0000000000..23b7ea4489
--- /dev/null
+++ b/libavformat/sbgdec.c
@@ -0,0 +1,1512 @@
+/*
+ * 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 "libavutil/time_internal.h"
+#include "avformat.h"
+#include "internal.h"
+
+#define SBG_SCALE (1 << 16)
+#define DAY (24 * 60 * 60)
+#define DAY_TS ((int64_t)DAY * AV_TIME_BASE)
+
+struct sbg_demuxer {
+ AVClass *class;
+ int sample_rate;
+ int frame_size;
+ int max_file_size;
+};
+
+struct sbg_string {
+ char *s;
+ char *e;
+};
+
+enum sbg_fade_type {
+ SBG_FADE_SILENCE = 0,
+ SBG_FADE_SAME = 1,
+ SBG_FADE_ADAPT = 3,
+};
+
+struct sbg_fade {
+ int8_t in, out, slide;
+};
+
+enum sbg_synth_type {
+ SBG_TYPE_NONE,
+ SBG_TYPE_SINE,
+ SBG_TYPE_NOISE,
+ SBG_TYPE_BELL,
+ SBG_TYPE_MIX,
+ SBG_TYPE_SPIN,
+};
+
+/* bell: freq constant, ampl decreases exponentially, can be approx lin */
+
+struct sbg_timestamp {
+ int64_t t;
+ char type; /* 0 for relative, 'N' for now, 'T' for absolute */
+};
+
+struct sbg_script_definition {
+ char *name;
+ int name_len;
+ int elements, nb_elements;
+ char type; /* 'S' or 'B' */
+};
+
+struct sbg_script_synth {
+ int carrier;
+ int beat;
+ int vol;
+ enum sbg_synth_type type;
+ struct {
+ int l, r;
+ } ref;
+};
+
+struct sbg_script_tseq {
+ struct sbg_timestamp ts;
+ char *name;
+ int name_len;
+ int lock;
+ struct sbg_fade fade;
+};
+
+struct sbg_script_event {
+ int64_t ts;
+ int64_t ts_int, ts_trans, ts_next;
+ int elements, nb_elements;
+ struct sbg_fade fade;
+};
+
+struct sbg_script {
+ struct sbg_script_definition *def;
+ struct sbg_script_synth *synth;
+ struct sbg_script_tseq *tseq;
+ struct sbg_script_tseq *block_tseq;
+ struct sbg_script_event *events;
+ int nb_def;
+ int nb_tseq;
+ int nb_events;
+ int nb_synth;
+ int64_t start_ts;
+ int64_t end_ts;
+ int64_t opt_fade_time;
+ int64_t opt_duration;
+ char *opt_mix;
+ int sample_rate;
+ uint8_t opt_start_at_first;
+ uint8_t opt_end_at_last;
+};
+
+struct sbg_parser {
+ void *log;
+ char *script, *end;
+ char *cursor;
+ struct sbg_script scs;
+ struct sbg_timestamp current_time;
+ int nb_block_tseq;
+ int nb_def_max, nb_synth_max, nb_tseq_max, nb_block_tseq_max;
+ int line_no;
+ char err_msg[128];
+};
+
+enum ws_interval_type {
+ WS_SINE = MKTAG('S','I','N','E'),
+ WS_NOISE = MKTAG('N','O','I','S'),
+};
+
+struct ws_interval {
+ int64_t ts1, ts2;
+ enum ws_interval_type type;
+ uint32_t channels;
+ int32_t f1, f2;
+ int32_t a1, a2;
+ uint32_t phi;
+};
+
+struct ws_intervals {
+ struct ws_interval *inter;
+ int nb_inter;
+ int max_inter;
+};
+
+static void *alloc_array_elem(void **array, size_t elsize,
+ int *size, int *max_size)
+{
+ void *ret;
+
+ if (*size == *max_size) {
+ int m = FFMAX(32, FFMIN(*max_size, INT_MAX / 2) * 2);
+ if (*size >= m)
+ return NULL;
+ *array = av_realloc_f(*array, m, elsize);
+ if (!*array)
+ return NULL;
+ *max_size = m;
+ }
+ ret = (char *)*array + elsize * *size;
+ memset(ret, 0, elsize);
+ (*size)++;
+ return ret;
+}
+
+static int str_to_time(const char *str, int64_t *rtime)
+{
+ const char *cur = str;
+ char *end;
+ int hours, minutes;
+ double seconds = 0;
+
+ if (*cur < '0' || *cur > '9')
+ return 0;
+ hours = strtol(cur, &end, 10);
+ if (end == cur || *end != ':' || end[1] < '0' || end[1] > '9')
+ return 0;
+ cur = end + 1;
+ minutes = strtol(cur, &end, 10);
+ if (end == cur)
+ return 0;
+ cur = end;
+ if (*end == ':'){
+ seconds = strtod(cur + 1, &end);
+ if (end > cur + 1)
+ cur = end;
+ }
+ *rtime = (hours * 3600 + minutes * 60 + seconds) * AV_TIME_BASE;
+ return cur - str;
+}
+
+static inline int is_space(char c)
+{
+ return c == ' ' || c == '\t' || c == '\r';
+}
+
+static inline int scale_double(void *log, double d, double m, int *r)
+{
+ m *= d * SBG_SCALE;
+ if (m < INT_MIN || m >= INT_MAX) {
+ if (log)
+ av_log(log, AV_LOG_ERROR, "%g is too large\n", d);
+ return AVERROR(EDOM);
+ }
+ *r = m;
+ return 0;
+}
+
+static int lex_space(struct sbg_parser *p)
+{
+ char *c = p->cursor;
+
+ while (p->cursor < p->end && is_space(*p->cursor))
+ p->cursor++;
+ return p->cursor > c;
+}
+
+static int lex_char(struct sbg_parser *p, char c)
+{
+ int r = p->cursor < p->end && *p->cursor == c;
+
+ p->cursor += r;
+ return r;
+}
+
+static int lex_double(struct sbg_parser *p, double *r)
+{
+ double d;
+ char *end;
+
+ if (p->cursor == p->end || is_space(*p->cursor) || *p->cursor == '\n')
+ return 0;
+ d = strtod(p->cursor, &end);
+ if (end > p->cursor) {
+ *r = d;
+ p->cursor = end;
+ return 1;
+ }
+ return 0;
+}
+
+static int lex_fixed(struct sbg_parser *p, const char *t, int l)
+{
+ if (p->end - p->cursor < l || memcmp(p->cursor, t, l))
+ return 0;
+ p->cursor += l;
+ return 1;
+}
+
+static int lex_line_end(struct sbg_parser *p)
+{
+ if (p->cursor < p->end && *p->cursor == '#') {
+ p->cursor++;
+ while (p->cursor < p->end && *p->cursor != '\n')
+ p->cursor++;
+ }
+ if (p->cursor == p->end)
+ /* simulate final LF for files lacking it */
+ return 1;
+ if (*p->cursor != '\n')
+ return 0;
+ p->cursor++;
+ p->line_no++;
+ lex_space(p);
+ return 1;
+}
+
+static int lex_wsword(struct sbg_parser *p, struct sbg_string *rs)
+{
+ char *s = p->cursor, *c = s;
+
+ if (s == p->end || *s == '\n')
+ return 0;
+ while (c < p->end && *c != '\n' && !is_space(*c))
+ c++;
+ rs->s = s;
+ rs->e = p->cursor = c;
+ lex_space(p);
+ return 1;
+}
+
+static int lex_name(struct sbg_parser *p, struct sbg_string *rs)
+{
+ char *s = p->cursor, *c = s;
+
+ while (c < p->end && ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z')
+ || (*c >= '0' && *c <= '9') || *c == '_' || *c == '-'))
+ c++;
+ if (c == s)
+ return 0;
+ rs->s = s;
+ rs->e = p->cursor = c;
+ return 1;
+}
+
+static int lex_time(struct sbg_parser *p, int64_t *rt)
+{
+ int r = str_to_time(p->cursor, rt);
+ p->cursor += r;
+ return r > 0;
+}
+
+#define FORWARD_ERROR(c) \
+ do { \
+ int errcode = c; \
+ if (errcode <= 0) \
+ return errcode ? errcode : AVERROR_INVALIDDATA; \
+ } while (0)
+
+static int parse_immediate(struct sbg_parser *p)
+{
+ snprintf(p->err_msg, sizeof(p->err_msg),
+ "immediate sequences not yet implemented");
+ return AVERROR_PATCHWELCOME;
+}
+
+static int parse_preprogrammed(struct sbg_parser *p)
+{
+ snprintf(p->err_msg, sizeof(p->err_msg),
+ "preprogrammed sequences not yet implemented");
+ return AVERROR_PATCHWELCOME;
+}
+
+static int parse_optarg(struct sbg_parser *p, char o, struct sbg_string *r)
+{
+ if (!lex_wsword(p, r)) {
+ snprintf(p->err_msg, sizeof(p->err_msg),
+ "option '%c' requires an argument", o);
+ return AVERROR_INVALIDDATA;
+ }
+ return 1;
+}
+
+static int parse_options(struct sbg_parser *p)
+{
+ struct sbg_string ostr, oarg;
+ char mode = 0;
+ int r;
+ char *tptr;
+ double v;
+
+ if (p->cursor == p->end || *p->cursor != '-')
+ return 0;
+ while (lex_char(p, '-') && lex_wsword(p, &ostr)) {
+ for (; ostr.s < ostr.e; ostr.s++) {
+ char opt = *ostr.s;
+ switch (opt) {
+ case 'S':
+ p->scs.opt_start_at_first = 1;
+ break;
+ case 'E':
+ p->scs.opt_end_at_last = 1;
+ break;
+ case 'i':
+ mode = 'i';
+ break;
+ case 'p':
+ mode = 'p';
+ break;
+ case 'F':
+ FORWARD_ERROR(parse_optarg(p, opt, &oarg));
+ v = strtod(oarg.s, &tptr);
+ if (oarg.e != tptr) {
+ snprintf(p->err_msg, sizeof(p->err_msg),
+ "syntax error for option -F");
+ return AVERROR_INVALIDDATA;
+ }
+ p->scs.opt_fade_time = v * AV_TIME_BASE / 1000;
+ break;
+ case 'L':
+ FORWARD_ERROR(parse_optarg(p, opt, &oarg));
+ r = str_to_time(oarg.s, &p->scs.opt_duration);
+ if (oarg.e != oarg.s + r) {
+ snprintf(p->err_msg, sizeof(p->err_msg),
+ "syntax error for option -L");
+ return AVERROR_INVALIDDATA;
+ }
+ break;
+ case 'T':
+ FORWARD_ERROR(parse_optarg(p, opt, &oarg));
+ r = str_to_time(oarg.s, &p->scs.start_ts);
+ if (oarg.e != oarg.s + r) {
+ snprintf(p->err_msg, sizeof(p->err_msg),
+ "syntax error for option -T");
+ return AVERROR_INVALIDDATA;
+ }
+ break;
+ case 'm':
+ FORWARD_ERROR(parse_optarg(p, opt, &oarg));
+ tptr = av_malloc(oarg.e - oarg.s + 1);
+ if (!tptr)
+ return AVERROR(ENOMEM);
+ memcpy(tptr, oarg.s, oarg.e - oarg.s);
+ tptr[oarg.e - oarg.s] = 0;
+ av_free(p->scs.opt_mix);
+ p->scs.opt_mix = tptr;
+ break;
+ case 'q':
+ FORWARD_ERROR(parse_optarg(p, opt, &oarg));
+ v = strtod(oarg.s, &tptr);
+ if (oarg.e != tptr) {
+ snprintf(p->err_msg, sizeof(p->err_msg),
+ "syntax error for option -q");
+ return AVERROR_INVALIDDATA;
+ }
+ if (v != 1) {
+ snprintf(p->err_msg, sizeof(p->err_msg),
+ "speed factor other than 1 not supported");
+ return AVERROR_PATCHWELCOME;
+ }
+ break;
+ case 'r':
+ FORWARD_ERROR(parse_optarg(p, opt, &oarg));
+ r = strtol(oarg.s, &tptr, 10);
+ if (oarg.e != tptr) {
+ snprintf(p->err_msg, sizeof(p->err_msg),
+ "syntax error for option -r");
+ return AVERROR_INVALIDDATA;
+ }
+ if (r < 40) {
+ snprintf(p->err_msg, sizeof(p->err_msg),
+ "invalid sample rate");
+ return AVERROR_PATCHWELCOME;
+ }
+ p->scs.sample_rate = r;
+ break;
+ default:
+ snprintf(p->err_msg, sizeof(p->err_msg),
+ "unknown option: '%c'", *ostr.s);
+ return AVERROR_INVALIDDATA;
+ }
+ }
+ }
+ switch (mode) {
+ case 'i':
+ return parse_immediate(p);
+ case 'p':
+ return parse_preprogrammed(p);
+ case 0:
+ if (!lex_line_end(p))
+ return AVERROR_INVALIDDATA;
+ return 1;
+ }
+ return AVERROR_BUG;
+}
+
+static int parse_timestamp(struct sbg_parser *p,
+ struct sbg_timestamp *rts, int64_t *rrel)
+{
+ int64_t abs = 0, rel = 0, dt;
+ char type = 0;
+ int r;
+
+ if (lex_fixed(p, "NOW", 3)) {
+ type = 'N';
+ r = 1;
+ } else {
+ r = lex_time(p, &abs);
+ if (r)
+ type = 'T';
+ }
+ while (lex_char(p, '+')) {
+ if (!lex_time(p, &dt))
+ return AVERROR_INVALIDDATA;
+ rel += dt;
+ r = 1;
+ }
+ if (r) {
+ if (!lex_space(p))
+ return AVERROR_INVALIDDATA;
+ rts->type = type;
+ rts->t = abs;
+ *rrel = rel;
+ }
+ return r;
+}
+
+static int parse_fade(struct sbg_parser *p, struct sbg_fade *fr)
+{
+ struct sbg_fade f = {0};
+
+ if (lex_char(p, '<'))
+ f.in = SBG_FADE_SILENCE;
+ else if (lex_char(p, '-'))
+ f.in = SBG_FADE_SAME;
+ else if (lex_char(p, '='))
+ f.in = SBG_FADE_ADAPT;
+ else
+ return 0;
+ if (lex_char(p, '>'))
+ f.out = SBG_FADE_SILENCE;
+ else if (lex_char(p, '-'))
+ f.out = SBG_FADE_SAME;
+ else if (lex_char(p, '='))
+ f.out = SBG_FADE_ADAPT;
+ else
+ return AVERROR_INVALIDDATA;
+ *fr = f;
+ return 1;
+}
+
+static int parse_time_sequence(struct sbg_parser *p, int inblock)
+{
+ struct sbg_timestamp ts;
+ int64_t rel_ts;
+ int r;
+ struct sbg_fade fade = { SBG_FADE_SAME, SBG_FADE_SAME, 0 };
+ struct sbg_string name;
+ struct sbg_script_tseq *tseq;
+
+ r = parse_timestamp(p, &ts, &rel_ts);
+ if (!r)
+ return 0;
+ if (r < 0)
+ return r;
+ if (ts.type) {
+ if (inblock)
+ return AVERROR_INVALIDDATA;
+ p->current_time.type = ts.type;
+ p->current_time.t = ts.t;
+ } else if(!inblock && !p->current_time.type) {
+ snprintf(p->err_msg, sizeof(p->err_msg),
+ "relative time without previous absolute time");
+ return AVERROR_INVALIDDATA;
+ }
+ ts.type = p->current_time.type;
+ ts.t = p->current_time.t + rel_ts;
+ r = parse_fade(p, &fade);
+ if (r < 0)
+ return r;
+ lex_space(p);
+ if (!lex_name(p, &name))
+ return AVERROR_INVALIDDATA;
+ lex_space(p);
+ if (lex_fixed(p, "->", 2)) {
+ fade.slide = SBG_FADE_ADAPT;
+ lex_space(p);
+ }
+ if (!lex_line_end(p))
+ return AVERROR_INVALIDDATA;
+ tseq = inblock ?
+ alloc_array_elem((void **)&p->scs.block_tseq, sizeof(*tseq),
+ &p->nb_block_tseq, &p->nb_block_tseq_max) :
+ alloc_array_elem((void **)&p->scs.tseq, sizeof(*tseq),
+ &p->scs.nb_tseq, &p->nb_tseq_max);
+ if (!tseq)
+ return AVERROR(ENOMEM);
+ tseq->ts = ts;
+ tseq->name = name.s;
+ tseq->name_len = name.e - name.s;
+ tseq->fade = fade;
+ return 1;
+}
+
+static int parse_wave_def(struct sbg_parser *p, int wavenum)
+{
+ snprintf(p->err_msg, sizeof(p->err_msg),
+ "waveform definitions not yet implemented");
+ return AVERROR_PATCHWELCOME;
+}
+
+static int parse_block_def(struct sbg_parser *p,
+ struct sbg_script_definition *def)
+{
+ int r, tseq;
+
+ lex_space(p);
+ if (!lex_line_end(p))
+ return AVERROR_INVALIDDATA;
+ tseq = p->nb_block_tseq;
+ while (1) {
+ r = parse_time_sequence(p, 1);
+ if (r < 0)
+ return r;
+ if (!r)
+ break;
+ }
+ if (!lex_char(p, '}'))
+ return AVERROR_INVALIDDATA;
+ lex_space(p);
+ if (!lex_line_end(p))
+ return AVERROR_INVALIDDATA;
+ def->type = 'B';
+ def->elements = tseq;
+ def->nb_elements = p->nb_block_tseq - tseq;
+ if (!def->nb_elements)
+ return AVERROR_INVALIDDATA;
+ return 1;
+}
+
+static int parse_volume(struct sbg_parser *p, int *vol)
+{
+ double v;
+
+ if (!lex_char(p, '/'))
+ return 0;
+ if (!lex_double(p, &v))
+ return AVERROR_INVALIDDATA;
+ if (scale_double(p->log, v, 0.01, vol))
+ return AVERROR(ERANGE);
+ return 1;
+}
+
+static int parse_synth_channel_sine(struct sbg_parser *p,
+ struct sbg_script_synth *synth)
+{
+ double carrierf, beatf;
+ int carrier, beat, vol;
+
+ if (!lex_double(p, &carrierf))
+ return 0;
+ if (!lex_double(p, &beatf))
+ beatf = 0;
+ FORWARD_ERROR(parse_volume(p, &vol));
+ if (scale_double(p->log, carrierf, 1, &carrier) < 0 ||
+ scale_double(p->log, beatf, 1, &beat) < 0)
+ return AVERROR(EDOM);
+ synth->type = SBG_TYPE_SINE;
+ synth->carrier = carrier;
+ synth->beat = beat;
+ synth->vol = vol;
+ return 1;
+}
+
+static int parse_synth_channel_pink(struct sbg_parser *p,
+ struct sbg_script_synth *synth)
+{
+ int vol;
+
+ if (!lex_fixed(p, "pink", 4))
+ return 0;
+ FORWARD_ERROR(parse_volume(p, &vol));
+ synth->type = SBG_TYPE_NOISE;
+ synth->vol = vol;
+ return 1;
+}
+
+static int parse_synth_channel_bell(struct sbg_parser *p,
+ struct sbg_script_synth *synth)
+{
+ double carrierf;
+ int carrier, vol;
+
+ if (!lex_fixed(p, "bell", 4))
+ return 0;
+ if (!lex_double(p, &carrierf))
+ return AVERROR_INVALIDDATA;
+ FORWARD_ERROR(parse_volume(p, &vol));
+ if (scale_double(p->log, carrierf, 1, &carrier) < 0)
+ return AVERROR(EDOM);
+ synth->type = SBG_TYPE_BELL;
+ synth->carrier = carrier;
+ synth->vol = vol;
+ return 1;
+}
+
+static int parse_synth_channel_mix(struct sbg_parser *p,
+ struct sbg_script_synth *synth)
+{
+ int vol;
+
+ if (!lex_fixed(p, "mix", 3))
+ return 0;
+ FORWARD_ERROR(parse_volume(p, &vol));
+ synth->type = SBG_TYPE_MIX;
+ synth->vol = vol;
+ return 1;
+}
+
+static int parse_synth_channel_spin(struct sbg_parser *p,
+ struct sbg_script_synth *synth)
+{
+ double carrierf, beatf;
+ int carrier, beat, vol;
+
+ if (!lex_fixed(p, "spin:", 5))
+ return 0;
+ if (!lex_double(p, &carrierf))
+ return AVERROR_INVALIDDATA;
+ if (!lex_double(p, &beatf))
+ return AVERROR_INVALIDDATA;
+ FORWARD_ERROR(parse_volume(p, &vol));
+ if (scale_double(p->log, carrierf, 1, &carrier) < 0 ||
+ scale_double(p->log, beatf, 1, &beat) < 0)
+ return AVERROR(EDOM);
+ synth->type = SBG_TYPE_SPIN;
+ synth->carrier = carrier;
+ synth->beat = beat;
+ synth->vol = vol;
+ return 1;
+}
+
+static int parse_synth_channel(struct sbg_parser *p)
+{
+ int r;
+ struct sbg_script_synth *synth;
+
+ synth = alloc_array_elem((void **)&p->scs.synth, sizeof(*synth),
+ &p->scs.nb_synth, &p->nb_synth_max);
+ if (!synth)
+ return AVERROR(ENOMEM);
+ r = lex_char(p, '-');
+ if (!r)
+ r = parse_synth_channel_pink(p, synth);
+ if (!r)
+ r = parse_synth_channel_bell(p, synth);
+ if (!r)
+ r = parse_synth_channel_mix(p, synth);
+ if (!r)
+ r = parse_synth_channel_spin(p, synth);
+ /* Unimplemented: wave%d:%f%f/vol (carrier, beat) */
+ if (!r)
+ r = parse_synth_channel_sine(p, synth);
+ if (r <= 0)
+ p->scs.nb_synth--;
+ return r;
+}
+
+static int parse_synth_def(struct sbg_parser *p,
+ struct sbg_script_definition *def)
+{
+ int r, synth;
+
+ synth = p->scs.nb_synth;
+ while (1) {
+ r = parse_synth_channel(p);
+ if (r < 0)
+ return r;
+ if (!r || !lex_space(p))
+ break;
+ }
+ lex_space(p);
+ if (synth == p->scs.nb_synth)
+ return AVERROR_INVALIDDATA;
+ if (!lex_line_end(p))
+ return AVERROR_INVALIDDATA;
+ def->type = 'S';
+ def->elements = synth;
+ def->nb_elements = p->scs.nb_synth - synth;
+ return 1;
+}
+
+static int parse_named_def(struct sbg_parser *p)
+{
+ char *cursor_save = p->cursor;
+ struct sbg_string name;
+ struct sbg_script_definition *def;
+
+ if (!lex_name(p, &name) || !lex_char(p, ':') || !lex_space(p)) {
+ p->cursor = cursor_save;
+ return 0;
+ }
+ if (name.e - name.s == 6 && !memcmp(name.s, "wave", 4) &&
+ name.s[4] >= '0' && name.s[4] <= '9' &&
+ name.s[5] >= '0' && name.s[5] <= '9') {
+ int wavenum = (name.s[4] - '0') * 10 + (name.s[5] - '0');
+ return parse_wave_def(p, wavenum);
+ }
+ def = alloc_array_elem((void **)&p->scs.def, sizeof(*def),
+ &p->scs.nb_def, &p->nb_def_max);
+ if (!def)
+ return AVERROR(ENOMEM);
+ def->name = name.s;
+ def->name_len = name.e - name.s;
+ if (lex_char(p, '{'))
+ return parse_block_def(p, def);
+ return parse_synth_def(p, def);
+}
+
+static void free_script(struct sbg_script *s)
+{
+ av_freep(&s->def);
+ av_freep(&s->synth);
+ av_freep(&s->tseq);
+ av_freep(&s->block_tseq);
+ av_freep(&s->events);
+ av_freep(&s->opt_mix);
+}
+
+static int parse_script(void *log, char *script, int script_len,
+ struct sbg_script *rscript)
+{
+ struct sbg_parser sp = {
+ .log = log,
+ .script = script,
+ .end = script + script_len,
+ .cursor = script,
+ .line_no = 1,
+ .err_msg = "",
+ .scs = {
+ /* default values */
+ .start_ts = AV_NOPTS_VALUE,
+ .sample_rate = 44100,
+ .opt_fade_time = 60 * AV_TIME_BASE,
+ },
+ };
+ int r;
+
+ lex_space(&sp);
+ while (sp.cursor < sp.end) {
+ r = parse_options(&sp);
+ if (r < 0)
+ goto fail;
+ if (!r && !lex_line_end(&sp))
+ break;
+ }
+ while (sp.cursor < sp.end) {
+ r = parse_named_def(&sp);
+ if (!r)
+ r = parse_time_sequence(&sp, 0);
+ if (!r)
+ r = lex_line_end(&sp) ? 1 : AVERROR_INVALIDDATA;
+ if (r < 0)
+ goto fail;
+ }
+ *rscript = sp.scs;
+ return 1;
+fail:
+ free_script(&sp.scs);
+ if (!*sp.err_msg)
+ if (r == AVERROR_INVALIDDATA)
+ snprintf(sp.err_msg, sizeof(sp.err_msg), "syntax error");
+ if (log && *sp.err_msg) {
+ const char *ctx = sp.cursor;
+ const char *ectx = av_x_if_null(memchr(ctx, '\n', sp.end - sp.cursor),
+ sp.end);
+ int lctx = ectx - ctx;
+ const char *quote = "\"";
+ if (lctx > 0 && ctx[lctx - 1] == '\r')
+ lctx--;
+ if (lctx == 0) {
+ ctx = "the end of line";
+ lctx = strlen(ctx);
+ quote = "";
+ }
+ av_log(log, AV_LOG_ERROR, "Error line %d: %s near %s%.*s%s.\n",
+ sp.line_no, sp.err_msg, quote, lctx, ctx, quote);
+ }
+ return r;
+}
+
+static int read_whole_file(AVIOContext *io, int max_size, char **rbuf)
+{
+ char *buf = NULL;
+ int size = 0, bufsize = 0, r;
+
+ while (1) {
+ if (bufsize - size < 1024) {
+ bufsize = FFMIN(FFMAX(2 * bufsize, 8192), max_size);
+ if (bufsize - size < 2) {
+ size = AVERROR(EFBIG);
+ goto fail;
+ }
+ buf = av_realloc_f(buf, bufsize, 1);
+ if (!buf) {
+ size = AVERROR(ENOMEM);
+ goto fail;
+ }
+ }
+ r = avio_read(io, buf, bufsize - size - 1);
+ if (r == AVERROR_EOF)
+ break;
+ if (r < 0)
+ goto fail;
+ size += r;
+ }
+ buf[size] = 0;
+ *rbuf = buf;
+ return size;
+fail:
+ av_free(buf);
+ return size;
+}
+
+static void expand_timestamps(void *log, struct sbg_script *s)
+{
+ int i, nb_rel = 0;
+ int64_t now, cur_ts, delta = 0;
+
+ for (i = 0; i < s->nb_tseq; i++)
+ nb_rel += s->tseq[i].ts.type == 'N';
+ if (nb_rel == s->nb_tseq) {
+ /* All ts are relative to NOW: consider NOW = 0 */
+ now = 0;
+ if (s->start_ts != AV_NOPTS_VALUE)
+ av_log(log, AV_LOG_WARNING,
+ "Start time ignored in a purely relative script.\n");
+ } else if (nb_rel == 0 && s->start_ts != AV_NOPTS_VALUE ||
+ s->opt_start_at_first) {
+ /* All ts are absolute and start time is specified */
+ if (s->start_ts == AV_NOPTS_VALUE)
+ s->start_ts = s->tseq[0].ts.t;
+ now = s->start_ts;
+ } else {
+ /* Mixed relative/absolute ts: expand */
+ time_t now0;
+ struct tm *tm, tmpbuf;
+
+ 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_r(&now0, &tmpbuf);
+ now = tm ? tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec :
+ now0 % DAY;
+ av_log(log, AV_LOG_INFO, "Using %02d:%02d:%02d as NOW.\n",
+ (int)(now / 3600), (int)(now / 60) % 60, (int)now % 60);
+ now *= AV_TIME_BASE;
+ for (i = 0; i < s->nb_tseq; i++) {
+ if (s->tseq[i].ts.type == 'N') {
+ s->tseq[i].ts.t += now;
+ s->tseq[i].ts.type = 'T'; /* not necessary */
+ }
+ }
+ }
+ if (s->start_ts == AV_NOPTS_VALUE)
+ s->start_ts = s->opt_start_at_first ? s->tseq[0].ts.t : now;
+ s->end_ts = s->opt_duration ? s->start_ts + s->opt_duration :
+ AV_NOPTS_VALUE; /* may be overridden later by -E option */
+ cur_ts = now;
+ for (i = 0; i < s->nb_tseq; i++) {
+ if (s->tseq[i].ts.t + delta < cur_ts)
+ delta += DAY_TS;
+ cur_ts = s->tseq[i].ts.t += delta;
+ }
+}
+
+static int expand_tseq(void *log, struct sbg_script *s, int *nb_ev_max,
+ int64_t t0, struct sbg_script_tseq *tseq)
+{
+ int i, r;
+ struct sbg_script_definition *def;
+ struct sbg_script_tseq *be;
+ struct sbg_script_event *ev;
+
+ if (tseq->lock++) {
+ av_log(log, AV_LOG_ERROR, "Recursion loop on \"%.*s\"\n",
+ tseq->name_len, tseq->name);
+ return AVERROR(EINVAL);
+ }
+ t0 += tseq->ts.t;
+ for (i = 0; i < s->nb_def; i++) {
+ if (s->def[i].name_len == tseq->name_len &&
+ !memcmp(s->def[i].name, tseq->name, tseq->name_len))
+ break;
+ }
+ if (i >= s->nb_def) {
+ av_log(log, AV_LOG_ERROR, "Tone-set \"%.*s\" not defined\n",
+ tseq->name_len, tseq->name);
+ return AVERROR(EINVAL);
+ }
+ def = &s->def[i];
+ if (def->type == 'B') {
+ be = s->block_tseq + def->elements;
+ for (i = 0; i < def->nb_elements; i++) {
+ r = expand_tseq(log, s, nb_ev_max, t0, &be[i]);
+ if (r < 0)
+ return r;
+ }
+ } else {
+ ev = alloc_array_elem((void **)&s->events, sizeof(*ev),
+ &s->nb_events, nb_ev_max);
+ ev->ts = tseq->ts.t;
+ ev->elements = def->elements;
+ ev->nb_elements = def->nb_elements;
+ ev->fade = tseq->fade;
+ }
+ tseq->lock--;
+ return 0;
+}
+
+static int expand_script(void *log, struct sbg_script *s)
+{
+ int i, r, nb_events_max = 0;
+
+ expand_timestamps(log, s);
+ for (i = 0; i < s->nb_tseq; i++) {
+ r = expand_tseq(log, s, &nb_events_max, 0, &s->tseq[i]);
+ if (r < 0)
+ return r;
+ }
+ if (!s->nb_events) {
+ av_log(log, AV_LOG_ERROR, "No events in script\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if (s->opt_end_at_last)
+ s->end_ts = s->events[s->nb_events - 1].ts;
+ return 0;
+}
+
+static int add_interval(struct ws_intervals *inter,
+ enum ws_interval_type type, uint32_t channels, int ref,
+ int64_t ts1, int32_t f1, int32_t a1,
+ int64_t ts2, int32_t f2, int32_t a2)
+{
+ struct ws_interval *i, *ri;
+
+ if (ref >= 0) {
+ ri = &inter->inter[ref];
+ /* ref and new intervals are constant, identical and adjacent */
+ if (ri->type == type && ri->channels == channels &&
+ ri->f1 == ri->f2 && ri->f2 == f1 && f1 == f2 &&
+ ri->a1 == ri->a2 && ri->a2 == a1 && a1 == a2 &&
+ ri->ts2 == ts1) {
+ ri->ts2 = ts2;
+ return ref;
+ }
+ }
+ i = alloc_array_elem((void **)&inter->inter, sizeof(*i),
+ &inter->nb_inter, &inter->max_inter);
+ if (!i)
+ return AVERROR(ENOMEM);
+ i->ts1 = ts1;
+ i->ts2 = ts2;
+ i->type = type;
+ i->channels = channels;
+ i->f1 = f1;
+ i->f2 = f2;
+ i->a1 = a1;
+ i->a2 = a2;
+ i->phi = ref >= 0 ? ref | 0x80000000 : 0;
+ return i - inter->inter;
+}
+
+static int add_bell(struct ws_intervals *inter, struct sbg_script *s,
+ int64_t ts1, int64_t ts2, int32_t f, int32_t a)
+{
+ /* SBaGen uses an exponential decrease every 50ms.
+ We approximate it with piecewise affine segments. */
+ int32_t cpoints[][2] = {
+ { 2, a },
+ { 4, a - a / 4 },
+ { 8, a / 2 },
+ { 16, a / 4 },
+ { 25, a / 10 },
+ { 50, a / 80 },
+ { 75, 0 },
+ };
+ int i, r;
+ int64_t dt = s->sample_rate / 20, ts3 = ts1, ts4;
+ for (i = 0; i < FF_ARRAY_ELEMS(cpoints); i++) {
+ ts4 = FFMIN(ts2, ts1 + cpoints[i][0] * dt);
+ r = add_interval(inter, WS_SINE, 3, -1,
+ ts3, f, a, ts4, f, cpoints[i][1]);
+ if (r < 0)
+ return r;
+ ts3 = ts4;
+ a = cpoints[i][1];
+ }
+ return 0;
+}
+
+static int generate_interval(void *log, struct sbg_script *s,
+ struct ws_intervals *inter,
+ int64_t ts1, int64_t ts2,
+ struct sbg_script_synth *s1,
+ struct sbg_script_synth *s2,
+ int transition)
+{
+ int r;
+
+ if (ts2 <= ts1 || (s1->vol == 0 && s2->vol == 0))
+ return 0;
+ switch (s1->type) {
+ case SBG_TYPE_NONE:
+ break;
+ case SBG_TYPE_SINE:
+ if (s1->beat == 0 && s2->beat == 0) {
+ r = add_interval(inter, WS_SINE, 3, s1->ref.l,
+ ts1, s1->carrier, s1->vol,
+ ts2, s2->carrier, s2->vol);
+ if (r < 0)
+ return r;
+ s2->ref.l = s2->ref.r = r;
+ } else {
+ r = add_interval(inter, WS_SINE, 1, s1->ref.l,
+ ts1, s1->carrier + s1->beat / 2, s1->vol,
+ ts2, s2->carrier + s2->beat / 2, s2->vol);
+ if (r < 0)
+ return r;
+ s2->ref.l = r;
+ r = add_interval(inter, WS_SINE, 2, s1->ref.r,
+ ts1, s1->carrier - s1->beat / 2, s1->vol,
+ ts2, s2->carrier - s2->beat / 2, s2->vol);
+ if (r < 0)
+ return r;
+ s2->ref.r = r;
+ }
+ break;
+
+ case SBG_TYPE_BELL:
+ if (transition == 2) {
+ r = add_bell(inter, s, ts1, ts2, s1->carrier, s2->vol);
+ if (r < 0)
+ return r;
+ }
+ break;
+
+ case SBG_TYPE_SPIN:
+ av_log(log, AV_LOG_WARNING, "Spinning noise not implemented, "
+ "using pink noise instead.\n");
+ /* fall through */
+ case SBG_TYPE_NOISE:
+ /* SBaGen's pink noise generator uses:
+ - 1 band of white noise, mean square: 1/3;
+ - 9 bands of subsampled white noise with linear
+ interpolation, mean square: 2/3 each;
+ with 1/10 weight each: the total mean square is 7/300.
+ Our pink noise generator uses 8 bands of white noise with
+ rectangular subsampling: the total mean square is 1/24.
+ Therefore, to match SBaGen's volume, we must multiply vol by
+ sqrt((7/300) / (1/24)) = sqrt(14/25) =~ 0.748
+ */
+ r = add_interval(inter, WS_NOISE, 3, s1->ref.l,
+ ts1, 0, s1->vol - s1->vol / 4,
+ ts2, 0, s2->vol - s2->vol / 4);
+ if (r < 0)
+ return r;
+ s2->ref.l = s2->ref.r = r;
+ break;
+
+ case SBG_TYPE_MIX:
+ /* Unimplemented: silence; warning present elsewhere */
+ default:
+ av_log(log, AV_LOG_ERROR,
+ "Type %d is not implemented\n", s1->type);
+ return AVERROR_PATCHWELCOME;
+ }
+ return 0;
+}
+
+static int generate_plateau(void *log, struct sbg_script *s,
+ struct ws_intervals *inter,
+ struct sbg_script_event *ev1)
+{
+ int64_t ts1 = ev1->ts_int, ts2 = ev1->ts_trans;
+ int i, r;
+ struct sbg_script_synth *s1;
+
+ for (i = 0; i < ev1->nb_elements; i++) {
+ s1 = &s->synth[ev1->elements + i];
+ r = generate_interval(log, s, inter, ts1, ts2, s1, s1, 0);
+ if (r < 0)
+ return r;
+ }
+ return 0;
+}
+
+/*
+
+ ts1 ts2 ts1 tsmid ts2
+ | | | | |
+ v v v | v
+____ ____ v ____
+ ''''.... ''.. ..''
+ ''''....____ ''....''
+
+ compatible transition incompatible transition
+ */
+
+static int generate_transition(void *log, struct sbg_script *s,
+ struct ws_intervals *inter,
+ struct sbg_script_event *ev1,
+ struct sbg_script_event *ev2)
+{
+ int64_t ts1 = ev1->ts_trans, ts2 = ev1->ts_next;
+ /* (ts1 + ts2) / 2 without overflow */
+ int64_t tsmid = (ts1 >> 1) + (ts2 >> 1) + (ts1 & ts2 & 1);
+ enum sbg_fade_type type = ev1->fade.slide | (ev1->fade.out & ev2->fade.in);
+ int nb_elements = FFMAX(ev1->nb_elements, ev2->nb_elements);
+ struct sbg_script_synth *s1, *s2, s1mod, s2mod, smid;
+ int pass, i, r;
+
+ for (pass = 0; pass < 2; pass++) {
+ /* pass = 0 -> compatible and first half of incompatible
+ pass = 1 -> second half of incompatible
+ Using two passes like that ensures that the intervals are generated
+ in increasing order according to their start timestamp.
+ Otherwise it would be necessary to sort them
+ while keeping the mutual references.
+ */
+ for (i = 0; i < nb_elements; i++) {
+ s1 = i < ev1->nb_elements ? &s->synth[ev1->elements + i] : &s1mod;
+ s2 = i < ev2->nb_elements ? &s->synth[ev2->elements + i] : &s2mod;
+ s1mod = s1 != &s1mod ? *s1 : (struct sbg_script_synth){ 0 };
+ s2mod = s2 != &s2mod ? *s2 : (struct sbg_script_synth){ 0 };
+ if (ev1->fade.slide) {
+ /* for slides, and only for slides, silence ("-") is equivalent
+ to anything with volume 0 */
+ if (s1mod.type == SBG_TYPE_NONE) {
+ s1mod = s2mod;
+ s1mod.vol = 0;
+ } else if (s2mod.type == SBG_TYPE_NONE) {
+ s2mod = s1mod;
+ s2mod.vol = 0;
+ }
+ }
+ if (s1mod.type == s2mod.type &&
+ s1mod.type != SBG_TYPE_BELL &&
+ (type == SBG_FADE_ADAPT ||
+ (s1mod.carrier == s2mod.carrier &&
+ s1mod.beat == s2mod.beat))) {
+ /* compatible: single transition */
+ if (!pass) {
+ r = generate_interval(log, s, inter,
+ ts1, ts2, &s1mod, &s2mod, 3);
+ if (r < 0)
+ return r;
+ s2->ref = s2mod.ref;
+ }
+ } else {
+ /* incompatible: silence at midpoint */
+ if (!pass) {
+ smid = s1mod;
+ smid.vol = 0;
+ r = generate_interval(log, s, inter,
+ ts1, tsmid, &s1mod, &smid, 1);
+ if (r < 0)
+ return r;
+ } else {
+ smid = s2mod;
+ smid.vol = 0;
+ r = generate_interval(log, s, inter,
+ tsmid, ts2, &smid, &s2mod, 2);
+ if (r < 0)
+ return r;
+ s2->ref = s2mod.ref;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ ev1 trats ev2 intts endts ev3
+ | | | | | |
+ v v v v v v
+ ________________
+.... .... ....
+ '''....________________....''' '''...._______________
+
+\_________/\______________/\_________/\______________/\_________/\_____________/
+ tr x->1 int1 tr 1->2 int2 tr 2->3 int3
+ */
+
+static int generate_intervals(void *log, struct sbg_script *s, int sample_rate,
+ struct ws_intervals *inter)
+{
+ int64_t trans_time = s->opt_fade_time / 2;
+ struct sbg_script_event ev0, *ev1, *ev2;
+ int64_t period;
+ int i, r;
+
+ /* SBaGen handles the time before and after the extremal events,
+ and the corresponding transitions, as if the sequence were cyclic
+ with a 24-hours period. */
+ period = s->events[s->nb_events - 1].ts - s->events[0].ts;
+ period = (period + (DAY_TS - 1)) / DAY_TS * DAY_TS;
+ period = FFMAX(period, DAY_TS);
+
+ /* Prepare timestamps for transitions */
+ for (i = 0; i < s->nb_events; i++) {
+ ev1 = &s->events[i];
+ ev2 = &s->events[(i + 1) % s->nb_events];
+ ev1->ts_int = ev1->ts;
+ ev1->ts_trans = ev1->fade.slide ? ev1->ts
+ : ev2->ts + (ev1 < ev2 ? 0 : period);
+ }
+ for (i = 0; i < s->nb_events; i++) {
+ ev1 = &s->events[i];
+ ev2 = &s->events[(i + 1) % s->nb_events];
+ if (!ev1->fade.slide) {
+ ev1->ts_trans = FFMAX(ev1->ts_int, ev1->ts_trans - trans_time);
+ ev2->ts_int = FFMIN(ev2->ts_trans, ev2->ts_int + trans_time);
+ }
+ ev1->ts_next = ev2->ts_int + (ev1 < ev2 ? 0 : period);
+ }
+
+ /* Pseudo event before the first one */
+ ev0 = s->events[s->nb_events - 1];
+ ev0.ts_int -= period;
+ ev0.ts_trans -= period;
+ ev0.ts_next -= period;
+
+ /* Convert timestamps */
+ for (i = -1; i < s->nb_events; i++) {
+ ev1 = i < 0 ? &ev0 : &s->events[i];
+ ev1->ts_int = av_rescale(ev1->ts_int, sample_rate, AV_TIME_BASE);
+ ev1->ts_trans = av_rescale(ev1->ts_trans, sample_rate, AV_TIME_BASE);
+ ev1->ts_next = av_rescale(ev1->ts_next, sample_rate, AV_TIME_BASE);
+ }
+
+ /* Generate intervals */
+ for (i = 0; i < s->nb_synth; i++)
+ s->synth[i].ref.l = s->synth[i].ref.r = -1;
+ for (i = -1; i < s->nb_events; i++) {
+ ev1 = i < 0 ? &ev0 : &s->events[i];
+ ev2 = &s->events[(i + 1) % s->nb_events];
+ r = generate_plateau(log, s, inter, ev1);
+ if (r < 0)
+ return r;
+ r = generate_transition(log, s, inter, ev1, ev2);
+ if (r < 0)
+ return r;
+ }
+ if (!inter->nb_inter)
+ av_log(log, AV_LOG_WARNING, "Completely silent script.\n");
+ return 0;
+}
+
+static int encode_intervals(struct sbg_script *s, AVCodecContext *avc,
+ struct ws_intervals *inter)
+{
+ int i, edata_size = 4;
+ uint8_t *edata;
+
+ for (i = 0; i < inter->nb_inter; i++) {
+ edata_size += inter->inter[i].type == WS_SINE ? 44 :
+ inter->inter[i].type == WS_NOISE ? 32 : 0;
+ if (edata_size < 0)
+ return AVERROR(ENOMEM);
+ }
+ if (ff_alloc_extradata(avc, edata_size))
+ return AVERROR(ENOMEM);
+ edata = avc->extradata;
+
+#define ADD_EDATA32(v) do { AV_WL32(edata, (v)); edata += 4; } while(0)
+#define ADD_EDATA64(v) do { AV_WL64(edata, (v)); edata += 8; } while(0)
+ ADD_EDATA32(inter->nb_inter);
+ for (i = 0; i < inter->nb_inter; i++) {
+ ADD_EDATA64(inter->inter[i].ts1);
+ ADD_EDATA64(inter->inter[i].ts2);
+ ADD_EDATA32(inter->inter[i].type);
+ ADD_EDATA32(inter->inter[i].channels);
+ switch (inter->inter[i].type) {
+ case WS_SINE:
+ ADD_EDATA32(inter->inter[i].f1);
+ ADD_EDATA32(inter->inter[i].f2);
+ ADD_EDATA32(inter->inter[i].a1);
+ ADD_EDATA32(inter->inter[i].a2);
+ ADD_EDATA32(inter->inter[i].phi);
+ break;
+ case WS_NOISE:
+ ADD_EDATA32(inter->inter[i].a1);
+ ADD_EDATA32(inter->inter[i].a2);
+ break;
+ }
+ }
+ if (edata != avc->extradata + edata_size)
+ return AVERROR_BUG;
+ return 0;
+}
+
+static av_cold int sbg_read_probe(AVProbeData *p)
+{
+ int r, score;
+ struct sbg_script script = { 0 };
+
+ r = parse_script(NULL, p->buf, p->buf_size, &script);
+ score = r < 0 || !script.nb_def || !script.nb_tseq ? 0 :
+ AVPROBE_SCORE_MAX / 3;
+ free_script(&script);
+ return score;
+}
+
+static av_cold int sbg_read_header(AVFormatContext *avf)
+{
+ struct sbg_demuxer *sbg = avf->priv_data;
+ int r;
+ char *buf = NULL;
+ struct sbg_script script = { 0 };
+ AVStream *st;
+ struct ws_intervals inter = { 0 };
+
+ r = read_whole_file(avf->pb, sbg->max_file_size, &buf);
+ if (r < 0)
+ goto fail;
+ r = parse_script(avf, buf, r, &script);
+ if (r < 0)
+ goto fail;
+ if (!sbg->sample_rate)
+ sbg->sample_rate = script.sample_rate;
+ else
+ script.sample_rate = sbg->sample_rate;
+ if (!sbg->frame_size)
+ sbg->frame_size = FFMAX(1, sbg->sample_rate / 10);
+ if (script.opt_mix)
+ av_log(avf, AV_LOG_WARNING, "Mix feature not implemented: "
+ "-m is ignored and mix channels will be silent.\n");
+ r = expand_script(avf, &script);
+ if (r < 0)
+ goto fail;
+ av_freep(&buf);
+ r = generate_intervals(avf, &script, sbg->sample_rate, &inter);
+ if (r < 0)
+ goto fail;
+
+ st = avformat_new_stream(avf, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codec->codec_id = AV_CODEC_ID_FFWAVESYNTH;
+ st->codec->channels = 2;
+ st->codec->channel_layout = AV_CH_LAYOUT_STEREO;
+ st->codec->sample_rate = sbg->sample_rate;
+ st->codec->frame_size = sbg->frame_size;
+ avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+ st->probe_packets = 0;
+ st->start_time = av_rescale(script.start_ts,
+ sbg->sample_rate, AV_TIME_BASE);
+ st->duration = script.end_ts == AV_NOPTS_VALUE ? AV_NOPTS_VALUE :
+ av_rescale(script.end_ts - script.start_ts,
+ sbg->sample_rate, AV_TIME_BASE);
+ st->cur_dts = st->start_time;
+ r = encode_intervals(&script, st->codec, &inter);
+ if (r < 0)
+ goto fail;
+
+ av_free(inter.inter);
+ free_script(&script);
+ return 0;
+
+fail:
+ av_free(inter.inter);
+ free_script(&script);
+ av_free(buf);
+ return r;
+}
+
+static int sbg_read_packet(AVFormatContext *avf, AVPacket *packet)
+{
+ int64_t ts, end_ts;
+
+ ts = avf->streams[0]->cur_dts;
+ end_ts = ts + avf->streams[0]->codec->frame_size;
+ if (avf->streams[0]->duration != AV_NOPTS_VALUE)
+ end_ts = FFMIN(avf->streams[0]->start_time + avf->streams[0]->duration,
+ end_ts);
+ if (end_ts <= ts)
+ return AVERROR_EOF;
+ if (av_new_packet(packet, 12) < 0)
+ return AVERROR(ENOMEM);
+ packet->dts = packet->pts = ts;
+ packet->duration = end_ts - ts;
+ AV_WL64(packet->data + 0, ts);
+ AV_WL32(packet->data + 8, packet->duration);
+ return packet->size;
+}
+
+static int sbg_read_seek2(AVFormatContext *avf, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ if (flags || stream_index > 0)
+ return AVERROR(EINVAL);
+ if (stream_index < 0)
+ ts = av_rescale_q(ts, AV_TIME_BASE_Q, avf->streams[0]->time_base);
+ avf->streams[0]->cur_dts = ts;
+ return 0;
+}
+
+static int sbg_read_seek(AVFormatContext *avf, int stream_index,
+ int64_t ts, int flags)
+{
+ return sbg_read_seek2(avf, stream_index, ts, ts, ts, 0);
+}
+
+static const AVOption sbg_options[] = {
+ { "sample_rate", "", offsetof(struct sbg_demuxer, sample_rate),
+ AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX,
+ AV_OPT_FLAG_DECODING_PARAM },
+ { "frame_size", "", offsetof(struct sbg_demuxer, frame_size),
+ AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX,
+ AV_OPT_FLAG_DECODING_PARAM },
+ { "max_file_size", "", offsetof(struct sbg_demuxer, max_file_size),
+ AV_OPT_TYPE_INT, { .i64 = 5000000 }, 0, INT_MAX,
+ AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+static const AVClass sbg_demuxer_class = {
+ .class_name = "sbg_demuxer",
+ .item_name = av_default_item_name,
+ .option = sbg_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_sbg_demuxer = {
+ .name = "sbg",
+ .long_name = NULL_IF_CONFIG_SMALL("SBaGen binaural beats script"),
+ .priv_data_size = sizeof(struct sbg_demuxer),
+ .read_probe = sbg_read_probe,
+ .read_header = sbg_read_header,
+ .read_packet = sbg_read_packet,
+ .read_seek = sbg_read_seek,
+ .read_seek2 = sbg_read_seek2,
+ .extensions = "sbg",
+ .priv_class = &sbg_demuxer_class,
+};
diff --git a/libavformat/sctp.c b/libavformat/sctp.c
index 9cfaeccd17..d7338950bd 100644
--- a/libavformat/sctp.c
+++ b/libavformat/sctp.c
@@ -2,20 +2,20 @@
* SCTP protocol
* Copyright (c) 2012 Luca Barbato
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -98,7 +98,7 @@ static int ff_sctp_recvmsg(int s, void *msg, size_t len, struct sockaddr *from,
if (msg_flags)
*msg_flags = inmsg.msg_flags;
- for (cmsg = CMSG_FIRSTHDR(&inmsg); cmsg != NULL;
+ for (cmsg = CMSG_FIRSTHDR(&inmsg); cmsg;
cmsg = CMSG_NXTHDR(&inmsg, cmsg)) {
if ((IPPROTO_SCTP == cmsg->cmsg_level) &&
(SCTP_SNDRCV == cmsg->cmsg_type))
@@ -121,7 +121,7 @@ static int ff_sctp_send(int s, const void *msg, size_t len,
outmsg.msg_name = NULL;
outmsg.msg_namelen = 0;
outmsg.msg_iov = &iov;
- iov.iov_base = msg;
+ iov.iov_base = (void*)msg;
iov.iov_len = len;
outmsg.msg_iovlen = 1;
outmsg.msg_controllen = 0;
@@ -296,8 +296,10 @@ static int sctp_write(URLContext *h, const uint8_t *buf, int size)
/*StreamId is introduced as a 2byte code into the stream*/
struct sctp_sndrcvinfo info = { 0 };
info.sinfo_stream = AV_RB16(buf);
- if (info.sinfo_stream > s->max_streams)
+ if (info.sinfo_stream > s->max_streams) {
+ av_log(h, AV_LOG_ERROR, "bad input data\n");
return AVERROR_BUG;
+ }
ret = ff_sctp_send(s->fd, buf + 2, size - 2, &info, MSG_EOR);
} else
ret = send(s->fd, buf, size, MSG_NOSIGNAL);
diff --git a/libavformat/sdp.c b/libavformat/sdp.c
index fe43e04361..4d621c7ffc 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
*/
@@ -203,7 +203,7 @@ static char *extradata2psets(AVCodecContext *c)
sps_end = r1;
}
if (!av_base64_encode(p, MAX_PSET_SIZE - (p - psets), r, r1 - r)) {
- av_log(c, AV_LOG_ERROR, "Cannot Base64-encode %td %td!\n", MAX_PSET_SIZE - (p - psets), r1 - r);
+ av_log(c, AV_LOG_ERROR, "Cannot Base64-encode %"PTRDIFF_SPECIFIER" %"PTRDIFF_SPECIFIER"!\n", MAX_PSET_SIZE - (p - psets), r1 - r);
av_free(psets);
av_free(tmpbuf);
@@ -348,7 +348,7 @@ static char *extradata2config(AVCodecContext *c)
static char *xiph_extradata2config(AVCodecContext *c)
{
char *config, *encoded_config;
- uint8_t *header_start[3];
+ const uint8_t *header_start[3];
int headers_len, header_len[3], config_len;
int first_header_size;
@@ -544,7 +544,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)
@@ -681,6 +681,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:
/* The opus RTP draft says that all opus streams MUST be declared
diff --git a/libavformat/sdr2.c b/libavformat/sdr2.c
new file mode 100644
index 0000000000..82405f69d1
--- /dev/null
+++ b/libavformat/sdr2.c
@@ -0,0 +1,121 @@
+/*
+ * SDR2 demuxer
+ * Copyright (c) 2014 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "internal.h"
+
+static int sdr2_probe(AVProbeData *p)
+{
+ if (AV_RL32(p->buf) != MKTAG('S', 'R', 'A', 1))
+ return 0;
+
+ return AVPROBE_SCORE_EXTENSION;
+}
+
+#define FIRST 0xA8
+
+static int sdr2_read_header(AVFormatContext *s)
+{
+ AVStream *st, *ast;
+
+ ast = avformat_new_stream(s, 0);
+ if (!ast)
+ return AVERROR(ENOMEM);
+
+ st = avformat_new_stream(s, 0);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avio_skip(s->pb, 20);
+ avpriv_set_pts_info(st, 64, 1, avio_rl32(s->pb));
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codec->width = avio_rl32(s->pb);
+ st->codec->height = avio_rl32(s->pb);
+ st->codec->codec_id = AV_CODEC_ID_H264;
+ st->need_parsing = AVSTREAM_PARSE_FULL;
+
+ ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ ast->codec->channels = 1;
+ ast->codec->sample_rate = 8000;
+ ast->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
+ avpriv_set_pts_info(ast, 64, 1, 8000);
+
+ avio_seek(s->pb, FIRST, SEEK_SET);
+
+ return 0;
+}
+
+static const uint8_t header[24] = {
+ 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e,
+ 0xa6, 0x80, 0xb0, 0x7e, 0x40, 0x00, 0x00, 0x00,
+ 0x01, 0x68, 0xce, 0x38, 0x80, 0x00, 0x00, 0x00
+};
+
+static int sdr2_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ int64_t pos;
+ unsigned next;
+ int flags, ret = 0, is_video;
+
+ pos = avio_tell(s->pb);
+
+ flags = avio_rl32(s->pb);
+ avio_skip(s->pb, 4);
+
+ next = avio_rl32(s->pb);
+ if (next <= 52)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(s->pb, 6);
+ is_video = avio_rl32(s->pb);
+ avio_skip(s->pb, 30);
+
+ if (pos == FIRST) {
+ if (av_new_packet(pkt, next - 52 + 24) < 0)
+ return AVERROR(ENOMEM);
+ memcpy(pkt->data, header, 24);
+ ret = avio_read(s->pb, pkt->data + 24, next - 52);
+ if (ret < 0) {
+ av_free_packet(pkt);
+ return ret;
+ }
+ av_shrink_packet(pkt, ret + 24);
+ } else {
+ ret = av_get_packet(s->pb, pkt, next - 52);
+ }
+ pkt->stream_index = !!is_video;
+ pkt->pos = pos;
+ if (flags & (1 << 12))
+ pkt->flags |= AV_PKT_FLAG_KEY;
+
+ return ret;
+}
+
+AVInputFormat ff_sdr2_demuxer = {
+ .name = "sdr2",
+ .long_name = NULL_IF_CONFIG_SMALL("SDR2"),
+ .read_probe = sdr2_probe,
+ .read_header = sdr2_read_header,
+ .read_packet = sdr2_read_packet,
+ .extensions = "sdr2",
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/seek-test.c b/libavformat/seek-test.c
index 143f0b5ace..8b0611db71 100644
--- a/libavformat/seek-test.c
+++ b/libavformat/seek-test.c
@@ -2,20 +2,20 @@
* Copyright (c) 2003 Fabrice Bellard
* Copyright (c) 2007 Michael Niedermayer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -45,13 +45,12 @@ static const char *ret_str(int v)
static void ts_str(char buffer[60], int64_t ts, AVRational base)
{
- double tsval;
if (ts == AV_NOPTS_VALUE) {
strcpy(buffer, " NOPTS ");
return;
}
- tsval = ts * av_q2d(base);
- snprintf(buffer, 60, "%9f", tsval);
+ ts= av_rescale_q(ts, base, (AVRational){1, 1000000});
+ snprintf(buffer, 60, "%c%"PRId64".%06"PRId64"", ts<0 ? '-' : ' ', FFABS(ts)/1000000, FFABS(ts)%1000000);
}
int main(int argc, char **argv)
@@ -59,8 +58,28 @@ int main(int argc, char **argv)
const char *filename;
AVFormatContext *ic = NULL;
int i, ret, stream_id;
+ int j;
int64_t timestamp;
AVDictionary *format_opts = NULL;
+ int64_t seekfirst = AV_NOPTS_VALUE;
+ int firstback=0;
+ int frame_count = 1;
+ int duration = 4;
+
+ for(i=2; i<argc; i+=2){
+ if (!strcmp(argv[i], "-seekforw")){
+ seekfirst = atoi(argv[i+1]);
+ } else if(!strcmp(argv[i], "-seekback")){
+ seekfirst = atoi(argv[i+1]);
+ firstback = 1;
+ } else if(!strcmp(argv[i], "-frames")){
+ frame_count = atoi(argv[i+1]);
+ } else if(!strcmp(argv[i], "-duration")){
+ duration = atoi(argv[i+1]);
+ } else {
+ argc = 1;
+ }
+ }
av_dict_set(&format_opts, "channels", "1", 0);
av_dict_set(&format_opts, "sample_rate", "22050", 0);
@@ -68,7 +87,7 @@ int main(int argc, char **argv)
/* initialize libavcodec, and register all codecs and formats */
av_register_all();
- if (argc != 2) {
+ if (argc < 2) {
printf("usage: %s input_file\n"
"\n", argv[0]);
return 1;
@@ -89,12 +108,17 @@ int main(int argc, char **argv)
return 1;
}
+ if(seekfirst != AV_NOPTS_VALUE){
+ if(firstback) avformat_seek_file(ic, -1, INT64_MIN, seekfirst, seekfirst, 0);
+ else avformat_seek_file(ic, -1, seekfirst, seekfirst, INT64_MAX, 0);
+ }
for(i=0; ; i++){
AVPacket pkt = { 0 };
AVStream *av_uninit(st);
char ts_buf[60];
if(ret>=0){
+ for(j=0; j<frame_count; j++) {
ret= av_read_frame(ic, &pkt);
if(ret>=0){
char dts_buf[60];
@@ -106,12 +130,13 @@ int main(int argc, char **argv)
} else
printf("ret:%s", ret_str(ret)); // necessary to avoid trailing whitespace
printf("\n");
+ }
}
if(i>25) break;
stream_id= (i>>1)%(ic->nb_streams+1) - 1;
- timestamp= (i*19362894167LL) % (4*AV_TIME_BASE) - AV_TIME_BASE;
+ timestamp= (i*19362894167LL) % (duration*AV_TIME_BASE) - AV_TIME_BASE;
if(stream_id>=0){
st= ic->streams[stream_id];
timestamp= av_rescale_q(timestamp, AV_TIME_BASE_Q, st->time_base);
diff --git a/libavformat/segafilm.c b/libavformat/segafilm.c
index 04b817789d..f8b3eee3c0 100644
--- a/libavformat/segafilm.c
+++ b/libavformat/segafilm.c
@@ -1,21 +1,21 @@
/*
* Sega FILM Format (CPK) Demuxer
- * Copyright (c) 2003 The ffmpeg Project
+ * Copyright (c) 2003 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -30,6 +30,7 @@
#include "libavutil/intreadwrite.h"
#include "avformat.h"
#include "internal.h"
+#include "avio_internal.h"
#define FILM_TAG MKBETAG('F', 'I', 'L', 'M')
#define FDSC_TAG MKBETAG('F', 'D', 'S', 'C')
@@ -61,10 +62,6 @@ typedef struct FilmDemuxContext {
unsigned int base_clock;
unsigned int version;
-
- /* buffer used for interleaving stereo PCM data */
- unsigned char *stereo_buffer;
- int stereo_buffer_size;
} FilmDemuxContext;
static int film_probe(AVProbeData *p)
@@ -72,6 +69,9 @@ static int film_probe(AVProbeData *p)
if (AV_RB32(&p->buf[0]) != FILM_TAG)
return 0;
+ if (AV_RB32(&p->buf[16]) != FDSC_TAG)
+ return 0;
+
return AVPROBE_SCORE_MAX;
}
@@ -80,7 +80,6 @@ static int film_read_close(AVFormatContext *s)
FilmDemuxContext *film = s->priv_data;
av_freep(&film->sample_table);
- av_freep(&film->stereo_buffer);
return 0;
}
@@ -96,8 +95,6 @@ static int film_read_header(AVFormatContext *s)
unsigned int audio_frame_counter;
film->sample_table = NULL;
- film->stereo_buffer = NULL;
- film->stereo_buffer_size = 0;
/* load the main FILM header */
if (avio_read(pb, scratch, 16) != 16)
@@ -121,19 +118,14 @@ static int film_read_header(AVFormatContext *s)
return AVERROR(EIO);
film->audio_samplerate = AV_RB16(&scratch[24]);
film->audio_channels = scratch[21];
- if (!film->audio_channels || film->audio_channels > 2) {
- av_log(s, AV_LOG_ERROR,
- "Invalid number of channels: %d\n", film->audio_channels);
- return AVERROR_INVALIDDATA;
- }
film->audio_bits = scratch[22];
- if (scratch[23] == 2)
+ if (scratch[23] == 2 && film->audio_channels > 0)
film->audio_type = AV_CODEC_ID_ADPCM_ADX;
else if (film->audio_channels > 0) {
if (film->audio_bits == 8)
- film->audio_type = AV_CODEC_ID_PCM_S8;
+ film->audio_type = AV_CODEC_ID_PCM_S8_PLANAR;
else if (film->audio_bits == 16)
- film->audio_type = AV_CODEC_ID_PCM_S16BE;
+ film->audio_type = AV_CODEC_ID_PCM_S16BE_PLANAR;
else
film->audio_type = AV_CODEC_ID_NONE;
} else
@@ -265,66 +257,19 @@ static int film_read_packet(AVFormatContext *s,
AVIOContext *pb = s->pb;
film_sample *sample;
int ret = 0;
- int i;
- int left, right;
if (film->current_sample >= film->sample_count)
- return AVERROR(EIO);
+ return AVERROR_EOF;
sample = &film->sample_table[film->current_sample];
/* position the stream (will probably be there anyway) */
avio_seek(pb, sample->sample_offset, SEEK_SET);
- /* do a special song and dance when loading FILM Cinepak chunks */
- if ((sample->stream == film->video_stream_index) &&
- (film->video_type == AV_CODEC_ID_CINEPAK)) {
- pkt->pos= avio_tell(pb);
- if (av_new_packet(pkt, sample->sample_size))
- return AVERROR(ENOMEM);
- avio_read(pb, pkt->data, sample->sample_size);
- } else if ((sample->stream == film->audio_stream_index) &&
- (film->audio_channels == 2) &&
- (film->audio_type != AV_CODEC_ID_ADPCM_ADX)) {
- /* stereo PCM needs to be interleaved */
-
- if (av_new_packet(pkt, sample->sample_size))
- return AVERROR(ENOMEM);
- /* make sure the interleave buffer is large enough */
- if (sample->sample_size > film->stereo_buffer_size) {
- av_free(film->stereo_buffer);
- film->stereo_buffer_size = sample->sample_size;
- film->stereo_buffer = av_malloc(film->stereo_buffer_size);
- if (!film->stereo_buffer) {
- film->stereo_buffer_size = 0;
- return AVERROR(ENOMEM);
- }
- }
-
- pkt->pos= avio_tell(pb);
- ret = avio_read(pb, film->stereo_buffer, sample->sample_size);
- if (ret != sample->sample_size)
- ret = AVERROR(EIO);
-
- left = 0;
- right = sample->sample_size / 2;
- for (i = 0; i < sample->sample_size; ) {
- if (film->audio_bits == 8) {
- pkt->data[i++] = film->stereo_buffer[left++];
- pkt->data[i++] = film->stereo_buffer[right++];
- } else {
- pkt->data[i++] = film->stereo_buffer[left++];
- pkt->data[i++] = film->stereo_buffer[left++];
- pkt->data[i++] = film->stereo_buffer[right++];
- pkt->data[i++] = film->stereo_buffer[right++];
- }
- }
- } else {
- ret= av_get_packet(pb, pkt, sample->sample_size);
- if (ret != sample->sample_size)
- ret = AVERROR(EIO);
- }
+ ret= av_get_packet(pb, pkt, sample->sample_size);
+ if (ret != sample->sample_size)
+ ret = AVERROR(EIO);
pkt->stream_index = sample->stream;
pkt->pts = sample->pts;
diff --git a/libavformat/segment.c b/libavformat/segment.c
index 55ba6569f2..b65a2ebdba 100644
--- a/libavformat/segment.c
+++ b/libavformat/segment.c
@@ -1,170 +1,499 @@
/*
- * Generic segmenter
* Copyright (c) 2011, Luca Barbato
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+/**
+ * @file generic segmenter
+ * M3U8 specification can be find here:
+ * @url{http://tools.ietf.org/id/draft-pantos-http-live-streaming}
+ */
+
+/* #define DEBUG */
+
#include <float.h>
+#include <time.h>
#include "avformat.h"
#include "internal.h"
+#include "libavutil/avassert.h"
#include "libavutil/log.h"
#include "libavutil/opt.h"
#include "libavutil/avstring.h"
#include "libavutil/parseutils.h"
#include "libavutil/mathematics.h"
+#include "libavutil/time.h"
+#include "libavutil/time_internal.h"
+#include "libavutil/timestamp.h"
+
+typedef struct SegmentListEntry {
+ int index;
+ double start_time, end_time;
+ int64_t start_pts;
+ int64_t offset_pts;
+ char *filename;
+ struct SegmentListEntry *next;
+ int64_t last_duration;
+} SegmentListEntry;
+
+typedef enum {
+ LIST_TYPE_UNDEFINED = -1,
+ LIST_TYPE_FLAT = 0,
+ LIST_TYPE_CSV,
+ LIST_TYPE_M3U8,
+ LIST_TYPE_EXT, ///< deprecated
+ LIST_TYPE_FFCONCAT,
+ LIST_TYPE_NB,
+} ListType;
+
+#define SEGMENT_LIST_FLAG_CACHE 1
+#define SEGMENT_LIST_FLAG_LIVE 2
typedef struct SegmentContext {
const AVClass *class; /**< Class for private options. */
- int number;
+ int segment_idx; ///< index of the segment file to write, starting from 0
+ int segment_idx_wrap; ///< number after which the index wraps
+ int segment_idx_wrap_nb; ///< number of time the index has wraped
+ int segment_count; ///< number of segment files already written
AVOutputFormat *oformat;
AVFormatContext *avf;
- char *format; /**< Set by a private option. */
- char *list; /**< Set by a private option. */
- char *entry_prefix; /**< Set by a private option. */
- int list_type; /**< Set by a private option. */
- float time; /**< Set by a private option. */
- int size; /**< Set by a private option. */
- int wrap; /**< Set by a private option. */
+ char *format; ///< format to use for output segment files
+ char *format_options_str; ///< format options to use for output segment files
+ AVDictionary *format_options;
+ char *list; ///< filename for the segment list file
+ int list_flags; ///< flags affecting list generation
+ int list_size; ///< number of entries for the segment list file
+
+ int use_clocktime; ///< flag to cut segments at regular clock time
+ int64_t last_val; ///< remember last time for wrap around detection
+ int64_t last_cut; ///< remember last cut
+ int cut_pending;
+
+ char *entry_prefix; ///< prefix to add to list entry filenames
+ ListType list_type; ///< set the list type
+ AVIOContext *list_pb; ///< list file put-byte context
+ char *time_str; ///< segment duration specification string
+ int64_t time; ///< segment duration
+ int use_strftime; ///< flag to expand filename with strftime
+
+ char *times_str; ///< segment times specification string
+ int64_t *times; ///< list of segment interval specification
+ int nb_times; ///< number of elments in the times array
+
+ char *frames_str; ///< segment frame numbers specification string
+ int *frames; ///< list of frame number specification
+ int nb_frames; ///< number of elments in the frames array
+ int frame_count; ///< total number of reference frames
+ int segment_frame_count; ///< number of reference frames in the segment
+
+ int64_t time_delta;
int individual_header_trailer; /**< Set by a private option. */
int write_header_trailer; /**< Set by a private option. */
- int64_t offset_time;
- int64_t recording_time;
- int has_video;
- AVIOContext *pb;
+
+ int reset_timestamps; ///< reset timestamps at the begin of each segment
+ int64_t initial_offset; ///< initial timestamps offset, expressed in microseconds
+ char *reference_stream_specifier; ///< reference stream specifier
+ int reference_stream_index;
+
+ SegmentListEntry cur_entry;
+ SegmentListEntry *segment_list_entries;
+ SegmentListEntry *segment_list_entries_end;
} SegmentContext;
-enum {
- LIST_FLAT,
- LIST_HLS
-};
+static void print_csv_escaped_str(AVIOContext *ctx, const char *str)
+{
+ int needs_quoting = !!str[strcspn(str, "\",\n\r")];
+
+ if (needs_quoting)
+ avio_w8(ctx, '"');
+
+ for (; *str; str++) {
+ if (*str == '"')
+ avio_w8(ctx, '"');
+ avio_w8(ctx, *str);
+ }
+ if (needs_quoting)
+ avio_w8(ctx, '"');
+}
static int segment_mux_init(AVFormatContext *s)
{
SegmentContext *seg = s->priv_data;
AVFormatContext *oc;
int i;
+ int ret;
- seg->avf = oc = avformat_alloc_context();
- if (!oc)
- return AVERROR(ENOMEM);
+ ret = avformat_alloc_output_context2(&seg->avf, seg->oformat, NULL, NULL);
+ if (ret < 0)
+ return ret;
+ oc = seg->avf;
- oc->oformat = seg->oformat;
oc->interrupt_callback = s->interrupt_callback;
+ oc->max_delay = s->max_delay;
+ av_dict_copy(&oc->metadata, s->metadata, 0);
for (i = 0; i < s->nb_streams; i++) {
AVStream *st;
+ AVCodecContext *icodec, *ocodec;
+
if (!(st = avformat_new_stream(oc, NULL)))
return AVERROR(ENOMEM);
- avcodec_copy_context(st->codec, s->streams[i]->codec);
+ icodec = s->streams[i]->codec;
+ ocodec = st->codec;
+ avcodec_copy_context(ocodec, icodec);
+ if (!oc->oformat->codec_tag ||
+ av_codec_get_id (oc->oformat->codec_tag, icodec->codec_tag) == ocodec->codec_id ||
+ av_codec_get_tag(oc->oformat->codec_tag, icodec->codec_id) <= 0) {
+ ocodec->codec_tag = icodec->codec_tag;
+ } else {
+ ocodec->codec_tag = 0;
+ }
st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
st->time_base = s->streams[i]->time_base;
+ av_dict_copy(&st->metadata, s->streams[i]->metadata, 0);
}
return 0;
}
-static int segment_hls_window(AVFormatContext *s, int last)
+static int set_segment_filename(AVFormatContext *s)
{
SegmentContext *seg = s->priv_data;
- int i, ret = 0;
- char buf[1024];
-
- if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
- &s->interrupt_callback, NULL)) < 0)
- goto fail;
-
- 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));
-
- av_log(s, AV_LOG_VERBOSE, "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);
- if (seg->entry_prefix) {
- avio_printf(seg->pb, "%s", seg->entry_prefix);
- }
- ret = av_get_frame_filename(buf, sizeof(buf), s->filename, i);
- if (ret < 0) {
- ret = AVERROR(EINVAL);
- goto fail;
+ AVFormatContext *oc = seg->avf;
+ size_t size;
+
+ if (seg->segment_idx_wrap)
+ seg->segment_idx %= seg->segment_idx_wrap;
+ if (seg->use_strftime) {
+ time_t now0;
+ struct tm *tm, tmpbuf;
+ time(&now0);
+ tm = localtime_r(&now0, &tmpbuf);
+ if (!strftime(oc->filename, sizeof(oc->filename), s->filename, tm)) {
+ av_log(oc, AV_LOG_ERROR, "Could not get segment filename with strftime\n");
+ return AVERROR(EINVAL);
}
- avio_printf(seg->pb, "%s\n", buf);
+ } else if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
+ s->filename, seg->segment_idx) < 0) {
+ av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", s->filename);
+ return AVERROR(EINVAL);
}
- if (last)
- avio_printf(seg->pb, "#EXT-X-ENDLIST\n");
-fail:
- avio_closep(&seg->pb);
- return ret;
+ /* copy modified name in list entry */
+ size = strlen(av_basename(oc->filename)) + 1;
+ if (seg->entry_prefix)
+ size += strlen(seg->entry_prefix);
+
+ seg->cur_entry.filename = av_mallocz(size);
+ if (!seg->cur_entry.filename)
+ return AVERROR(ENOMEM);
+ snprintf(seg->cur_entry.filename, size, "%s%s",
+ seg->entry_prefix ? seg->entry_prefix : "",
+ av_basename(oc->filename));
+
+ return 0;
}
static int segment_start(AVFormatContext *s, int write_header)
{
- SegmentContext *c = s->priv_data;
- AVFormatContext *oc = c->avf;
+ SegmentContext *seg = s->priv_data;
+ AVFormatContext *oc = seg->avf;
int err = 0;
if (write_header) {
avformat_free_context(oc);
- c->avf = NULL;
+ seg->avf = NULL;
if ((err = segment_mux_init(s)) < 0)
return err;
- oc = c->avf;
+ oc = seg->avf;
}
- if (c->wrap)
- c->number %= c->wrap;
+ seg->segment_idx++;
+ if ((seg->segment_idx_wrap) && (seg->segment_idx%seg->segment_idx_wrap == 0))
+ seg->segment_idx_wrap_nb++;
- if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
- s->filename, c->number++) < 0)
- return AVERROR(EINVAL);
+ if ((err = set_segment_filename(s)) < 0)
+ return err;
if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
- &s->interrupt_callback, NULL)) < 0)
+ &s->interrupt_callback, NULL)) < 0) {
+ av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", oc->filename);
return err;
+ }
if (oc->oformat->priv_class && oc->priv_data)
- av_opt_set(oc->priv_data, "resend_headers", "1", 0); /* mpegts specific */
+ av_opt_set(oc->priv_data, "mpegts_flags", "+resend_headers", 0);
if (write_header) {
if ((err = avformat_write_header(oc, NULL)) < 0)
return err;
}
+ seg->segment_frame_count = 0;
return 0;
}
-static int segment_end(AVFormatContext *oc, int write_trailer)
+static int segment_list_open(AVFormatContext *s)
{
+ SegmentContext *seg = s->priv_data;
+ int ret;
+
+ ret = avio_open2(&seg->list_pb, seg->list, AVIO_FLAG_WRITE,
+ &s->interrupt_callback, NULL);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "Failed to open segment list '%s'\n", seg->list);
+ return ret;
+ }
+
+ if (seg->list_type == LIST_TYPE_M3U8 && seg->segment_list_entries) {
+ SegmentListEntry *entry;
+ double max_duration = 0;
+
+ avio_printf(seg->list_pb, "#EXTM3U\n");
+ avio_printf(seg->list_pb, "#EXT-X-VERSION:3\n");
+ avio_printf(seg->list_pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg->segment_list_entries->index);
+ avio_printf(seg->list_pb, "#EXT-X-ALLOW-CACHE:%s\n",
+ seg->list_flags & SEGMENT_LIST_FLAG_CACHE ? "YES" : "NO");
+
+ av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%d\n",
+ seg->segment_list_entries->index);
+
+ for (entry = seg->segment_list_entries; entry; entry = entry->next)
+ max_duration = FFMAX(max_duration, entry->end_time - entry->start_time);
+ avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%"PRId64"\n", (int64_t)ceil(max_duration));
+ } else if (seg->list_type == LIST_TYPE_FFCONCAT) {
+ avio_printf(seg->list_pb, "ffconcat version 1.0\n");
+ }
+
+ return ret;
+}
+
+static void segment_list_print_entry(AVIOContext *list_ioctx,
+ ListType list_type,
+ const SegmentListEntry *list_entry,
+ void *log_ctx)
+{
+ switch (list_type) {
+ case LIST_TYPE_FLAT:
+ avio_printf(list_ioctx, "%s\n", list_entry->filename);
+ break;
+ case LIST_TYPE_CSV:
+ case LIST_TYPE_EXT:
+ print_csv_escaped_str(list_ioctx, list_entry->filename);
+ avio_printf(list_ioctx, ",%f,%f\n", list_entry->start_time, list_entry->end_time);
+ break;
+ case LIST_TYPE_M3U8:
+ avio_printf(list_ioctx, "#EXTINF:%f,\n%s\n",
+ list_entry->end_time - list_entry->start_time, list_entry->filename);
+ break;
+ case LIST_TYPE_FFCONCAT:
+ {
+ char *buf;
+ if (av_escape(&buf, list_entry->filename, NULL, AV_ESCAPE_MODE_AUTO, AV_ESCAPE_FLAG_WHITESPACE) < 0) {
+ av_log(log_ctx, AV_LOG_WARNING,
+ "Error writing list entry '%s' in list file\n", list_entry->filename);
+ return;
+ }
+ avio_printf(list_ioctx, "file %s\n", buf);
+ av_free(buf);
+ break;
+ }
+ default:
+ av_assert0(!"Invalid list type");
+ }
+}
+
+static int segment_end(AVFormatContext *s, int write_trailer, int is_last)
+{
+ SegmentContext *seg = s->priv_data;
+ AVFormatContext *oc = seg->avf;
int ret = 0;
av_write_frame(oc, NULL); /* Flush any buffered data (fragmented mp4) */
if (write_trailer)
- av_write_trailer(oc);
- avio_close(oc->pb);
+ ret = av_write_trailer(oc);
+
+ if (ret < 0)
+ av_log(s, AV_LOG_ERROR, "Failure occurred when ending segment '%s'\n",
+ oc->filename);
+
+ if (seg->list) {
+ if (seg->list_size || seg->list_type == LIST_TYPE_M3U8) {
+ SegmentListEntry *entry = av_mallocz(sizeof(*entry));
+ if (!entry) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+
+ /* append new element */
+ memcpy(entry, &seg->cur_entry, sizeof(*entry));
+ if (!seg->segment_list_entries)
+ seg->segment_list_entries = seg->segment_list_entries_end = entry;
+ else
+ seg->segment_list_entries_end->next = entry;
+ seg->segment_list_entries_end = entry;
+
+ /* drop first item */
+ if (seg->list_size && seg->segment_count >= seg->list_size) {
+ entry = seg->segment_list_entries;
+ seg->segment_list_entries = seg->segment_list_entries->next;
+ av_freep(&entry->filename);
+ av_freep(&entry);
+ }
+
+ avio_closep(&seg->list_pb);
+ if ((ret = segment_list_open(s)) < 0)
+ goto end;
+ for (entry = seg->segment_list_entries; entry; entry = entry->next)
+ segment_list_print_entry(seg->list_pb, seg->list_type, entry, s);
+ if (seg->list_type == LIST_TYPE_M3U8 && is_last)
+ avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n");
+ } else {
+ segment_list_print_entry(seg->list_pb, seg->list_type, &seg->cur_entry, s);
+ }
+ avio_flush(seg->list_pb);
+ }
+
+ av_log(s, AV_LOG_VERBOSE, "segment:'%s' count:%d ended\n",
+ seg->avf->filename, seg->segment_count);
+ seg->segment_count++;
+
+end:
+ avio_closep(&oc->pb);
+
+ return ret;
+}
+
+static int parse_times(void *log_ctx, int64_t **times, int *nb_times,
+ const char *times_str)
+{
+ char *p;
+ int i, ret = 0;
+ char *times_str1 = av_strdup(times_str);
+ char *saveptr = NULL;
+
+ if (!times_str1)
+ return AVERROR(ENOMEM);
+
+#define FAIL(err) ret = err; goto end
+
+ *nb_times = 1;
+ for (p = times_str1; *p; p++)
+ if (*p == ',')
+ (*nb_times)++;
+
+ *times = av_malloc_array(*nb_times, sizeof(**times));
+ if (!*times) {
+ av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced times array\n");
+ FAIL(AVERROR(ENOMEM));
+ }
+
+ p = times_str1;
+ for (i = 0; i < *nb_times; i++) {
+ int64_t t;
+ char *tstr = av_strtok(p, ",", &saveptr);
+ p = NULL;
+
+ if (!tstr || !tstr[0]) {
+ av_log(log_ctx, AV_LOG_ERROR, "Empty time specification in times list %s\n",
+ times_str);
+ FAIL(AVERROR(EINVAL));
+ }
+
+ ret = av_parse_time(&t, tstr, 1);
+ if (ret < 0) {
+ av_log(log_ctx, AV_LOG_ERROR,
+ "Invalid time duration specification '%s' in times list %s\n", tstr, times_str);
+ FAIL(AVERROR(EINVAL));
+ }
+ (*times)[i] = t;
+
+ /* check on monotonicity */
+ if (i && (*times)[i-1] > (*times)[i]) {
+ av_log(log_ctx, AV_LOG_ERROR,
+ "Specified time %f is greater than the following time %f\n",
+ (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000);
+ FAIL(AVERROR(EINVAL));
+ }
+ }
+
+end:
+ av_free(times_str1);
+ return ret;
+}
+
+static int parse_frames(void *log_ctx, int **frames, int *nb_frames,
+ const char *frames_str)
+{
+ char *p;
+ int i, ret = 0;
+ char *frames_str1 = av_strdup(frames_str);
+ char *saveptr = NULL;
+
+ if (!frames_str1)
+ return AVERROR(ENOMEM);
+
+#define FAIL(err) ret = err; goto end
+
+ *nb_frames = 1;
+ for (p = frames_str1; *p; p++)
+ if (*p == ',')
+ (*nb_frames)++;
+
+ *frames = av_malloc_array(*nb_frames, sizeof(**frames));
+ if (!*frames) {
+ av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced frames array\n");
+ FAIL(AVERROR(ENOMEM));
+ }
+
+ p = frames_str1;
+ for (i = 0; i < *nb_frames; i++) {
+ long int f;
+ char *tailptr;
+ char *fstr = av_strtok(p, ",", &saveptr);
+
+ p = NULL;
+ if (!fstr) {
+ av_log(log_ctx, AV_LOG_ERROR, "Empty frame specification in frame list %s\n",
+ frames_str);
+ FAIL(AVERROR(EINVAL));
+ }
+ f = strtol(fstr, &tailptr, 10);
+ if (*tailptr || f <= 0 || f >= INT_MAX) {
+ av_log(log_ctx, AV_LOG_ERROR,
+ "Invalid argument '%s', must be a positive integer <= INT64_MAX\n",
+ fstr);
+ FAIL(AVERROR(EINVAL));
+ }
+ (*frames)[i] = f;
+
+ /* check on monotonicity */
+ if (i && (*frames)[i-1] > (*frames)[i]) {
+ av_log(log_ctx, AV_LOG_ERROR,
+ "Specified frame %d is greater than the following frame %d\n",
+ (*frames)[i], (*frames)[i-1]);
+ FAIL(AVERROR(EINVAL));
+ }
+ }
+end:
+ av_free(frames_str1);
return ret;
}
@@ -182,15 +511,72 @@ static int open_null_ctx(AVIOContext **ctx)
return 0;
}
-static void close_null_ctx(AVIOContext *pb)
+static void close_null_ctxp(AVIOContext **pb)
{
- av_free(pb->buffer);
- av_free(pb);
+ av_freep(&(*pb)->buffer);
+ av_freep(pb);
+}
+
+static int select_reference_stream(AVFormatContext *s)
+{
+ SegmentContext *seg = s->priv_data;
+ int ret, i;
+
+ seg->reference_stream_index = -1;
+ if (!strcmp(seg->reference_stream_specifier, "auto")) {
+ /* select first index of type with highest priority */
+ int type_index_map[AVMEDIA_TYPE_NB];
+ static const enum AVMediaType type_priority_list[] = {
+ AVMEDIA_TYPE_VIDEO,
+ AVMEDIA_TYPE_AUDIO,
+ AVMEDIA_TYPE_SUBTITLE,
+ AVMEDIA_TYPE_DATA,
+ AVMEDIA_TYPE_ATTACHMENT
+ };
+ enum AVMediaType type;
+
+ for (i = 0; i < AVMEDIA_TYPE_NB; i++)
+ type_index_map[i] = -1;
+
+ /* select first index for each type */
+ for (i = 0; i < s->nb_streams; i++) {
+ type = s->streams[i]->codec->codec_type;
+ if ((unsigned)type < AVMEDIA_TYPE_NB && type_index_map[type] == -1
+ /* ignore attached pictures/cover art streams */
+ && !(s->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC))
+ type_index_map[type] = i;
+ }
+
+ for (i = 0; i < FF_ARRAY_ELEMS(type_priority_list); i++) {
+ type = type_priority_list[i];
+ if ((seg->reference_stream_index = type_index_map[type]) >= 0)
+ break;
+ }
+ } else {
+ for (i = 0; i < s->nb_streams; i++) {
+ ret = avformat_match_stream_specifier(s, s->streams[i],
+ seg->reference_stream_specifier);
+ if (ret < 0)
+ return ret;
+ if (ret > 0) {
+ seg->reference_stream_index = i;
+ break;
+ }
+ }
+ }
+
+ if (seg->reference_stream_index < 0) {
+ av_log(s, AV_LOG_ERROR, "Could not select stream matching identifier '%s'\n",
+ seg->reference_stream_specifier);
+ return AVERROR(EINVAL);
+ }
+
+ return 0;
}
static void seg_free_context(SegmentContext *seg)
{
- avio_closep(&seg->pb);
+ avio_closep(&seg->list_pb);
avformat_free_context(seg->avf);
seg->avf = NULL;
}
@@ -199,27 +585,67 @@ static int seg_write_header(AVFormatContext *s)
{
SegmentContext *seg = s->priv_data;
AVFormatContext *oc = NULL;
- int ret, i;
+ AVDictionary *options = NULL;
+ int ret;
+ int 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 + !!seg->frames_str > 1) {
+ av_log(s, AV_LOG_ERROR,
+ "segment_time, segment_times, and segment_frames options "
+ "are mutually exclusive, select just one of them\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (seg->times_str) {
+ if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0)
+ return ret;
+ } else if (seg->frames_str) {
+ if ((ret = parse_frames(s, &seg->frames, &seg->nb_frames, seg->frames_str)) < 0)
+ return ret;
+ } else {
+ /* set default value if not specified */
+ if (!seg->time_str)
+ seg->time_str = av_strdup("2");
+ if ((ret = av_parse_time(&seg->time, seg->time_str, 1)) < 0) {
+ av_log(s, AV_LOG_ERROR,
+ "Invalid time duration specification '%s' for segment_time option\n",
+ seg->time_str);
+ return ret;
+ }
+ }
+
+ if (seg->format_options_str) {
+ ret = av_dict_parse_string(&seg->format_options, seg->format_options_str, "=", ":", 0);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "Could not parse format options list '%s'\n",
+ seg->format_options_str);
goto fail;
+ }
+ }
- for (i = 0; i < s->nb_streams; i++)
- seg->has_video +=
- (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO);
+ if (seg->list) {
+ if (seg->list_type == LIST_TYPE_UNDEFINED) {
+ if (av_match_ext(seg->list, "csv" )) seg->list_type = LIST_TYPE_CSV;
+ else if (av_match_ext(seg->list, "ext" )) seg->list_type = LIST_TYPE_EXT;
+ else if (av_match_ext(seg->list, "m3u8")) seg->list_type = LIST_TYPE_M3U8;
+ else if (av_match_ext(seg->list, "ffcat,ffconcat")) seg->list_type = LIST_TYPE_FFCONCAT;
+ else seg->list_type = LIST_TYPE_FLAT;
+ }
+ if ((ret = segment_list_open(s)) < 0)
+ goto fail;
+ }
+ if (seg->list_type == LIST_TYPE_EXT)
+ av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n");
- if (seg->has_video > 1)
- av_log(s, AV_LOG_WARNING,
- "More than a single video stream present, "
- "expect issues decoding it.\n");
+ if ((ret = select_reference_stream(s)) < 0)
+ goto fail;
+ av_log(s, AV_LOG_VERBOSE, "Selected stream id:%d type:%s\n",
+ seg->reference_stream_index,
+ av_get_media_type_string(s->streams[seg->reference_stream_index]->codec->codec_type));
seg->oformat = av_guess_format(seg->format, s->filename, NULL);
@@ -238,44 +664,54 @@ static int seg_write_header(AVFormatContext *s)
goto fail;
oc = seg->avf;
- if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
- s->filename, seg->number++) < 0) {
- ret = AVERROR(EINVAL);
+ if ((ret = set_segment_filename(s)) < 0)
goto fail;
- }
if (seg->write_header_trailer) {
if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
- &s->interrupt_callback, NULL)) < 0)
+ &s->interrupt_callback, NULL)) < 0) {
+ av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", oc->filename);
goto fail;
+ }
} else {
if ((ret = open_null_ctx(&oc->pb)) < 0)
goto fail;
}
- if ((ret = avformat_write_header(oc, NULL)) < 0) {
- avio_close(oc->pb);
+ av_dict_copy(&options, seg->format_options, 0);
+ ret = avformat_write_header(oc, &options);
+ if (av_dict_count(options)) {
+ av_log(s, AV_LOG_ERROR,
+ "Some of the provided format options in '%s' are not recognized\n", seg->format_options_str);
+ ret = AVERROR(EINVAL);
goto fail;
}
+ if (ret < 0) {
+ avio_closep(&oc->pb);
+ goto fail;
+ }
+ seg->segment_frame_count = 0;
+
+ av_assert0(s->nb_streams == oc->nb_streams);
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *inner_st = oc->streams[i];
+ AVStream *outer_st = s->streams[i];
+ avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
+ }
+
+ 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);
+ close_null_ctxp(&oc->pb);
if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
&s->interrupt_callback, NULL)) < 0)
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:
+ av_dict_free(&options);
if (ret < 0)
seg_free_context(seg);
@@ -285,54 +721,107 @@ fail:
static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)
{
SegmentContext *seg = s->priv_data;
- AVFormatContext *oc = seg->avf;
AVStream *st = s->streams[pkt->stream_index];
- int64_t end_pts = seg->recording_time * seg->number;
- int ret, can_split = 1;
-
- if (!oc)
+ int64_t end_pts = INT64_MAX, offset;
+ int start_frame = INT_MAX;
+ int ret;
+ struct tm ti;
+ int64_t usecs;
+ int64_t wrapped_val;
+
+ if (!seg->avf)
return AVERROR(EINVAL);
- if (seg->has_video) {
- can_split = st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
- pkt->flags & AV_PKT_FLAG_KEY;
+ if (seg->times) {
+ end_pts = seg->segment_count < seg->nb_times ?
+ seg->times[seg->segment_count] : INT64_MAX;
+ } else if (seg->frames) {
+ start_frame = seg->segment_count < seg->nb_frames ?
+ seg->frames[seg->segment_count] : INT_MAX;
+ } else {
+ if (seg->use_clocktime) {
+ int64_t avgt = av_gettime();
+ time_t sec = avgt / 1000000;
+ localtime_r(&sec, &ti);
+ usecs = (int64_t)(ti.tm_hour*3600 + ti.tm_min*60 + ti.tm_sec) * 1000000 + (avgt % 1000000);
+ wrapped_val = usecs % seg->time;
+ if (seg->last_cut != usecs && wrapped_val < seg->last_val) {
+ seg->cut_pending = 1;
+ seg->last_cut = usecs;
+ }
+ seg->last_val = wrapped_val;
+ } else {
+ end_pts = seg->time * (seg->segment_count+1);
+ }
}
- if (can_split && av_compare_ts(pkt->pts, st->time_base, end_pts,
- AV_TIME_BASE_Q) >= 0) {
- av_log(s, AV_LOG_DEBUG, "Next segment starts at %d %"PRId64"\n",
- pkt->stream_index, pkt->pts);
-
- ret = segment_end(oc, seg->individual_header_trailer);
-
- if (!ret)
- ret = segment_start(s, seg->individual_header_trailer);
+ av_dlog(s, "packet stream:%d pts:%s pts_time:%s duration_time:%s is_key:%d frame:%d\n",
+ pkt->stream_index, av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
+ av_ts2timestr(pkt->duration, &st->time_base),
+ pkt->flags & AV_PKT_FLAG_KEY,
+ pkt->stream_index == seg->reference_stream_index ? seg->frame_count : -1);
+
+ if (pkt->stream_index == seg->reference_stream_index &&
+ pkt->flags & AV_PKT_FLAG_KEY &&
+ seg->segment_frame_count > 0 &&
+ (seg->cut_pending || seg->frame_count >= start_frame ||
+ (pkt->pts != AV_NOPTS_VALUE &&
+ av_compare_ts(pkt->pts, st->time_base,
+ end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0))) {
+ /* sanitize end time in case last packet didn't have a defined duration */
+ if (seg->cur_entry.last_duration == 0)
+ seg->cur_entry.end_time = (double)pkt->pts * av_q2d(st->time_base);
+
+ if ((ret = segment_end(s, seg->individual_header_trailer, 0)) < 0)
+ goto fail;
- if (ret)
+ if ((ret = segment_start(s, seg->individual_header_trailer)) < 0)
goto fail;
- oc = seg->avf;
+ seg->cut_pending = 0;
+ seg->cur_entry.index = seg->segment_idx + seg->segment_idx_wrap*seg->segment_idx_wrap_nb;
+ seg->cur_entry.start_time = (double)pkt->pts * av_q2d(st->time_base);
+ seg->cur_entry.start_pts = av_rescale_q(pkt->pts, st->time_base, AV_TIME_BASE_Q);
+ seg->cur_entry.end_time = seg->cur_entry.start_time +
+ pkt->pts != AV_NOPTS_VALUE ? (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base) : 0;
+ } else if (pkt->pts != AV_NOPTS_VALUE && pkt->stream_index == seg->reference_stream_index) {
+ seg->cur_entry.end_time =
+ FFMAX(seg->cur_entry.end_time, (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base));
+ seg->cur_entry.last_duration = pkt->duration;
+ }
- if (seg->list) {
- if (seg->list_type == LIST_HLS) {
- if ((ret = segment_hls_window(s, 0)) < 0)
- goto fail;
- } else {
- avio_printf(seg->pb, "%s\n", oc->filename);
- avio_flush(seg->pb);
- if (seg->size && !(seg->number % seg->size)) {
- avio_closep(&seg->pb);
- if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
- &s->interrupt_callback, NULL)) < 0)
- goto fail;
- }
- }
- }
+ if (seg->segment_frame_count == 0) {
+ av_log(s, AV_LOG_VERBOSE, "segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
+ seg->avf->filename, pkt->stream_index,
+ av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), seg->frame_count);
}
- ret = ff_write_chained(oc, pkt->stream_index, pkt, s);
+ av_log(s, AV_LOG_DEBUG, "stream:%d start_pts_time:%s pts:%s pts_time:%s dts:%s dts_time:%s",
+ pkt->stream_index,
+ av_ts2timestr(seg->cur_entry.start_pts, &AV_TIME_BASE_Q),
+ av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
+ av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
+
+ /* compute new timestamps */
+ offset = av_rescale_q(seg->initial_offset - (seg->reset_timestamps ? seg->cur_entry.start_pts : 0),
+ AV_TIME_BASE_Q, st->time_base);
+ if (pkt->pts != AV_NOPTS_VALUE)
+ pkt->pts += offset;
+ if (pkt->dts != AV_NOPTS_VALUE)
+ pkt->dts += offset;
+
+ av_log(s, AV_LOG_DEBUG, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n",
+ av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
+ av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
+
+ ret = ff_write_chained(seg->avf, pkt->stream_index, pkt, s, seg->initial_offset || seg->reset_timestamps);
fail:
+ if (pkt->stream_index == seg->reference_stream_index) {
+ seg->frame_count++;
+ seg->segment_frame_count++;
+ }
+
if (ret < 0)
seg_free_context(seg);
@@ -343,49 +832,80 @@ static int seg_write_trailer(struct AVFormatContext *s)
{
SegmentContext *seg = s->priv_data;
AVFormatContext *oc = seg->avf;
+ SegmentListEntry *cur, *next;
int ret = 0;
if (!oc)
goto fail;
if (!seg->write_header_trailer) {
- if ((ret = segment_end(oc, 0)) < 0)
+ if ((ret = segment_end(s, 0, 1)) < 0)
goto fail;
open_null_ctx(&oc->pb);
ret = av_write_trailer(oc);
- close_null_ctx(oc->pb);
+ close_null_ctxp(&oc->pb);
} else {
- ret = segment_end(oc, 1);
+ ret = segment_end(s, 1, 1);
}
-
- if (ret < 0)
- goto fail;
-
- if (seg->list && seg->list_type == LIST_HLS) {
- if ((ret = segment_hls_window(s, 1) < 0))
- goto fail;
+fail:
+ if (seg->list)
+ avio_closep(&seg->list_pb);
+
+ av_dict_free(&seg->format_options);
+ av_opt_free(seg);
+ av_freep(&seg->times);
+ av_freep(&seg->frames);
+
+ cur = seg->segment_list_entries;
+ while (cur) {
+ next = cur->next;
+ av_freep(&cur->filename);
+ av_free(cur);
+ cur = next;
}
-fail:
- avio_close(seg->pb);
avformat_free_context(oc);
+ seg->avf = NULL;
return ret;
}
#define OFFSET(x) offsetof(SegmentContext, x)
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
- { "segment_format", "container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
- { "segment_time", "segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E },
- { "segment_list", "output the segment list", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
- { "segment_list_size", "maximum number of playlist entries", OFFSET(size), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E },
- { "segment_list_type", "segment list format", OFFSET(list_type), AV_OPT_TYPE_INT, {.i64 = LIST_FLAT}, 0, 2, E, "list_type" },
- { "flat", "plain list (default)", 0, AV_OPT_TYPE_CONST, {.i64 = LIST_FLAT}, 0, 0, E, "list_type" },
- { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST, {.i64 = LIST_HLS}, 0, 0, E, "list_type" },
- { "segment_wrap", "number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
- { "segment_list_entry_prefix", "base url prefix for segments", OFFSET(entry_prefix), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
+ { "reference_stream", "set reference stream", OFFSET(reference_stream_specifier), AV_OPT_TYPE_STRING, {.str = "auto"}, CHAR_MIN, CHAR_MAX, E },
+ { "segment_format", "set container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
+ { "segment_format_options", "set list of options for the container format used for the segments", OFFSET(format_options_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
+ { "segment_list", "set the segment list filename", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
+
+ { "segment_list_flags","set flags affecting segment list generation", OFFSET(list_flags), AV_OPT_TYPE_FLAGS, {.i64 = SEGMENT_LIST_FLAG_CACHE }, 0, UINT_MAX, E, "list_flags"},
+ { "cache", "allow list caching", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_CACHE }, INT_MIN, INT_MAX, E, "list_flags"},
+ { "live", "enable live-friendly list generation (useful for HLS)", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_LIVE }, INT_MIN, INT_MAX, E, "list_flags"},
+
+ { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
+
+ { "segment_list_type", "set the segment list type", OFFSET(list_type), AV_OPT_TYPE_INT, {.i64 = LIST_TYPE_UNDEFINED}, -1, LIST_TYPE_NB-1, E, "list_type" },
+ { "flat", "flat format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, E, "list_type" },
+ { "csv", "csv format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_CSV }, INT_MIN, INT_MAX, E, "list_type" },
+ { "ext", "extended format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_EXT }, INT_MIN, INT_MAX, E, "list_type" },
+ { "ffconcat", "ffconcat format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FFCONCAT }, INT_MIN, INT_MAX, E, "list_type" },
+ { "m3u8", "M3U8 format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" },
+ { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" },
+
+ { "segment_atclocktime", "set segment to be cut at clocktime", OFFSET(use_clocktime), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E},
+ { "segment_time", "set segment duration", OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
+ { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, 0, E },
+ { "segment_times", "set segment split time points", OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E },
+ { "segment_frames", "set segment split frame numbers", OFFSET(frames_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E },
+ { "segment_wrap", "set number after which the index wraps", OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
+ { "segment_list_entry_prefix", "set base url prefix for segments", OFFSET(entry_prefix), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
+ { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
+ { "segment_wrap_number", "set the number of wrap before the first segment", OFFSET(segment_idx_wrap_nb), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
+ { "strftime", "set filename expansion with strftime at segment creation", OFFSET(use_strftime), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, E },
+
{ "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
{ "write_header_trailer", "write a header to the first segment and a trailer to the last one", OFFSET(write_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
+ { "reset_timestamps", "reset timestamps at the begin of each segment", OFFSET(reset_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
+ { "initial_offset", "set initial timestamp offset", OFFSET(initial_offset), AV_OPT_TYPE_DURATION, {.i64 = 0}, -INT64_MAX, INT64_MAX, E },
{ NULL },
};
@@ -396,14 +916,31 @@ static const AVClass seg_class = {
.version = LIBAVUTIL_VERSION_INT,
};
-
AVOutputFormat ff_segment_muxer = {
.name = "segment",
.long_name = NULL_IF_CONFIG_SMALL("segment"),
.priv_data_size = sizeof(SegmentContext),
- .flags = AVFMT_NOFILE,
+ .flags = AVFMT_NOFILE|AVFMT_GLOBALHEADER,
.write_header = seg_write_header,
.write_packet = seg_write_packet,
.write_trailer = seg_write_trailer,
.priv_class = &seg_class,
};
+
+static const AVClass sseg_class = {
+ .class_name = "stream_segment muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVOutputFormat ff_stream_segment_muxer = {
+ .name = "stream_segment,ssegment",
+ .long_name = NULL_IF_CONFIG_SMALL("streaming segment muxer"),
+ .priv_data_size = sizeof(SegmentContext),
+ .flags = AVFMT_NOFILE,
+ .write_header = seg_write_header,
+ .write_packet = seg_write_packet,
+ .write_trailer = seg_write_trailer,
+ .priv_class = &sseg_class,
+};
diff --git a/libavformat/sierravmd.c b/libavformat/sierravmd.c
index 279633fe99..8bdd973080 100644
--- a/libavformat/sierravmd.c
+++ b/libavformat/sierravmd.c
@@ -1,21 +1,21 @@
/*
* Sierra VMD Format Demuxer
- * Copyright (c) 2004 The ffmpeg Project
+ * Copyright (c) 2004 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -31,6 +31,7 @@
#include "libavutil/intreadwrite.h"
#include "avformat.h"
#include "internal.h"
+#include "avio_internal.h"
#define VMD_HEADER_SIZE 0x0330
#define BYTES_PER_FRAME_RECORD 16
@@ -63,8 +64,8 @@ typedef struct VmdDemuxContext {
static int vmd_probe(AVProbeData *p)
{
- int w, h;
- if (p->buf_size < 16)
+ int w, h, sample_rate;
+ if (p->buf_size < 806)
return 0;
/* check if the first 2 bytes of the file contain the appropriate size
* of a VMD header chunk */
@@ -72,7 +73,9 @@ static int vmd_probe(AVProbeData *p)
return 0;
w = AV_RL16(&p->buf[12]);
h = AV_RL16(&p->buf[14]);
- if (!w || w > 2048 || !h || h > 2048)
+ sample_rate = AV_RL16(&p->buf[804]);
+ if ((!w || w > 2048 || !h || h > 2048) &&
+ sample_rate != 22050)
return 0;
/* only return half certainty since this check is a bit sketchy */
@@ -83,12 +86,13 @@ static int vmd_read_header(AVFormatContext *s)
{
VmdDemuxContext *vmd = s->priv_data;
AVIOContext *pb = s->pb;
- AVStream *st = NULL, *vst;
+ AVStream *st = NULL, *vst = NULL;
unsigned int toc_offset;
unsigned char *raw_frame_table;
int raw_frame_table_size;
int64_t current_offset;
int i, j, ret;
+ int width, height;
unsigned int total_frames;
int64_t current_audio_pts = 0;
unsigned char chunk[BYTES_PER_FRAME_RECORD];
@@ -100,28 +104,33 @@ static int vmd_read_header(AVFormatContext *s)
if (avio_read(pb, vmd->vmd_header, VMD_HEADER_SIZE) != VMD_HEADER_SIZE)
return AVERROR(EIO);
- if(vmd->vmd_header[24] == 'i' && vmd->vmd_header[25] == 'v' && vmd->vmd_header[26] == '3')
- vmd->is_indeo3 = 1;
- else
- vmd->is_indeo3 = 0;
- /* start up the decoders */
- vst = avformat_new_stream(s, NULL);
- if (!vst)
- return AVERROR(ENOMEM);
- avpriv_set_pts_info(vst, 33, 1, 10);
- vmd->video_stream_index = vst->index;
- vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
- vst->codec->codec_id = vmd->is_indeo3 ? AV_CODEC_ID_INDEO3 : AV_CODEC_ID_VMDVIDEO;
- vst->codec->codec_tag = 0; /* no fourcc */
- vst->codec->width = AV_RL16(&vmd->vmd_header[12]);
- vst->codec->height = AV_RL16(&vmd->vmd_header[14]);
- if(vmd->is_indeo3 && vst->codec->width > 320){
- vst->codec->width >>= 1;
- vst->codec->height >>= 1;
+ width = AV_RL16(&vmd->vmd_header[12]);
+ height = AV_RL16(&vmd->vmd_header[14]);
+ if (width && height) {
+ if(vmd->vmd_header[24] == 'i' && vmd->vmd_header[25] == 'v' && vmd->vmd_header[26] == '3') {
+ vmd->is_indeo3 = 1;
+ } else {
+ vmd->is_indeo3 = 0;
+ }
+ /* start up the decoders */
+ vst = avformat_new_stream(s, NULL);
+ if (!vst)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(vst, 33, 1, 10);
+ vmd->video_stream_index = vst->index;
+ vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ vst->codec->codec_id = vmd->is_indeo3 ? AV_CODEC_ID_INDEO3 : AV_CODEC_ID_VMDVIDEO;
+ vst->codec->codec_tag = 0; /* no fourcc */
+ vst->codec->width = width;
+ vst->codec->height = height;
+ if(vmd->is_indeo3 && vst->codec->width > 320){
+ vst->codec->width >>= 1;
+ vst->codec->height >>= 1;
+ }
+ if (ff_alloc_extradata(vst->codec, VMD_HEADER_SIZE))
+ return AVERROR(ENOMEM);
+ memcpy(vst->codec->extradata, vmd->vmd_header, VMD_HEADER_SIZE);
}
- vst->codec->extradata_size = VMD_HEADER_SIZE;
- vst->codec->extradata = av_mallocz(VMD_HEADER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
- memcpy(vst->codec->extradata, vmd->vmd_header, VMD_HEADER_SIZE);
/* if sample rate is 0, assume no audio */
vmd->sample_rate = AV_RL16(&vmd->vmd_header[804]);
@@ -154,8 +163,9 @@ static int vmd_read_header(AVFormatContext *s)
/* calculate pts */
num = st->codec->block_align;
den = st->codec->sample_rate * st->codec->channels;
- av_reduce(&den, &num, den, num, (1UL<<31)-1);
- avpriv_set_pts_info(vst, 33, num, den);
+ av_reduce(&num, &den, num, den, (1UL<<31)-1);
+ if (vst)
+ avpriv_set_pts_info(vst, 33, num, den);
avpriv_set_pts_info(st, 33, num, den);
}
@@ -194,7 +204,12 @@ static int vmd_read_header(AVFormatContext *s)
int type;
uint32_t size;
- avio_read(pb, chunk, BYTES_PER_FRAME_RECORD);
+ if ((ret = avio_read(pb, chunk, BYTES_PER_FRAME_RECORD)) != BYTES_PER_FRAME_RECORD) {
+ av_log(s, AV_LOG_ERROR, "Failed to read frame record\n");
+ if (ret >= 0)
+ ret = AVERROR_INVALIDDATA;
+ goto error;
+ }
type = chunk[0];
size = AV_RL32(&chunk[2]);
if (size > INT_MAX / 2) {
@@ -240,8 +255,8 @@ static int vmd_read_header(AVFormatContext *s)
return 0;
error:
- av_free(raw_frame_table);
- av_free(vmd->frame_table);
+ av_freep(&raw_frame_table);
+ av_freep(&vmd->frame_table);
return ret;
}
@@ -254,12 +269,14 @@ static int vmd_read_packet(AVFormatContext *s,
vmd_frame *frame;
if (vmd->current_frame >= vmd->frame_count)
- return AVERROR(EIO);
+ return AVERROR_EOF;
frame = &vmd->frame_table[vmd->current_frame];
/* position the stream (will probably be there already) */
avio_seek(pb, frame->frame_offset, SEEK_SET);
+ if(ffio_limit(pb, frame->frame_size) != frame->frame_size)
+ return AVERROR(EIO);
if (av_new_packet(pkt, frame->frame_size + BYTES_PER_FRAME_RECORD))
return AVERROR(ENOMEM);
pkt->pos= avio_tell(pb);
@@ -290,7 +307,7 @@ static int vmd_read_close(AVFormatContext *s)
{
VmdDemuxContext *vmd = s->priv_data;
- av_free(vmd->frame_table);
+ av_freep(&vmd->frame_table);
return 0;
}
diff --git a/libavformat/siff.c b/libavformat/siff.c
index cf715143e9..83ff8be3c3 100644
--- a/libavformat/siff.c
+++ b/libavformat/siff.c
@@ -2,20 +2,20 @@
* Beam Software SIFF demuxer
* Copyright (c) 2007 Konstantin Shishkov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -24,6 +24,7 @@
#include "avformat.h"
#include "internal.h"
+#include "avio_internal.h"
enum SIFFTags {
TAG_SIFF = MKTAG('S', 'I', 'F', 'F'),
@@ -128,6 +129,8 @@ static int siff_parse_vbv1(AVFormatContext *s, SIFFContext *c, AVIOContext *pb)
st->codec->width = width;
st->codec->height = height;
st->codec->pix_fmt = AV_PIX_FMT_PAL8;
+ st->nb_frames =
+ st->duration = c->frames;
avpriv_set_pts_info(st, 16, 1, 12);
c->cur_frame = 0;
@@ -193,7 +196,7 @@ static int siff_read_packet(AVFormatContext *s, AVPacket *pkt)
if (c->has_video) {
if (c->cur_frame >= c->frames)
- return AVERROR(EIO);
+ return AVERROR_EOF;
if (c->curstrm == -1) {
c->pktsize = avio_rl32(s->pb) - 4;
c->flags = avio_rl16(s->pb);
@@ -205,13 +208,19 @@ static int siff_read_packet(AVFormatContext *s, AVPacket *pkt)
}
if (!c->curstrm) {
- size = c->pktsize - c->sndsize;
- if (av_new_packet(pkt, size) < 0)
+ size = c->pktsize - c->sndsize - c->gmcsize - 2;
+ size = ffio_limit(s->pb, size);
+ if (size < 0 || c->pktsize < c->sndsize)
+ return AVERROR_INVALIDDATA;
+ if (av_new_packet(pkt, size + c->gmcsize + 2) < 0)
return AVERROR(ENOMEM);
AV_WL16(pkt->data, c->flags);
if (c->gmcsize)
memcpy(pkt->data + 2, c->gmc, c->gmcsize);
- avio_read(s->pb, pkt->data + 2 + c->gmcsize, size - c->gmcsize - 2);
+ if (avio_read(s->pb, pkt->data + 2 + c->gmcsize, size) != size) {
+ av_free_packet(pkt);
+ return AVERROR_INVALIDDATA;
+ }
pkt->stream_index = 0;
c->curstrm = -1;
} else {
@@ -227,7 +236,9 @@ static int siff_read_packet(AVFormatContext *s, AVPacket *pkt)
c->cur_frame++;
} else {
size = av_get_packet(s->pb, pkt, c->block_align);
- if (size <= 0)
+ if (!size)
+ return AVERROR_EOF;
+ if (size < 0)
return AVERROR(EIO);
pkt->duration = size;
}
diff --git a/libavformat/smacker.c b/libavformat/smacker.c
index 2fa531bf64..5dcf4adafe 100644
--- a/libavformat/smacker.c
+++ b/libavformat/smacker.c
@@ -2,20 +2,20 @@
* Smacker demuxer
* Copyright (c) 2006 Konstantin Shishkov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -94,11 +94,14 @@ static const uint8_t smk_pal[64] = {
static int smacker_probe(AVProbeData *p)
{
- if(p->buf[0] == 'S' && p->buf[1] == 'M' && p->buf[2] == 'K'
- && (p->buf[3] == '2' || p->buf[3] == '4'))
- return AVPROBE_SCORE_MAX;
- else
+ if ( AV_RL32(p->buf) != MKTAG('S', 'M', 'K', '2')
+ && AV_RL32(p->buf) != MKTAG('S', 'M', 'K', '4'))
return 0;
+
+ if (AV_RL32(p->buf+4) > 32768U || AV_RL32(p->buf+8) > 32768U)
+ return AVPROBE_SCORE_MAX/4;
+
+ return AVPROBE_SCORE_MAX;
}
static int smacker_read_header(AVFormatContext *s)
@@ -112,7 +115,7 @@ static int smacker_read_header(AVFormatContext *s)
/* read and check header */
smk->magic = avio_rl32(pb);
if (smk->magic != MKTAG('S', 'M', 'K', '2') && smk->magic != MKTAG('S', 'M', 'K', '4'))
- return -1;
+ return AVERROR_INVALIDDATA;
smk->width = avio_rl32(pb);
smk->height = avio_rl32(pb);
smk->frames = avio_rl32(pb);
@@ -126,7 +129,7 @@ static int smacker_read_header(AVFormatContext *s)
if(smk->treesize >= UINT_MAX/4){ // smk->treesize + 16 must not overflow (this check is probably redundant)
av_log(s, AV_LOG_ERROR, "treesize too large\n");
- return -1;
+ return AVERROR_INVALIDDATA;
}
//FIXME remove extradata "rebuilding"
@@ -142,10 +145,15 @@ static int smacker_read_header(AVFormatContext *s)
/* setup data */
if(smk->frames > 0xFFFFFF) {
av_log(s, AV_LOG_ERROR, "Too many frames: %"PRIu32"\n", smk->frames);
- return -1;
+ return AVERROR_INVALIDDATA;
}
- smk->frm_size = av_malloc(smk->frames * 4);
+ smk->frm_size = av_malloc_array(smk->frames, sizeof(*smk->frm_size));
smk->frm_flags = av_malloc(smk->frames);
+ if (!smk->frm_size || !smk->frm_flags) {
+ av_freep(&smk->frm_size);
+ av_freep(&smk->frm_flags);
+ return AVERROR(ENOMEM);
+ }
smk->is_ver4 = (smk->magic != MKTAG('S', 'M', 'K', '2'));
@@ -160,7 +168,7 @@ static int smacker_read_header(AVFormatContext *s)
/* init video codec */
st = avformat_new_stream(s, NULL);
if (!st)
- return -1;
+ return AVERROR(ENOMEM);
smk->videoindex = st->index;
st->codec->width = smk->width;
st->codec->height = smk->height;
@@ -182,6 +190,8 @@ static int smacker_read_header(AVFormatContext *s)
smk->indexes[i] = -1;
if (smk->rates[i]) {
ast[i] = avformat_new_stream(s, NULL);
+ if (!ast[i])
+ return AVERROR(ENOMEM);
smk->indexes[i] = ast[i]->index;
ast[i]->codec->codec_type = AVMEDIA_TYPE_AUDIO;
if (smk->aflags[i] & SMK_AUD_BINKAUD) {
@@ -212,21 +222,18 @@ static int smacker_read_header(AVFormatContext *s)
/* load trees to extradata, they will be unpacked by decoder */
- st->codec->extradata = av_mallocz(smk->treesize + 16 +
- FF_INPUT_BUFFER_PADDING_SIZE);
- st->codec->extradata_size = smk->treesize + 16;
- if(!st->codec->extradata){
+ if(ff_alloc_extradata(st->codec, smk->treesize + 16)){
av_log(s, AV_LOG_ERROR,
"Cannot allocate %"PRIu32" bytes of extradata\n",
smk->treesize + 16);
- av_free(smk->frm_size);
- av_free(smk->frm_flags);
- return -1;
+ av_freep(&smk->frm_size);
+ av_freep(&smk->frm_flags);
+ return AVERROR(ENOMEM);
}
ret = avio_read(pb, st->codec->extradata + 16, st->codec->extradata_size - 16);
if(ret != st->codec->extradata_size - 16){
- av_free(smk->frm_size);
- av_free(smk->frm_flags);
+ av_freep(&smk->frm_size);
+ av_freep(&smk->frm_flags);
return AVERROR(EIO);
}
((int32_t*)st->codec->extradata)[0] = av_le2ne32(smk->mmap_size);
@@ -250,7 +257,7 @@ static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt)
int frame_size = 0;
int palchange = 0;
- if (s->pb->eof_reached || smk->cur_frame >= smk->frames)
+ if (avio_feof(s->pb) || smk->cur_frame >= smk->frames)
return AVERROR_EOF;
/* if we demuxed all streams, pass another frame */
@@ -267,6 +274,8 @@ static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt)
memcpy(oldpal, pal, 768);
size = avio_r8(s->pb);
size = size * 4 - 1;
+ if(size + 1 > frame_size)
+ return AVERROR_INVALIDDATA;
frame_size -= size;
frame_size--;
sz = 0;
@@ -312,7 +321,7 @@ static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt)
int err;
size = avio_rl32(s->pb) - 4;
- if (!size || size > frame_size) {
+ if (!size || size + 4LL > frame_size) {
av_log(s, AV_LOG_ERROR, "Invalid audio part size\n");
return AVERROR_INVALIDDATA;
}
@@ -348,7 +357,7 @@ static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt)
smk->cur_frame++;
smk->nextpos = avio_tell(s->pb);
} else {
- if (smk->stream_id[smk->curstream] < 0)
+ if (smk->stream_id[smk->curstream] < 0 || !smk->bufs[smk->curstream])
return AVERROR_INVALIDDATA;
if (av_new_packet(pkt, smk->buf_sizes[smk->curstream]))
return AVERROR(ENOMEM);
@@ -369,16 +378,16 @@ static int smacker_read_close(AVFormatContext *s)
int i;
for(i = 0; i < 7; i++)
- av_free(smk->bufs[i]);
- av_free(smk->frm_size);
- av_free(smk->frm_flags);
+ av_freep(&smk->bufs[i]);
+ av_freep(&smk->frm_size);
+ av_freep(&smk->frm_flags);
return 0;
}
AVInputFormat ff_smacker_demuxer = {
.name = "smk",
- .long_name = NULL_IF_CONFIG_SMALL("Smacker video"),
+ .long_name = NULL_IF_CONFIG_SMALL("Smacker"),
.priv_data_size = sizeof(SmackerContext),
.read_probe = smacker_probe,
.read_header = smacker_read_header,
diff --git a/libavformat/smjpeg.c b/libavformat/smjpeg.c
index 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 a9ed28e261..99ca2ff8c9 100644
--- a/libavformat/smjpegdec.c
+++ b/libavformat/smjpegdec.c
@@ -2,20 +2,20 @@
* SMJPEG demuxer
* Copyright (c) 2011 Paul B Mahol
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -58,7 +58,7 @@ static int smjpeg_read_header(AVFormatContext *s)
duration = avio_rb32(pb); // in msec
- while (!pb->eof_reached) {
+ while (!avio_feof(pb)) {
htype = avio_rl32(pb);
switch (htype) {
case SMJPEG_TXT:
@@ -108,10 +108,10 @@ static int smjpeg_read_header(AVFormatContext *s)
hlength = avio_rb32(pb);
if (hlength < 12)
return AVERROR_INVALIDDATA;
- avio_skip(pb, 4); // number of frames
vst = avformat_new_stream(s, 0);
if (!vst)
return AVERROR(ENOMEM);
+ vst->nb_frames = avio_rb32(pb);
vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
vst->codec->width = avio_rb16(pb);
vst->codec->height = avio_rb16(pb);
@@ -141,7 +141,7 @@ static int smjpeg_read_packet(AVFormatContext *s, AVPacket *pkt)
int64_t pos;
int ret;
- if (s->pb->eof_reached)
+ if (avio_feof(s->pb))
return AVERROR_EOF;
pos = avio_tell(s->pb);
dtype = avio_rl32(s->pb);
diff --git a/libavformat/smjpegenc.c b/libavformat/smjpegenc.c
index 551af8911d..430a497c6e 100644
--- a/libavformat/smjpegenc.c
+++ b/libavformat/smjpegenc.c
@@ -2,20 +2,20 @@
* SMJPEG muxer
* Copyright (c) 2012 Paul B Mahol
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -66,7 +66,7 @@ static int smjpeg_write_header(AVFormatContext *s)
avio_wl32(pb, SMJPEG_SND);
avio_wb32(pb, 8);
avio_wb16(pb, codec->sample_rate);
- avio_w8(pb, av_get_bits_per_sample(codec->codec_id));
+ avio_w8(pb, codec->bits_per_coded_sample);
avio_w8(pb, codec->channels);
avio_wl32(pb, tag);
avpriv_set_pts_info(st, 32, 1, 1000);
diff --git a/libavformat/smoothstreamingenc.c b/libavformat/smoothstreamingenc.c
index 6af8b3991c..07173a96f9 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
*/
@@ -179,13 +179,13 @@ static void ism_free(AVFormatContext *s)
if (os->ctx && os->ctx_inited)
av_write_trailer(os->ctx);
if (os->ctx && os->ctx->pb)
- av_free(os->ctx->pb);
+ av_freep(&os->ctx->pb);
if (os->ctx)
avformat_free_context(os->ctx);
- av_free(os->private_str);
+ av_freep(&os->private_str);
for (j = 0; j < os->nb_fragments; j++)
- av_free(os->fragments[j]);
- av_free(os->fragments);
+ av_freep(&os->fragments[j]);
+ av_freep(&os->fragments);
}
av_freep(&c->streams);
}
@@ -283,7 +283,7 @@ static int write_manifest(AVFormatContext *s, int final)
avio_printf(out, "</SmoothStreamingMedia>\n");
avio_flush(out);
avio_close(out);
- return ff_rename(temp_filename, filename);
+ return ff_rename(temp_filename, filename, s);
}
static int ism_write_header(AVFormatContext *s)
@@ -294,6 +294,7 @@ static int ism_write_header(AVFormatContext *s)
if (mkdir(s->filename, 0777) == -1 && errno != EEXIST) {
ret = AVERROR(errno);
+ av_log(s, AV_LOG_ERROR, "mkdir failed\n");
goto fail;
}
@@ -303,7 +304,7 @@ static int ism_write_header(AVFormatContext *s)
goto fail;
}
- c->streams = av_mallocz(sizeof(*c->streams) * s->nb_streams);
+ c->streams = av_mallocz_array(s->nb_streams, sizeof(*c->streams));
if (!c->streams) {
ret = AVERROR(ENOMEM);
goto fail;
@@ -314,7 +315,6 @@ static int ism_write_header(AVFormatContext *s)
AVFormatContext *ctx;
AVStream *st;
AVDictionary *opts = NULL;
- char buf[10];
if (!s->streams[i]->codec->bit_rate) {
av_log(s, AV_LOG_ERROR, "No bit rate set for stream %d\n", i);
@@ -324,6 +324,7 @@ static int ism_write_header(AVFormatContext *s)
snprintf(os->dirname, sizeof(os->dirname), "%s/QualityLevels(%d)", s->filename, s->streams[i]->codec->bit_rate);
if (mkdir(os->dirname, 0777) == -1 && errno != EEXIST) {
ret = AVERROR(errno);
+ av_log(s, AV_LOG_ERROR, "mkdir failed\n");
goto fail;
}
@@ -350,8 +351,7 @@ static int ism_write_header(AVFormatContext *s)
goto fail;
}
- snprintf(buf, sizeof(buf), "%d", c->lookahead_count);
- av_dict_set(&opts, "ism_lookahead", buf, 0);
+ av_dict_set_int(&opts, "ism_lookahead", c->lookahead_count, 0);
av_dict_set(&opts, "movflags", "frag_custom", 0);
if ((ret = avformat_write_header(ctx, &opts)) < 0) {
goto fail;
@@ -433,7 +433,7 @@ static int parse_fragment(AVFormatContext *s, const char *filename, int64_t *sta
if (len < 8 || len >= *moof_size)
goto fail;
if (tag == MKTAG('u','u','i','d')) {
- const uint8_t tfxd[] = {
+ static const uint8_t tfxd[] = {
0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6,
0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2
};
@@ -541,7 +541,7 @@ static int ism_flush(AVFormatContext *s, int final)
snprintf(header_filename, sizeof(header_filename), "%s/FragmentInfo(%s=%"PRIu64")", os->dirname, os->stream_type_tag, start_ts);
snprintf(target_filename, sizeof(target_filename), "%s/Fragments(%s=%"PRIu64")", os->dirname, os->stream_type_tag, start_ts);
copy_moof(s, filename, header_filename, moof_size);
- ret = ff_rename(filename, target_filename);
+ ret = ff_rename(filename, target_filename, s);
if (ret < 0)
break;
add_fragment(os, target_filename, header_filename, start_ts, duration,
@@ -559,7 +559,7 @@ static int ism_flush(AVFormatContext *s, int final)
for (j = 0; j < remove; j++) {
unlink(os->fragments[j]->file);
unlink(os->fragments[j]->infofile);
- av_free(os->fragments[j]);
+ av_freep(&os->fragments[j]);
}
os->nb_fragments -= remove;
memmove(os->fragments, os->fragments + remove, os->nb_fragments * sizeof(*os->fragments));
@@ -596,7 +596,7 @@ static int ism_write_packet(AVFormatContext *s, AVPacket *pkt)
}
os->packets_written++;
- return ff_write_chained(os->ctx, 0, pkt, s);
+ return ff_write_chained(os->ctx, 0, pkt, s, 0);
}
static int ism_write_trailer(AVFormatContext *s)
diff --git a/libavformat/smush.c b/libavformat/smush.c
index 1615b45417..abb6989fe4 100644
--- a/libavformat/smush.c
+++ b/libavformat/smush.c
@@ -2,20 +2,20 @@
* LucasArts Smush demuxer
* Copyright (c) 2006 Cyril Zorin
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -102,7 +102,7 @@ static int smush_read_header(AVFormatContext *ctx)
while (!got_audio && ((read + 8) < size)) {
uint32_t sig, chunk_size;
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR_EOF;
sig = avio_rb32(pb);
@@ -158,11 +158,7 @@ static int smush_read_header(AVFormatContext *ctx)
vst->codec->height = height;
if (!smush->version) {
- av_free(vst->codec->extradata);
- vst->codec->extradata_size = 1024 + 2;
- vst->codec->extradata = av_malloc(vst->codec->extradata_size +
- FF_INPUT_BUFFER_PADDING_SIZE);
- if (!vst->codec->extradata)
+ if (ff_alloc_extradata(vst->codec, 1024 + 2))
return AVERROR(ENOMEM);
AV_WL16(vst->codec->extradata, subversion);
@@ -200,7 +196,7 @@ static int smush_read_packet(AVFormatContext *ctx, AVPacket *pkt)
while (!done) {
uint32_t sig, size;
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR_EOF;
sig = avio_rb32(pb);
diff --git a/libavformat/sol.c b/libavformat/sol.c
index 30f0547d1d..c9434535ef 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 (avio_feof(s->pb))
return AVERROR(EIO);
ret= av_get_packet(s->pb, pkt, MAX_SIZE);
if (ret < 0)
return ret;
+ pkt->flags &= ~AV_PKT_FLAG_CORRUPT;
pkt->stream_index = 0;
-
- /* note: we need to modify the packet size here to handle the last
- packet */
- pkt->size = ret;
return 0;
}
diff --git a/libavformat/sox.h b/libavformat/sox.h
index e59531bea3..f4a12e93ff 100644
--- a/libavformat/sox.h
+++ b/libavformat/sox.h
@@ -2,20 +2,20 @@
* SoX native format common data
* Copyright (c) 2009 Daniel Verkamp <daniel@drv.nu>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/soxdec.c b/libavformat/soxdec.c
index ec94675830..aec42843cb 100644
--- a/libavformat/soxdec.c
+++ b/libavformat/soxdec.c
@@ -5,20 +5,20 @@
* Based on libSoX sox-fmt.c
* Copyright (c) 2008 robs@users.sourceforge.net
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -75,12 +75,12 @@ static int sox_read_header(AVFormatContext *s)
if (comment_size > 0xFFFFFFFFU - SOX_FIXED_HDR - 4U) {
av_log(s, AV_LOG_ERROR, "invalid comment size (%u)\n", comment_size);
- return -1;
+ return AVERROR_INVALIDDATA;
}
if (sample_rate <= 0 || sample_rate > INT_MAX) {
av_log(s, AV_LOG_ERROR, "invalid sample rate (%f)\n", sample_rate);
- return -1;
+ return AVERROR_INVALIDDATA;
}
sample_rate_frac = sample_rate - floor(sample_rate);
@@ -92,11 +92,13 @@ static int sox_read_header(AVFormatContext *s)
if ((header_size + 4) & 7 || header_size < SOX_FIXED_HDR + comment_size
|| st->codec->channels > 65535) /* Reserve top 16 bits */ {
av_log(s, AV_LOG_ERROR, "invalid header\n");
- return -1;
+ return AVERROR_INVALIDDATA;
}
if (comment_size && comment_size < UINT_MAX) {
char *comment = av_malloc(comment_size+1);
+ if(!comment)
+ return AVERROR(ENOMEM);
if (avio_read(pb, comment, comment_size) != comment_size) {
av_freep(&comment);
return AVERROR(EIO);
@@ -122,31 +124,11 @@ static int sox_read_header(AVFormatContext *s)
return 0;
}
-#define SOX_SAMPLES 1024
-
-static int sox_read_packet(AVFormatContext *s,
- AVPacket *pkt)
-{
- int ret, size;
-
- if (s->pb->eof_reached)
- return AVERROR_EOF;
-
- size = SOX_SAMPLES*s->streams[0]->codec->block_align;
- ret = av_get_packet(s->pb, pkt, size);
- if (ret < 0)
- return AVERROR(EIO);
- pkt->stream_index = 0;
- pkt->size = ret;
-
- return 0;
-}
-
AVInputFormat ff_sox_demuxer = {
.name = "sox",
.long_name = NULL_IF_CONFIG_SMALL("SoX native"),
.read_probe = sox_probe,
.read_header = sox_read_header,
- .read_packet = sox_read_packet,
+ .read_packet = ff_pcm_read_packet,
.read_seek = ff_pcm_read_seek,
};
diff --git a/libavformat/soxenc.c b/libavformat/soxenc.c
index 17de1789a1..bfa90025e1 100644
--- a/libavformat/soxenc.c
+++ b/libavformat/soxenc.c
@@ -5,20 +5,20 @@
* Based on libSoX sox-fmt.c
* Copyright (c) 2008 robs@users.sourceforge.net
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -34,6 +34,7 @@
#include "libavutil/dict.h"
#include "avformat.h"
#include "avio_internal.h"
+#include "rawenc.h"
#include "sox.h"
typedef struct SoXContext {
@@ -51,7 +52,7 @@ static int sox_write_header(AVFormatContext *s)
comment = av_dict_get(s->metadata, "comment", NULL, 0);
if (comment)
comment_len = strlen(comment->value);
- comment_size = (comment_len + 7) & ~7;
+ comment_size = FFALIGN(comment_len, 8);
sox->header_size = SOX_FIXED_HDR + comment_size;
@@ -71,27 +72,19 @@ static int sox_write_header(AVFormatContext *s)
avio_wb32(pb, comment_size);
} else {
av_log(s, AV_LOG_ERROR, "invalid codec; use pcm_s32le or pcm_s32be\n");
- return -1;
+ return AVERROR(EINVAL);
}
if (comment_len)
avio_write(pb, comment->value, comment_len);
- for ( ; comment_size > comment_len; comment_len++)
- avio_w8(pb, 0);
+ ffio_fill(pb, 0, comment_size - comment_len);
avio_flush(pb);
return 0;
}
-static int sox_write_packet(AVFormatContext *s, AVPacket *pkt)
-{
- AVIOContext *pb = s->pb;
- avio_write(pb, pkt->data, pkt->size);
- return 0;
-}
-
static int sox_write_trailer(AVFormatContext *s)
{
SoXContext *sox = s->priv_data;
@@ -123,7 +116,7 @@ AVOutputFormat ff_sox_muxer = {
.audio_codec = AV_CODEC_ID_PCM_S32LE,
.video_codec = AV_CODEC_ID_NONE,
.write_header = sox_write_header,
- .write_packet = sox_write_packet,
+ .write_packet = ff_raw_write_packet,
.write_trailer = sox_write_trailer,
.flags = AVFMT_NOTIMESTAMPS,
};
diff --git a/libavformat/spdif.c b/libavformat/spdif.c
index 777ac47ba5..604141a261 100644
--- a/libavformat/spdif.c
+++ b/libavformat/spdif.c
@@ -2,20 +2,20 @@
* IEC 61937 common code
* Copyright (c) 2009 Bartlomiej Wolowiec
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/spdif.h b/libavformat/spdif.h
index f5b15ebbb1..fee4ff710b 100644
--- a/libavformat/spdif.h
+++ b/libavformat/spdif.h
@@ -2,20 +2,20 @@
* IEC 61937 common header
* Copyright (c) 2009 Bartlomiej Wolowiec
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -23,6 +23,7 @@
#define AVFORMAT_SPDIF_H
#include <stdint.h>
+#include "avformat.h"
#define SYNCWORD1 0xF872
#define SYNCWORD2 0x4E1F
@@ -58,5 +59,7 @@ static const uint16_t spdif_mpeg_pkt_offset[2][3] = {
};
void ff_spdif_bswap_buf16(uint16_t *dst, const uint16_t *src, int w);
+int ff_spdif_read_packet(AVFormatContext *s, AVPacket *pkt);
+int ff_spdif_probe(const uint8_t *p_buf, int buf_size, enum AVCodecID *codec);
#endif /* AVFORMAT_SPDIF_H */
diff --git a/libavformat/spdifdec.c b/libavformat/spdifdec.c
index 2fb94773a5..7c04afaf93 100644
--- a/libavformat/spdifdec.c
+++ b/libavformat/spdifdec.c
@@ -2,20 +2,20 @@
* IEC 61937 demuxer
* Copyright (c) 2010 Anssi Hannula <anssi.hannula at iki.fi>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -57,7 +57,7 @@ static int spdif_get_offset_and_codec(AVFormatContext *s,
break;
case IEC61937_MPEG2_AAC:
init_get_bits(&gbc, buf, AAC_ADTS_HEADER_SIZE * 8);
- if (avpriv_aac_parse_header(&gbc, &aac_hdr)) {
+ if (avpriv_aac_parse_header(&gbc, &aac_hdr) < 0) {
if (s) /* be silent during a probe */
av_log(s, AV_LOG_ERROR, "Invalid AAC packet in IEC 61937\n");
return AVERROR_INVALIDDATA;
@@ -105,14 +105,19 @@ static int spdif_get_offset_and_codec(AVFormatContext *s,
static int spdif_probe(AVProbeData *p)
{
- const uint8_t *buf = p->buf;
- const uint8_t *probe_end = p->buf + FFMIN(2 * SPDIF_MAX_OFFSET, p->buf_size - 1);
+ enum AVCodecID codec;
+ return ff_spdif_probe (p->buf, p->buf_size, &codec);
+}
+
+int ff_spdif_probe(const uint8_t *p_buf, int buf_size, enum AVCodecID *codec)
+{
+ const uint8_t *buf = p_buf;
+ const uint8_t *probe_end = p_buf + FFMIN(2 * SPDIF_MAX_OFFSET, buf_size - 1);
const uint8_t *expected_code = buf + 7;
uint32_t state = 0;
int sync_codes = 0;
int consecutive_codes = 0;
int offset;
- enum AVCodecID codec;
for (; buf < probe_end; buf++) {
state = (state << 8) | *buf;
@@ -127,16 +132,16 @@ static int spdif_probe(AVProbeData *p)
} else
consecutive_codes = 0;
- if (buf + 4 + AAC_ADTS_HEADER_SIZE > p->buf + p->buf_size)
+ if (buf + 4 + AAC_ADTS_HEADER_SIZE > p_buf + buf_size)
break;
/* continue probing to find more sync codes */
- probe_end = FFMIN(buf + SPDIF_MAX_OFFSET, p->buf + p->buf_size - 1);
+ probe_end = FFMIN(buf + SPDIF_MAX_OFFSET, p_buf + buf_size - 1);
/* skip directly to the next sync code */
if (!spdif_get_offset_and_codec(NULL, (buf[2] << 8) | buf[1],
- &buf[5], &offset, &codec)) {
- if (buf + offset >= p->buf + p->buf_size)
+ &buf[5], &offset, codec)) {
+ if (buf + offset >= p_buf + buf_size)
break;
expected_code = buf + offset;
buf = expected_code - 7;
@@ -161,7 +166,7 @@ static int spdif_read_header(AVFormatContext *s)
return 0;
}
-static int spdif_read_packet(AVFormatContext *s, AVPacket *pkt)
+int ff_spdif_read_packet(AVFormatContext *s, AVPacket *pkt)
{
AVIOContext *pb = s->pb;
enum IEC61937DataType data_type;
@@ -171,7 +176,7 @@ static int spdif_read_packet(AVFormatContext *s, AVPacket *pkt)
while (state != (AV_BSWAP16C(SYNCWORD1) << 16 | AV_BSWAP16C(SYNCWORD2))) {
state = (state << 8) | avio_r8(pb);
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR_EOF;
}
@@ -230,6 +235,6 @@ AVInputFormat ff_spdif_demuxer = {
.long_name = NULL_IF_CONFIG_SMALL("IEC 61937 (compressed data in S/PDIF)"),
.read_probe = spdif_probe,
.read_header = spdif_read_header,
- .read_packet = spdif_read_packet,
+ .read_packet = ff_spdif_read_packet,
.flags = AVFMT_GENERIC_INDEX,
};
diff --git a/libavformat/spdifenc.c b/libavformat/spdifenc.c
index f3acf48bd6..18afd42801 100644
--- a/libavformat/spdifenc.c
+++ b/libavformat/spdifenc.c
@@ -4,20 +4,20 @@
* Copyright (c) 2010 Anssi Hannula
* Copyright (c) 2010 Carl Eugen Hoyos
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -94,7 +94,7 @@ static const AVOption options[] = {
{ NULL },
};
-static const AVClass class = {
+static const AVClass spdif_class = {
.class_name = "spdif",
.item_name = av_default_item_name,
.option = options,
@@ -397,15 +397,15 @@ static int spdif_header_truehd(AVFormatContext *s, AVPacket *pkt)
{
IEC61937Context *ctx = s->priv_data;
int mat_code_length = 0;
- const char mat_end_code[16] = { 0xC3, 0xC2, 0xC0, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x11 };
+ static const char mat_end_code[16] = { 0xC3, 0xC2, 0xC0, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x11 };
if (!ctx->hd_buf_count) {
- const char mat_start_code[20] = { 0x07, 0x9E, 0x00, 0x03, 0x84, 0x01, 0x01, 0x01, 0x80, 0x00, 0x56, 0xA5, 0x3B, 0xF4, 0x81, 0x83, 0x49, 0x80, 0x77, 0xE0 };
+ static const char mat_start_code[20] = { 0x07, 0x9E, 0x00, 0x03, 0x84, 0x01, 0x01, 0x01, 0x80, 0x00, 0x56, 0xA5, 0x3B, 0xF4, 0x81, 0x83, 0x49, 0x80, 0x77, 0xE0 };
mat_code_length = sizeof(mat_start_code) + BURST_HEADER_SIZE;
memcpy(ctx->hd_buf, mat_start_code, sizeof(mat_start_code));
} else if (ctx->hd_buf_count == 12) {
- const char mat_middle_code[12] = { 0xC3, 0xC1, 0x42, 0x49, 0x3B, 0xFA, 0x82, 0x83, 0x49, 0x80, 0x77, 0xE0 };
+ static const char mat_middle_code[12] = { 0xC3, 0xC1, 0x42, 0x49, 0x3B, 0xFA, 0x82, 0x83, 0x49, 0x80, 0x77, 0xE0 };
mat_code_length = sizeof(mat_middle_code) + MAT_MIDDLE_CODE_OFFSET;
memcpy(&ctx->hd_buf[12 * TRUEHD_FRAME_OFFSET - BURST_HEADER_SIZE + MAT_MIDDLE_CODE_OFFSET],
mat_middle_code, sizeof(mat_middle_code));
@@ -522,13 +522,13 @@ static int spdif_write_packet(struct AVFormatContext *s, AVPacket *pkt)
}
if (ctx->extra_bswap ^ (ctx->spdif_flags & SPDIF_FLAG_BIGENDIAN)) {
- avio_write(s->pb, ctx->out_buf, ctx->out_bytes & ~1);
+ avio_write(s->pb, ctx->out_buf, ctx->out_bytes & ~1);
} else {
- av_fast_malloc(&ctx->buffer, &ctx->buffer_size, ctx->out_bytes + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!ctx->buffer)
- return AVERROR(ENOMEM);
- ff_spdif_bswap_buf16((uint16_t *)ctx->buffer, (uint16_t *)ctx->out_buf, ctx->out_bytes >> 1);
- avio_write(s->pb, ctx->buffer, ctx->out_bytes & ~1);
+ av_fast_malloc(&ctx->buffer, &ctx->buffer_size, ctx->out_bytes + FF_INPUT_BUFFER_PADDING_SIZE);
+ if (!ctx->buffer)
+ return AVERROR(ENOMEM);
+ ff_spdif_bswap_buf16((uint16_t *)ctx->buffer, (uint16_t *)ctx->out_buf, ctx->out_bytes >> 1);
+ avio_write(s->pb, ctx->buffer, ctx->out_bytes & ~1);
}
/* a final lone byte has to be MSB aligned */
@@ -554,5 +554,5 @@ AVOutputFormat ff_spdif_muxer = {
.write_packet = spdif_write_packet,
.write_trailer = spdif_write_trailer,
.flags = AVFMT_NOTIMESTAMPS,
- .priv_class = &class,
+ .priv_class = &spdif_class,
};
diff --git a/libavformat/srtdec.c b/libavformat/srtdec.c
index 9db5133a21..b35e50fc36 100644
--- a/libavformat/srtdec.c
+++ b/libavformat/srtdec.c
@@ -2,101 +2,173 @@
* SubRip subtitle demuxer
* Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "avformat.h"
#include "internal.h"
+#include "subtitles.h"
+#include "libavutil/bprint.h"
#include "libavutil/intreadwrite.h"
+typedef struct {
+ FFDemuxSubtitlesQueue q;
+} SRTContext;
+
static int srt_probe(AVProbeData *p)
{
- unsigned char *ptr = p->buf;
- int i, v, num = 0;
+ int v;
+ char buf[64], *pbuf;
+ FFTextReader tr;
+
+ ff_text_init_buf(&tr, p->buf, p->buf_size);
+
+ while (ff_text_peek_r8(&tr) == '\r' || ff_text_peek_r8(&tr) == '\n')
+ ff_text_r8(&tr);
+
+ /* Check if the first non-empty line is a number. We do not check what the
+ * number is because in practice it can be anything. */
+ if (ff_subtitles_read_line(&tr, buf, sizeof(buf)) < 0 ||
+ strtol(buf, &pbuf, 10) < 0 || *pbuf)
+ return 0;
+
+ /* Check if the next line matches a SRT timestamp */
+ if (ff_subtitles_read_line(&tr, buf, sizeof(buf)) < 0)
+ return 0;
+ if (buf[0] >= '0' && buf[1] <= '9' && strstr(buf, " --> ")
+ && sscanf(buf, "%*d:%*2d:%*2d%*1[,.]%*3d --> %*d:%*2d:%*2d%*1[,.]%3d", &v) == 1)
+ return AVPROBE_SCORE_MAX;
+
+ return 0;
+}
- if (AV_RB24(ptr) == 0xEFBBBF)
- ptr += 3; /* skip UTF-8 BOM */
+static int64_t get_pts(const char **buf, int *duration,
+ int32_t *x1, int32_t *y1, int32_t *x2, int32_t *y2)
+{
+ int i;
for (i=0; i<2; i++) {
- if (num == i && sscanf(ptr, "%*d:%*2d:%*2d%*1[,.]%*3d --> %*d:%*2d:%*2d%*1[,.]%3d", &v) == 1)
- return AVPROBE_SCORE_MAX;
- num = atoi(ptr);
- ptr += strcspn(ptr, "\n") + 1;
+ int hh1, mm1, ss1, ms1;
+ int hh2, mm2, ss2, ms2;
+ if (sscanf(*buf, "%d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d"
+ "%*[ ]X1:%u X2:%u Y1:%u Y2:%u",
+ &hh1, &mm1, &ss1, &ms1,
+ &hh2, &mm2, &ss2, &ms2,
+ x1, x2, y1, y2) >= 8) {
+ int64_t start = (hh1*3600LL + mm1*60LL + ss1) * 1000LL + ms1;
+ int64_t end = (hh2*3600LL + mm2*60LL + ss2) * 1000LL + ms2;
+ *duration = end - start;
+ *buf += ff_subtitles_next_line(*buf);
+ return start;
+ }
+ *buf += ff_subtitles_next_line(*buf);
}
- return 0;
+ return AV_NOPTS_VALUE;
}
static int srt_read_header(AVFormatContext *s)
{
+ SRTContext *srt = s->priv_data;
+ AVBPrint buf;
AVStream *st = avformat_new_stream(s, NULL);
+ int res = 0;
+ FFTextReader tr;
+ ff_text_init_avio(s, &tr, s->pb);
+
if (!st)
- return -1;
+ return AVERROR(ENOMEM);
avpriv_set_pts_info(st, 64, 1, 1000);
st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
- st->codec->codec_id = AV_CODEC_ID_SRT;
- return 0;
-}
+ st->codec->codec_id = AV_CODEC_ID_SUBRIP;
-static int64_t get_pts(const char *buf)
-{
- int i, v, hour, min, sec, hsec;
+ av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
- for (i=0; i<2; i++) {
- if (sscanf(buf, "%d:%2d:%2d%*1[,.]%3d --> %*d:%*2d:%*2d%*1[,.]%3d",
- &hour, &min, &sec, &hsec, &v) == 5) {
- min += 60*hour;
- sec += 60*min;
- return sec*1000+hsec;
+ while (!ff_text_eof(&tr)) {
+ ff_subtitles_read_text_chunk(&tr, &buf);
+
+ if (buf.len) {
+ int64_t pos = ff_text_pos(&tr);
+ int64_t pts;
+ int duration;
+ const char *ptr = buf.str;
+ int32_t x1 = -1, y1 = -1, x2 = -1, y2 = -1;
+ AVPacket *sub;
+
+ pts = get_pts(&ptr, &duration, &x1, &y1, &x2, &y2);
+ if (pts != AV_NOPTS_VALUE) {
+ int len = buf.len - (ptr - buf.str);
+ if (len <= 0)
+ continue;
+ sub = ff_subtitles_queue_insert(&srt->q, ptr, len, 0);
+ if (!sub) {
+ res = AVERROR(ENOMEM);
+ goto end;
+ }
+ sub->pos = pos;
+ sub->pts = pts;
+ sub->duration = duration;
+ if (x1 != -1) {
+ uint8_t *p = av_packet_new_side_data(sub, AV_PKT_DATA_SUBTITLE_POSITION, 16);
+ if (p) {
+ AV_WL32(p, x1);
+ AV_WL32(p + 4, y1);
+ AV_WL32(p + 8, x2);
+ AV_WL32(p + 12, y2);
+ }
+ }
+ }
}
- buf += strcspn(buf, "\n") + 1;
}
- return AV_NOPTS_VALUE;
+
+ ff_subtitles_queue_finalize(&srt->q);
+
+end:
+ av_bprint_finalize(&buf, NULL);
+ return res;
}
-static inline int is_eol(char c)
+static int srt_read_packet(AVFormatContext *s, AVPacket *pkt)
{
- return c == '\r' || c == '\n';
+ SRTContext *srt = s->priv_data;
+ return ff_subtitles_queue_read_packet(&srt->q, pkt);
}
-static int srt_read_packet(AVFormatContext *s, AVPacket *pkt)
+static int srt_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
{
- char buffer[2048], *ptr = buffer, *ptr2;
- int64_t pos = avio_tell(s->pb);
- int res = AVERROR_EOF;
-
- do {
- ptr2 = ptr;
- ptr += ff_get_line(s->pb, ptr, sizeof(buffer)+buffer-ptr);
- } while (!is_eol(*ptr2) && !s->pb->eof_reached && ptr-buffer<sizeof(buffer)-1);
-
- if (buffer[0] && !(res = av_new_packet(pkt, ptr-buffer))) {
- memcpy(pkt->data, buffer, pkt->size);
- pkt->flags |= AV_PKT_FLAG_KEY;
- pkt->pos = pos;
- pkt->pts = pkt->dts = get_pts(pkt->data);
- }
- return res;
+ SRTContext *srt = s->priv_data;
+ return ff_subtitles_queue_seek(&srt->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int srt_read_close(AVFormatContext *s)
+{
+ SRTContext *srt = s->priv_data;
+ ff_subtitles_queue_clean(&srt->q);
+ return 0;
}
AVInputFormat ff_srt_demuxer = {
.name = "srt",
.long_name = NULL_IF_CONFIG_SMALL("SubRip subtitle"),
+ .priv_data_size = sizeof(SRTContext),
.read_probe = srt_probe,
.read_header = srt_read_header,
.read_packet = srt_read_packet,
- .flags = AVFMT_GENERIC_INDEX,
+ .read_seek2 = srt_read_seek,
+ .read_close = srt_read_close,
};
diff --git a/libavformat/srtenc.c b/libavformat/srtenc.c
new file mode 100644
index 0000000000..9bb83d645d
--- /dev/null
+++ b/libavformat/srtenc.c
@@ -0,0 +1,112 @@
+/*
+ * SubRip subtitle muxer
+ * Copyright (c) 2012 Nicolas George <nicolas.george@normalesup.org>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "libavutil/log.h"
+#include "libavutil/intreadwrite.h"
+
+/* TODO: add options for:
+ - character encoding;
+ - LF / CRLF;
+ - byte order mark.
+ */
+
+typedef struct SRTContext{
+ unsigned index;
+} SRTContext;
+
+static int srt_write_header(AVFormatContext *avf)
+{
+ SRTContext *srt = avf->priv_data;
+
+ if (avf->nb_streams != 1 ||
+ avf->streams[0]->codec->codec_type != AVMEDIA_TYPE_SUBTITLE) {
+ av_log(avf, AV_LOG_ERROR,
+ "SRT supports only a single subtitles stream.\n");
+ return AVERROR(EINVAL);
+ }
+ if (avf->streams[0]->codec->codec_id != AV_CODEC_ID_TEXT &&
+ avf->streams[0]->codec->codec_id != AV_CODEC_ID_SUBRIP) {
+ av_log(avf, AV_LOG_ERROR,
+ "Unsupported subtitles codec: %s\n",
+ avcodec_get_name(avf->streams[0]->codec->codec_id));
+ return AVERROR(EINVAL);
+ }
+ avpriv_set_pts_info(avf->streams[0], 64, 1, 1000);
+ srt->index = 1;
+ return 0;
+}
+
+static int srt_write_packet(AVFormatContext *avf, AVPacket *pkt)
+{
+ SRTContext *srt = avf->priv_data;
+
+ // TODO: reindent
+ int64_t s = pkt->pts, e, d = pkt->duration;
+ int size, x1 = -1, y1 = -1, x2 = -1, y2 = -1;
+ const uint8_t *p;
+
+ p = av_packet_get_side_data(pkt, AV_PKT_DATA_SUBTITLE_POSITION, &size);
+ if (p && size == 16) {
+ x1 = AV_RL32(p );
+ y1 = AV_RL32(p + 4);
+ x2 = AV_RL32(p + 8);
+ y2 = AV_RL32(p + 12);
+ }
+
+ if (d <= 0)
+ /* For backward compatibility, fallback to convergence_duration. */
+ d = pkt->convergence_duration;
+ if (s == AV_NOPTS_VALUE || d < 0) {
+ av_log(avf, AV_LOG_WARNING,
+ "Insufficient timestamps in event number %d.\n", srt->index);
+ return 0;
+ }
+ e = s + d;
+ avio_printf(avf->pb, "%d\n%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d",
+ srt->index,
+ (int)(s / 3600000), (int)(s / 60000) % 60,
+ (int)(s / 1000) % 60, (int)(s % 1000),
+ (int)(e / 3600000), (int)(e / 60000) % 60,
+ (int)(e / 1000) % 60, (int)(e % 1000));
+ if (p)
+ avio_printf(avf->pb, " X1:%03d X2:%03d Y1:%03d Y2:%03d",
+ x1, x2, y1, y2);
+ avio_printf(avf->pb, "\n");
+
+ avio_write(avf->pb, pkt->data, pkt->size);
+ avio_write(avf->pb, "\n\n", 2);
+ srt->index++;
+ return 0;
+}
+
+AVOutputFormat ff_srt_muxer = {
+ .name = "srt",
+ .long_name = NULL_IF_CONFIG_SMALL("SubRip subtitle"),
+ .mime_type = "application/x-subrip",
+ .extensions = "srt",
+ .priv_data_size = sizeof(SRTContext),
+ .write_header = srt_write_header,
+ .write_packet = srt_write_packet,
+ .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT,
+ .subtitle_codec = AV_CODEC_ID_SUBRIP,
+};
diff --git a/libavformat/srtp.c b/libavformat/srtp.c
index 6659bfc5c9..b6e821115c 100644
--- a/libavformat/srtp.c
+++ b/libavformat/srtp.c
@@ -2,20 +2,20 @@
* SRTP encryption/decryption
* Copyright (c) 2012 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/srtp.h b/libavformat/srtp.h
index 18ed177af5..3189f8f54b 100644
--- a/libavformat/srtp.h
+++ b/libavformat/srtp.h
@@ -2,20 +2,20 @@
* SRTP encryption/decryption
* Copyright (c) 2012 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/srtpproto.c b/libavformat/srtpproto.c
index 1f818d78b4..0124696ad0 100644
--- a/libavformat/srtpproto.c
+++ b/libavformat/srtpproto.c
@@ -2,20 +2,20 @@
* SRTP network protocol
* Copyright (c) 2012 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/stldec.c b/libavformat/stldec.c
new file mode 100644
index 0000000000..b84c7e9eb0
--- /dev/null
+++ b/libavformat/stldec.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2014 Eejya Singh
+ *
+ * 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
+ * STL subtitles format demuxer
+ * @see https://documentation.apple.com/en/dvdstudiopro/usermanual/index.html#chapter=19%26section=13%26tasks=true
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "subtitles.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/avstring.h"
+
+typedef struct {
+ FFDemuxSubtitlesQueue q;
+} STLContext;
+
+static int stl_probe(AVProbeData *p)
+{
+ char c;
+ const unsigned char *ptr = p->buf;
+
+ if (AV_RB24(ptr) == 0xEFBBBF)
+ ptr += 3; /* skip UTF-8 BOM */
+
+ while (*ptr == '\r' || *ptr == '\n' || *ptr == '$' || !strncmp(ptr, "//" , 2))
+ ptr += ff_subtitles_next_line(ptr);
+
+ if (sscanf(ptr, "%*d:%*d:%*d:%*d , %*d:%*d:%*d:%*d , %c", &c) == 1)
+ return AVPROBE_SCORE_MAX;
+
+ return 0;
+}
+
+static int64_t get_pts(char **buf, int *duration)
+{
+ int hh1, mm1, ss1, ms1;
+ int hh2, mm2, ss2, ms2;
+ int len = 0;
+
+ if (sscanf(*buf, "%2d:%2d:%2d:%2d , %2d:%2d:%2d:%2d , %n",
+ &hh1, &mm1, &ss1, &ms1,
+ &hh2, &mm2, &ss2, &ms2, &len) >= 8 && len > 0) {
+ int64_t start = (hh1*3600LL + mm1*60LL + ss1) * 100LL + ms1;
+ int64_t end = (hh2*3600LL + mm2*60LL + ss2) * 100LL + ms2;
+ *duration = end - start;
+ *buf += len;
+ return start;
+ }
+ return AV_NOPTS_VALUE;
+}
+
+static int stl_read_header(AVFormatContext *s)
+{
+ STLContext *stl = s->priv_data;
+ AVStream *st = avformat_new_stream(s, NULL);
+
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 64, 1, 100);
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_STL;
+
+ while (!avio_feof(s->pb)) {
+ char line[4096];
+ char *p = line;
+ const int64_t pos = avio_tell(s->pb);
+ int len = ff_get_line(s->pb, line, sizeof(line));
+ int64_t pts_start;
+ int duration;
+
+ if (!len)
+ break;
+
+ line[strcspn(line, "\r\n")] = 0;
+ pts_start = get_pts(&p , &duration);
+
+ if (pts_start != AV_NOPTS_VALUE) {
+ AVPacket *sub;
+ sub = ff_subtitles_queue_insert(&stl->q, p, strlen(p), 0);
+ if (!sub)
+ return AVERROR(ENOMEM);
+ sub->pos = pos;
+ sub->pts = pts_start;
+ sub->duration = duration;
+ }
+ }
+ ff_subtitles_queue_finalize(&stl->q);
+ return 0;
+}
+static int stl_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ STLContext *stl = s->priv_data;
+ return ff_subtitles_queue_read_packet(&stl->q, pkt);
+}
+
+static int stl_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ STLContext *stl = s->priv_data;
+ return ff_subtitles_queue_seek(&stl->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int stl_read_close(AVFormatContext *s)
+{
+ STLContext *stl = s->priv_data;
+ ff_subtitles_queue_clean(&stl->q);
+ return 0;
+}
+
+AVInputFormat ff_stl_demuxer = {
+ .name = "stl",
+ .long_name = NULL_IF_CONFIG_SMALL("Spruce subtitle format"),
+ .priv_data_size = sizeof(STLContext),
+ .read_probe = stl_probe,
+ .read_header = stl_read_header,
+ .read_packet = stl_read_packet,
+ .read_seek2 = stl_read_seek,
+ .read_close = stl_read_close,
+ .extensions = "stl",
+};
diff --git a/libavformat/subfile.c b/libavformat/subfile.c
new file mode 100644
index 0000000000..0e84384600
--- /dev/null
+++ b/libavformat/subfile.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2014 Nicolas George
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "url.h"
+
+typedef struct SubfileContext {
+ const AVClass *class;
+ URLContext *h;
+ int64_t start;
+ int64_t end;
+ int64_t pos;
+} SubfileContext;
+
+#define OFFSET(field) offsetof(SubfileContext, field)
+#define D AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption subfile_options[] = {
+ { "start", "start offset", OFFSET(start), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, D },
+ { "end", "end offset", OFFSET(end), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, D },
+ { NULL }
+};
+
+#undef OFFSET
+#undef D
+
+static const AVClass subfile_class = {
+ .class_name = "subfile",
+ .item_name = av_default_item_name,
+ .option = subfile_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+static int slave_seek(URLContext *h)
+{
+ SubfileContext *c = h->priv_data;
+ int64_t ret;
+
+ if ((ret = ffurl_seek(c->h, c->pos, SEEK_SET)) != c->pos) {
+ if (ret >= 0)
+ ret = AVERROR_BUG;
+ av_log(h, AV_LOG_ERROR, "Impossible to seek in file: %s\n",
+ av_err2str(ret));
+ return ret;
+ }
+ return 0;
+}
+
+static int subfile_open(URLContext *h, const char *filename, int flags,
+ AVDictionary **options)
+{
+ SubfileContext *c = h->priv_data;
+ int ret;
+
+ if (c->end <= c->start) {
+ av_log(h, AV_LOG_ERROR, "end before start\n");
+ return AVERROR(EINVAL);
+ }
+ av_strstart(filename, "subfile:", &filename);
+ ret = ffurl_open(&c->h, filename, flags, &h->interrupt_callback, options);
+ if (ret < 0)
+ return ret;
+ c->pos = c->start;
+ if ((ret = slave_seek(h)) < 0) {
+ ffurl_close(c->h);
+ return ret;
+ }
+ return 0;
+}
+
+static int subfile_close(URLContext *h)
+{
+ SubfileContext *c = h->priv_data;
+ return ffurl_close(c->h);
+}
+
+static int subfile_read(URLContext *h, unsigned char *buf, int size)
+{
+ SubfileContext *c = h->priv_data;
+ int64_t rest = c->end - c->pos;
+ int ret;
+
+ if (rest <= 0)
+ return 0;
+ size = FFMIN(size, rest);
+ ret = ffurl_read(c->h, buf, size);
+ if (ret >= 0)
+ c->pos += ret;
+ return ret;
+}
+
+static int64_t subfile_seek(URLContext *h, int64_t pos, int whence)
+{
+ SubfileContext *c = h->priv_data;
+ int64_t new_pos = -1;
+ int ret;
+
+ if (whence == AVSEEK_SIZE)
+ return c->end - c->start;
+ switch (whence) {
+ case SEEK_SET:
+ new_pos = c->start + pos;
+ break;
+ case SEEK_CUR:
+ new_pos += pos;
+ break;
+ case SEEK_END:
+ new_pos = c->end + c->pos;
+ break;
+ }
+ if (new_pos < c->start)
+ return AVERROR(EINVAL);
+ c->pos = new_pos;
+ if ((ret = slave_seek(h)) < 0)
+ return ret;
+ return c->pos - c->start;
+}
+
+URLProtocol ff_subfile_protocol = {
+ .name = "subfile",
+ .url_open2 = subfile_open,
+ .url_read = subfile_read,
+ .url_seek = subfile_seek,
+ .url_close = subfile_close,
+ .priv_data_size = sizeof(SubfileContext),
+ .priv_data_class = &subfile_class,
+};
diff --git a/libavformat/subtitles.c b/libavformat/subtitles.c
new file mode 100644
index 0000000000..67624fcf32
--- /dev/null
+++ b/libavformat/subtitles.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2012-2013 Clément Bœsch <u pkh me>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "subtitles.h"
+#include "avio_internal.h"
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+
+void ff_text_init_avio(void *s, FFTextReader *r, AVIOContext *pb)
+{
+ int i;
+ r->pb = pb;
+ r->buf_pos = r->buf_len = 0;
+ r->type = FF_UTF_8;
+ for (i = 0; i < 2; i++)
+ r->buf[r->buf_len++] = avio_r8(r->pb);
+ if (strncmp("\xFF\xFE", r->buf, 2) == 0) {
+ r->type = FF_UTF16LE;
+ r->buf_pos += 2;
+ } else if (strncmp("\xFE\xFF", r->buf, 2) == 0) {
+ r->type = FF_UTF16BE;
+ r->buf_pos += 2;
+ } else {
+ r->buf[r->buf_len++] = avio_r8(r->pb);
+ if (strncmp("\xEF\xBB\xBF", r->buf, 3) == 0) {
+ // UTF8
+ r->buf_pos += 3;
+ }
+ }
+ if (s && (r->type == FF_UTF16LE || r->type == FF_UTF16BE))
+ av_log(s, AV_LOG_INFO,
+ "UTF16 is automatically converted to UTF8, do not specify a character encoding\n");
+}
+
+void ff_text_init_buf(FFTextReader *r, void *buf, size_t size)
+{
+ memset(&r->buf_pb, 0, sizeof(r->buf_pb));
+ ffio_init_context(&r->buf_pb, buf, size, 0, NULL, NULL, NULL, NULL);
+ ff_text_init_avio(NULL, r, &r->buf_pb);
+}
+
+int64_t ff_text_pos(FFTextReader *r)
+{
+ return avio_tell(r->pb) - r->buf_len + r->buf_pos;
+}
+
+int ff_text_r8(FFTextReader *r)
+{
+ uint32_t val;
+ uint8_t tmp;
+ if (r->buf_pos < r->buf_len)
+ return r->buf[r->buf_pos++];
+ if (r->type == FF_UTF16LE) {
+ GET_UTF16(val, avio_rl16(r->pb), return 0;)
+ } else if (r->type == FF_UTF16BE) {
+ GET_UTF16(val, avio_rb16(r->pb), return 0;)
+ } else {
+ return avio_r8(r->pb);
+ }
+ if (!val)
+ return 0;
+ r->buf_pos = 0;
+ r->buf_len = 0;
+ PUT_UTF8(val, tmp, r->buf[r->buf_len++] = tmp;)
+ return r->buf[r->buf_pos++]; // buf_len is at least 1
+}
+
+void ff_text_read(FFTextReader *r, char *buf, size_t size)
+{
+ for ( ; size > 0; size--)
+ *buf++ = ff_text_r8(r);
+}
+
+int ff_text_eof(FFTextReader *r)
+{
+ return r->buf_pos >= r->buf_len && avio_feof(r->pb);
+}
+
+int ff_text_peek_r8(FFTextReader *r)
+{
+ int c;
+ if (r->buf_pos < r->buf_len)
+ return r->buf[r->buf_pos];
+ c = ff_text_r8(r);
+ if (!avio_feof(r->pb)) {
+ r->buf_pos = 0;
+ r->buf_len = 1;
+ r->buf[0] = c;
+ }
+ return c;
+}
+
+AVPacket *ff_subtitles_queue_insert(FFDemuxSubtitlesQueue *q,
+ const uint8_t *event, int len, int merge)
+{
+ AVPacket *subs, *sub;
+
+ if (merge && q->nb_subs > 0) {
+ /* merge with previous event */
+
+ int old_len;
+ sub = &q->subs[q->nb_subs - 1];
+ old_len = sub->size;
+ if (av_grow_packet(sub, len) < 0)
+ return NULL;
+ memcpy(sub->data + old_len, event, len);
+ } else {
+ /* new event */
+
+ if (q->nb_subs >= INT_MAX/sizeof(*q->subs) - 1)
+ return NULL;
+ subs = av_fast_realloc(q->subs, &q->allocated_size,
+ (q->nb_subs + 1) * sizeof(*q->subs));
+ if (!subs)
+ return NULL;
+ q->subs = subs;
+ sub = &subs[q->nb_subs++];
+ if (av_new_packet(sub, len) < 0)
+ return NULL;
+ sub->flags |= AV_PKT_FLAG_KEY;
+ sub->pts = sub->dts = 0;
+ memcpy(sub->data, event, len);
+ }
+ return sub;
+}
+
+static int cmp_pkt_sub_ts_pos(const void *a, const void *b)
+{
+ const AVPacket *s1 = a;
+ const AVPacket *s2 = b;
+ if (s1->pts == s2->pts) {
+ if (s1->pos == s2->pos)
+ return 0;
+ return s1->pos > s2->pos ? 1 : -1;
+ }
+ return s1->pts > s2->pts ? 1 : -1;
+}
+
+static int cmp_pkt_sub_pos_ts(const void *a, const void *b)
+{
+ const AVPacket *s1 = a;
+ const AVPacket *s2 = b;
+ if (s1->pos == s2->pos) {
+ if (s1->pts == s2->pts)
+ return 0;
+ return s1->pts > s2->pts ? 1 : -1;
+ }
+ return s1->pos > s2->pos ? 1 : -1;
+}
+
+void ff_subtitles_queue_finalize(FFDemuxSubtitlesQueue *q)
+{
+ int i;
+
+ qsort(q->subs, q->nb_subs, sizeof(*q->subs),
+ q->sort == SUB_SORT_TS_POS ? cmp_pkt_sub_ts_pos
+ : cmp_pkt_sub_pos_ts);
+ for (i = 0; i < q->nb_subs; i++)
+ if (q->subs[i].duration == -1 && i < q->nb_subs - 1)
+ q->subs[i].duration = q->subs[i + 1].pts - q->subs[i].pts;
+}
+
+int ff_subtitles_queue_read_packet(FFDemuxSubtitlesQueue *q, AVPacket *pkt)
+{
+ AVPacket *sub = q->subs + q->current_sub_idx;
+
+ if (q->current_sub_idx == q->nb_subs)
+ return AVERROR_EOF;
+ if (av_copy_packet(pkt, sub) < 0) {
+ return AVERROR(ENOMEM);
+ }
+
+ pkt->dts = pkt->pts;
+ q->current_sub_idx++;
+ return 0;
+}
+
+static int search_sub_ts(const FFDemuxSubtitlesQueue *q, int64_t ts)
+{
+ int s1 = 0, s2 = q->nb_subs - 1;
+
+ if (s2 < s1)
+ return AVERROR(ERANGE);
+
+ for (;;) {
+ int mid;
+
+ if (s1 == s2)
+ return s1;
+ if (s1 == s2 - 1)
+ return q->subs[s1].pts <= q->subs[s2].pts ? s1 : s2;
+ mid = (s1 + s2) / 2;
+ if (q->subs[mid].pts <= ts)
+ s1 = mid;
+ else
+ s2 = mid;
+ }
+}
+
+int ff_subtitles_queue_seek(FFDemuxSubtitlesQueue *q, AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ if (flags & AVSEEK_FLAG_BYTE) {
+ return AVERROR(ENOSYS);
+ } else if (flags & AVSEEK_FLAG_FRAME) {
+ if (ts < 0 || ts >= q->nb_subs)
+ return AVERROR(ERANGE);
+ q->current_sub_idx = ts;
+ } else {
+ int i, idx = search_sub_ts(q, ts);
+ int64_t ts_selected;
+
+ if (idx < 0)
+ return idx;
+ for (i = idx; i < q->nb_subs && q->subs[i].pts < min_ts; i++)
+ if (stream_index == -1 || q->subs[i].stream_index == stream_index)
+ idx = i;
+ for (i = idx; i > 0 && q->subs[i].pts > max_ts; i--)
+ if (stream_index == -1 || q->subs[i].stream_index == stream_index)
+ idx = i;
+
+ ts_selected = q->subs[idx].pts;
+ if (ts_selected < min_ts || ts_selected > max_ts)
+ return AVERROR(ERANGE);
+
+ /* look back in the latest subtitles for overlapping subtitles */
+ for (i = idx - 1; i >= 0; i--) {
+ int64_t pts = q->subs[i].pts;
+ if (q->subs[i].duration <= 0 ||
+ (stream_index != -1 && q->subs[i].stream_index != stream_index))
+ continue;
+ if (pts >= min_ts && pts > ts_selected - q->subs[i].duration)
+ idx = i;
+ else
+ break;
+ }
+
+ /* If the queue is used to store multiple subtitles streams (like with
+ * VobSub) and the stream index is not specified, we need to make sure
+ * to focus on the smallest file position offset for a same timestamp;
+ * queue is ordered by pts and then filepos, so we can take the first
+ * entry for a given timestamp. */
+ if (stream_index == -1)
+ while (idx > 0 && q->subs[idx - 1].pts == q->subs[idx].pts)
+ idx--;
+
+ q->current_sub_idx = idx;
+ }
+ return 0;
+}
+
+void ff_subtitles_queue_clean(FFDemuxSubtitlesQueue *q)
+{
+ int i;
+
+ for (i = 0; i < q->nb_subs; i++)
+ av_free_packet(&q->subs[i]);
+ av_freep(&q->subs);
+ q->nb_subs = q->allocated_size = q->current_sub_idx = 0;
+}
+
+int ff_smil_extract_next_text_chunk(FFTextReader *tr, AVBPrint *buf, char *c)
+{
+ int i = 0;
+ char end_chr;
+
+ if (!*c) // cached char?
+ *c = ff_text_r8(tr);
+ if (!*c)
+ return 0;
+
+ end_chr = *c == '<' ? '>' : '<';
+ do {
+ av_bprint_chars(buf, *c, 1);
+ *c = ff_text_r8(tr);
+ i++;
+ } while (*c != end_chr && *c);
+ if (end_chr == '>') {
+ av_bprint_chars(buf, '>', 1);
+ *c = 0;
+ }
+ return i;
+}
+
+const char *ff_smil_get_attr_ptr(const char *s, const char *attr)
+{
+ int in_quotes = 0;
+ const int len = strlen(attr);
+
+ while (*s) {
+ while (*s) {
+ if (!in_quotes && av_isspace(*s))
+ break;
+ in_quotes ^= *s == '"'; // XXX: support escaping?
+ s++;
+ }
+ while (av_isspace(*s))
+ s++;
+ if (!av_strncasecmp(s, attr, len) && s[len] == '=')
+ return s + len + 1 + (s[len + 1] == '"');
+ }
+ return NULL;
+}
+
+static inline int is_eol(char c)
+{
+ return c == '\r' || c == '\n';
+}
+
+void ff_subtitles_read_text_chunk(FFTextReader *tr, AVBPrint *buf)
+{
+ char eol_buf[5], last_was_cr = 0;
+ int n = 0, i = 0, nb_eol = 0;
+
+ av_bprint_clear(buf);
+
+ for (;;) {
+ char c = ff_text_r8(tr);
+
+ if (!c)
+ break;
+
+ /* ignore all initial line breaks */
+ if (n == 0 && is_eol(c))
+ continue;
+
+ /* line break buffering: we don't want to add the trailing \r\n */
+ if (is_eol(c)) {
+ nb_eol += c == '\n' || last_was_cr;
+ if (nb_eol == 2)
+ break;
+ eol_buf[i++] = c;
+ if (i == sizeof(eol_buf) - 1)
+ break;
+ last_was_cr = c == '\r';
+ continue;
+ }
+
+ /* only one line break followed by data: we flush the line breaks
+ * buffer */
+ if (i) {
+ eol_buf[i] = 0;
+ av_bprintf(buf, "%s", eol_buf);
+ i = nb_eol = 0;
+ }
+
+ av_bprint_chars(buf, c, 1);
+ n++;
+ }
+}
+
+void ff_subtitles_read_chunk(AVIOContext *pb, AVBPrint *buf)
+{
+ FFTextReader tr;
+ tr.buf_pos = tr.buf_len = 0;
+ tr.type = 0;
+ tr.pb = pb;
+ ff_subtitles_read_text_chunk(&tr, buf);
+}
+
+ptrdiff_t ff_subtitles_read_line(FFTextReader *tr, char *buf, size_t size)
+{
+ size_t cur = 0;
+ if (!size)
+ return 0;
+ while (cur + 1 < size) {
+ unsigned char c = ff_text_r8(tr);
+ if (!c)
+ return ff_text_eof(tr) ? cur : AVERROR_INVALIDDATA;
+ if (c == '\r' || c == '\n')
+ break;
+ buf[cur++] = c;
+ buf[cur] = '\0';
+ }
+ if (ff_text_peek_r8(tr) == '\r')
+ ff_text_r8(tr);
+ if (ff_text_peek_r8(tr) == '\n')
+ ff_text_r8(tr);
+ return cur;
+}
diff --git a/libavformat/subtitles.h b/libavformat/subtitles.h
new file mode 100644
index 0000000000..eb719ea770
--- /dev/null
+++ b/libavformat/subtitles.h
@@ -0,0 +1,210 @@
+/*
+ * 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 <stddef.h>
+#include "avformat.h"
+#include "libavutil/bprint.h"
+
+enum sub_sort {
+ SUB_SORT_TS_POS = 0, ///< sort by timestamps, then position
+ SUB_SORT_POS_TS, ///< sort by position, then timestamps
+};
+
+enum ff_utf_type {
+ FF_UTF_8, // or other 8 bit encodings
+ FF_UTF16LE,
+ FF_UTF16BE,
+};
+
+typedef struct {
+ int type;
+ AVIOContext *pb;
+ unsigned char buf[8];
+ int buf_pos, buf_len;
+ AVIOContext buf_pb;
+} FFTextReader;
+
+/**
+ * Initialize the FFTextReader from the given AVIOContext. This function will
+ * read some bytes from pb, and test for UTF-8 or UTF-16 BOMs. Further accesses
+ * to FFTextReader will read more data from pb.
+ * If s is not NULL, the user will be warned if a UTF-16 conversion takes place.
+ *
+ * The purpose of FFTextReader is to transparently convert read data to UTF-8
+ * if the stream had a UTF-16 BOM.
+ *
+ * @param s Pointer to provide av_log context
+ * @param r object which will be initialized
+ * @param pb stream to read from (referenced as long as FFTextReader is in use)
+ */
+void ff_text_init_avio(void *s, FFTextReader *r, AVIOContext *pb);
+
+/**
+ * Similar to ff_text_init_avio(), but sets it up to read from a bounded buffer.
+ *
+ * @param r object which will be initialized
+ * @param buf buffer to read from (referenced as long as FFTextReader is in use)
+ * @param size size of buf
+ */
+void ff_text_init_buf(FFTextReader *r, void *buf, size_t size);
+
+/**
+ * Return the byte position of the next byte returned by ff_text_r8(). For
+ * UTF-16 source streams, this will return the original position, but it will
+ * be incorrect if a codepoint was only partially read with ff_text_r8().
+ */
+int64_t ff_text_pos(FFTextReader *r);
+
+/**
+ * Return the next byte. The return value is always 0 - 255. Returns 0 on EOF.
+ * If the source stream is UTF-16, this reads from the stream converted to
+ * UTF-8. On invalid UTF-16, 0 is returned.
+ */
+int ff_text_r8(FFTextReader *r);
+
+/**
+ * Return non-zero if EOF was reached.
+ */
+int ff_text_eof(FFTextReader *r);
+
+/**
+ * Like ff_text_r8(), but don't remove the byte from the buffer.
+ */
+int ff_text_peek_r8(FFTextReader *r);
+
+/**
+ * Read the given number of bytes (in UTF-8). On error or EOF, \0 bytes are
+ * written.
+ */
+void ff_text_read(FFTextReader *r, char *buf, size_t size);
+
+typedef struct {
+ AVPacket *subs; ///< array of subtitles packets
+ int nb_subs; ///< number of subtitles packets
+ int allocated_size; ///< allocated size for subs
+ int current_sub_idx; ///< current position for the read packet callback
+ enum sub_sort sort; ///< sort method to use when finalizing subtitles
+} FFDemuxSubtitlesQueue;
+
+/**
+ * Insert a new subtitle event.
+ *
+ * @param event the subtitle line, may not be zero terminated
+ * @param len the length of the event (in strlen() sense, so without '\0')
+ * @param merge set to 1 if the current event should be concatenated with the
+ * previous one instead of adding a new entry, 0 otherwise
+ */
+AVPacket *ff_subtitles_queue_insert(FFDemuxSubtitlesQueue *q,
+ const uint8_t *event, int len, int merge);
+
+/**
+ * Set missing durations and sort subtitles by PTS, and then byte position.
+ */
+void ff_subtitles_queue_finalize(FFDemuxSubtitlesQueue *q);
+
+/**
+ * Generic read_packet() callback for subtitles demuxers using this queue
+ * system.
+ */
+int ff_subtitles_queue_read_packet(FFDemuxSubtitlesQueue *q, AVPacket *pkt);
+
+/**
+ * Update current_sub_idx to emulate a seek. Except the first parameter, it
+ * matches AVInputFormat->read_seek2 prototypes.
+ */
+int ff_subtitles_queue_seek(FFDemuxSubtitlesQueue *q, AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags);
+
+/**
+ * Remove and destroy all the subtitles packets.
+ */
+void ff_subtitles_queue_clean(FFDemuxSubtitlesQueue *q);
+
+/**
+ * SMIL helper to load next chunk ("<...>" or untagged content) in buf.
+ *
+ * @param c cached character, to avoid a backward seek
+ */
+int ff_smil_extract_next_text_chunk(FFTextReader *tr, AVBPrint *buf, char *c);
+
+/**
+ * SMIL helper to point on the value of an attribute in the given tag.
+ *
+ * @param s SMIL tag ("<...>")
+ * @param attr the attribute to look for
+ */
+const char *ff_smil_get_attr_ptr(const char *s, const char *attr);
+
+/**
+ * @brief Same as ff_subtitles_read_text_chunk(), but read from an AVIOContext.
+ */
+void ff_subtitles_read_chunk(AVIOContext *pb, AVBPrint *buf);
+
+/**
+ * @brief Read a subtitles chunk from FFTextReader.
+ *
+ * A chunk is defined by a multiline "event", ending with a second line break.
+ * The trailing line breaks are trimmed. CRLF are supported.
+ * Example: "foo\r\nbar\r\n\r\nnext" will print "foo\r\nbar" into buf, and pb
+ * will focus on the 'n' of the "next" string.
+ *
+ * @param tr I/O context
+ * @param buf an initialized buf where the chunk is written
+ *
+ * @note buf is cleared before writing into it.
+ */
+void ff_subtitles_read_text_chunk(FFTextReader *tr, AVBPrint *buf);
+
+/**
+ * Get the number of characters to increment to jump to the next line, or to
+ * the end of the string.
+ * The function handles the following line breaks schemes:
+ * LF, CRLF (MS), or standalone CR (old MacOS).
+ */
+static av_always_inline int ff_subtitles_next_line(const char *ptr)
+{
+ int n = strcspn(ptr, "\r\n");
+ ptr += n;
+ if (*ptr == '\r') {
+ ptr++;
+ n++;
+ }
+ if (*ptr == '\n')
+ n++;
+ return n;
+}
+
+/**
+ * Read a line of text. Discards line ending characters.
+ * The function handles the following line breaks schemes:
+ * LF, CRLF (MS), or standalone CR (old MacOS).
+ *
+ * Returns the number of bytes written to buf. Always writes a terminating 0,
+ * similar as with snprintf.
+ *
+ * @note returns a negative error code if a \0 byte is found
+ */
+ptrdiff_t ff_subtitles_read_line(FFTextReader *tr, char *buf, size_t size);
+
+#endif /* AVFORMAT_SUBTITLES_H */
diff --git a/libavformat/subviewer1dec.c b/libavformat/subviewer1dec.c
new file mode 100644
index 0000000000..6b38533a88
--- /dev/null
+++ b/libavformat/subviewer1dec.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2012 Clément Bœsch
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * SubViewer v1 subtitle demuxer
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "subtitles.h"
+
+typedef struct {
+ FFDemuxSubtitlesQueue q;
+} SubViewer1Context;
+
+static int subviewer1_probe(AVProbeData *p)
+{
+ const unsigned char *ptr = p->buf;
+
+ if (strstr(ptr, "******** START SCRIPT ********"))
+ return AVPROBE_SCORE_EXTENSION;
+ return 0;
+}
+
+static int subviewer1_read_header(AVFormatContext *s)
+{
+ int delay = 0;
+ AVPacket *sub = NULL;
+ SubViewer1Context *subviewer1 = s->priv_data;
+ AVStream *st = avformat_new_stream(s, NULL);
+
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 64, 1, 1);
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_SUBVIEWER1;
+
+ while (!avio_feof(s->pb)) {
+ char line[4096];
+ int len = ff_get_line(s->pb, line, sizeof(line));
+ int hh, mm, ss;
+
+ if (!len)
+ break;
+
+ if (!strncmp(line, "[DELAY]", 7)) {
+ ff_get_line(s->pb, line, sizeof(line));
+ sscanf(line, "%d", &delay);
+ }
+
+ if (sscanf(line, "[%d:%d:%d]", &hh, &mm, &ss) == 3) {
+ const int64_t pos = avio_tell(s->pb);
+ int64_t pts_start = hh*3600LL + mm*60LL + ss + delay;
+
+ len = ff_get_line(s->pb, line, sizeof(line));
+ line[strcspn(line, "\r\n")] = 0;
+ if (!*line) {
+ if (sub)
+ sub->duration = pts_start - sub->pts;
+ } else {
+ sub = ff_subtitles_queue_insert(&subviewer1->q, line, len, 0);
+ if (!sub)
+ return AVERROR(ENOMEM);
+ sub->pos = pos;
+ sub->pts = pts_start;
+ sub->duration = -1;
+ }
+ }
+ }
+
+ ff_subtitles_queue_finalize(&subviewer1->q);
+ return 0;
+}
+
+static int subviewer1_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ SubViewer1Context *subviewer1 = s->priv_data;
+ return ff_subtitles_queue_read_packet(&subviewer1->q, pkt);
+}
+
+static int subviewer1_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ SubViewer1Context *subviewer1 = s->priv_data;
+ return ff_subtitles_queue_seek(&subviewer1->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int subviewer1_read_close(AVFormatContext *s)
+{
+ SubViewer1Context *subviewer1 = s->priv_data;
+ ff_subtitles_queue_clean(&subviewer1->q);
+ return 0;
+}
+
+AVInputFormat ff_subviewer1_demuxer = {
+ .name = "subviewer1",
+ .long_name = NULL_IF_CONFIG_SMALL("SubViewer v1 subtitle format"),
+ .priv_data_size = sizeof(SubViewer1Context),
+ .read_probe = subviewer1_probe,
+ .read_header = subviewer1_read_header,
+ .read_packet = subviewer1_read_packet,
+ .read_seek2 = subviewer1_read_seek,
+ .read_close = subviewer1_read_close,
+ .extensions = "sub",
+};
diff --git a/libavformat/subviewerdec.c b/libavformat/subviewerdec.c
new file mode 100644
index 0000000000..f1b0fdf0a5
--- /dev/null
+++ b/libavformat/subviewerdec.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2012 Clément Bœsch
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * SubViewer subtitle demuxer
+ * @see https://en.wikipedia.org/wiki/SubViewer
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "subtitles.h"
+#include "libavcodec/internal.h"
+#include "libavutil/avstring.h"
+#include "libavutil/bprint.h"
+#include "libavutil/intreadwrite.h"
+
+typedef struct {
+ FFDemuxSubtitlesQueue q;
+} SubViewerContext;
+
+static int subviewer_probe(AVProbeData *p)
+{
+ char c;
+ const unsigned char *ptr = p->buf;
+
+ if (AV_RB24(ptr) == 0xEFBBBF)
+ ptr += 3; /* skip UTF-8 BOM */
+ if (sscanf(ptr, "%*u:%*u:%*u.%*u,%*u:%*u:%*u.%*u%c", &c) == 1)
+ return AVPROBE_SCORE_EXTENSION;
+ if (!strncmp(ptr, "[INFORMATION]", 13))
+ return AVPROBE_SCORE_MAX/3;
+ return 0;
+}
+
+static int read_ts(const char *s, int64_t *start, int *duration)
+{
+ int64_t end;
+ int hh1, mm1, ss1, ms1;
+ int hh2, mm2, ss2, ms2;
+
+ if (sscanf(s, "%u:%u:%u.%u,%u:%u:%u.%u",
+ &hh1, &mm1, &ss1, &ms1, &hh2, &mm2, &ss2, &ms2) == 8) {
+ end = (hh2*3600LL + mm2*60LL + ss2) * 100LL + ms2;
+ *start = (hh1*3600LL + mm1*60LL + ss1) * 100LL + ms1;
+ *duration = end - *start;
+ return 0;
+ }
+ return -1;
+}
+
+static int subviewer_read_header(AVFormatContext *s)
+{
+ SubViewerContext *subviewer = s->priv_data;
+ AVStream *st = avformat_new_stream(s, NULL);
+ AVBPrint header;
+ int res = 0, new_event = 1;
+ int64_t pts_start = AV_NOPTS_VALUE;
+ int duration = -1;
+ AVPacket *sub = NULL;
+
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 64, 1, 100);
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_SUBVIEWER;
+
+ av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+ while (!avio_feof(s->pb)) {
+ char line[2048];
+ int64_t pos = 0;
+ int len = ff_get_line(s->pb, line, sizeof(line));
+
+ if (!len)
+ break;
+
+ line[strcspn(line, "\r\n")] = 0;
+
+ if (line[0] == '[' && strncmp(line, "[br]", 4)) {
+
+ /* ignore event style, XXX: add to side_data? */
+ if (strstr(line, "[COLF]") || strstr(line, "[SIZE]") ||
+ strstr(line, "[FONT]") || strstr(line, "[STYLE]"))
+ continue;
+
+ if (!st->codec->extradata) { // header not finalized yet
+ av_bprintf(&header, "%s\n", line);
+ if (!strncmp(line, "[END INFORMATION]", 17) || !strncmp(line, "[SUBTITLE]", 10)) {
+ /* end of header */
+ res = avpriv_bprint_to_extradata(st->codec, &header);
+ if (res < 0)
+ goto end;
+ } else if (strncmp(line, "[INFORMATION]", 13)) {
+ /* assume file metadata at this point */
+ int i, j = 0;
+ char key[32], value[128];
+
+ for (i = 1; i < sizeof(key) - 1 && line[i] && line[i] != ']'; i++)
+ key[i - 1] = av_tolower(line[i]);
+ key[i - 1] = 0;
+
+ if (line[i] == ']')
+ i++;
+ while (line[i] == ' ')
+ i++;
+ while (j < sizeof(value) - 1 && line[i] && line[i] != ']')
+ value[j++] = line[i++];
+ value[j] = 0;
+
+ av_dict_set(&s->metadata, key, value, 0);
+ }
+ }
+ } else if (read_ts(line, &pts_start, &duration) >= 0) {
+ new_event = 1;
+ pos = avio_tell(s->pb);
+ } else if (*line) {
+ if (!new_event) {
+ sub = ff_subtitles_queue_insert(&subviewer->q, "\n", 1, 1);
+ if (!sub) {
+ res = AVERROR(ENOMEM);
+ goto end;
+ }
+ }
+ sub = ff_subtitles_queue_insert(&subviewer->q, line, strlen(line), !new_event);
+ if (!sub) {
+ res = AVERROR(ENOMEM);
+ goto end;
+ }
+ if (new_event) {
+ sub->pos = pos;
+ sub->pts = pts_start;
+ sub->duration = duration;
+ }
+ new_event = 0;
+ }
+ }
+
+ ff_subtitles_queue_finalize(&subviewer->q);
+
+end:
+ av_bprint_finalize(&header, NULL);
+ return res;
+}
+
+static int subviewer_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ SubViewerContext *subviewer = s->priv_data;
+ return ff_subtitles_queue_read_packet(&subviewer->q, pkt);
+}
+
+static int subviewer_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ SubViewerContext *subviewer = s->priv_data;
+ return ff_subtitles_queue_seek(&subviewer->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int subviewer_read_close(AVFormatContext *s)
+{
+ SubViewerContext *subviewer = s->priv_data;
+ ff_subtitles_queue_clean(&subviewer->q);
+ return 0;
+}
+
+AVInputFormat ff_subviewer_demuxer = {
+ .name = "subviewer",
+ .long_name = NULL_IF_CONFIG_SMALL("SubViewer subtitle format"),
+ .priv_data_size = sizeof(SubViewerContext),
+ .read_probe = subviewer_probe,
+ .read_header = subviewer_read_header,
+ .read_packet = subviewer_read_packet,
+ .read_seek2 = subviewer_read_seek,
+ .read_close = subviewer_read_close,
+ .extensions = "sub",
+};
diff --git a/libavformat/supdec.c b/libavformat/supdec.c
new file mode 100644
index 0000000000..48083b2c53
--- /dev/null
+++ b/libavformat/supdec.c
@@ -0,0 +1,109 @@
+/*
+ * 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/intreadwrite.h"
+
+#define SUP_PGS_MAGIC 0x5047 /* "PG", big endian */
+
+static int sup_read_header(AVFormatContext *s)
+{
+ AVStream *st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_HDMV_PGS_SUBTITLE;
+ avpriv_set_pts_info(st, 32, 1, 90000);
+
+ return 0;
+}
+
+static int sup_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ int64_t pts, dts, pos;
+ int ret;
+
+ pos = avio_tell(s->pb);
+
+ if (avio_rb16(s->pb) != SUP_PGS_MAGIC)
+ return avio_feof(s->pb) ? AVERROR_EOF : AVERROR_INVALIDDATA;
+
+ pts = avio_rb32(s->pb);
+ dts = avio_rb32(s->pb);
+
+ if ((ret = av_get_packet(s->pb, pkt, 3)) < 0)
+ return ret;
+
+ pkt->stream_index = 0;
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ pkt->pos = pos;
+ pkt->pts = pts;
+ // Many files have DTS set to 0 for all packets, so assume 0 means unset.
+ pkt->dts = dts ? dts : AV_NOPTS_VALUE;
+
+ if (pkt->size >= 3) {
+ // The full packet size is stored as part of the packet.
+ size_t len = AV_RB16(pkt->data + 1);
+
+ if ((ret = av_append_packet(s->pb, pkt, len)) < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sup_probe(AVProbeData *p)
+{
+ unsigned char *buf = p->buf;
+ size_t buf_size = p->buf_size;
+ int nb_packets;
+
+ for (nb_packets = 0; nb_packets < 10; nb_packets++) {
+ size_t full_packet_size;
+ if (buf_size < 10 + 3)
+ break;
+ if (AV_RB16(buf) != SUP_PGS_MAGIC)
+ return 0;
+ full_packet_size = AV_RB16(buf + 10 + 1) + 10 + 3;
+ if (buf_size < full_packet_size)
+ break;
+ buf += full_packet_size;
+ buf_size -= full_packet_size;
+ }
+ if (!nb_packets)
+ return 0;
+ if (nb_packets < 2)
+ return AVPROBE_SCORE_RETRY / 2;
+ if (nb_packets < 4)
+ return AVPROBE_SCORE_RETRY;
+ if (nb_packets < 10)
+ return AVPROBE_SCORE_EXTENSION;
+ return AVPROBE_SCORE_MAX;
+}
+
+AVInputFormat ff_sup_demuxer = {
+ .name = "sup",
+ .long_name = NULL_IF_CONFIG_SMALL("raw HDMV Presentation Graphic Stream subtitles"),
+ .extensions = "sup",
+ .mime_type = "application/x-pgs",
+ .read_probe = sup_probe,
+ .read_header = sup_read_header,
+ .read_packet = sup_read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+};
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 8eb3f70541..93a094c26d 100644
--- a/libavformat/swf.h
+++ b/libavformat/swf.h
@@ -3,26 +3,32 @@
* Copyright (c) 2000 Fabrice Bellard
* Copyright (c) 2003 Tinic Uro
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef AVFORMAT_SWF_H
#define AVFORMAT_SWF_H
+#include "config.h"
+
+#if CONFIG_ZLIB
+#include <zlib.h>
+#endif
+
#include "libavutil/fifo.h"
#include "avformat.h"
#include "avio.h"
@@ -32,20 +38,73 @@
#define DUMMY_FILE_SIZE (100 * 1024 * 1024)
#define DUMMY_DURATION 600 /* in seconds */
-#define TAG_END 0
-#define TAG_SHOWFRAME 1
-#define TAG_DEFINESHAPE 2
-#define TAG_FREECHARACTER 3
-#define TAG_PLACEOBJECT 4
-#define TAG_REMOVEOBJECT 5
-#define TAG_STREAMHEAD 18
-#define TAG_STREAMBLOCK 19
-#define TAG_JPEG2 21
-#define TAG_PLACEOBJECT2 26
-#define TAG_STREAMHEAD2 45
-#define TAG_VIDEOSTREAM 60
-#define TAG_VIDEOFRAME 61
-#define TAG_FILEATTRIBUTES 69
+enum {
+ TAG_END = 0,
+ TAG_SHOWFRAME = 1,
+ TAG_DEFINESHAPE = 2,
+ TAG_FREECHARACTER = 3,
+ TAG_PLACEOBJECT = 4,
+ TAG_REMOVEOBJECT = 5,
+ TAG_DEFINEBITS = 6,
+ TAG_DEFINEBUTTON = 7,
+ TAG_JPEGTABLES = 8,
+ TAG_SETBACKGROUNDCOLOR = 9,
+ TAG_DEFINEFONT = 10,
+ TAG_DEFINETEXT = 11,
+ TAG_DOACTION = 12,
+ TAG_DEFINEFONTINFO = 13,
+ TAG_DEFINESOUND = 14,
+ TAG_STARTSOUND = 15,
+ TAG_DEFINEBUTTONSOUND = 17,
+ TAG_STREAMHEAD = 18,
+ TAG_STREAMBLOCK = 19,
+ TAG_DEFINEBITSLOSSLESS = 20,
+ TAG_JPEG2 = 21,
+ TAG_DEFINESHAPE2 = 22,
+ TAG_DEFINEBUTTONCXFORM = 23,
+ TAG_PROTECT = 24,
+ TAG_PLACEOBJECT2 = 26,
+ TAG_REMOVEOBJECT2 = 28,
+ TAG_DEFINESHAPE3 = 32,
+ TAG_DEFINETEXT2 = 33,
+ TAG_DEFINEBUTTON2 = 34,
+ TAG_DEFINEBITSJPEG3 = 35,
+ TAG_DEFINEBITSLOSSLESS2 = 36,
+ TAG_DEFINEEDITTEXT = 37,
+ TAG_DEFINESPRITE = 39,
+ TAG_FRAMELABEL = 43,
+ TAG_STREAMHEAD2 = 45,
+ TAG_DEFINEMORPHSHAPE = 46,
+ TAG_DEFINEFONT2 = 48,
+ TAG_EXPORTASSETS = 56,
+ TAG_IMPORTASSETS = 57,
+ TAG_ENABLEDEBUGGER = 58,
+ TAG_DOINITACTION = 59,
+ TAG_VIDEOSTREAM = 60,
+ TAG_VIDEOFRAME = 61,
+ TAG_DEFINEFONTINFO2 = 62,
+ TAG_ENABLEDEBUGGER2 = 64,
+ TAG_SCRIPTLIMITS = 65,
+ TAG_SETTABINDEX = 66,
+ TAG_FILEATTRIBUTES = 69,
+ TAG_PLACEOBJECT3 = 70,
+ TAG_IMPORTASSETS2 = 71,
+ TAG_DEFINEFONTALIGNZONES = 73,
+ TAG_CSMTEXTSETTINGS = 74,
+ TAG_DEFINEFONT3 = 75,
+ TAG_SYMBOLCLASS = 76,
+ TAG_METADATA = 77,
+ TAG_DEFINESCALINGGRID = 78,
+ TAG_DOABC = 82,
+ TAG_DEFINESHAPE4 = 83,
+ TAG_DEFINEMORPHSHAPE2 = 84,
+ TAG_DEFINESCENEANDFRAMELABELDATA = 86,
+ TAG_DEFINEBINARYDATA = 87,
+ TAG_DEFINEFONTNAME = 88,
+ TAG_STARTSOUND2 = 89,
+ TAG_DEFINEBITSJPEG4 = 90,
+ TAG_DEFINEFONT4 = 91,
+};
#define TAG_LONG 0x100
@@ -61,9 +120,6 @@
#define VIDEO_ID 0
#define SHAPE_ID 1
-#undef NDEBUG
-#include <assert.h>
-
typedef struct SWFContext {
int64_t duration_pos;
int64_t tag_pos;
@@ -77,6 +133,13 @@ typedef struct SWFContext {
AVFifoBuffer *audio_fifo;
AVCodecContext *audio_enc, *video_enc;
AVStream *video_st;
+#if CONFIG_ZLIB
+ AVIOContext *zpb;
+#define ZBUF_SIZE 4096
+ uint8_t *zbuf_in;
+ uint8_t *zbuf_out;
+ z_stream zstream;
+#endif
} SWFContext;
extern const AVCodecTag ff_swf_codec_tags[];
diff --git a/libavformat/swfdec.c b/libavformat/swfdec.c
index d7a53145c6..d34d3d90df 100644
--- a/libavformat/swfdec.c
+++ b/libavformat/swfdec.c
@@ -3,25 +3,28 @@
* Copyright (c) 2000 Fabrice Bellard
* Copyright (c) 2003 Tinic Uro
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
+#include "libavutil/imgutils.h"
#include "libavutil/intreadwrite.h"
+#include "libavcodec/get_bits.h"
#include "swf.h"
static const AVCodecTag swf_audio_codec_tags[] = {
@@ -37,8 +40,8 @@ static int get_swf_tag(AVIOContext *pb, int *len_ptr)
{
int tag, len;
- if (pb->eof_reached)
- return -1;
+ if (avio_feof(pb))
+ return AVERROR_EOF;
tag = avio_rl16(pb);
len = tag & 0x3f;
@@ -53,13 +56,68 @@ static int get_swf_tag(AVIOContext *pb, int *len_ptr)
static int swf_probe(AVProbeData *p)
{
+ GetBitContext gb;
+ int len, xmin, xmax, ymin, ymax;
+
+ if(p->buf_size < 15)
+ return 0;
+
/* check file header */
- if ((p->buf[0] == 'F' || p->buf[0] == 'C') && p->buf[1] == 'W' &&
- p->buf[2] == 'S')
- return AVPROBE_SCORE_MAX;
- else
+ if ( AV_RB24(p->buf) != AV_RB24("CWS")
+ && AV_RB24(p->buf) != AV_RB24("FWS"))
+ return 0;
+
+ init_get_bits8(&gb, p->buf + 3, p->buf_size - 3);
+
+ skip_bits(&gb, 40);
+ len = get_bits(&gb, 5);
+ if (!len)
return 0;
+ xmin = get_bits_long(&gb, len);
+ xmax = get_bits_long(&gb, len);
+ ymin = get_bits_long(&gb, len);
+ ymax = get_bits_long(&gb, len);
+ if (xmin || ymin || !xmax || !ymax)
+ return 0;
+
+ if (p->buf[3] >= 20 || xmax < 16 || ymax < 16)
+ return AVPROBE_SCORE_MAX / 4;
+
+ return AVPROBE_SCORE_MAX;
+}
+
+#if CONFIG_ZLIB
+static int zlib_refill(void *opaque, uint8_t *buf, int buf_size)
+{
+ AVFormatContext *s = opaque;
+ SWFContext *swf = s->priv_data;
+ z_stream *z = &swf->zstream;
+ int ret;
+
+retry:
+ if (!z->avail_in) {
+ int n = avio_read(s->pb, swf->zbuf_in, ZBUF_SIZE);
+ if (n < 0)
+ return n;
+ z->next_in = swf->zbuf_in;
+ z->avail_in = n;
+ }
+
+ z->next_out = buf;
+ z->avail_out = buf_size;
+
+ ret = inflate(z, Z_NO_FLUSH);
+ if (ret < 0)
+ return AVERROR(EINVAL);
+ if (ret == Z_STREAM_END)
+ return AVERROR_EOF;
+
+ if (buf_size - z->avail_out == 0)
+ goto retry;
+
+ return buf_size - z->avail_out;
}
+#endif
static int swf_read_header(AVFormatContext *s)
{
@@ -68,14 +126,29 @@ static int swf_read_header(AVFormatContext *s)
int nbits, len, tag;
tag = avio_rb32(pb) & 0xffffff00;
+ avio_rl32(pb);
if (tag == MKBETAG('C', 'W', 'S', 0)) {
- av_log(s, AV_LOG_ERROR, "Compressed SWF format not supported\n");
+ av_log(s, AV_LOG_INFO, "SWF compressed file detected\n");
+#if CONFIG_ZLIB
+ swf->zbuf_in = av_malloc(ZBUF_SIZE);
+ swf->zbuf_out = av_malloc(ZBUF_SIZE);
+ swf->zpb = avio_alloc_context(swf->zbuf_out, ZBUF_SIZE, 0, s,
+ zlib_refill, NULL, NULL);
+ if (!swf->zbuf_in || !swf->zbuf_out || !swf->zpb)
+ return AVERROR(ENOMEM);
+ swf->zpb->seekable = 0;
+ if (inflateInit(&swf->zstream) != Z_OK) {
+ av_log(s, AV_LOG_ERROR, "Unable to init zlib context\n");
+ return AVERROR(EINVAL);
+ }
+ pb = swf->zpb;
+#else
+ av_log(s, AV_LOG_ERROR, "zlib support is required to read SWF compressed files\n");
return AVERROR(EIO);
- }
- if (tag != MKBETAG('F', 'W', 'S', 0))
+#endif
+ } else if (tag != MKBETAG('F', 'W', 'S', 0))
return AVERROR(EIO);
- avio_rl32(pb);
/* skip rectangle size */
nbits = avio_r8(pb) >> 3;
len = (4 * nbits - 3 + 7) / 8;
@@ -88,6 +161,32 @@ static int swf_read_header(AVFormatContext *s)
return 0;
}
+static AVStream *create_new_audio_stream(AVFormatContext *s, int id, int info)
+{
+ int sample_rate_code, sample_size_code;
+ AVStream *ast = avformat_new_stream(s, NULL);
+ if (!ast)
+ return NULL;
+ ast->id = id;
+ if (info & 1) {
+ ast->codec->channels = 2;
+ ast->codec->channel_layout = AV_CH_LAYOUT_STEREO;
+ } else {
+ ast->codec->channels = 1;
+ ast->codec->channel_layout = AV_CH_LAYOUT_MONO;
+ }
+ ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ ast->codec->codec_id = ff_codec_get_id(swf_audio_codec_tags, info>>4 & 15);
+ ast->need_parsing = AVSTREAM_PARSE_FULL;
+ sample_rate_code = info>>2 & 3;
+ sample_size_code = info>>1 & 1;
+ if (!sample_size_code && ast->codec->codec_id == AV_CODEC_ID_PCM_S16LE)
+ ast->codec->codec_id = AV_CODEC_ID_PCM_U8;
+ ast->codec->sample_rate = 44100 >> (3 - sample_rate_code);
+ avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate);
+ return ast;
+}
+
static int swf_read_packet(AVFormatContext *s, AVPacket *pkt)
{
SWFContext *swf = s->priv_data;
@@ -95,11 +194,16 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt)
AVStream *vst = NULL, *ast = NULL, *st = 0;
int tag, len, i, frame, v, res;
+#if CONFIG_ZLIB
+ if (swf->zpb)
+ pb = swf->zpb;
+#endif
+
for(;;) {
uint64_t pos = avio_tell(pb);
tag = get_swf_tag(pb, &len);
if (tag < 0)
- return AVERROR(EIO);
+ return tag;
if (len < 0) {
av_log(s, AV_LOG_ERROR, "invalid tag length: %d\n", len);
return AVERROR_INVALIDDATA;
@@ -121,7 +225,7 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt)
/* Check for FLV1 */
vst = avformat_new_stream(s, NULL);
if (!vst)
- return -1;
+ return AVERROR(ENOMEM);
vst->id = ch_id;
vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
vst->codec->codec_id = ff_codec_get_id(ff_swf_codec_tags, avio_r8(pb));
@@ -129,7 +233,6 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt)
len -= 8;
} else if (tag == TAG_STREAMHEAD || tag == TAG_STREAMHEAD2) {
/* streaming found */
- int sample_rate_code;
for (i=0; i<s->nb_streams; i++) {
st = s->streams[i];
@@ -140,24 +243,38 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt)
avio_r8(pb);
v = avio_r8(pb);
swf->samples_per_frame = avio_rl16(pb);
- ast = avformat_new_stream(s, NULL);
+ ast = create_new_audio_stream(s, -1, v); /* -1 to avoid clash with video stream ch_id */
if (!ast)
- return -1;
- ast->id = -1; /* -1 to avoid clash with video stream ch_id */
- if (v & 1) {
- ast->codec->channels = 2;
- ast->codec->channel_layout = AV_CH_LAYOUT_STEREO;
- } else {
- ast->codec->channels = 1;
- ast->codec->channel_layout = AV_CH_LAYOUT_MONO;
- }
- ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
- ast->codec->codec_id = ff_codec_get_id(swf_audio_codec_tags, (v>>4) & 15);
- ast->need_parsing = AVSTREAM_PARSE_FULL;
- sample_rate_code= (v>>2) & 3;
- ast->codec->sample_rate = 44100 >> (3 - sample_rate_code);
- avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate);
+ return AVERROR(ENOMEM);
len -= 4;
+ } else if (tag == TAG_DEFINESOUND) {
+ /* audio stream */
+ int ch_id = avio_rl16(pb);
+
+ for (i=0; i<s->nb_streams; i++) {
+ st = s->streams[i];
+ if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && st->id == ch_id)
+ goto skip;
+ }
+
+ // FIXME: The entire audio stream is stored in a single chunk/tag. Normally,
+ // these are smaller audio streams in DEFINESOUND tags, but it's technically
+ // possible they could be huge. Break it up into multiple packets if it's big.
+ v = avio_r8(pb);
+ ast = create_new_audio_stream(s, ch_id, v);
+ if (!ast)
+ return AVERROR(ENOMEM);
+ ast->duration = avio_rl32(pb); // number of samples
+ if (((v>>4) & 15) == 2) { // MP3 sound data record
+ ast->skip_samples = avio_rl16(pb);
+ len -= 2;
+ }
+ len -= 7;
+ if ((res = av_get_packet(pb, pkt, len)) < 0)
+ return res;
+ pkt->pos = pos;
+ pkt->stream_index = ast->index;
+ return pkt->size;
} else if (tag == TAG_VIDEOFRAME) {
int ch_id = avio_rl16(pb);
len -= 2;
@@ -176,6 +293,143 @@ 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);
+ int pix_fmt;
+
+ 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;
+ }
+
+ if ((res = av_new_packet(pkt, out_len - colormapsize * colormapbpp)) < 0)
+ goto bitmap_end;
+ if (!st->codec->width && !st->codec->height) {
+ st->codec->width = width;
+ st->codec->height = height;
+ } else {
+ ff_add_param_change(pkt, 0, 0, 0, width, height);
+ }
+ pkt->pos = pos;
+ pkt->stream_index = st->index;
+
+ switch (bmp_fmt) {
+ case 3:
+ 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:
+ pix_fmt = AV_PIX_FMT_RGB555;
+ break;
+ case 5:
+ pix_fmt = alpha_bmp ? AV_PIX_FMT_ARGB : AV_PIX_FMT_0RGB;
+ break;
+ default:
+ av_assert0(0);
+ }
+ if (st->codec->pix_fmt != AV_PIX_FMT_NONE && st->codec->pix_fmt != pix_fmt) {
+ av_log(s, AV_LOG_ERROR, "pixel format change unsupported\n");
+ res = AVERROR_PATCHWELCOME;
+ goto bitmap_end;
+ }
+ st->codec->pix_fmt = pix_fmt;
+
+ if (linesize * height > pkt->size) {
+ res = AVERROR_INVALIDDATA;
+ goto bitmap_end;
+ }
+ memcpy(pkt->data, buf + colormapsize*colormapbpp, linesize * height);
+
+ res = pkt->size;
+
+bitmap_end:
+ av_freep(&zbuf);
+ av_freep(&buf);
+ return res;
+bitmap_end_skip:
+ av_freep(&zbuf);
+ av_freep(&buf);
+#else
+ av_log(s, AV_LOG_ERROR, "this file requires zlib support compiled in\n");
+#endif
} else if (tag == TAG_STREAMBLOCK) {
for (i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
@@ -207,7 +461,7 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt)
if (i == s->nb_streams) {
vst = avformat_new_stream(s, NULL);
if (!vst)
- return -1;
+ return AVERROR(ENOMEM);
vst->id = -2; /* -2 to avoid clash with video stream and audio stream */
vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
vst->codec->codec_id = AV_CODEC_ID_MJPEG;
@@ -220,26 +474,56 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt)
goto skip;
if ((res = av_new_packet(pkt, len)) < 0)
return res;
- avio_read(pb, pkt->data, 4);
+ if (avio_read(pb, pkt->data, 4) != 4) {
+ av_free_packet(pkt);
+ return AVERROR_INVALIDDATA;
+ }
if (AV_RB32(pkt->data) == 0xffd8ffd9 ||
AV_RB32(pkt->data) == 0xffd9ffd8) {
/* old SWF files containing SOI/EOI as data start */
/* files created by swink have reversed tag */
pkt->size -= 4;
- avio_read(pb, pkt->data, pkt->size);
+ memset(pkt->data+pkt->size, 0, 4);
+ res = avio_read(pb, pkt->data, pkt->size);
} else {
- avio_read(pb, pkt->data + 4, pkt->size - 4);
+ res = avio_read(pb, pkt->data + 4, pkt->size - 4);
+ if (res >= 0)
+ res += 4;
+ }
+ if (res != pkt->size) {
+ if (res < 0) {
+ av_free_packet(pkt);
+ return res;
+ }
+ av_shrink_packet(pkt, res);
}
+
pkt->pos = pos;
pkt->stream_index = st->index;
return pkt->size;
+ } else {
+ av_log(s, AV_LOG_DEBUG, "Unknown tag: %d\n", tag);
}
skip:
+ if(len<0)
+ av_log(s, AV_LOG_WARNING, "Cliping len %d\n", len);
len = FFMAX(0, len);
avio_skip(pb, len);
}
}
+#if CONFIG_ZLIB
+static av_cold int swf_read_close(AVFormatContext *avctx)
+{
+ SWFContext *s = avctx->priv_data;
+ inflateEnd(&s->zstream);
+ av_freep(&s->zbuf_in);
+ av_freep(&s->zbuf_out);
+ av_freep(&s->zpb);
+ return 0;
+}
+#endif
+
AVInputFormat ff_swf_demuxer = {
.name = "swf",
.long_name = NULL_IF_CONFIG_SMALL("SWF (ShockWave Flash)"),
@@ -247,4 +531,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 a1fc7b337c..3b6f9b3f83 100644
--- a/libavformat/swfenc.c
+++ b/libavformat/swfenc.c
@@ -3,24 +3,25 @@
* Copyright (c) 2000 Fabrice Bellard
* Copyright (c) 2003 Tinic Uro
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libavcodec/put_bits.h"
+#include "libavutil/avassert.h"
#include "avformat.h"
#include "swf.h"
@@ -56,7 +57,7 @@ static void put_swf_end_tag(AVFormatContext *s)
avio_wl16(pb, (tag << 6) | 0x3f);
avio_wl32(pb, tag_len - 4);
} else {
- assert(tag_len < 0x3f);
+ av_assert0(tag_len < 0x3f);
avio_wl16(pb, (tag << 6) | tag_len);
}
avio_seek(pb, pos, SEEK_SET);
@@ -191,6 +192,10 @@ static int swf_write_header(AVFormatContext *s)
return AVERROR_INVALIDDATA;
}
if (enc->codec_id == AV_CODEC_ID_MP3) {
+ if (!enc->frame_size) {
+ av_log(s, AV_LOG_ERROR, "audio frame size not set\n");
+ return -1;
+ }
swf->audio_enc = enc;
swf->audio_fifo= av_fifo_alloc(AUDIO_FIFO_SIZE);
if (!swf->audio_fifo)
@@ -427,7 +432,7 @@ static int swf_write_video(AVFormatContext *s,
put_swf_tag(s, TAG_STREAMBLOCK | TAG_LONG);
avio_wl16(pb, swf->sound_samples);
avio_wl16(pb, 0); // seek samples
- av_fifo_generic_read(swf->audio_fifo, pb, frame_size, &avio_write);
+ av_fifo_generic_read(swf->audio_fifo, pb, frame_size, (void*)avio_write);
put_swf_end_tag(s);
/* update FIFO */
@@ -456,7 +461,7 @@ static int swf_write_audio(AVFormatContext *s,
}
av_fifo_generic_write(swf->audio_fifo, buf, size, NULL);
- swf->sound_samples += av_get_audio_frame_duration(enc, size);
+ swf->sound_samples += enc->frame_size;
/* if audio only stream make sure we add swf frames */
if (!swf->video_enc)
@@ -486,8 +491,9 @@ static int swf_write_trailer(AVFormatContext *s)
enc = s->streams[i]->codec;
if (enc->codec_type == AVMEDIA_TYPE_VIDEO)
video_enc = enc;
- else
- av_fifo_free(swf->audio_fifo);
+ else {
+ av_fifo_freep(&swf->audio_fifo);
+ }
}
put_swf_tag(s, TAG_END);
@@ -500,8 +506,10 @@ static int swf_write_trailer(AVFormatContext *s)
avio_wl32(pb, file_size);
avio_seek(pb, swf->duration_pos, SEEK_SET);
avio_wl16(pb, swf->video_frame_number);
+ if (swf->vframes_pos) {
avio_seek(pb, swf->vframes_pos, SEEK_SET);
avio_wl16(pb, swf->video_frame_number);
+ }
avio_seek(pb, file_size, SEEK_SET);
}
return 0;
diff --git a/libavformat/takdec.c b/libavformat/takdec.c
index 584cbccb50..3eb1a8ec2d 100644
--- a/libavformat/takdec.c
+++ b/libavformat/takdec.c
@@ -2,25 +2,27 @@
* Raw TAK demuxer
* Copyright (c) 2012 Paul B Mahol
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/crc.h"
#include "libavcodec/tak.h"
#include "avformat.h"
+#include "avio_internal.h"
#include "internal.h"
#include "rawdec.h"
#include "apetag.h"
@@ -37,6 +39,12 @@ static int tak_probe(AVProbeData *p)
return 0;
}
+static unsigned long tak_check_crc(unsigned long checksum, const uint8_t *buf,
+ unsigned int len)
+{
+ return av_crc(av_crc_get_table(AV_CRC_24_IEEE), checksum, buf, len);
+}
+
static int tak_read_header(AVFormatContext *s)
{
TAKDemuxContext *tc = s->priv_data;
@@ -52,7 +60,7 @@ static int tak_read_header(AVFormatContext *s)
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = AV_CODEC_ID_TAK;
- st->need_parsing = AVSTREAM_PARSE_FULL;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
tc->mlast_frame = 0;
if (avio_rl32(pb) != MKTAG('t', 'B', 'a', 'K')) {
@@ -60,7 +68,7 @@ static int tak_read_header(AVFormatContext *s)
return 0;
}
- while (!pb->eof_reached) {
+ while (!avio_feof(pb)) {
enum TAKMetaDataType type;
int size;
@@ -71,16 +79,28 @@ static int tak_read_header(AVFormatContext *s)
case TAK_METADATA_STREAMINFO:
case TAK_METADATA_LAST_FRAME:
case TAK_METADATA_ENCODER:
- buffer = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
+ if (size <= 3)
+ return AVERROR_INVALIDDATA;
+
+ buffer = av_malloc(size - 3 + FF_INPUT_BUFFER_PADDING_SIZE);
if (!buffer)
return AVERROR(ENOMEM);
+ memset(buffer + size - 3, 0, FF_INPUT_BUFFER_PADDING_SIZE);
- if (avio_read(pb, buffer, size) != size) {
+ ffio_init_checksum(pb, tak_check_crc, 0xCE04B7U);
+ if (avio_read(pb, buffer, size - 3) != size - 3) {
av_freep(&buffer);
return AVERROR(EIO);
}
+ if (ffio_get_checksum(s->pb) != avio_rb24(pb)) {
+ av_log(s, AV_LOG_ERROR, "%d metadata block CRC error.\n", type);
+ if (s->error_recognition & AV_EF_EXPLODE) {
+ av_freep(&buffer);
+ return AVERROR_INVALIDDATA;
+ }
+ }
- init_get_bits(&gb, buffer, size * 8);
+ init_get_bits8(&gb, buffer, size - 3);
break;
case TAK_METADATA_MD5: {
uint8_t md5[16];
@@ -88,8 +108,14 @@ static int tak_read_header(AVFormatContext *s)
if (size != 19)
return AVERROR_INVALIDDATA;
+ ffio_init_checksum(pb, tak_check_crc, 0xCE04B7U);
avio_read(pb, md5, 16);
- avio_skip(pb, 3);
+ if (ffio_get_checksum(s->pb) != avio_rb24(pb)) {
+ av_log(s, AV_LOG_ERROR, "MD5 metadata block CRC error.\n");
+ if (s->error_recognition & AV_EF_EXPLODE)
+ return AVERROR_INVALIDDATA;
+ }
+
av_log(s, AV_LOG_VERBOSE, "MD5=");
for (i = 0; i < 16; i++)
av_log(s, AV_LOG_VERBOSE, "%02x", md5[i]);
@@ -127,7 +153,7 @@ static int tak_read_header(AVFormatContext *s)
st->start_time = 0;
avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
st->codec->extradata = buffer;
- st->codec->extradata_size = size;
+ st->codec->extradata_size = size - 3;
buffer = NULL;
} else if (type == TAK_METADATA_LAST_FRAME) {
if (size != 11)
@@ -155,7 +181,7 @@ static int raw_read_packet(AVFormatContext *s, AVPacket *pkt)
AVIOContext *pb = s->pb;
int64_t size, left;
- left = tc->data_end - avio_tell(s->pb);
+ left = tc->data_end - avio_tell(pb);
size = FFMIN(left, 1024);
if (size <= 0)
return AVERROR_EOF;
diff --git a/libavformat/tcp.c b/libavformat/tcp.c
index a2426117c8..0aabc9db07 100644
--- a/libavformat/tcp.c
+++ b/libavformat/tcp.c
@@ -2,25 +2,26 @@
* TCP protocol
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "avformat.h"
#include "libavutil/parseutils.h"
#include "libavutil/opt.h"
+#include "libavutil/time.h"
#include "internal.h"
#include "network.h"
@@ -34,7 +35,8 @@ typedef struct TCPContext {
const AVClass *class;
int fd;
int listen;
- int timeout;
+ int open_timeout;
+ int rw_timeout;
int listen_timeout;
} TCPContext;
@@ -43,8 +45,8 @@ typedef struct TCPContext {
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
{ "listen", "Listen for incoming connections", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E },
- { "timeout", "Connection timeout", OFFSET(timeout), AV_OPT_TYPE_INT, { .i64 = 10000 }, INT_MIN, INT_MAX, .flags = D|E },
- { "listen_timeout", "Bind timeout", OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, INT_MIN, INT_MAX, .flags = D|E },
+ { "timeout", "set timeout (in microseconds) of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
+ { "listen_timeout", "Connection awaiting timeout", OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
{ NULL }
};
@@ -66,6 +68,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
int ret;
char hostname[1024],proto[1024],path[1024];
char portstr[10];
+ s->open_timeout = 5000000;
av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
&port, path, sizeof(path), uri);
@@ -77,15 +80,24 @@ 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))
- s->listen = 1;
+ if (av_find_info_tag(buf, sizeof(buf), "listen", p)) {
+ char *endptr = NULL;
+ s->listen = strtol(buf, &endptr, 10);
+ /* assume if no digits were found it is a request to enable it */
+ if (buf == endptr)
+ s->listen = 1;
+ }
if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
- s->timeout = strtol(buf, NULL, 10) * 100;
+ s->rw_timeout = strtol(buf, NULL, 10);
}
if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
s->listen_timeout = strtol(buf, NULL, 10);
}
}
+ if (s->rw_timeout >= 0) {
+ s->open_timeout =
+ h->rw_timeout = s->rw_timeout;
+ }
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
snprintf(portstr, sizeof(portstr), "%d", port);
@@ -121,7 +133,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
}
} else {
if ((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
- s->timeout, h, !!cur_ai->ai_next)) < 0) {
+ s->open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) {
if (ret == AVERROR_EXIT)
goto fail1;
@@ -157,8 +169,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);
@@ -171,8 +183,8 @@ static int tcp_write(URLContext *h, const uint8_t *buf, int size)
int ret;
if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
- ret = ff_network_wait_fd(s->fd, 1);
- if (ret < 0)
+ ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback);
+ if (ret)
return ret;
}
ret = send(s->fd, buf, size, MSG_NOSIGNAL);
diff --git a/libavformat/tedcaptionsdec.c b/libavformat/tedcaptionsdec.c
new file mode 100644
index 0000000000..68063fafbe
--- /dev/null
+++ b/libavformat/tedcaptionsdec.c
@@ -0,0 +1,366 @@
+/*
+ * TED Talks captions format decoder
+ * Copyright (c) 2012 Nicolas George
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/bprint.h"
+#include "libavutil/log.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "internal.h"
+#include "subtitles.h"
+
+typedef struct {
+ AVClass *class;
+ int64_t start_time;
+ FFDemuxSubtitlesQueue subs;
+} TEDCaptionsDemuxer;
+
+static const AVOption tedcaptions_options[] = {
+ { "start_time", "set the start time (offset) of the subtitles, in ms",
+ offsetof(TEDCaptionsDemuxer, start_time), FF_OPT_TYPE_INT64,
+ { .i64 = 15000 }, INT64_MIN, INT64_MAX,
+ AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+static const AVClass tedcaptions_demuxer_class = {
+ .class_name = "tedcaptions_demuxer",
+ .item_name = av_default_item_name,
+ .option = tedcaptions_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+#define BETWEEN(a, amin, amax) ((unsigned)((a) - (amin)) <= (amax) - (amin))
+
+#define HEX_DIGIT_TEST(c) (BETWEEN(c, '0', '9') || BETWEEN((c) | 32, 'a', 'z'))
+#define HEX_DIGIT_VAL(c) ((c) <= '9' ? (c) - '0' : ((c) | 32) - 'a' + 10)
+#define ERR_CODE(c) (c < 0 ? c : AVERROR_INVALIDDATA)
+
+static void av_bprint_utf8(AVBPrint *bp, unsigned c)
+{
+ int bytes, i;
+
+ if (c <= 0x7F) {
+ av_bprint_chars(bp, c, 1);
+ return;
+ }
+ bytes = (av_log2(c) - 2) / 5;
+ av_bprint_chars(bp, (c >> (bytes * 6)) | ((0xFF80 >> bytes) & 0xFF), 1);
+ for (i = bytes - 1; i >= 0; i--)
+ av_bprint_chars(bp, ((c >> (i * 6)) & 0x3F) | 0x80, 1);
+}
+
+static void next_byte(AVIOContext *pb, int *cur_byte)
+{
+ uint8_t b;
+ int ret = avio_read(pb, &b, 1);
+ *cur_byte = ret > 0 ? b : ret == 0 ? AVERROR_EOF : ret;
+}
+
+static void skip_spaces(AVIOContext *pb, int *cur_byte)
+{
+ while (*cur_byte == ' ' || *cur_byte == '\t' ||
+ *cur_byte == '\n' || *cur_byte == '\r')
+ next_byte(pb, cur_byte);
+}
+
+static int expect_byte(AVIOContext *pb, int *cur_byte, uint8_t c)
+{
+ skip_spaces(pb, cur_byte);
+ if (*cur_byte != c)
+ return ERR_CODE(*cur_byte);
+ next_byte(pb, cur_byte);
+ return 0;
+}
+
+static int parse_string(AVIOContext *pb, int *cur_byte, AVBPrint *bp, int full)
+{
+ int ret;
+
+ av_bprint_init(bp, 0, full ? -1 : 1);
+ ret = expect_byte(pb, cur_byte, '"');
+ if (ret < 0)
+ goto fail;
+ while (*cur_byte > 0 && *cur_byte != '"') {
+ if (*cur_byte == '\\') {
+ next_byte(pb, cur_byte);
+ if (*cur_byte < 0) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ if ((*cur_byte | 32) == 'u') {
+ unsigned chr = 0, i;
+ for (i = 0; i < 4; i++) {
+ next_byte(pb, cur_byte);
+ if (!HEX_DIGIT_TEST(*cur_byte)) {
+ ret = ERR_CODE(*cur_byte);
+ goto fail;
+ }
+ chr = chr * 16 + HEX_DIGIT_VAL(*cur_byte);
+ }
+ av_bprint_utf8(bp, chr);
+ } else {
+ av_bprint_chars(bp, *cur_byte, 1);
+ }
+ } else {
+ av_bprint_chars(bp, *cur_byte, 1);
+ }
+ next_byte(pb, cur_byte);
+ }
+ ret = expect_byte(pb, cur_byte, '"');
+ if (ret < 0)
+ goto fail;
+ if (full && !av_bprint_is_complete(bp)) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ return 0;
+
+fail:
+ av_bprint_finalize(bp, NULL);
+ return ret;
+}
+
+static int parse_label(AVIOContext *pb, int *cur_byte, AVBPrint *bp)
+{
+ int ret;
+
+ ret = parse_string(pb, cur_byte, bp, 0);
+ if (ret < 0)
+ return ret;
+ ret = expect_byte(pb, cur_byte, ':');
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int parse_boolean(AVIOContext *pb, int *cur_byte, int *result)
+{
+ static const char * const text[] = { "false", "true" };
+ const char *p;
+ int i;
+
+ skip_spaces(pb, cur_byte);
+ for (i = 0; i < 2; i++) {
+ p = text[i];
+ if (*cur_byte != *p)
+ continue;
+ for (; *p; p++, next_byte(pb, cur_byte))
+ if (*cur_byte != *p)
+ return AVERROR_INVALIDDATA;
+ if (BETWEEN(*cur_byte | 32, 'a', 'z'))
+ return AVERROR_INVALIDDATA;
+ *result = i;
+ return 0;
+ }
+ return AVERROR_INVALIDDATA;
+}
+
+static int parse_int(AVIOContext *pb, int *cur_byte, int64_t *result)
+{
+ int64_t val = 0;
+
+ skip_spaces(pb, cur_byte);
+ if ((unsigned)*cur_byte - '0' > 9)
+ return AVERROR_INVALIDDATA;
+ while (BETWEEN(*cur_byte, '0', '9')) {
+ val = val * 10 + (*cur_byte - '0');
+ next_byte(pb, cur_byte);
+ }
+ *result = val;
+ return 0;
+}
+
+static int parse_file(AVIOContext *pb, FFDemuxSubtitlesQueue *subs)
+{
+ int ret, cur_byte, start_of_par;
+ AVBPrint label, content;
+ int64_t pos, start, duration;
+ AVPacket *pkt;
+
+ next_byte(pb, &cur_byte);
+ ret = expect_byte(pb, &cur_byte, '{');
+ if (ret < 0)
+ return AVERROR_INVALIDDATA;
+ ret = parse_label(pb, &cur_byte, &label);
+ if (ret < 0 || strcmp(label.str, "captions"))
+ return AVERROR_INVALIDDATA;
+ ret = expect_byte(pb, &cur_byte, '[');
+ if (ret < 0)
+ return AVERROR_INVALIDDATA;
+ while (1) {
+ content.size = 0;
+ start = duration = AV_NOPTS_VALUE;
+ ret = expect_byte(pb, &cur_byte, '{');
+ if (ret < 0)
+ return ret;
+ pos = avio_tell(pb) - 1;
+ while (1) {
+ ret = parse_label(pb, &cur_byte, &label);
+ if (ret < 0)
+ return ret;
+ if (!strcmp(label.str, "startOfParagraph")) {
+ ret = parse_boolean(pb, &cur_byte, &start_of_par);
+ if (ret < 0)
+ return ret;
+ } else if (!strcmp(label.str, "content")) {
+ ret = parse_string(pb, &cur_byte, &content, 1);
+ if (ret < 0)
+ return ret;
+ } else if (!strcmp(label.str, "startTime")) {
+ ret = parse_int(pb, &cur_byte, &start);
+ if (ret < 0)
+ return ret;
+ } else if (!strcmp(label.str, "duration")) {
+ ret = parse_int(pb, &cur_byte, &duration);
+ if (ret < 0)
+ return ret;
+ } else {
+ return AVERROR_INVALIDDATA;
+ }
+ skip_spaces(pb, &cur_byte);
+ if (cur_byte != ',')
+ break;
+ next_byte(pb, &cur_byte);
+ }
+ ret = expect_byte(pb, &cur_byte, '}');
+ if (ret < 0)
+ return ret;
+
+ if (!content.size || start == AV_NOPTS_VALUE ||
+ duration == AV_NOPTS_VALUE)
+ return AVERROR_INVALIDDATA;
+ pkt = ff_subtitles_queue_insert(subs, content.str, content.len, 0);
+ if (!pkt)
+ return AVERROR(ENOMEM);
+ pkt->pos = pos;
+ pkt->pts = start;
+ pkt->duration = duration;
+ av_bprint_finalize(&content, NULL);
+
+ skip_spaces(pb, &cur_byte);
+ if (cur_byte != ',')
+ break;
+ next_byte(pb, &cur_byte);
+ }
+ ret = expect_byte(pb, &cur_byte, ']');
+ if (ret < 0)
+ return ret;
+ ret = expect_byte(pb, &cur_byte, '}');
+ if (ret < 0)
+ return ret;
+ skip_spaces(pb, &cur_byte);
+ if (cur_byte != AVERROR_EOF)
+ return ERR_CODE(cur_byte);
+ return 0;
+}
+
+static av_cold int tedcaptions_read_header(AVFormatContext *avf)
+{
+ TEDCaptionsDemuxer *tc = avf->priv_data;
+ AVStream *st;
+ int ret, i;
+ AVPacket *last;
+
+ ret = parse_file(avf->pb, &tc->subs);
+ if (ret < 0) {
+ if (ret == AVERROR_INVALIDDATA)
+ av_log(avf, AV_LOG_ERROR, "Syntax error near offset %"PRId64".\n",
+ avio_tell(avf->pb));
+ ff_subtitles_queue_clean(&tc->subs);
+ return ret;
+ }
+ ff_subtitles_queue_finalize(&tc->subs);
+ for (i = 0; i < tc->subs.nb_subs; i++)
+ tc->subs.subs[i].pts += tc->start_time;
+
+ last = &tc->subs.subs[tc->subs.nb_subs - 1];
+ st = avformat_new_stream(avf, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_TEXT;
+ avpriv_set_pts_info(st, 64, 1, 1000);
+ st->probe_packets = 0;
+ st->start_time = 0;
+ st->duration = last->pts + last->duration;
+ st->cur_dts = 0;
+
+ return 0;
+}
+
+static int tedcaptions_read_packet(AVFormatContext *avf, AVPacket *packet)
+{
+ TEDCaptionsDemuxer *tc = avf->priv_data;
+
+ return ff_subtitles_queue_read_packet(&tc->subs, packet);
+}
+
+static int tedcaptions_read_close(AVFormatContext *avf)
+{
+ TEDCaptionsDemuxer *tc = avf->priv_data;
+
+ ff_subtitles_queue_clean(&tc->subs);
+ return 0;
+}
+
+static av_cold int tedcaptions_read_probe(AVProbeData *p)
+{
+ static const char *const tags[] = {
+ "\"captions\"", "\"duration\"", "\"content\"",
+ "\"startOfParagraph\"", "\"startTime\"",
+ };
+ unsigned i, count = 0;
+ const char *t;
+
+ if (p->buf[strspn(p->buf, " \t\r\n")] != '{')
+ return 0;
+ for (i = 0; i < FF_ARRAY_ELEMS(tags); i++) {
+ if (!(t = strstr(p->buf, tags[i])))
+ continue;
+ t += strlen(tags[i]);
+ t += strspn(t, " \t\r\n");
+ if (*t == ':')
+ count++;
+ }
+ return count == FF_ARRAY_ELEMS(tags) ? AVPROBE_SCORE_MAX :
+ count ? AVPROBE_SCORE_EXTENSION : 0;
+}
+
+static int tedcaptions_read_seek(AVFormatContext *avf, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts,
+ int flags)
+{
+ TEDCaptionsDemuxer *tc = avf->priv_data;
+ return ff_subtitles_queue_seek(&tc->subs, avf, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+AVInputFormat ff_tedcaptions_demuxer = {
+ .name = "tedcaptions",
+ .long_name = NULL_IF_CONFIG_SMALL("TED Talks captions"),
+ .priv_data_size = sizeof(TEDCaptionsDemuxer),
+ .priv_class = &tedcaptions_demuxer_class,
+ .read_header = tedcaptions_read_header,
+ .read_packet = tedcaptions_read_packet,
+ .read_close = tedcaptions_read_close,
+ .read_probe = tedcaptions_read_probe,
+ .read_seek2 = tedcaptions_read_seek,
+};
diff --git a/libavformat/tee.c b/libavformat/tee.c
new file mode 100644
index 0000000000..e3d466a3eb
--- /dev/null
+++ b/libavformat/tee.c
@@ -0,0 +1,495 @@
+/*
+ * Tee pseudo-muxer
+ * Copyright (c) 2012 Nicolas George
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FFmpeg; if not, write to the Free Software * Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#include "libavutil/avutil.h"
+#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+
+#define MAX_SLAVES 16
+
+typedef struct {
+ AVFormatContext *avf;
+ AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream
+
+ /** map from input to output streams indexes,
+ * disabled output streams are set to -1 */
+ int *stream_map;
+} TeeSlave;
+
+typedef struct TeeContext {
+ const AVClass *class;
+ unsigned nb_slaves;
+ TeeSlave slaves[MAX_SLAVES];
+} TeeContext;
+
+static const char *const slave_delim = "|";
+static const char *const slave_opt_open = "[";
+static const char *const slave_opt_close = "]";
+static const char *const slave_opt_delim = ":]"; /* must have the close too */
+static const char *const slave_bsfs_spec_sep = "/";
+
+static const AVClass tee_muxer_class = {
+ .class_name = "Tee muxer",
+ .item_name = av_default_item_name,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+static int parse_slave_options(void *log, char *slave,
+ AVDictionary **options, char **filename)
+{
+ const char *p;
+ char *key, *val;
+ int ret;
+
+ if (!strspn(slave, slave_opt_open)) {
+ *filename = slave;
+ return 0;
+ }
+ p = slave + 1;
+ if (strspn(p, slave_opt_close)) {
+ *filename = (char *)p + 1;
+ return 0;
+ }
+ while (1) {
+ ret = av_opt_get_key_value(&p, "=", slave_opt_delim, 0, &key, &val);
+ if (ret < 0) {
+ av_log(log, AV_LOG_ERROR, "No option found near \"%s\"\n", p);
+ goto fail;
+ }
+ ret = av_dict_set(options, key, val,
+ AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
+ if (ret < 0)
+ goto fail;
+ if (strspn(p, slave_opt_close))
+ break;
+ p++;
+ }
+ *filename = (char *)p + 1;
+ return 0;
+
+fail:
+ av_dict_free(options);
+ return ret;
+}
+
+/**
+ * Parse list of bitstream filters and add them to the list of filters
+ * pointed to by bsfs.
+ *
+ * The list must be specified in the form:
+ * BSFS ::= BSF[,BSFS]
+ */
+static int parse_bsfs(void *log_ctx, const char *bsfs_spec,
+ AVBitStreamFilterContext **bsfs)
+{
+ char *bsf_name, *buf, *dup, *saveptr;
+ int ret = 0;
+
+ if (!(dup = buf = av_strdup(bsfs_spec)))
+ return AVERROR(ENOMEM);
+
+ while (bsf_name = av_strtok(buf, ",", &saveptr)) {
+ AVBitStreamFilterContext *bsf = av_bitstream_filter_init(bsf_name);
+
+ if (!bsf) {
+ av_log(log_ctx, AV_LOG_ERROR,
+ "Cannot initialize bitstream filter with name '%s', "
+ "unknown filter or internal error happened\n",
+ bsf_name);
+ ret = AVERROR_UNKNOWN;
+ goto end;
+ }
+
+ /* append bsf context to the list of bsf contexts */
+ *bsfs = bsf;
+ bsfs = &bsf->next;
+
+ buf = NULL;
+ }
+
+end:
+ av_free(dup);
+ return ret;
+}
+
+static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
+{
+ int i, ret;
+ AVDictionary *options = NULL;
+ AVDictionaryEntry *entry;
+ char *filename;
+ char *format = NULL, *select = NULL;
+ AVFormatContext *avf2 = NULL;
+ AVStream *st, *st2;
+ int stream_count;
+
+ if ((ret = parse_slave_options(avf, slave, &options, &filename)) < 0)
+ return ret;
+
+#define STEAL_OPTION(option, field) do { \
+ if ((entry = av_dict_get(options, option, NULL, 0))) { \
+ field = entry->value; \
+ entry->value = NULL; /* prevent it from being freed */ \
+ av_dict_set(&options, option, NULL, 0); \
+ } \
+ } while (0)
+
+ STEAL_OPTION("f", format);
+ STEAL_OPTION("select", select);
+
+ ret = avformat_alloc_output_context2(&avf2, NULL, format, filename);
+ if (ret < 0)
+ goto end;
+ av_dict_copy(&avf2->metadata, avf->metadata, 0);
+
+ tee_slave->stream_map = av_calloc(avf->nb_streams, sizeof(*tee_slave->stream_map));
+ if (!tee_slave->stream_map) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+
+ stream_count = 0;
+ for (i = 0; i < avf->nb_streams; i++) {
+ st = avf->streams[i];
+ if (select) {
+ ret = avformat_match_stream_specifier(avf, avf->streams[i], select);
+ if (ret < 0) {
+ av_log(avf, AV_LOG_ERROR,
+ "Invalid stream specifier '%s' for output '%s'\n",
+ select, slave);
+ goto end;
+ }
+
+ if (ret == 0) { /* no match */
+ tee_slave->stream_map[i] = -1;
+ continue;
+ }
+ }
+ tee_slave->stream_map[i] = stream_count++;
+
+ if (!(st2 = avformat_new_stream(avf2, NULL))) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ st2->id = st->id;
+ st2->r_frame_rate = st->r_frame_rate;
+ st2->time_base = st->time_base;
+ st2->start_time = st->start_time;
+ st2->duration = st->duration;
+ st2->nb_frames = st->nb_frames;
+ st2->disposition = st->disposition;
+ st2->sample_aspect_ratio = st->sample_aspect_ratio;
+ st2->avg_frame_rate = st->avg_frame_rate;
+ av_dict_copy(&st2->metadata, st->metadata, 0);
+ if ((ret = avcodec_copy_context(st2->codec, st->codec)) < 0)
+ goto end;
+ }
+
+ if (!(avf2->oformat->flags & AVFMT_NOFILE)) {
+ if ((ret = avio_open(&avf2->pb, filename, AVIO_FLAG_WRITE)) < 0) {
+ av_log(avf, AV_LOG_ERROR, "Slave '%s': error opening: %s\n",
+ slave, av_err2str(ret));
+ goto end;
+ }
+ }
+
+ if ((ret = avformat_write_header(avf2, &options)) < 0) {
+ av_log(avf, AV_LOG_ERROR, "Slave '%s': error writing header: %s\n",
+ slave, av_err2str(ret));
+ goto end;
+ }
+
+ tee_slave->avf = avf2;
+ tee_slave->bsfs = av_calloc(avf2->nb_streams, sizeof(TeeSlave));
+ if (!tee_slave->bsfs) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+
+ entry = NULL;
+ while (entry = av_dict_get(options, "bsfs", NULL, AV_DICT_IGNORE_SUFFIX)) {
+ const char *spec = entry->key + strlen("bsfs");
+ if (*spec) {
+ if (strspn(spec, slave_bsfs_spec_sep) != 1) {
+ av_log(avf, AV_LOG_ERROR,
+ "Specifier separator in '%s' is '%c', but only characters '%s' "
+ "are allowed\n", entry->key, *spec, slave_bsfs_spec_sep);
+ return AVERROR(EINVAL);
+ }
+ spec++; /* consume separator */
+ }
+
+ for (i = 0; i < avf2->nb_streams; i++) {
+ ret = avformat_match_stream_specifier(avf2, avf2->streams[i], spec);
+ if (ret < 0) {
+ av_log(avf, AV_LOG_ERROR,
+ "Invalid stream specifier '%s' in bsfs option '%s' for slave "
+ "output '%s'\n", spec, entry->key, filename);
+ goto end;
+ }
+
+ if (ret > 0) {
+ av_log(avf, AV_LOG_DEBUG, "spec:%s bsfs:%s matches stream %d of slave "
+ "output '%s'\n", spec, entry->value, i, filename);
+ if (tee_slave->bsfs[i]) {
+ av_log(avf, AV_LOG_WARNING,
+ "Duplicate bsfs specification associated to stream %d of slave "
+ "output '%s', filters will be ignored\n", i, filename);
+ continue;
+ }
+ ret = parse_bsfs(avf, entry->value, &tee_slave->bsfs[i]);
+ if (ret < 0) {
+ av_log(avf, AV_LOG_ERROR,
+ "Error parsing bitstream filter sequence '%s' associated to "
+ "stream %d of slave output '%s'\n", entry->value, i, filename);
+ goto end;
+ }
+ }
+ }
+
+ av_dict_set(&options, entry->key, NULL, 0);
+ }
+
+ if (options) {
+ entry = NULL;
+ while ((entry = av_dict_get(options, "", entry, AV_DICT_IGNORE_SUFFIX)))
+ av_log(avf2, AV_LOG_ERROR, "Unknown option '%s'\n", entry->key);
+ ret = AVERROR_OPTION_NOT_FOUND;
+ goto end;
+ }
+
+end:
+ av_free(format);
+ av_free(select);
+ av_dict_free(&options);
+ return ret;
+}
+
+static void close_slaves(AVFormatContext *avf)
+{
+ TeeContext *tee = avf->priv_data;
+ AVFormatContext *avf2;
+ unsigned i, j;
+
+ for (i = 0; i < tee->nb_slaves; i++) {
+ avf2 = tee->slaves[i].avf;
+
+ for (j = 0; j < avf2->nb_streams; j++) {
+ AVBitStreamFilterContext *bsf_next, *bsf = tee->slaves[i].bsfs[j];
+ while (bsf) {
+ bsf_next = bsf->next;
+ av_bitstream_filter_close(bsf);
+ bsf = bsf_next;
+ }
+ }
+ av_freep(&tee->slaves[i].stream_map);
+ av_freep(&tee->slaves[i].bsfs);
+
+ avio_closep(&avf2->pb);
+ avformat_free_context(avf2);
+ tee->slaves[i].avf = NULL;
+ }
+}
+
+static void log_slave(TeeSlave *slave, void *log_ctx, int log_level)
+{
+ int i;
+ av_log(log_ctx, log_level, "filename:'%s' format:%s\n",
+ slave->avf->filename, slave->avf->oformat->name);
+ for (i = 0; i < slave->avf->nb_streams; i++) {
+ AVStream *st = slave->avf->streams[i];
+ AVBitStreamFilterContext *bsf = slave->bsfs[i];
+
+ av_log(log_ctx, log_level, " stream:%d codec:%s type:%s",
+ i, avcodec_get_name(st->codec->codec_id),
+ av_get_media_type_string(st->codec->codec_type));
+ if (bsf) {
+ av_log(log_ctx, log_level, " bsfs:");
+ while (bsf) {
+ av_log(log_ctx, log_level, "%s%s",
+ bsf->filter->name, bsf->next ? "," : "");
+ bsf = bsf->next;
+ }
+ }
+ av_log(log_ctx, log_level, "\n");
+ }
+}
+
+static int tee_write_header(AVFormatContext *avf)
+{
+ TeeContext *tee = avf->priv_data;
+ unsigned nb_slaves = 0, i;
+ const char *filename = avf->filename;
+ char *slaves[MAX_SLAVES];
+ int ret;
+
+ while (*filename) {
+ if (nb_slaves == MAX_SLAVES) {
+ av_log(avf, AV_LOG_ERROR, "Maximum %d slave muxers reached.\n",
+ MAX_SLAVES);
+ ret = AVERROR_PATCHWELCOME;
+ goto fail;
+ }
+ if (!(slaves[nb_slaves++] = av_get_token(&filename, slave_delim))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ if (strspn(filename, slave_delim))
+ filename++;
+ }
+
+ for (i = 0; i < nb_slaves; i++) {
+ if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0)
+ goto fail;
+ log_slave(&tee->slaves[i], avf, AV_LOG_VERBOSE);
+ av_freep(&slaves[i]);
+ }
+
+ tee->nb_slaves = nb_slaves;
+
+ for (i = 0; i < avf->nb_streams; i++) {
+ int j, mapped = 0;
+ for (j = 0; j < tee->nb_slaves; j++)
+ mapped += tee->slaves[j].stream_map[i] >= 0;
+ if (!mapped)
+ av_log(avf, AV_LOG_WARNING, "Input stream #%d is not mapped "
+ "to any slave.\n", i);
+ }
+ return 0;
+
+fail:
+ for (i = 0; i < nb_slaves; i++)
+ av_freep(&slaves[i]);
+ close_slaves(avf);
+ return ret;
+}
+
+static int filter_packet(void *log_ctx, AVPacket *pkt,
+ AVFormatContext *fmt_ctx, AVBitStreamFilterContext *bsf_ctx)
+{
+ AVCodecContext *enc_ctx = fmt_ctx->streams[pkt->stream_index]->codec;
+ int ret = 0;
+
+ while (bsf_ctx) {
+ AVPacket new_pkt = *pkt;
+ ret = av_bitstream_filter_filter(bsf_ctx, enc_ctx, NULL,
+ &new_pkt.data, &new_pkt.size,
+ pkt->data, pkt->size,
+ pkt->flags & AV_PKT_FLAG_KEY);
+ if (ret == 0 && new_pkt.data != pkt->data && new_pkt.destruct) {
+ if ((ret = av_copy_packet(&new_pkt, pkt)) < 0)
+ break;
+ ret = 1;
+ }
+
+ if (ret > 0) {
+ av_free_packet(pkt);
+ new_pkt.buf = av_buffer_create(new_pkt.data, new_pkt.size,
+ av_buffer_default_free, NULL, 0);
+ if (!new_pkt.buf)
+ break;
+ }
+ if (ret < 0) {
+ av_log(log_ctx, AV_LOG_ERROR,
+ "Failed to filter bitstream with filter %s for stream %d in file '%s' with codec %s\n",
+ bsf_ctx->filter->name, pkt->stream_index, fmt_ctx->filename,
+ avcodec_get_name(enc_ctx->codec_id));
+ }
+ *pkt = new_pkt;
+
+ bsf_ctx = bsf_ctx->next;
+ }
+
+ return ret;
+}
+
+static int tee_write_trailer(AVFormatContext *avf)
+{
+ TeeContext *tee = avf->priv_data;
+ AVFormatContext *avf2;
+ int ret_all = 0, ret;
+ unsigned i;
+
+ for (i = 0; i < tee->nb_slaves; i++) {
+ avf2 = tee->slaves[i].avf;
+ if ((ret = av_write_trailer(avf2)) < 0)
+ if (!ret_all)
+ ret_all = ret;
+ if (!(avf2->oformat->flags & AVFMT_NOFILE)) {
+ if ((ret = avio_closep(&avf2->pb)) < 0)
+ if (!ret_all)
+ ret_all = ret;
+ }
+ }
+ close_slaves(avf);
+ return ret_all;
+}
+
+static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt)
+{
+ TeeContext *tee = avf->priv_data;
+ AVFormatContext *avf2;
+ AVPacket pkt2;
+ int ret_all = 0, ret;
+ unsigned i, s;
+ int s2;
+ AVRational tb, tb2;
+
+ for (i = 0; i < tee->nb_slaves; i++) {
+ avf2 = tee->slaves[i].avf;
+ s = pkt->stream_index;
+ s2 = tee->slaves[i].stream_map[s];
+ if (s2 < 0)
+ continue;
+
+ if ((ret = av_copy_packet(&pkt2, pkt)) < 0 ||
+ (ret = av_dup_packet(&pkt2))< 0)
+ if (!ret_all) {
+ ret_all = ret;
+ continue;
+ }
+ tb = avf ->streams[s ]->time_base;
+ tb2 = avf2->streams[s2]->time_base;
+ pkt2.pts = av_rescale_q(pkt->pts, tb, tb2);
+ pkt2.dts = av_rescale_q(pkt->dts, tb, tb2);
+ pkt2.duration = av_rescale_q(pkt->duration, tb, tb2);
+ pkt2.stream_index = s2;
+
+ filter_packet(avf2, &pkt2, avf2, tee->slaves[i].bsfs[s2]);
+ if ((ret = av_interleaved_write_frame(avf2, &pkt2)) < 0)
+ if (!ret_all)
+ ret_all = ret;
+ }
+ return ret_all;
+}
+
+AVOutputFormat ff_tee_muxer = {
+ .name = "tee",
+ .long_name = NULL_IF_CONFIG_SMALL("Multiple muxer tee"),
+ .priv_data_size = sizeof(TeeContext),
+ .write_header = tee_write_header,
+ .write_trailer = tee_write_trailer,
+ .write_packet = tee_write_packet,
+ .priv_class = &tee_muxer_class,
+ .flags = AVFMT_NOFILE,
+};
diff --git a/libavformat/thp.c b/libavformat/thp.c
index e8ca04f939..727fb5095c 100644
--- a/libavformat/thp.c
+++ b/libavformat/thp.c
@@ -2,20 +2,20 @@
* THP Demuxer
* Copyright (c) 2007 Marco Gerards
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -26,32 +26,37 @@
typedef struct ThpDemuxContext {
int version;
- int first_frame;
- int first_framesz;
- int last_frame;
+ unsigned first_frame;
+ unsigned first_framesz;
+ unsigned last_frame;
int compoff;
- int framecnt;
+ unsigned framecnt;
AVRational fps;
- int frame;
- int next_frame;
- int next_framesz;
+ unsigned frame;
+ int64_t next_frame;
+ unsigned next_framesz;
int video_stream_index;
int audio_stream_index;
int compcount;
unsigned char components[16];
AVStream* vst;
int has_audio;
- int audiosize;
+ unsigned audiosize;
} ThpDemuxContext;
static int thp_probe(AVProbeData *p)
{
+ double d;
/* check file header */
- if (AV_RL32(p->buf) == MKTAG('T', 'H', 'P', '\0'))
- return AVPROBE_SCORE_MAX;
- else
+ if (AV_RL32(p->buf) != MKTAG('T', 'H', 'P', '\0'))
return 0;
+
+ d = av_int2float(AV_RB32(p->buf + 16));
+ if (d < 0.1 || d > 1000 || isnan(d))
+ return AVPROBE_SCORE_MAX/4;
+
+ return AVPROBE_SCORE_MAX;
}
static int thp_read_header(AVFormatContext *s)
@@ -59,6 +64,7 @@ static int thp_read_header(AVFormatContext *s)
ThpDemuxContext *thp = s->priv_data;
AVStream *st;
AVIOContext *pb = s->pb;
+ int64_t fsize= avio_size(pb);
int i;
/* Read the file header. */
@@ -71,7 +77,9 @@ static int thp_read_header(AVFormatContext *s)
thp->fps = av_d2q(av_int2float(avio_rb32(pb)), INT_MAX);
thp->framecnt = avio_rb32(pb);
thp->first_framesz = avio_rb32(pb);
- avio_rb32(pb); /* Data size. */
+ pb->maxsize = avio_rb32(pb);
+ if(fsize>0 && (!pb->maxsize || fsize < pb->maxsize))
+ pb->maxsize= fsize;
thp->compoff = avio_rb32(pb);
avio_rb32(pb); /* offsetDataOffset. */
@@ -90,7 +98,7 @@ static int thp_read_header(AVFormatContext *s)
for (i = 0; i < thp->compcount; i++) {
if (thp->components[i] == 0) {
- if (thp->vst != 0)
+ if (thp->vst)
break;
/* Video component. */
@@ -106,7 +114,8 @@ static int thp_read_header(AVFormatContext *s)
st->codec->codec_tag = 0; /* no fourcc */
st->codec->width = avio_rb32(pb);
st->codec->height = avio_rb32(pb);
- st->codec->sample_rate = av_q2d(thp->fps);
+ st->nb_frames =
+ st->duration = thp->framecnt;
thp->vst = st;
thp->video_stream_index = st->index;
@@ -142,18 +151,18 @@ static int thp_read_packet(AVFormatContext *s,
{
ThpDemuxContext *thp = s->priv_data;
AVIOContext *pb = s->pb;
- int size;
+ unsigned int size;
int ret;
if (thp->audiosize == 0) {
/* Terminate when last frame is reached. */
if (thp->frame >= thp->framecnt)
- return AVERROR(EIO);
+ return AVERROR_EOF;
avio_seek(pb, thp->next_frame, SEEK_SET);
/* Locate the next frame and read out its size. */
- thp->next_frame += thp->next_framesz;
+ thp->next_frame += FFMAX(thp->next_framesz, 1);
thp->next_framesz = avio_rb32(pb);
avio_rb32(pb); /* Previous total size. */
@@ -167,6 +176,8 @@ static int thp_read_packet(AVFormatContext *s,
thp->frame++;
ret = av_get_packet(pb, pkt, size);
+ if (ret < 0)
+ return ret;
if (ret != size) {
av_free_packet(pkt);
return AVERROR(EIO);
@@ -175,6 +186,8 @@ static int thp_read_packet(AVFormatContext *s,
pkt->stream_index = thp->video_stream_index;
} else {
ret = av_get_packet(pb, pkt, thp->audiosize);
+ if (ret < 0)
+ return ret;
if (ret != thp->audiosize) {
av_free_packet(pkt);
return AVERROR(EIO);
diff --git a/libavformat/tiertexseq.c b/libavformat/tiertexseq.c
index 45300f9e8a..659ba06dd7 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
*/
@@ -301,7 +301,7 @@ static int seq_read_close(AVFormatContext *s)
SeqDemuxContext *seq = s->priv_data;
for (i = 0; i < SEQ_NUM_FRAME_BUFFERS; i++)
- av_free(seq->frame_buffers[i].data);
+ av_freep(&seq->frame_buffers[i].data);
return 0;
}
diff --git a/libavformat/tls.c b/libavformat/tls.c
index 1832f2b3aa..a19415d9c0 100644
--- a/libavformat/tls.c
+++ b/libavformat/tls.c
@@ -2,20 +2,20 @@
* TLS/SSL Protocol
* Copyright (c) 2011 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -24,6 +24,9 @@
#include "libavutil/avstring.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
+#include "network.h"
+#include "os_support.h"
+#include "internal.h"
#if CONFIG_GNUTLS
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
@@ -50,9 +53,6 @@
SSL_CTX_free(c->ctx); \
} while (0)
#endif
-#include "network.h"
-#include "os_support.h"
-#include "internal.h"
#if HAVE_POLL_H
#include <poll.h>
#endif
@@ -80,6 +80,7 @@ typedef struct TLSContext {
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
{"ca_file", "Certificate Authority database file", OFFSET(ca_file), AV_OPT_TYPE_STRING, .flags = D|E },
+ {"cafile", "Certificate Authority database file", OFFSET(ca_file), AV_OPT_TYPE_STRING, .flags = D|E },
{"tls_verify", "Verify the peer certificate", OFFSET(verify), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E },
{"cert_file", "Certificate file", OFFSET(cert_file), AV_OPT_TYPE_STRING, .flags = D|E },
{"key_file", "Private key file", OFFSET(key_file), AV_OPT_TYPE_STRING, .flags = D|E },
@@ -137,6 +138,31 @@ static int do_tls_poll(URLContext *h, int ret)
return 0;
}
+static void set_options(URLContext *h, const char *uri)
+{
+ TLSContext *c = h->priv_data;
+ char buf[1024];
+ const char *p = strchr(uri, '?');
+ if (!p)
+ return;
+
+ if (!c->ca_file && av_find_info_tag(buf, sizeof(buf), "cafile", p))
+ c->ca_file = av_strdup(buf);
+
+ if (!c->verify && av_find_info_tag(buf, sizeof(buf), "verify", p)) {
+ char *endptr = NULL;
+ c->verify = strtol(buf, &endptr, 10);
+ if (buf == endptr)
+ c->verify = 1;
+ }
+
+ if (!c->cert_file && av_find_info_tag(buf, sizeof(buf), "cert", p))
+ c->cert_file = av_strdup(buf);
+
+ if (!c->key_file && av_find_info_tag(buf, sizeof(buf), "key", p))
+ c->key_file = av_strdup(buf);
+}
+
static int tls_open(URLContext *h, const char *uri, int flags)
{
TLSContext *c = h->priv_data;
@@ -149,7 +175,8 @@ static int tls_open(URLContext *h, const char *uri, int flags)
const char *proxy_path;
int use_proxy;
- ff_tls_init();
+ if ((ret = ff_tls_init()) < 0)
+ return ret;
if (c->listen)
snprintf(opts, sizeof(opts), "?listen=1");
@@ -199,8 +226,12 @@ static int tls_open(URLContext *h, const char *uri, int flags)
if (!c->listen && !numerichost)
gnutls_server_name_set(c->session, GNUTLS_NAME_DNS, host, strlen(host));
gnutls_certificate_allocate_credentials(&c->cred);
- if (c->ca_file)
- gnutls_certificate_set_x509_trust_file(c->cred, c->ca_file, GNUTLS_X509_FMT_PEM);
+ set_options(h, uri);
+ if (c->ca_file) {
+ ret = gnutls_certificate_set_x509_trust_file(c->cred, c->ca_file, GNUTLS_X509_FMT_PEM);
+ if (ret < 0)
+ av_log(h, AV_LOG_ERROR, "%s\n", gnutls_strerror(ret));
+ }
#if GNUTLS_VERSION_MAJOR >= 3
else
gnutls_certificate_set_x509_system_trust(c->cred);
@@ -218,7 +249,8 @@ static int tls_open(URLContext *h, const char *uri, int flags)
ret = AVERROR(EIO);
goto fail;
}
- }
+ } else if (c->cert_file || c->key_file)
+ av_log(h, AV_LOG_ERROR, "cert and key required\n");
gnutls_credentials_set(c->session, GNUTLS_CRD_CERTIFICATE, c->cred);
gnutls_transport_set_ptr(c->session, (gnutls_transport_ptr_t)
(intptr_t) c->fd);
@@ -269,8 +301,11 @@ static int tls_open(URLContext *h, const char *uri, int flags)
ret = AVERROR(EIO);
goto fail;
}
- if (c->ca_file)
- SSL_CTX_load_verify_locations(c->ctx, c->ca_file, NULL);
+ set_options(h, uri);
+ if (c->ca_file) {
+ if (!SSL_CTX_load_verify_locations(c->ctx, c->ca_file, NULL))
+ av_log(h, AV_LOG_ERROR, "SSL_CTX_load_verify_locations %s\n", ERR_error_string(ERR_get_error(), NULL));
+ }
if (c->cert_file && !SSL_CTX_use_certificate_chain_file(c->ctx, c->cert_file)) {
av_log(h, AV_LOG_ERROR, "Unable to load cert file %s: %s\n",
c->cert_file, ERR_error_string(ERR_get_error(), NULL));
@@ -286,7 +321,7 @@ static int tls_open(URLContext *h, const char *uri, int flags)
// Note, this doesn't check that the peer certificate actually matches
// the requested hostname.
if (c->verify)
- SSL_CTX_set_verify(c->ctx, SSL_VERIFY_PEER, NULL);
+ SSL_CTX_set_verify(c->ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
c->ssl = SSL_new(c->ctx);
if (!c->ssl) {
av_log(h, AV_LOG_ERROR, "%s\n", ERR_error_string(ERR_get_error(), NULL));
diff --git a/libavformat/tmv.c b/libavformat/tmv.c
index 103ac4af20..ad172f4308 100644
--- a/libavformat/tmv.c
+++ b/libavformat/tmv.c
@@ -2,20 +2,20 @@
* 8088flex TMV file demuxer
* Copyright (c) 2009 Daniel Verkamp <daniel at drv.nu>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -154,7 +154,7 @@ static int tmv_read_packet(AVFormatContext *s, AVPacket *pkt)
int ret, pkt_size = tmv->stream_index ?
tmv->audio_chunk_size : tmv->video_chunk_size;
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR_EOF;
ret = av_get_packet(pb, pkt, pkt_size);
diff --git a/libavformat/tta.c b/libavformat/tta.c
index bc86a777b3..7566939d48 100644
--- a/libavformat/tta.c
+++ b/libavformat/tta.c
@@ -2,27 +2,30 @@
* TTA demuxer
* Copyright (c) 2006 Alex Beregszaszi
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libavcodec/get_bits.h"
+#include "apetag.h"
#include "avformat.h"
+#include "avio_internal.h"
#include "internal.h"
#include "id3v1.h"
+#include "libavutil/crc.h"
#include "libavutil/dict.h"
typedef struct TTAContext {
@@ -31,11 +34,19 @@ typedef struct TTAContext {
int last_frame_size;
} TTAContext;
-static int tta_probe(AVProbeData *p)
+static unsigned long tta_check_crc(unsigned long checksum, const uint8_t *buf,
+ unsigned int len)
{
- const uint8_t *d = p->buf;
+ return av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE), checksum, buf, len);
+}
- if (d[0] == 'T' && d[1] == 'T' && d[2] == 'A' && d[3] == '1')
+static int tta_probe(AVProbeData *p)
+{
+ if (AV_RL32(&p->buf[0]) == MKTAG('T', 'T', 'A', '1') &&
+ (AV_RL16(&p->buf[4]) == 1 || AV_RL16(&p->buf[4]) == 2) &&
+ AV_RL16(&p->buf[6]) > 0 &&
+ AV_RL16(&p->buf[8]) > 0 &&
+ AV_RL32(&p->buf[10]) > 0)
return AVPROBE_SCORE_EXTENSION + 30;
return 0;
}
@@ -44,17 +55,18 @@ 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;
int64_t framepos, start_offset;
+ uint32_t nb_samples, crc;
- if (!av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX))
- ff_id3v1_read(s);
+ ff_id3v1_read(s);
start_offset = avio_tell(s->pb);
if (start_offset < 0)
return start_offset;
+ ffio_init_checksum(s->pb, tta_check_crc, UINT32_MAX);
if (avio_rl32(s->pb) != AV_RL32("TTA1"))
- return -1; // not tta file
+ return AVERROR_INVALIDDATA;
avio_skip(s->pb, 2); // FIXME: flags
channels = avio_rl16(s->pb);
@@ -62,27 +74,31 @@ static int tta_read_header(AVFormatContext *s)
samplerate = avio_rl32(s->pb);
if(samplerate <= 0 || samplerate > 1000000){
av_log(s, AV_LOG_ERROR, "nonsense samplerate\n");
- return -1;
+ return AVERROR_INVALIDDATA;
}
- datalen = avio_rl32(s->pb);
- if(datalen < 0){
- av_log(s, AV_LOG_ERROR, "nonsense datalen\n");
- return -1;
+ nb_samples = avio_rl32(s->pb);
+ if (!nb_samples) {
+ av_log(s, AV_LOG_ERROR, "invalid number of samples\n");
+ return AVERROR_INVALIDDATA;
}
- avio_skip(s->pb, 4); // header crc
+ crc = ffio_get_checksum(s->pb) ^ UINT32_MAX;
+ if (crc != avio_rl32(s->pb) && s->error_recognition & AV_EF_CRCCHECK) {
+ av_log(s, AV_LOG_ERROR, "Header CRC error\n");
+ return AVERROR_INVALIDDATA;
+ }
c->frame_size = samplerate * 256 / 245;
- c->last_frame_size = datalen % c->frame_size;
+ c->last_frame_size = nb_samples % c->frame_size;
if (!c->last_frame_size)
c->last_frame_size = c->frame_size;
- c->totalframes = datalen / c->frame_size + (c->last_frame_size < c->frame_size);
+ c->totalframes = nb_samples / c->frame_size + (c->last_frame_size < c->frame_size);
c->currentframe = 0;
- if(c->totalframes >= UINT_MAX/sizeof(uint32_t)){
- av_log(s, AV_LOG_ERROR, "totalframes too large\n");
- return -1;
+ if(c->totalframes >= UINT_MAX/sizeof(uint32_t) || c->totalframes <= 0){
+ av_log(s, AV_LOG_ERROR, "totalframes %d invalid\n", c->totalframes);
+ return AVERROR_INVALIDDATA;
}
st = avformat_new_stream(s, NULL);
@@ -91,20 +107,33 @@ static int tta_read_header(AVFormatContext *s)
avpriv_set_pts_info(st, 64, 1, samplerate);
st->start_time = 0;
- st->duration = datalen;
+ st->duration = nb_samples;
framepos = avio_tell(s->pb);
if (framepos < 0)
return framepos;
framepos += 4 * c->totalframes + 4;
+ if (ff_alloc_extradata(st->codec, avio_tell(s->pb) - start_offset))
+ return AVERROR(ENOMEM);
+
+ avio_seek(s->pb, start_offset, SEEK_SET);
+ avio_read(s->pb, st->codec->extradata, st->codec->extradata_size);
+
+ ffio_init_checksum(s->pb, tta_check_crc, UINT32_MAX);
for (i = 0; i < c->totalframes; i++) {
uint32_t size = avio_rl32(s->pb);
- av_add_index_entry(st, framepos, i * c->frame_size, size, 0,
- AVINDEX_KEYFRAME);
+ int r;
+ if ((r = av_add_index_entry(st, framepos, i * c->frame_size, size, 0,
+ AVINDEX_KEYFRAME)) < 0)
+ return r;
framepos += size;
}
- avio_skip(s->pb, 4); // seektable crc
+ crc = ffio_get_checksum(s->pb) ^ UINT32_MAX;
+ if (crc != avio_rl32(s->pb) && s->error_recognition & AV_EF_CRCCHECK) {
+ av_log(s, AV_LOG_ERROR, "Seek table CRC error\n");
+ return AVERROR_INVALIDDATA;
+ }
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = AV_CODEC_ID_TTA;
@@ -112,19 +141,11 @@ static int tta_read_header(AVFormatContext *s)
st->codec->sample_rate = samplerate;
st->codec->bits_per_coded_sample = bps;
- st->codec->extradata_size = avio_tell(s->pb) - start_offset;
- if(st->codec->extradata_size+FF_INPUT_BUFFER_PADDING_SIZE <= (unsigned)st->codec->extradata_size){
- //this check is redundant as avio_read should fail
- av_log(s, AV_LOG_ERROR, "extradata_size too large\n");
- return -1;
- }
- 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);
+ if (s->pb->seekable) {
+ int64_t pos = avio_tell(s->pb);
+ ff_ape_parse_tag(s);
+ avio_seek(s->pb, pos, SEEK_SET);
}
- avio_seek(s->pb, start_offset, SEEK_SET);
- avio_read(s->pb, st->codec->extradata, st->codec->extradata_size);
return 0;
}
@@ -139,6 +160,11 @@ static int tta_read_packet(AVFormatContext *s, AVPacket *pkt)
if (c->currentframe >= c->totalframes)
return AVERROR_EOF;
+ if (st->nb_index_entries < c->totalframes) {
+ av_log(s, AV_LOG_ERROR, "Index entry disappeared\n");
+ return AVERROR_INVALIDDATA;
+ }
+
size = st->index_entries[c->currentframe].size;
ret = av_get_packet(s->pb, pkt, size);
diff --git a/libavformat/tty.c b/libavformat/tty.c
index 3a34dd865a..9022e915fa 100644
--- a/libavformat/tty.c
+++ b/libavformat/tty.c
@@ -2,20 +2,20 @@
* Tele-typewriter demuxer
* Copyright (c) 2010 Peter Ross <pross@xvid.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -38,8 +38,8 @@ typedef struct TtyDemuxContext {
AVClass *class;
int chars_per_frame;
uint64_t fsize; /**< file size less metadata buffer */
- char *video_size;/**< A string describing video size, set by a private option. */
- char *framerate; /**< Set by a private option. */
+ int width, height; /**< Set by a private option. */
+ AVRational framerate; /**< Set by a private option. */
} TtyDemuxContext;
/**
@@ -75,9 +75,8 @@ static int efi_read(AVFormatContext *avctx, uint64_t start_pos)
static int read_header(AVFormatContext *avctx)
{
TtyDemuxContext *s = avctx->priv_data;
- int width = 0, height = 0, ret = 0;
+ int ret = 0;
AVStream *st = avformat_new_stream(avctx, NULL);
- AVRational framerate;
if (!st) {
ret = AVERROR(ENOMEM);
@@ -87,18 +86,10 @@ static int read_header(AVFormatContext *avctx)
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
st->codec->codec_id = AV_CODEC_ID_ANSI;
- if (s->video_size && (ret = av_parse_video_size(&width, &height, s->video_size)) < 0) {
- av_log (avctx, AV_LOG_ERROR, "Couldn't parse video size.\n");
- goto fail;
- }
- if ((ret = av_parse_video_rate(&framerate, s->framerate)) < 0) {
- av_log(avctx, AV_LOG_ERROR, "Could not parse framerate: %s.\n", s->framerate);
- goto fail;
- }
- st->codec->width = width;
- st->codec->height = height;
- avpriv_set_pts_info(st, 60, framerate.den, framerate.num);
- st->avg_frame_rate = framerate;
+ st->codec->width = s->width;
+ st->codec->height = s->height;
+ avpriv_set_pts_info(st, 60, s->framerate.den, s->framerate.num);
+ st->avg_frame_rate = s->framerate;
/* simulate tty display speed */
s->chars_per_frame = FFMAX(av_q2d(st->time_base)*s->chars_per_frame, 1);
@@ -122,20 +113,22 @@ static int read_packet(AVFormatContext *avctx, AVPacket *pkt)
TtyDemuxContext *s = avctx->priv_data;
int n;
- if (avctx->pb->eof_reached)
+ if (avio_feof(avctx->pb))
return AVERROR_EOF;
n = s->chars_per_frame;
if (s->fsize) {
// ignore metadata buffer
uint64_t p = avio_tell(avctx->pb);
+ if (p == s->fsize)
+ return AVERROR_EOF;
if (p + s->chars_per_frame > s->fsize)
n = s->fsize - p;
}
pkt->size = av_get_packet(avctx->pb, pkt, n);
- if (pkt->size <= 0)
- return AVERROR(EIO);
+ if (pkt->size < 0)
+ return pkt->size;
pkt->flags |= AV_PKT_FLAG_KEY;
return 0;
}
@@ -144,8 +137,8 @@ static int read_packet(AVFormatContext *avctx, AVPacket *pkt)
#define DEC AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[] = {
{ "chars_per_frame", "", offsetof(TtyDemuxContext, chars_per_frame), AV_OPT_TYPE_INT, {.i64 = 6000}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM},
- { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC },
- { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0, DEC },
+ { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC },
+ { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, DEC },
{ NULL },
};
diff --git a/libavformat/txd.c b/libavformat/txd.c
index e6522c3a73..cac37d54b4 100644
--- a/libavformat/txd.c
+++ b/libavformat/txd.c
@@ -2,20 +2,20 @@
* Renderware TeXture Dictionary (.txd) demuxer
* Copyright (c) 2007 Ivo van Poorten
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -49,6 +49,7 @@ static int txd_read_header(AVFormatContext *s) {
avpriv_set_pts_info(st, 64, 1, 5);
st->avg_frame_rate = av_inv_q(st->time_base);
/* the parameters will be extracted from the compressed bitstream */
+
return 0;
}
@@ -62,7 +63,7 @@ next_chunk:
chunk_size = avio_rl32(pb);
marker = avio_rl32(pb);
- if (s->pb->eof_reached)
+ if (avio_feof(s->pb))
return AVERROR_EOF;
if (marker != TXD_MARKER && marker != TXD_MARKER2) {
av_log(s, AV_LOG_ERROR, "marker does not match\n");
@@ -70,17 +71,17 @@ next_chunk:
}
switch (id) {
- case TXD_INFO:
- if (chunk_size > 100)
- break;
- case TXD_EXTRA:
- avio_skip(s->pb, chunk_size);
- case TXD_FILE:
- case TXD_TEXTURE:
- goto next_chunk;
- default:
- av_log(s, AV_LOG_ERROR, "unknown chunk id %i\n", id);
- return AVERROR_INVALIDDATA;
+ case TXD_INFO:
+ if (chunk_size > 100)
+ break;
+ case TXD_EXTRA:
+ avio_skip(s->pb, chunk_size);
+ case TXD_FILE:
+ case TXD_TEXTURE:
+ goto next_chunk;
+ default:
+ av_log(s, AV_LOG_ERROR, "unknown chunk id %i\n", id);
+ return AVERROR_INVALIDDATA;
}
ret = av_get_packet(s->pb, pkt, chunk_size);
diff --git a/libavformat/udp.c b/libavformat/udp.c
index 939d2c1214..3a65f55882 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,114 @@
#include "avformat.h"
#include "avio_internal.h"
#include "libavutil/parseutils.h"
+#include "libavutil/fifo.h"
+#include "libavutil/intreadwrite.h"
#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "libavutil/log.h"
+#include "libavutil/time.h"
#include "internal.h"
#include "network.h"
#include "os_support.h"
#include "url.h"
+#if HAVE_UDPLITE_H
+#include "udplite.h"
+#else
+/* On many Linux systems, udplite.h is missing but the kernel supports UDP-Lite.
+ * So, we provide a fallback here.
+ */
+#define UDPLITE_SEND_CSCOV 10
+#define UDPLITE_RECV_CSCOV 11
+#endif
+
+#ifndef IPPROTO_UDPLITE
+#define IPPROTO_UDPLITE 136
+#endif
+
+#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
+#define UDP_HEADER_SIZE 8
+
typedef struct UDPContext {
+ const AVClass *class;
int udp_fd;
int ttl;
+ int udplite_coverage;
int buffer_size;
int is_multicast;
+ int is_broadcast;
int local_port;
int reuse_socket;
+ int overrun_nonfatal;
struct sockaddr_storage dest_addr;
int dest_addr_len;
int is_connected;
+
+ /* Circular Buffer variables for use in UDP receive code */
+ int circular_buffer_size;
+ AVFifoBuffer *fifo;
+ int circular_buffer_error;
+#if HAVE_PTHREAD_CANCEL
+ pthread_t circular_buffer_thread;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ int thread_started;
+#endif
+ uint8_t tmp[UDP_MAX_PKT_SIZE+4];
+ int remaining_in_dg;
+ char *local_addr;
+ int packet_size;
+ int timeout;
+ struct sockaddr_storage local_addr_storage;
} UDPContext;
-#define UDP_TX_BUF_SIZE 32768
-#define UDP_MAX_PKT_SIZE 65536
+#define OFFSET(x) offsetof(UDPContext, x)
+#define D AV_OPT_FLAG_DECODING_PARAM
+#define E AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+{"buffer_size", "set packet buffer size in bytes", OFFSET(buffer_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D|E },
+{"localport", "set local port to bind to", OFFSET(local_port), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D|E },
+{"localaddr", "choose local IP address", OFFSET(local_addr), AV_OPT_TYPE_STRING, {.str = ""}, 0, 0, D|E },
+{"udplite_coverage", "choose UDPLite head size which should be validated by checksum", OFFSET(udplite_coverage), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D|E },
+{"pkt_size", "set size of UDP packets", OFFSET(packet_size), AV_OPT_TYPE_INT, {.i64 = 1472}, 0, INT_MAX, D|E },
+{"reuse", "explicitly allow or disallow reusing UDP sockets", OFFSET(reuse_socket), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E },
+{"broadcast", "explicitly allow or disallow broadcast destination", OFFSET(is_broadcast), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
+{"ttl", "set the time to live value (for multicast only)", OFFSET(ttl), AV_OPT_TYPE_INT, {.i64 = 16}, 0, INT_MAX, E },
+{"connect", "set if connect() should be called on socket", OFFSET(is_connected), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E },
+/* TODO 'sources', 'block' option */
+{"fifo_size", "set the UDP receiving circular buffer size, expressed as a number of packets with size of 188 bytes", OFFSET(circular_buffer_size), AV_OPT_TYPE_INT, {.i64 = 7*4096}, 0, INT_MAX, D },
+{"overrun_nonfatal", "survive in case of UDP receiving circular buffer overrun", OFFSET(overrun_nonfatal), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D },
+{"timeout", "set raise error timeout (only in read mode)", OFFSET(timeout), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D },
+{NULL}
+};
+
+static const AVClass udp_context_class = {
+ .class_name = "udp",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+static const AVClass udplite_context_class = {
+ .class_name = "udplite",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
static void log_net_error(void *ctx, int level, const char* prefix)
{
@@ -84,14 +167,17 @@ static int udp_set_multicast_ttl(int sockfd, int mcastTTL,
return 0;
}
-static int udp_join_multicast_group(int sockfd, struct sockaddr *addr)
+static int udp_join_multicast_group(int sockfd, struct sockaddr *addr,struct sockaddr *local_addr)
{
#ifdef IP_ADD_MEMBERSHIP
if (addr->sa_family == AF_INET) {
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
- mreq.imr_interface.s_addr= INADDR_ANY;
+ if (local_addr)
+ mreq.imr_interface= ((struct sockaddr_in *)local_addr)->sin_addr;
+ else
+ mreq.imr_interface.s_addr= INADDR_ANY;
if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0) {
log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_ADD_MEMBERSHIP)");
return -1;
@@ -113,14 +199,17 @@ static int udp_join_multicast_group(int sockfd, struct sockaddr *addr)
return 0;
}
-static int udp_leave_multicast_group(int sockfd, struct sockaddr *addr)
+static int udp_leave_multicast_group(int sockfd, struct sockaddr *addr,struct sockaddr *local_addr)
{
#ifdef IP_DROP_MEMBERSHIP
if (addr->sa_family == AF_INET) {
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
- mreq.imr_interface.s_addr= INADDR_ANY;
+ if (local_addr)
+ mreq.imr_interface= ((struct sockaddr_in *)local_addr)->sin_addr;
+ else
+ mreq.imr_interface.s_addr= INADDR_ANY;
if (setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0) {
log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_DROP_MEMBERSHIP)");
return -1;
@@ -248,7 +337,7 @@ static int udp_set_url(struct sockaddr_storage *addr,
int addr_len;
res0 = udp_resolve_host(hostname, port, SOCK_DGRAM, AF_UNSPEC, 0);
- if (res0 == 0) return AVERROR(EIO);
+ if (!res0) return AVERROR(EIO);
memcpy(addr, res0->ai_addr, res0->ai_addrlen);
addr_len = res0->ai_addrlen;
freeaddrinfo(res0);
@@ -260,17 +349,20 @@ static int udp_socket_create(UDPContext *s, struct sockaddr_storage *addr,
socklen_t *addr_len, const char *localaddr)
{
int udp_fd = -1;
- struct addrinfo *res0 = NULL, *res = NULL;
+ struct addrinfo *res0, *res;
int family = AF_UNSPEC;
if (((struct sockaddr *) &s->dest_addr)->sa_family)
family = ((struct sockaddr *) &s->dest_addr)->sa_family;
res0 = udp_resolve_host(localaddr[0] ? localaddr : NULL, s->local_port,
SOCK_DGRAM, family, AI_PASSIVE);
- if (res0 == 0)
+ if (!res0)
goto fail;
for (res = res0; res; res=res->ai_next) {
- udp_fd = ff_socket(res->ai_family, SOCK_DGRAM, 0);
+ if (s->udplite_coverage)
+ udp_fd = ff_socket(res->ai_family, SOCK_DGRAM, IPPROTO_UDPLITE);
+ else
+ udp_fd = ff_socket(res->ai_family, SOCK_DGRAM, 0);
if (udp_fd != -1) break;
log_net_error(NULL, AV_LOG_ERROR, "socket");
}
@@ -317,6 +409,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 +471,65 @@ static int udp_get_file_handle(URLContext *h)
return s->udp_fd;
}
+#if HAVE_PTHREAD_CANCEL
+static void *circular_buffer_task( void *_URLContext)
+{
+ URLContext *h = _URLContext;
+ UDPContext *s = h->priv_data;
+ int old_cancelstate;
+
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate);
+ pthread_mutex_lock(&s->mutex);
+ if (ff_socket_nonblock(s->udp_fd, 0) < 0) {
+ av_log(h, AV_LOG_ERROR, "Failed to set blocking mode");
+ s->circular_buffer_error = AVERROR(EIO);
+ goto end;
+ }
+ while(1) {
+ int len;
+
+ pthread_mutex_unlock(&s->mutex);
+ /* Blocking operations are always cancellation points;
+ see "General Information" / "Thread Cancelation Overview"
+ in Single Unix. */
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelstate);
+ len = recv(s->udp_fd, s->tmp+4, sizeof(s->tmp)-4, 0);
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate);
+ pthread_mutex_lock(&s->mutex);
+ if (len < 0) {
+ if (ff_neterrno() != AVERROR(EAGAIN) && ff_neterrno() != AVERROR(EINTR)) {
+ s->circular_buffer_error = ff_neterrno();
+ goto end;
+ }
+ continue;
+ }
+ AV_WL32(s->tmp, len);
+
+ if(av_fifo_space(s->fifo) < len + 4) {
+ /* No Space left */
+ if (s->overrun_nonfatal) {
+ av_log(h, AV_LOG_WARNING, "Circular buffer overrun. "
+ "Surviving due to overrun_nonfatal option\n");
+ continue;
+ } else {
+ av_log(h, AV_LOG_ERROR, "Circular buffer overrun. "
+ "To avoid, increase fifo_size URL option. "
+ "To survive in such case, use overrun_nonfatal option\n");
+ s->circular_buffer_error = AVERROR(EIO);
+ goto end;
+ }
+ }
+ av_fifo_generic_write(s->fifo, s->tmp, len+4, NULL);
+ pthread_cond_signal(&s->cond);
+ }
+
+end:
+ pthread_cond_signal(&s->cond);
+ pthread_mutex_unlock(&s->mutex);
+ return NULL;
+}
+#endif
+
static int parse_source_list(char *buf, char **sources, int *num_sources,
int max_sources)
{
@@ -404,7 +556,7 @@ static int parse_source_list(char *buf, char **sources, int *num_sources,
static int udp_open(URLContext *h, const char *uri, int flags)
{
char hostname[1024], localaddr[1024] = "";
- int port, udp_fd = -1, tmp, bind_ret = -1;
+ int port, udp_fd = -1, tmp, bind_ret = -1, dscp = -1;
UDPContext *s = h->priv_data;
int is_output;
const char *p;
@@ -416,12 +568,10 @@ static int udp_open(URLContext *h, const char *uri, int flags)
char *include_sources[32], *exclude_sources[32];
h->is_streamed = 1;
- h->max_packet_size = 1472;
is_output = !(flags & AVIO_FLAG_READ);
-
- s->ttl = 16;
- s->buffer_size = is_output ? UDP_TX_BUF_SIZE : UDP_MAX_PKT_SIZE;
+ if (!s->buffer_size) /* if not set explicitly */
+ s->buffer_size = is_output ? UDP_TX_BUF_SIZE : UDP_MAX_PKT_SIZE;
p = strchr(uri, '?');
if (p) {
@@ -433,14 +583,28 @@ 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);
}
+ if (av_find_info_tag(buf, sizeof(buf), "udplite_coverage", p)) {
+ s->udplite_coverage = strtol(buf, NULL, 10);
+ }
if (av_find_info_tag(buf, sizeof(buf), "localport", p)) {
s->local_port = strtol(buf, NULL, 10);
}
if (av_find_info_tag(buf, sizeof(buf), "pkt_size", p)) {
- h->max_packet_size = strtol(buf, NULL, 10);
+ s->packet_size = strtol(buf, NULL, 10);
}
if (av_find_info_tag(buf, sizeof(buf), "buffer_size", p)) {
s->buffer_size = strtol(buf, NULL, 10);
@@ -448,6 +612,16 @@ 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), "dscp", p)) {
+ dscp = strtol(buf, NULL, 10);
+ }
+ if (av_find_info_tag(buf, sizeof(buf), "fifo_size", p)) {
+ s->circular_buffer_size = strtol(buf, NULL, 10);
+ if (!HAVE_PTHREAD_CANCEL)
+ av_log(h, AV_LOG_WARNING,
+ "'circular_buffer_size' option was set but it is not supported "
+ "on this build (pthread support is required)\n");
+ }
if (av_find_info_tag(buf, sizeof(buf), "localaddr", p)) {
av_strlcpy(localaddr, buf, sizeof(localaddr));
}
@@ -461,7 +635,19 @@ static int udp_open(URLContext *h, const char *uri, int flags)
FF_ARRAY_ELEMS(exclude_sources)))
goto fail;
}
+ if (!is_output && av_find_info_tag(buf, sizeof(buf), "timeout", p))
+ s->timeout = strtol(buf, NULL, 10);
+ if (is_output && av_find_info_tag(buf, sizeof(buf), "broadcast", p))
+ s->is_broadcast = strtol(buf, NULL, 10);
}
+ /* handling needed to support options picking from both AVOption and URL */
+ s->circular_buffer_size *= 188;
+ if (flags & AVIO_FLAG_WRITE) {
+ h->max_packet_size = s->packet_size;
+ } else {
+ h->max_packet_size = UDP_MAX_PKT_SIZE;
+ }
+ h->rw_timeout = s->timeout;
/* fill the dest addr */
av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, NULL, 0, uri);
@@ -478,10 +664,12 @@ static int udp_open(URLContext *h, const char *uri, int flags)
if ((s->is_multicast || !s->local_port) && (h->flags & AVIO_FLAG_READ))
s->local_port = port;
- udp_fd = udp_socket_create(s, &my_addr, &len, localaddr);
+ udp_fd = udp_socket_create(s, &my_addr, &len, localaddr[0] ? localaddr : s->local_addr);
if (udp_fd < 0)
goto fail;
+ s->local_addr_storage=my_addr; //store for future multicast join
+
/* Follow the requested reuse option, unless it's multicast in which
* case enable reuse unless explicitly disabled.
*/
@@ -491,6 +679,31 @@ static int udp_open(URLContext *h, const char *uri, int flags)
goto fail;
}
+ if (s->is_broadcast) {
+#ifdef SO_BROADCAST
+ if (setsockopt (udp_fd, SOL_SOCKET, SO_BROADCAST, &(s->is_broadcast), sizeof(s->is_broadcast)) != 0)
+#endif
+ goto fail;
+ }
+
+ /* Set the checksum coverage for UDP-Lite (RFC 3828) for sending and receiving.
+ * The receiver coverage has to be less than or equal to the sender coverage.
+ * Otherwise, the receiver will drop all packets.
+ */
+ if (s->udplite_coverage) {
+ if (setsockopt (udp_fd, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV, &(s->udplite_coverage), sizeof(s->udplite_coverage)) != 0)
+ av_log(h, AV_LOG_WARNING, "socket option UDPLITE_SEND_CSCOV not available");
+
+ if (setsockopt (udp_fd, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV, &(s->udplite_coverage), sizeof(s->udplite_coverage)) != 0)
+ av_log(h, AV_LOG_WARNING, "socket option UDPLITE_RECV_CSCOV not available");
+ }
+
+ if (dscp >= 0) {
+ dscp <<= 2;
+ if (setsockopt (udp_fd, IPPROTO_IP, IP_TOS, &dscp, sizeof(dscp)) != 0)
+ goto fail;
+ }
+
/* If multicast, try binding the multicast address first, to avoid
* receiving UDP packets from other sources aimed at the same UDP
* port. This fails on windows. This makes sending to the same address
@@ -526,7 +739,7 @@ static int udp_open(URLContext *h, const char *uri, int flags)
if (udp_set_multicast_sources(udp_fd, (struct sockaddr *)&s->dest_addr, s->dest_addr_len, include_sources, num_include_sources, 1) < 0)
goto fail;
} else {
- if (udp_join_multicast_group(udp_fd, (struct sockaddr *)&s->dest_addr) < 0)
+ if (udp_join_multicast_group(udp_fd, (struct sockaddr *)&s->dest_addr,(struct sockaddr *)&s->local_addr_storage) < 0)
goto fail;
}
if (num_exclude_sources) {
@@ -544,12 +757,20 @@ static int udp_open(URLContext *h, const char *uri, int flags)
goto fail;
}
} else {
- /* set udp recv buffer size to the largest possible udp packet size to
- * avoid losing data on OSes that set this too low by default. */
+ /* set udp recv buffer size to the requested value (default 64K) */
tmp = s->buffer_size;
if (setsockopt(udp_fd, SOL_SOCKET, SO_RCVBUF, &tmp, sizeof(tmp)) < 0) {
log_net_error(h, AV_LOG_WARNING, "setsockopt(SO_RECVBUF)");
}
+ len = sizeof(tmp);
+ if (getsockopt(udp_fd, SOL_SOCKET, SO_RCVBUF, &tmp, &len) < 0) {
+ log_net_error(h, AV_LOG_WARNING, "getsockopt(SO_RCVBUF)");
+ } else {
+ av_log(h, AV_LOG_DEBUG, "end receive buffer size reported is %d\n", tmp);
+ if(tmp < s->buffer_size)
+ av_log(h, AV_LOG_WARNING, "attempted to set receive buffer to size %d but it only ended up set as %d", s->buffer_size, tmp);
+ }
+
/* make the socket non-blocking */
ff_socket_nonblock(udp_fd, 1);
}
@@ -566,10 +787,43 @@ static int udp_open(URLContext *h, const char *uri, int flags)
av_freep(&exclude_sources[i]);
s->udp_fd = udp_fd;
+
+#if HAVE_PTHREAD_CANCEL
+ if (!is_output && s->circular_buffer_size) {
+ int ret;
+
+ /* start the task going */
+ s->fifo = av_fifo_alloc(s->circular_buffer_size);
+ ret = pthread_mutex_init(&s->mutex, NULL);
+ if (ret != 0) {
+ av_log(h, AV_LOG_ERROR, "pthread_mutex_init failed : %s\n", strerror(ret));
+ goto fail;
+ }
+ ret = pthread_cond_init(&s->cond, NULL);
+ if (ret != 0) {
+ av_log(h, AV_LOG_ERROR, "pthread_cond_init failed : %s\n", strerror(ret));
+ goto cond_fail;
+ }
+ ret = pthread_create(&s->circular_buffer_thread, NULL, circular_buffer_task, h);
+ if (ret != 0) {
+ av_log(h, AV_LOG_ERROR, "pthread_create failed : %s\n", strerror(ret));
+ goto thread_fail;
+ }
+ s->thread_started = 1;
+ }
+#endif
+
return 0;
+#if HAVE_PTHREAD_CANCEL
+ thread_fail:
+ pthread_cond_destroy(&s->cond);
+ cond_fail:
+ pthread_mutex_destroy(&s->mutex);
+#endif
fail:
if (udp_fd >= 0)
closesocket(udp_fd);
+ av_fifo_freep(&s->fifo);
for (i = 0; i < num_include_sources; i++)
av_freep(&include_sources[i]);
for (i = 0; i < num_exclude_sources; i++)
@@ -577,10 +831,64 @@ static int udp_open(URLContext *h, const char *uri, int flags)
return AVERROR(EIO);
}
+static int udplite_open(URLContext *h, const char *uri, int flags)
+{
+ UDPContext *s = h->priv_data;
+
+ // set default checksum coverage
+ s->udplite_coverage = UDP_HEADER_SIZE;
+
+ return udp_open(h, uri, flags);
+}
+
static int udp_read(URLContext *h, uint8_t *buf, int size)
{
UDPContext *s = h->priv_data;
int ret;
+#if HAVE_PTHREAD_CANCEL
+ int avail, nonblock = h->flags & AVIO_FLAG_NONBLOCK;
+
+ if (s->fifo) {
+ pthread_mutex_lock(&s->mutex);
+ do {
+ avail = av_fifo_size(s->fifo);
+ if (avail) { // >=size) {
+ uint8_t tmp[4];
+
+ av_fifo_generic_read(s->fifo, tmp, 4, NULL);
+ avail= AV_RL32(tmp);
+ if(avail > size){
+ av_log(h, AV_LOG_WARNING, "Part of datagram lost due to insufficient buffer size\n");
+ avail= size;
+ }
+
+ av_fifo_generic_read(s->fifo, buf, avail, NULL);
+ av_fifo_drain(s->fifo, AV_RL32(tmp) - avail);
+ pthread_mutex_unlock(&s->mutex);
+ return avail;
+ } else if(s->circular_buffer_error){
+ int err = s->circular_buffer_error;
+ pthread_mutex_unlock(&s->mutex);
+ return err;
+ } else if(nonblock) {
+ pthread_mutex_unlock(&s->mutex);
+ return AVERROR(EAGAIN);
+ }
+ else {
+ /* FIXME: using the monotonic clock would be better,
+ but it does not exist on all supported platforms. */
+ int64_t t = av_gettime() + 100000;
+ struct timespec tv = { .tv_sec = t / 1000000,
+ .tv_nsec = (t % 1000000) * 1000 };
+ if (pthread_cond_timedwait(&s->cond, &s->mutex, &tv) < 0) {
+ pthread_mutex_unlock(&s->mutex);
+ return AVERROR(errno == ETIMEDOUT ? EAGAIN : errno);
+ }
+ nonblock = 1;
+ }
+ } while( 1);
+ }
+#endif
if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
ret = ff_network_wait_fd(s->udp_fd, 0);
@@ -588,6 +896,7 @@ static int udp_read(URLContext *h, uint8_t *buf, int size)
return ret;
}
ret = recv(s->udp_fd, buf, size, 0);
+
return ret < 0 ? ff_neterrno() : ret;
}
@@ -617,8 +926,20 @@ static int udp_close(URLContext *h)
UDPContext *s = h->priv_data;
if (s->is_multicast && (h->flags & AVIO_FLAG_READ))
- udp_leave_multicast_group(s->udp_fd, (struct sockaddr *)&s->dest_addr);
+ udp_leave_multicast_group(s->udp_fd, (struct sockaddr *)&s->dest_addr,(struct sockaddr *)&s->local_addr_storage);
closesocket(s->udp_fd);
+#if HAVE_PTHREAD_CANCEL
+ if (s->thread_started) {
+ int ret;
+ pthread_cancel(s->circular_buffer_thread);
+ ret = pthread_join(s->circular_buffer_thread, NULL);
+ if (ret != 0)
+ av_log(h, AV_LOG_ERROR, "pthread_join(): %s\n", strerror(ret));
+ pthread_mutex_destroy(&s->mutex);
+ pthread_cond_destroy(&s->cond);
+ }
+#endif
+ av_fifo_freep(&s->fifo);
return 0;
}
@@ -630,5 +951,18 @@ 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,
+};
+
+URLProtocol ff_udplite_protocol = {
+ .name = "udplite",
+ .url_open = udplite_open,
+ .url_read = udp_read,
+ .url_write = udp_write,
+ .url_close = udp_close,
+ .url_get_file_handle = udp_get_file_handle,
+ .priv_data_size = sizeof(UDPContext),
+ .priv_data_class = &udplite_context_class,
.flags = URL_PROTOCOL_FLAG_NETWORK,
};
diff --git a/libavformat/uncodedframecrcenc.c b/libavformat/uncodedframecrcenc.c
new file mode 100644
index 0000000000..414683fe23
--- /dev/null
+++ b/libavformat/uncodedframecrcenc.c
@@ -0,0 +1,172 @@
+/*
+* Copyright (c) 2013 Nicolas George
+*
+* This file is part of FFmpeg.
+*
+* FFmpeg is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public License
+* as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* FFmpeg is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "libavutil/adler32.h"
+#include "libavutil/avassert.h"
+#include "libavutil/bprint.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/pixdesc.h"
+#include "avformat.h"
+#include "internal.h"
+
+/* Identical to Adler32 when the type is uint8_t. */
+#define DEFINE_CKSUM_LINE(name, type, conv) \
+static void cksum_line_ ## name(unsigned *cksum, void *data, unsigned size) \
+{ \
+ type *p = data; \
+ unsigned a = *cksum & 0xFFFF, b = *cksum >> 16; \
+ for (; size > 0; size--, p++) { \
+ a = (a + (unsigned)(conv)) % 65521; \
+ b = (b + a) % 65521; \
+ } \
+ *cksum = a | (b << 16); \
+}
+
+DEFINE_CKSUM_LINE(u8, uint8_t, *p)
+DEFINE_CKSUM_LINE(s16, int16_t, *p + 0x8000)
+DEFINE_CKSUM_LINE(s32, int32_t, *p + 0x80000000)
+DEFINE_CKSUM_LINE(flt, float, *p * 0x80000000 + 0x80000000)
+DEFINE_CKSUM_LINE(dbl, double, *p * 0x80000000 + 0x80000000)
+
+static void video_frame_cksum(AVBPrint *bp, AVFrame *frame)
+{
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
+ int i, y;
+ uint8_t *data;
+ int linesize[5] = { 0 };
+
+ av_bprintf(bp, ", %d x %d", frame->width, frame->height);
+ if (!desc) {
+ av_bprintf(bp, ", unknown");
+ return;
+ }
+ if (av_image_fill_linesizes(linesize, frame->format, frame->width) < 0)
+ return;
+ av_bprintf(bp, ", %s", desc->name);
+ for (i = 0; linesize[i]; i++) {
+ unsigned cksum = 0;
+ int h = frame->height;
+ if ((i == 1 || i == 2) && desc->nb_components >= 3)
+ h = -((-h) >> desc->log2_chroma_h);
+ data = frame->data[i];
+ for (y = 0; y < h; y++) {
+ cksum = av_adler32_update(cksum, data, linesize[i]);
+ data += frame->linesize[i];
+ }
+ av_bprintf(bp, ", 0x%08x", cksum);
+ }
+}
+
+static void audio_frame_cksum(AVBPrint *bp, AVFrame *frame)
+{
+ int nb_planes, nb_samples, p;
+ const char *name;
+
+ nb_planes = av_frame_get_channels(frame);
+ nb_samples = frame->nb_samples;
+ if (!av_sample_fmt_is_planar(frame->format)) {
+ nb_samples *= nb_planes;
+ nb_planes = 1;
+ }
+ name = av_get_sample_fmt_name(frame->format);
+ av_bprintf(bp, ", %d samples", frame->nb_samples);
+ av_bprintf(bp, ", %s", name ? name : "unknown");
+ for (p = 0; p < nb_planes; p++) {
+ uint32_t cksum = 0;
+ void *d = frame->extended_data[p];
+ switch (frame->format) {
+ case AV_SAMPLE_FMT_U8:
+ case AV_SAMPLE_FMT_U8P:
+ cksum_line_u8(&cksum, d, nb_samples);
+ break;
+ case AV_SAMPLE_FMT_S16:
+ case AV_SAMPLE_FMT_S16P:
+ cksum_line_s16(&cksum, d, nb_samples);
+ break;
+ case AV_SAMPLE_FMT_S32:
+ case AV_SAMPLE_FMT_S32P:
+ cksum_line_s32(&cksum, d, nb_samples);
+ break;
+ case AV_SAMPLE_FMT_FLT:
+ case AV_SAMPLE_FMT_FLTP:
+ cksum_line_flt(&cksum, d, nb_samples);
+ break;
+ case AV_SAMPLE_FMT_DBL:
+ case AV_SAMPLE_FMT_DBLP:
+ cksum_line_dbl(&cksum, d, nb_samples);
+ break;
+ default:
+ av_assert0(!"reached");
+ }
+ av_bprintf(bp, ", 0x%08x", cksum);
+ }
+}
+
+static int write_frame(struct AVFormatContext *s, int stream_index,
+ AVFrame **frame, unsigned flags)
+{
+ AVBPrint bp;
+ int ret = 0;
+ enum AVMediaType type;
+ const char *type_name;
+
+ if ((flags & AV_WRITE_UNCODED_FRAME_QUERY))
+ return 0;
+
+ av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
+ av_bprintf(&bp, "%d, %10"PRId64"",
+ stream_index, (*frame)->pts);
+ type = s->streams[stream_index]->codec->codec_type;
+ type_name = av_get_media_type_string(type);
+ av_bprintf(&bp, ", %s", type_name ? type_name : "unknown");
+ switch (type) {
+ case AVMEDIA_TYPE_VIDEO:
+ video_frame_cksum(&bp, *frame);
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ audio_frame_cksum(&bp, *frame);
+ break;
+ }
+
+ av_bprint_chars(&bp, '\n', 1);
+ if (av_bprint_is_complete(&bp))
+ avio_write(s->pb, bp.str, bp.len);
+ else
+ ret = AVERROR(ENOMEM);
+ av_bprint_finalize(&bp, NULL);
+ return ret;
+}
+
+static int write_packet(struct AVFormatContext *s, AVPacket *pkt)
+{
+ return AVERROR(ENOSYS);
+}
+
+AVOutputFormat ff_uncodedframecrc_muxer = {
+ .name = "uncodedframecrc",
+ .long_name = NULL_IF_CONFIG_SMALL("uncoded framecrc testing"),
+ .audio_codec = AV_CODEC_ID_PCM_S16LE,
+ .video_codec = AV_CODEC_ID_RAWVIDEO,
+ .write_header = ff_framehash_write_header,
+ .write_packet = write_packet,
+ .write_uncoded_frame = write_frame,
+ .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT |
+ AVFMT_TS_NEGATIVE,
+};
diff --git a/libavformat/unix.c b/libavformat/unix.c
index 5c6f576487..397706528a 100644
--- a/libavformat/unix.c
+++ b/libavformat/unix.c
@@ -2,20 +2,20 @@
* Unix socket protocol
* Copyright (c) 2013 Luca Barbato
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -26,12 +26,11 @@
*
*/
-#include <sys/un.h>
-
#include "libavutil/avstring.h"
#include "libavutil/opt.h"
#include "os_support.h"
#include "network.h"
+#include <sys/un.h>
#include "url.h"
typedef struct UnixContext {
diff --git a/libavformat/url-test.c b/libavformat/url-test.c
index 503b36e572..a1da82e493 100644
--- a/libavformat/url-test.c
+++ b/libavformat/url-test.c
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2012 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/url.c b/libavformat/url.c
index 92cd5f183a..acfb0cf2f0 100644
--- a/libavformat/url.c
+++ b/libavformat/url.c
@@ -2,20 +2,20 @@
* URL utility functions
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/url.h b/libavformat/url.h
index ca252aa7c2..d0035f3617 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
*/
@@ -46,6 +46,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 {
@@ -98,7 +99,7 @@ typedef struct URLProtocol {
* is to be opened
* @param int_cb interrupt callback to use for the URLContext, may be
* NULL
- * @return 0 in case of success, a negative value corresponding to an
+ * @return >= 0 in case of success, a negative value corresponding to an
* AVERROR code in case of failure
*/
int ffurl_alloc(URLContext **puc, const char *filename, int flags,
@@ -127,7 +128,7 @@ int ffurl_connect(URLContext *uc, AVDictionary **options);
* @param options A dictionary filled with protocol-private options. On return
* this parameter will be destroyed and replaced with a dict containing options
* that were not found. May be NULL.
- * @return 0 in case of success, a negative value corresponding to an
+ * @return >= 0 in case of success, a negative value corresponding to an
* AVERROR code in case of failure
*/
int ffurl_open(URLContext **puc, const char *filename, int flags,
@@ -179,11 +180,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);
/**
@@ -267,7 +269,7 @@ int ff_url_join(char *str, int size, const char *proto,
const char *authorization, const char *hostname,
int port, const char *fmt, ...) av_printf_format(7, 8);
-/*
+/**
* Convert a relative url into an absolute url, given a base url.
*
* @param buf the buffer where output absolute url is written
diff --git a/libavformat/urldecode.c b/libavformat/urldecode.c
index 49af9ba1e0..283d912671 100644
--- a/libavformat/urldecode.c
+++ b/libavformat/urldecode.c
@@ -9,20 +9,20 @@
* based on http://www.icosaedro.it/apache/urldecode.c
* from Umberto Salsi (salsi@icosaedro.it)
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/urldecode.h b/libavformat/urldecode.h
index b43f319c9e..cb81ebc6f7 100644
--- a/libavformat/urldecode.h
+++ b/libavformat/urldecode.h
@@ -1,18 +1,18 @@
/*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/utils.c b/libavformat/utils.c
index 6248c1e5cb..917f8ceaf0 100644
--- a/libavformat/utils.c
+++ b/libavformat/utils.c
@@ -1,26 +1,24 @@
/*
- * 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
*/
-#undef NDEBUG
-#include <assert.h>
#include <stdarg.h>
#include <stdint.h>
@@ -35,12 +33,15 @@
#include "libavutil/parseutils.h"
#include "libavutil/pixdesc.h"
#include "libavutil/time.h"
+#include "libavutil/timestamp.h"
#include "libavcodec/bytestream.h"
#include "libavcodec/internal.h"
+#include "libavcodec/raw.h"
#include "audiointerleave.h"
#include "avformat.h"
+#include "avio_internal.h"
#include "id3v2.h"
#include "internal.h"
#include "metadata.h"
@@ -50,38 +51,156 @@
#include "riff.h"
#include "url.h"
+#include "libavutil/ffversion.h"
+const char av_format_ffversion[] = "FFmpeg version " FFMPEG_VERSION;
+
/**
* @file
- * various utility functions for use within Libav
+ * various utility functions for use within FFmpeg
*/
unsigned avformat_version(void)
{
+ av_assert0(LIBAVFORMAT_VERSION_MICRO >= 100);
return LIBAVFORMAT_VERSION_INT;
}
const char *avformat_configuration(void)
{
- return LIBAV_CONFIGURATION;
+ return FFMPEG_CONFIGURATION;
}
const char *avformat_license(void)
{
#define LICENSE_PREFIX "libavformat license: "
- return LICENSE_PREFIX LIBAV_LICENSE + sizeof(LICENSE_PREFIX) - 1;
+ return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1;
+}
+
+#define RELATIVE_TS_BASE (INT64_MAX - (1LL<<48))
+
+static int is_relative(int64_t ts) {
+ return ts > (RELATIVE_TS_BASE - (1LL<<48));
+}
+
+/**
+ * Wrap a given time stamp, if there is an indication for an overflow
+ *
+ * @param st stream
+ * @param timestamp the time stamp to wrap
+ * @return resulting time stamp
+ */
+static int64_t wrap_timestamp(AVStream *st, int64_t timestamp)
+{
+ if (st->pts_wrap_behavior != AV_PTS_WRAP_IGNORE &&
+ st->pts_wrap_reference != AV_NOPTS_VALUE && timestamp != AV_NOPTS_VALUE) {
+ if (st->pts_wrap_behavior == AV_PTS_WRAP_ADD_OFFSET &&
+ timestamp < st->pts_wrap_reference)
+ return timestamp + (1ULL << st->pts_wrap_bits);
+ else if (st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET &&
+ timestamp >= st->pts_wrap_reference)
+ return timestamp - (1ULL << st->pts_wrap_bits);
+ }
+ return timestamp;
+}
+
+MAKE_ACCESSORS(AVStream, stream, AVRational, r_frame_rate)
+MAKE_ACCESSORS(AVStream, stream, char *, recommended_encoder_configuration)
+MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, video_codec)
+MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, audio_codec)
+MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, subtitle_codec)
+MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, data_codec)
+MAKE_ACCESSORS(AVFormatContext, format, int, metadata_header_padding)
+MAKE_ACCESSORS(AVFormatContext, format, void *, opaque)
+MAKE_ACCESSORS(AVFormatContext, format, av_format_control_message, control_message_cb)
+
+int64_t av_stream_get_end_pts(const AVStream *st)
+{
+ return st->pts.val;
+}
+
+struct AVCodecParserContext *av_stream_get_parser(const AVStream *st)
+{
+ return st->parser;
+}
+
+void av_format_inject_global_side_data(AVFormatContext *s)
+{
+ int i;
+ s->internal->inject_global_side_data = 1;
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ st->inject_global_side_data = 1;
+ }
+}
+
+int ff_copy_whitelists(AVFormatContext *dst, AVFormatContext *src)
+{
+ av_assert0(!dst->codec_whitelist && !dst->format_whitelist);
+ dst-> codec_whitelist = av_strdup(src->codec_whitelist);
+ dst->format_whitelist = av_strdup(src->format_whitelist);
+ if ( (src-> codec_whitelist && !dst-> codec_whitelist)
+ || (src->format_whitelist && !dst->format_whitelist)) {
+ av_log(dst, AV_LOG_ERROR, "Failed to duplicate whitelist\n");
+ return AVERROR(ENOMEM);
+ }
+ return 0;
+}
+
+static const AVCodec *find_decoder(AVFormatContext *s, AVStream *st, enum AVCodecID codec_id)
+{
+ if (st->codec->codec)
+ return st->codec->codec;
+
+ switch (st->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ if (s->video_codec) return s->video_codec;
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ if (s->audio_codec) return s->audio_codec;
+ break;
+ case AVMEDIA_TYPE_SUBTITLE:
+ if (s->subtitle_codec) return s->subtitle_codec;
+ break;
+ }
+
+ return avcodec_find_decoder(codec_id);
+}
+
+int av_format_get_probe_score(const AVFormatContext *s)
+{
+ return s->probe_score;
}
/* an arbitrarily chosen "sane" max packet size -- 50M */
#define SANE_CHUNK_SIZE (50000000)
+int ffio_limit(AVIOContext *s, int size)
+{
+ if (s->maxsize>= 0) {
+ int64_t remaining= s->maxsize - avio_tell(s);
+ if (remaining < size) {
+ int64_t newsize = avio_size(s);
+ if (!s->maxsize || s->maxsize<newsize)
+ s->maxsize = newsize - !newsize;
+ remaining= s->maxsize - avio_tell(s);
+ remaining= FFMAX(remaining, 0);
+ }
+
+ if (s->maxsize>= 0 && remaining+1 < size) {
+ av_log(NULL, remaining ? AV_LOG_ERROR : AV_LOG_DEBUG, "Truncating packet of size %d to %"PRId64"\n", size, remaining+1);
+ size = remaining+1;
+ }
+ }
+ return size;
+}
+
/* Read the data in sane-sized chunks and append to pkt.
* Return the number of bytes read or an error. */
static int append_packet_chunked(AVIOContext *s, AVPacket *pkt, int size)
{
- int64_t chunk_size = size;
int64_t orig_pos = pkt->pos; // av_grow_packet might reset pos
int orig_size = pkt->size;
- int ret = 0;
+ int ret;
do {
int prev_size = pkt->size;
@@ -89,11 +208,13 @@ static int append_packet_chunked(AVIOContext *s, AVPacket *pkt, int size)
/* When the caller requests a lot of data, limit it to the amount
* left in file or SANE_CHUNK_SIZE when it is not known. */
- if (size > SANE_CHUNK_SIZE) {
- int64_t filesize = avio_size(s) - avio_tell(s);
- chunk_size = FFMAX(filesize, SANE_CHUNK_SIZE);
+ read_size = size;
+ if (read_size > SANE_CHUNK_SIZE/10) {
+ read_size = ffio_limit(s, read_size);
+ // If filesize/maxsize is unknown, limit to SANE_CHUNK_SIZE
+ if (s->maxsize < 0)
+ read_size = FFMIN(read_size, SANE_CHUNK_SIZE);
}
- read_size = FFMIN(size, chunk_size);
ret = av_grow_packet(pkt, read_size);
if (ret < 0)
@@ -107,6 +228,8 @@ static int append_packet_chunked(AVIOContext *s, AVPacket *pkt, int size)
size -= read_size;
} while (size > 0);
+ if (size > 0)
+ pkt->flags |= AV_PKT_FLAG_CORRUPT;
pkt->pos = orig_pos;
if (!pkt->size)
@@ -139,7 +262,7 @@ int av_filename_number_test(const char *filename)
}
static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st,
- AVProbeData *pd, int score)
+ AVProbeData *pd)
{
static const struct {
const char *name;
@@ -149,17 +272,20 @@ static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st,
{ "aac", AV_CODEC_ID_AAC, AVMEDIA_TYPE_AUDIO },
{ "ac3", AV_CODEC_ID_AC3, AVMEDIA_TYPE_AUDIO },
{ "dts", AV_CODEC_ID_DTS, AVMEDIA_TYPE_AUDIO },
+ { "dvbsub", AV_CODEC_ID_DVB_SUBTITLE,AVMEDIA_TYPE_SUBTITLE },
{ "eac3", AV_CODEC_ID_EAC3, AVMEDIA_TYPE_AUDIO },
{ "h264", AV_CODEC_ID_H264, AVMEDIA_TYPE_VIDEO },
- { "latm", AV_CODEC_ID_AAC_LATM, AVMEDIA_TYPE_AUDIO },
+ { "hevc", AV_CODEC_ID_HEVC, AVMEDIA_TYPE_VIDEO },
+ { "loas", AV_CODEC_ID_AAC_LATM, AVMEDIA_TYPE_AUDIO },
{ "m4v", AV_CODEC_ID_MPEG4, AVMEDIA_TYPE_VIDEO },
{ "mp3", AV_CODEC_ID_MP3, AVMEDIA_TYPE_AUDIO },
{ "mpegvideo", AV_CODEC_ID_MPEG2VIDEO, AVMEDIA_TYPE_VIDEO },
{ 0 }
};
- AVInputFormat *fmt = av_probe_input_format2(pd, 1, &score);
+ int score;
+ AVInputFormat *fmt = av_probe_input_format3(pd, 1, &score);
- if (fmt) {
+ if (fmt && st->request_probe <= score) {
int i;
av_log(s, AV_LOG_DEBUG,
"Probe with size=%d, packets=%d detected %s with score=%d\n",
@@ -169,44 +295,66 @@ static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st,
if (!strcmp(fmt->name, fmt_id_type[i].name)) {
st->codec->codec_id = fmt_id_type[i].id;
st->codec->codec_type = fmt_id_type[i].type;
- break;
+ return score;
}
}
}
- return !!fmt;
+ return 0;
}
/************************************************************/
/* input media file */
+int av_demuxer_open(AVFormatContext *ic) {
+ int err;
+
+ if (ic->format_whitelist && av_match_list(ic->iformat->name, ic->format_whitelist, ',') <= 0) {
+ av_log(ic, AV_LOG_ERROR, "Format not on whitelist\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (ic->iformat->read_header) {
+ err = ic->iformat->read_header(ic);
+ if (err < 0)
+ return err;
+ }
+
+ if (ic->pb && !ic->internal->data_offset)
+ ic->internal->data_offset = avio_tell(ic->pb);
+
+ return 0;
+}
+
/* Open input file and probe the format if necessary. */
static int init_input(AVFormatContext *s, const char *filename,
AVDictionary **options)
{
int ret;
AVProbeData pd = { filename, NULL, 0 };
+ int score = AVPROBE_SCORE_RETRY;
if (s->pb) {
s->flags |= AVFMT_FLAG_CUSTOM_IO;
if (!s->iformat)
- return av_probe_input_buffer(s->pb, &s->iformat, filename,
- s, 0, s->probesize);
+ return av_probe_input_buffer2(s->pb, &s->iformat, filename,
+ s, 0, s->format_probesize);
else if (s->iformat->flags & AVFMT_NOFILE)
- return AVERROR(EINVAL);
+ av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and "
+ "will be ignored with AVFMT_NOFILE format.\n");
return 0;
}
if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
- (!s->iformat && (s->iformat = av_probe_input_format(&pd, 0))))
- return 0;
+ (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
+ return score;
- if ((ret = avio_open2(&s->pb, filename, AVIO_FLAG_READ,
+ if ((ret = avio_open2(&s->pb, filename, AVIO_FLAG_READ | s->avio_flags,
&s->interrupt_callback, options)) < 0)
return ret;
if (s->iformat)
return 0;
- return av_probe_input_buffer(s->pb, &s->iformat, filename,
- s, 0, s->probesize);
+ return av_probe_input_buffer2(s->pb, &s->iformat, filename,
+ s, 0, s->format_probesize);
}
static AVPacket *add_to_pktbuf(AVPacketList **packet_buffer, AVPacket *pkt,
@@ -227,13 +375,19 @@ static AVPacket *add_to_pktbuf(AVPacketList **packet_buffer, AVPacket *pkt,
return &pktl->pkt;
}
-static int queue_attached_pictures(AVFormatContext *s)
+int avformat_queue_attached_pictures(AVFormatContext *s)
{
int i;
for (i = 0; i < s->nb_streams; i++)
if (s->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC &&
s->streams[i]->discard < AVDISCARD_ALL) {
AVPacket copy = s->streams[i]->attached_pic;
+ if (copy.size <= 0) {
+ av_log(s, AV_LOG_WARNING,
+ "Attached picture on stream %d has invalid size, "
+ "ignoring\n", i);
+ continue;
+ }
copy.buf = av_buffer_ref(copy.buf);
if (!copy.buf)
return AVERROR(ENOMEM);
@@ -254,6 +408,10 @@ int avformat_open_input(AVFormatContext **ps, const char *filename,
if (!s && !(s = avformat_alloc_context()))
return AVERROR(ENOMEM);
+ if (!s->av_class) {
+ av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
+ return AVERROR(EINVAL);
+ }
if (fmt)
s->iformat = fmt;
@@ -265,6 +423,15 @@ int avformat_open_input(AVFormatContext **ps, const char *filename,
if ((ret = init_input(s, filename, &tmp)) < 0)
goto fail;
+ s->probe_score = ret;
+
+ if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
+ av_log(s, AV_LOG_ERROR, "Format not on whitelist\n");
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+
+ avio_skip(s->pb, s->skip_initial_bytes);
/* Check filename in case an image number is expected. */
if (s->iformat->flags & AVFMT_NEEDNUMBER) {
@@ -293,21 +460,26 @@ int avformat_open_input(AVFormatContext **ps, const char *filename,
/* e.g. AVFMT_NOFILE formats will not have a AVIOContext */
if (s->pb)
- ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);
+ ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, 0);
- if (s->iformat->read_header)
+ if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
if ((ret = s->iformat->read_header(s)) < 0)
goto fail;
- if (id3v2_extra_meta &&
- (ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0)
- goto fail;
+ if (id3v2_extra_meta) {
+ if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||
+ !strcmp(s->iformat->name, "tta")) {
+ if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0)
+ goto fail;
+ } else
+ av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n");
+ }
ff_id3v2_free_extra_meta(&id3v2_extra_meta);
- if ((ret = queue_attached_pictures(s)) < 0)
+ if ((ret = avformat_queue_attached_pictures(s)) < 0)
goto fail;
- if (s->pb && !s->internal->data_offset)
+ if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->internal->data_offset)
s->internal->data_offset = avio_tell(s->pb);
s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
@@ -323,7 +495,7 @@ fail:
ff_id3v2_free_extra_meta(&id3v2_extra_meta);
av_dict_free(&tmp);
if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
- avio_close(s->pb);
+ avio_closep(&s->pb);
avformat_free_context(s);
*ps = NULL;
return ret;
@@ -331,44 +503,140 @@ fail:
/*******************************************************/
+static void force_codec_ids(AVFormatContext *s, AVStream *st)
+{
+ switch (st->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ if (s->video_codec_id)
+ st->codec->codec_id = s->video_codec_id;
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ if (s->audio_codec_id)
+ st->codec->codec_id = s->audio_codec_id;
+ break;
+ case AVMEDIA_TYPE_SUBTITLE:
+ if (s->subtitle_codec_id)
+ st->codec->codec_id = s->subtitle_codec_id;
+ break;
+ }
+}
+
static int probe_codec(AVFormatContext *s, AVStream *st, const AVPacket *pkt)
{
- if (st->codec->codec_id == AV_CODEC_ID_PROBE) {
+ if (st->request_probe>0) {
AVProbeData *pd = &st->probe_data;
- av_log(s, AV_LOG_DEBUG, "probing stream %d\n", st->index);
+ int end;
+ av_log(s, AV_LOG_DEBUG, "probing stream %d pp:%d\n", st->index, st->probe_packets);
--st->probe_packets;
if (pkt) {
- int err;
- if ((err = av_reallocp(&pd->buf, pd->buf_size + pkt->size +
- AVPROBE_PADDING_SIZE)) < 0)
- return err;
+ uint8_t *new_buf = av_realloc(pd->buf, pd->buf_size+pkt->size+AVPROBE_PADDING_SIZE);
+ if (!new_buf) {
+ av_log(s, AV_LOG_WARNING,
+ "Failed to reallocate probe buffer for stream %d\n",
+ st->index);
+ goto no_packet;
+ }
+ pd->buf = new_buf;
memcpy(pd->buf + pd->buf_size, pkt->data, pkt->size);
pd->buf_size += pkt->size;
memset(pd->buf + pd->buf_size, 0, AVPROBE_PADDING_SIZE);
} else {
+no_packet:
st->probe_packets = 0;
if (!pd->buf_size) {
- av_log(s, AV_LOG_ERROR,
+ av_log(s, AV_LOG_WARNING,
"nothing to probe for stream %d\n", st->index);
- return 0;
}
}
- if (!st->probe_packets ||
- av_log2(pd->buf_size) != av_log2(pd->buf_size - pkt->size)) {
- set_codec_from_probe_data(s, st, pd, st->probe_packets > 0
- ? AVPROBE_SCORE_MAX / 4 : 0);
- if (st->codec->codec_id != AV_CODEC_ID_PROBE) {
+ end= s->internal->raw_packet_buffer_remaining_size <= 0
+ || st->probe_packets<= 0;
+
+ if (end || av_log2(pd->buf_size) != av_log2(pd->buf_size - pkt->size)) {
+ int score = set_codec_from_probe_data(s, st, pd);
+ if ( (st->codec->codec_id != AV_CODEC_ID_NONE && score > AVPROBE_SCORE_STREAM_RETRY)
+ || end) {
pd->buf_size = 0;
av_freep(&pd->buf);
- av_log(s, AV_LOG_DEBUG, "probed stream %d\n", st->index);
+ st->request_probe = -1;
+ if (st->codec->codec_id != AV_CODEC_ID_NONE) {
+ av_log(s, AV_LOG_DEBUG, "probed stream %d\n", st->index);
+ } else
+ av_log(s, AV_LOG_WARNING, "probed stream %d failed\n", st->index);
}
+ force_codec_ids(s, st);
}
}
return 0;
}
+static int update_wrap_reference(AVFormatContext *s, AVStream *st, int stream_index, AVPacket *pkt)
+{
+ int64_t ref = pkt->dts;
+ int i, pts_wrap_behavior;
+ int64_t pts_wrap_reference;
+ AVProgram *first_program;
+
+ if (ref == AV_NOPTS_VALUE)
+ ref = pkt->pts;
+ if (st->pts_wrap_reference != AV_NOPTS_VALUE || st->pts_wrap_bits >= 63 || ref == AV_NOPTS_VALUE || !s->correct_ts_overflow)
+ return 0;
+ ref &= (1LL << st->pts_wrap_bits)-1;
+
+ // reference time stamp should be 60 s before first time stamp
+ pts_wrap_reference = ref - av_rescale(60, st->time_base.den, st->time_base.num);
+ // if first time stamp is not more than 1/8 and 60s before the wrap point, subtract rather than add wrap offset
+ pts_wrap_behavior = (ref < (1LL << st->pts_wrap_bits) - (1LL << st->pts_wrap_bits-3)) ||
+ (ref < (1LL << st->pts_wrap_bits) - av_rescale(60, st->time_base.den, st->time_base.num)) ?
+ AV_PTS_WRAP_ADD_OFFSET : AV_PTS_WRAP_SUB_OFFSET;
+
+ first_program = av_find_program_from_stream(s, NULL, stream_index);
+
+ if (!first_program) {
+ int default_stream_index = av_find_default_stream_index(s);
+ if (s->streams[default_stream_index]->pts_wrap_reference == AV_NOPTS_VALUE) {
+ for (i = 0; i < s->nb_streams; i++) {
+ if (av_find_program_from_stream(s, NULL, i))
+ continue;
+ s->streams[i]->pts_wrap_reference = pts_wrap_reference;
+ s->streams[i]->pts_wrap_behavior = pts_wrap_behavior;
+ }
+ }
+ else {
+ st->pts_wrap_reference = s->streams[default_stream_index]->pts_wrap_reference;
+ st->pts_wrap_behavior = s->streams[default_stream_index]->pts_wrap_behavior;
+ }
+ }
+ else {
+ AVProgram *program = first_program;
+ while (program) {
+ if (program->pts_wrap_reference != AV_NOPTS_VALUE) {
+ pts_wrap_reference = program->pts_wrap_reference;
+ pts_wrap_behavior = program->pts_wrap_behavior;
+ break;
+ }
+ program = av_find_program_from_stream(s, program, stream_index);
+ }
+
+ // update every program with differing pts_wrap_reference
+ program = first_program;
+ while (program) {
+ if (program->pts_wrap_reference != pts_wrap_reference) {
+ for (i = 0; i<program->nb_stream_indexes; i++) {
+ s->streams[program->stream_index[i]]->pts_wrap_reference = pts_wrap_reference;
+ s->streams[program->stream_index[i]]->pts_wrap_behavior = pts_wrap_behavior;
+ }
+
+ program->pts_wrap_reference = pts_wrap_reference;
+ program->pts_wrap_behavior = pts_wrap_behavior;
+ }
+ program = av_find_program_from_stream(s, program, stream_index);
+ }
+ }
+ return 1;
+}
+
int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
{
int ret, i, err;
@@ -380,16 +648,10 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
if (pktl) {
*pkt = pktl->pkt;
st = s->streams[pkt->stream_index];
- if (st->codec->codec_id != AV_CODEC_ID_PROBE ||
- !st->probe_packets ||
- s->internal->raw_packet_buffer_remaining_size < pkt->size) {
- AVProbeData *pd;
- if (st->probe_packets)
- if ((err = probe_codec(s, st, NULL)) < 0)
- return err;
- pd = &st->probe_data;
- av_freep(&pd->buf);
- pd->buf_size = 0;
+ if (s->internal->raw_packet_buffer_remaining_size <= 0)
+ if ((err = probe_codec(s, st, NULL)) < 0)
+ return err;
+ if (st->request_probe <= 0) {
s->internal->raw_packet_buffer = pktl->next;
s->internal->raw_packet_buffer_remaining_size += pkt->size;
av_free(pktl);
@@ -409,6 +671,7 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
if (st->probe_packets)
if ((err = probe_codec(s, st, NULL)) < 0)
return err;
+ av_assert0(st->request_probe <= 0);
}
continue;
}
@@ -422,25 +685,33 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
continue;
}
+ if (pkt->stream_index >= (unsigned)s->nb_streams) {
+ av_log(s, AV_LOG_ERROR, "Invalid stream index %d\n", pkt->stream_index);
+ continue;
+ }
+
st = s->streams[pkt->stream_index];
- switch (st->codec->codec_type) {
- case AVMEDIA_TYPE_VIDEO:
- if (s->video_codec_id)
- st->codec->codec_id = s->video_codec_id;
- break;
- case AVMEDIA_TYPE_AUDIO:
- if (s->audio_codec_id)
- st->codec->codec_id = s->audio_codec_id;
- break;
- case AVMEDIA_TYPE_SUBTITLE:
- if (s->subtitle_codec_id)
- st->codec->codec_id = s->subtitle_codec_id;
- break;
+ if (update_wrap_reference(s, st, pkt->stream_index, pkt) && st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) {
+ // correct first time stamps to negative values
+ if (!is_relative(st->first_dts))
+ st->first_dts = wrap_timestamp(st, st->first_dts);
+ if (!is_relative(st->start_time))
+ st->start_time = wrap_timestamp(st, st->start_time);
+ if (!is_relative(st->cur_dts))
+ st->cur_dts = wrap_timestamp(st, st->cur_dts);
}
- if (!pktl && (st->codec->codec_id != AV_CODEC_ID_PROBE ||
- !st->probe_packets))
+ pkt->dts = wrap_timestamp(st, pkt->dts);
+ pkt->pts = wrap_timestamp(st, pkt->pts);
+
+ force_codec_ids(s, st);
+
+ /* TODO: audio: time filter; video: frame reordering (pts != dts) */
+ if (s->use_wallclock_as_timestamps)
+ pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base);
+
+ if (!pktl && st->request_probe <= 0)
return ret;
add_to_pktbuf(&s->internal->raw_packet_buffer, pkt,
@@ -452,8 +723,20 @@ int ff_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;
+}
+
/**
* Return the frame duration in seconds. Return 0 if not available.
*/
@@ -461,27 +744,32 @@ void ff_compute_frame_duration(AVFormatContext *s, int *pnum, int *pden, AVStrea
AVCodecParserContext *pc, AVPacket *pkt)
{
AVRational codec_framerate = s->iformat ? st->codec->framerate :
- av_inv_q(st->codec->time_base);
+ av_mul_q(av_inv_q(st->codec->time_base), (AVRational){1, st->codec->ticks_per_frame});
int frame_size;
*pnum = 0;
*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 && s->iformat) {
+ *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;
} else if (codec_framerate.den * 1000LL > codec_framerate.num) {
- *pnum = codec_framerate.den;
- *pden = codec_framerate.num;
+ av_assert0(st->codec->ticks_per_frame);
+ av_reduce(pnum, pden,
+ codec_framerate.den,
+ codec_framerate.num * (int64_t)st->codec->ticks_per_frame,
+ INT_MAX);
+
if (pc && pc->repeat_pict) {
- if (*pnum > INT_MAX / (1 + pc->repeat_pict))
- *pden /= 1 + pc->repeat_pict;
- else
- *pnum *= 1 + pc->repeat_pict;
+ av_assert0(s->iformat); // this may be wrong for interlaced encoding but its not used for that case
+ av_reduce(pnum, pden,
+ (*pnum) * (1LL + pc->repeat_pict),
+ (*pden),
+ INT_MAX);
}
/* If this codec can be interlaced or progressive then we need
* a parser to compute duration of a packet. Thus if we have
@@ -502,43 +790,138 @@ void ff_compute_frame_duration(AVFormatContext *s, int *pnum, int *pden, AVStrea
}
}
-static int is_intra_only(enum AVCodecID id)
+static int is_intra_only(AVCodecContext *enc) {
+ const AVCodecDescriptor *desc;
+
+ if (enc->codec_type != AVMEDIA_TYPE_VIDEO)
+ return 1;
+
+ desc = av_codec_get_codec_descriptor(enc);
+ if (!desc) {
+ desc = avcodec_descriptor_get(enc->codec_id);
+ av_codec_set_codec_descriptor(enc, desc);
+ }
+ if (desc)
+ return !!(desc->props & AV_CODEC_PROP_INTRA_ONLY);
+ return 0;
+}
+
+static int has_decode_delay_been_guessed(AVStream *st)
{
- const AVCodecDescriptor *d = avcodec_descriptor_get(id);
- if (!d)
- return 0;
- if (d->type == AVMEDIA_TYPE_VIDEO && !(d->props & AV_CODEC_PROP_INTRA_ONLY))
- return 0;
- return 1;
+ if (st->codec->codec_id != AV_CODEC_ID_H264) return 1;
+ if (!st->info) // if we have left find_stream_info then nb_decoded_frames won't increase anymore for stream copy
+ return 1;
+#if CONFIG_H264_DECODER
+ if (st->codec->has_b_frames &&
+ avpriv_h264_has_num_reorder_frames(st->codec) == st->codec->has_b_frames)
+ return 1;
+#endif
+ if (st->codec->has_b_frames<3)
+ return st->nb_decoded_frames >= 7;
+ else if (st->codec->has_b_frames<4)
+ return st->nb_decoded_frames >= 18;
+ else
+ return st->nb_decoded_frames >= 20;
+}
+
+static AVPacketList *get_next_pkt(AVFormatContext *s, AVStream *st, AVPacketList *pktl)
+{
+ if (pktl->next)
+ return pktl->next;
+ if (pktl == s->internal->packet_buffer_end)
+ return s->internal->parse_queue;
+ return NULL;
+}
+
+static int64_t select_from_pts_buffer(AVStream *st, int64_t *pts_buffer, int64_t dts) {
+ int onein_oneout = st->codec->codec_id != AV_CODEC_ID_H264 &&
+ st->codec->codec_id != AV_CODEC_ID_HEVC;
+
+ if(!onein_oneout) {
+ int delay = st->codec->has_b_frames;
+ int i;
+
+ if (dts == AV_NOPTS_VALUE) {
+ int64_t best_score = INT64_MAX;
+ for (i = 0; i<delay; i++) {
+ if (st->pts_reorder_error_count[i]) {
+ int64_t score = st->pts_reorder_error[i] / st->pts_reorder_error_count[i];
+ if (score < best_score) {
+ best_score = score;
+ dts = pts_buffer[i];
+ }
+ }
+ }
+ } else {
+ for (i = 0; i<delay; i++) {
+ if (pts_buffer[i] != AV_NOPTS_VALUE) {
+ int64_t diff = FFABS(pts_buffer[i] - dts)
+ + (uint64_t)st->pts_reorder_error[i];
+ diff = FFMAX(diff, st->pts_reorder_error[i]);
+ st->pts_reorder_error[i] = diff;
+ st->pts_reorder_error_count[i]++;
+ if (st->pts_reorder_error_count[i] > 250) {
+ st->pts_reorder_error[i] >>= 1;
+ st->pts_reorder_error_count[i] >>= 1;
+ }
+ }
+ }
+ }
+ }
+
+ if (dts == AV_NOPTS_VALUE)
+ dts = pts_buffer[0];
+
+ return dts;
}
static void update_initial_timestamps(AVFormatContext *s, int stream_index,
- int64_t dts, int64_t pts)
+ int64_t dts, int64_t pts, AVPacket *pkt)
{
AVStream *st = s->streams[stream_index];
- AVPacketList *pktl = s->internal->packet_buffer;
+ AVPacketList *pktl = s->internal->packet_buffer ? s->internal->packet_buffer : s->internal->parse_queue;
+ int64_t pts_buffer[MAX_REORDER_DELAY+1];
+ int64_t shift;
+ int i, delay;
if (st->first_dts != AV_NOPTS_VALUE ||
dts == AV_NOPTS_VALUE ||
- st->cur_dts == AV_NOPTS_VALUE)
+ st->cur_dts == AV_NOPTS_VALUE ||
+ is_relative(dts))
return;
- st->first_dts = dts - st->cur_dts;
+ delay = st->codec->has_b_frames;
+ st->first_dts = dts - (st->cur_dts - RELATIVE_TS_BASE);
st->cur_dts = dts;
+ shift = st->first_dts - RELATIVE_TS_BASE;
+
+ for (i = 0; i<MAX_REORDER_DELAY+1; i++)
+ pts_buffer[i] = AV_NOPTS_VALUE;
+
+ if (is_relative(pts))
+ pts += shift;
- for (; pktl; pktl = pktl->next) {
+ for (; pktl; pktl = get_next_pkt(s, st, pktl)) {
if (pktl->pkt.stream_index != stream_index)
continue;
- // FIXME: think more about this check
- if (pktl->pkt.pts != AV_NOPTS_VALUE && pktl->pkt.pts == pktl->pkt.dts)
- pktl->pkt.pts += st->first_dts;
+ if (is_relative(pktl->pkt.pts))
+ pktl->pkt.pts += shift;
- if (pktl->pkt.dts != AV_NOPTS_VALUE)
- pktl->pkt.dts += st->first_dts;
+ if (is_relative(pktl->pkt.dts))
+ pktl->pkt.dts += shift;
if (st->start_time == AV_NOPTS_VALUE && pktl->pkt.pts != AV_NOPTS_VALUE)
st->start_time = pktl->pkt.pts;
+
+ if (pktl->pkt.pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY && has_decode_delay_been_guessed(st)) {
+ pts_buffer[0] = pktl->pkt.pts;
+ for (i = 0; i<delay && pts_buffer[i] > pts_buffer[i + 1]; i++)
+ FFSWAP(int64_t, pts_buffer[i], pts_buffer[i + 1]);
+
+ pktl->pkt.dts = select_from_pts_buffer(st, pts_buffer, pktl->pkt.dts);
+ }
}
+
if (st->start_time == AV_NOPTS_VALUE)
st->start_time = pts;
}
@@ -546,12 +929,15 @@ static void update_initial_timestamps(AVFormatContext *s, int stream_index,
static void update_initial_durations(AVFormatContext *s, AVStream *st,
int stream_index, int duration)
{
- AVPacketList *pktl = s->internal->packet_buffer;
- int64_t cur_dts = 0;
+ AVPacketList *pktl = s->internal->packet_buffer ? s->internal->packet_buffer : s->internal->parse_queue;
+ int64_t cur_dts = RELATIVE_TS_BASE;
if (st->first_dts != AV_NOPTS_VALUE) {
+ if (st->update_initial_durations_done)
+ return;
+ st->update_initial_durations_done = 1;
cur_dts = st->first_dts;
- for (; pktl; pktl = pktl->next) {
+ for (; pktl; pktl = get_next_pkt(s, st, pktl)) {
if (pktl->pkt.stream_index == stream_index) {
if (pktl->pkt.pts != pktl->pkt.dts ||
pktl->pkt.dts != AV_NOPTS_VALUE ||
@@ -560,42 +946,82 @@ static void update_initial_durations(AVFormatContext *s, AVStream *st,
cur_dts -= duration;
}
}
- pktl = s->internal->packet_buffer;
+ if (pktl && pktl->pkt.dts != st->first_dts) {
+ av_log(s, AV_LOG_DEBUG, "first_dts %s not matching first dts %s (pts %s, duration %d) in the queue\n",
+ av_ts2str(st->first_dts), av_ts2str(pktl->pkt.dts), av_ts2str(pktl->pkt.pts), pktl->pkt.duration);
+ return;
+ }
+ if (!pktl) {
+ av_log(s, AV_LOG_DEBUG, "first_dts %s but no packet with dts in the queue\n", av_ts2str(st->first_dts));
+ return;
+ }
+ pktl = s->internal->packet_buffer ? s->internal->packet_buffer : s->internal->parse_queue;
st->first_dts = cur_dts;
- } else if (st->cur_dts)
+ } else if (st->cur_dts != RELATIVE_TS_BASE)
return;
- for (; pktl; pktl = pktl->next) {
+ for (; pktl; pktl = get_next_pkt(s, st, pktl)) {
if (pktl->pkt.stream_index != stream_index)
continue;
if (pktl->pkt.pts == pktl->pkt.dts &&
- pktl->pkt.dts == AV_NOPTS_VALUE &&
+ (pktl->pkt.dts == AV_NOPTS_VALUE || pktl->pkt.dts == st->first_dts) &&
!pktl->pkt.duration) {
pktl->pkt.dts = cur_dts;
if (!st->codec->has_b_frames)
pktl->pkt.pts = cur_dts;
- cur_dts += duration;
- if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO)
+// if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO)
pktl->pkt.duration = duration;
} else
break;
+ cur_dts = pktl->pkt.dts + pktl->pkt.duration;
}
- if (st->first_dts == AV_NOPTS_VALUE)
+ if (!pktl)
st->cur_dts = cur_dts;
}
static void compute_pkt_fields(AVFormatContext *s, AVStream *st,
- AVCodecParserContext *pc, AVPacket *pkt)
+ AVCodecParserContext *pc, AVPacket *pkt,
+ int64_t next_dts, int64_t next_pts)
{
int num, den, presentation_delayed, delay, i;
int64_t offset;
+ AVRational duration;
+ int onein_oneout = st->codec->codec_id != AV_CODEC_ID_H264 &&
+ st->codec->codec_id != AV_CODEC_ID_HEVC;
if (s->flags & AVFMT_FLAG_NOFILLIN)
return;
+ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && pkt->dts != AV_NOPTS_VALUE) {
+ if (pkt->dts == pkt->pts && st->last_dts_for_order_check != AV_NOPTS_VALUE) {
+ if (st->last_dts_for_order_check <= pkt->dts) {
+ st->dts_ordered++;
+ } else {
+ av_log(s, st->dts_misordered ? AV_LOG_DEBUG : AV_LOG_WARNING,
+ "DTS %"PRIi64" < %"PRIi64" out of order\n",
+ pkt->dts,
+ st->last_dts_for_order_check);
+ st->dts_misordered++;
+ }
+ if (st->dts_ordered + st->dts_misordered > 250) {
+ st->dts_ordered >>= 1;
+ st->dts_misordered >>= 1;
+ }
+ }
+
+ st->last_dts_for_order_check = pkt->dts;
+ if (st->dts_ordered < 8*st->dts_misordered && pkt->dts == pkt->pts)
+ pkt->dts = AV_NOPTS_VALUE;
+ }
+
if ((s->flags & AVFMT_FLAG_IGNDTS) && pkt->pts != AV_NOPTS_VALUE)
pkt->dts = AV_NOPTS_VALUE;
+ if (pc && pc->pict_type == AV_PICTURE_TYPE_B
+ && !st->codec->has_b_frames)
+ //FIXME Set low_delay = 0 when has_b_frames = 1
+ st->codec->has_b_frames = 1;
+
/* do we have a video B-frame ? */
delay = st->codec->has_b_frames;
presentation_delayed = 0;
@@ -609,7 +1035,10 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st,
if (pkt->pts != AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE &&
st->pts_wrap_bits < 63 &&
pkt->dts - (1LL << (st->pts_wrap_bits - 1)) > pkt->pts) {
- pkt->dts -= 1LL << st->pts_wrap_bits;
+ if (is_relative(st->cur_dts) || pkt->dts - (1LL<<(st->pts_wrap_bits - 1)) > st->cur_dts) {
+ pkt->dts -= 1LL << st->pts_wrap_bits;
+ } else
+ pkt->pts += 1LL << st->pts_wrap_bits;
}
/* Some MPEG-2 in MPEG-PS lack dts (issue #171 / input_file.mpg).
@@ -618,23 +1047,27 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st,
* presentation_delayed is not set correctly. */
if (delay == 1 && pkt->dts == pkt->pts &&
pkt->dts != AV_NOPTS_VALUE && presentation_delayed) {
- av_log(s, AV_LOG_DEBUG, "invalid dts/pts combination\n");
- pkt->dts = AV_NOPTS_VALUE;
+ av_log(s, AV_LOG_DEBUG, "invalid dts/pts combination %"PRIi64"\n", pkt->dts);
+ if ( strcmp(s->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2")
+ && strcmp(s->iformat->name, "flv")) // otherwise we discard correct timestamps for vc1-wmapro.ism
+ pkt->dts = AV_NOPTS_VALUE;
}
- if (pkt->duration == 0 && st->codec->codec_type != AVMEDIA_TYPE_AUDIO) {
+ duration = av_mul_q((AVRational) {pkt->duration, 1}, st->time_base);
+ if (pkt->duration == 0) {
ff_compute_frame_duration(s, &num, &den, st, pc, pkt);
if (den && num) {
- pkt->duration = av_rescale_rnd(1, num * (int64_t) st->time_base.den,
+ duration = (AVRational) {num, den};
+ pkt->duration = av_rescale_rnd(1,
+ num * (int64_t) st->time_base.den,
den * (int64_t) st->time_base.num,
AV_ROUND_DOWN);
-
- if (pkt->duration != 0 && s->internal->packet_buffer)
- update_initial_durations(s, st, pkt->stream_index,
- pkt->duration);
}
}
+ if (pkt->duration != 0 && (s->internal->packet_buffer || s->internal->parse_queue))
+ update_initial_durations(s, st, pkt->stream_index, pkt->duration);
+
/* Correct timestamps with byte offset if demuxers only have timestamps
* on packet boundaries */
if (pc && st->need_parsing == AVSTREAM_PARSE_TIMESTAMPS && pkt->size) {
@@ -653,20 +1086,19 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st,
presentation_delayed = 1;
av_dlog(NULL,
- "IN delayed:%d pts:%"PRId64", dts:%"PRId64" "
- "cur_dts:%"PRId64" st:%d pc:%p\n",
- presentation_delayed, pkt->pts, pkt->dts, st->cur_dts,
- pkt->stream_index, pc);
- /* Interpolate PTS and DTS if they are not present. We skip H.264
+ "IN delayed:%d pts:%s, dts:%s cur_dts:%s st:%d pc:%p duration:%d delay:%d onein_oneout:%d\n",
+ presentation_delayed, av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts),
+ pkt->stream_index, pc, pkt->duration, delay, onein_oneout);
+ /* Interpolate PTS and DTS if they are not present. We skip H264
* currently because delay and has_b_frames are not reliably set. */
if ((delay == 0 || (delay == 1 && pc)) &&
- st->codec->codec_id != AV_CODEC_ID_H264) {
+ onein_oneout) {
if (presentation_delayed) {
/* DTS = decompression timestamp */
/* PTS = presentation timestamp */
if (pkt->dts == AV_NOPTS_VALUE)
pkt->dts = st->last_IP_pts;
- update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts);
+ update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts, pkt);
if (pkt->dts == AV_NOPTS_VALUE)
pkt->dts = st->cur_dts;
@@ -676,64 +1108,54 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st,
st->last_IP_duration = pkt->duration;
if (pkt->dts != AV_NOPTS_VALUE)
st->cur_dts = pkt->dts + st->last_IP_duration;
+ if (pkt->dts != AV_NOPTS_VALUE &&
+ pkt->pts == AV_NOPTS_VALUE &&
+ st->last_IP_duration > 0 &&
+ ((uint64_t)st->cur_dts - (uint64_t)next_dts + 1) <= 2 &&
+ next_dts != next_pts &&
+ next_pts != AV_NOPTS_VALUE)
+ pkt->pts = next_dts;
+
st->last_IP_duration = pkt->duration;
st->last_IP_pts = pkt->pts;
/* Cannot compute PTS if not present (we can compute it only
* by knowing the future. */
} else if (pkt->pts != AV_NOPTS_VALUE ||
pkt->dts != AV_NOPTS_VALUE ||
- pkt->duration ||
- st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
- int duration = pkt->duration;
- if (!duration && st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
- ff_compute_frame_duration(s, &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->internal->packet_buffer)
- update_initial_durations(s, st, pkt->stream_index,
- duration);
- }
- }
-
- if (pkt->pts != AV_NOPTS_VALUE || pkt->dts != AV_NOPTS_VALUE ||
- duration) {
- /* presentation is not delayed : PTS and DTS are the same */
- if (pkt->pts == AV_NOPTS_VALUE)
- pkt->pts = pkt->dts;
- update_initial_timestamps(s, pkt->stream_index, pkt->pts,
- pkt->pts);
- if (pkt->pts == AV_NOPTS_VALUE)
- pkt->pts = st->cur_dts;
- pkt->dts = pkt->pts;
- if (pkt->pts != AV_NOPTS_VALUE)
- st->cur_dts = pkt->pts + duration;
- }
+ pkt->duration ) {
+
+ /* presentation is not delayed : PTS and DTS are the same */
+ if (pkt->pts == AV_NOPTS_VALUE)
+ pkt->pts = pkt->dts;
+ update_initial_timestamps(s, pkt->stream_index, pkt->pts,
+ pkt->pts, pkt);
+ if (pkt->pts == AV_NOPTS_VALUE)
+ pkt->pts = st->cur_dts;
+ pkt->dts = pkt->pts;
+ if (pkt->pts != AV_NOPTS_VALUE)
+ st->cur_dts = av_add_stable(st->time_base, pkt->pts, duration, 1);
}
}
- if (pkt->pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY) {
+ if (pkt->pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY && has_decode_delay_been_guessed(st)) {
st->pts_buffer[0] = pkt->pts;
for (i = 0; i<delay && st->pts_buffer[i] > st->pts_buffer[i + 1]; i++)
FFSWAP(int64_t, st->pts_buffer[i], st->pts_buffer[i + 1]);
- if (pkt->dts == AV_NOPTS_VALUE)
- pkt->dts = st->pts_buffer[0];
- // We skipped it above so we try here.
- if (st->codec->codec_id == AV_CODEC_ID_H264)
- // This should happen on the first packet
- update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts);
- if (pkt->dts > st->cur_dts)
- st->cur_dts = pkt->dts;
+
+ pkt->dts = select_from_pts_buffer(st, st->pts_buffer, pkt->dts);
}
+ // We skipped it above so we try here.
+ if (!onein_oneout)
+ // This should happen on the first packet
+ update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts, pkt);
+ if (pkt->dts > st->cur_dts)
+ st->cur_dts = pkt->dts;
- av_dlog(NULL,
- "OUTdelayed:%d/%d pts:%"PRId64", dts:%"PRId64" cur_dts:%"PRId64"\n",
- presentation_delayed, delay, pkt->pts, pkt->dts, st->cur_dts);
+ av_dlog(NULL, "OUTdelayed:%d/%d pts:%s, dts:%s cur_dts:%s\n",
+ presentation_delayed, delay, av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts));
/* update flags */
- if (is_intra_only(st->codec->codec_id))
+ if (is_intra_only(st->codec))
pkt->flags |= AV_PKT_FLAG_KEY;
if (pc)
pkt->convergence_duration = pc->convergence_duration;
@@ -767,10 +1189,15 @@ 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, AV_NOPTS_VALUE, AV_NOPTS_VALUE);
}
while (size > 0 || (pkt == &flush_pkt && got_output)) {
int len;
+ int64_t next_pts = pkt->pts;
+ int64_t next_dts = pkt->dts;
av_init_packet(&out_pkt);
len = av_parser_parse2(st->parser, st->codec,
@@ -778,6 +1205,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;
@@ -811,19 +1239,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, next_dts, next_pts);
if (out_pkt.data == pkt->data && out_pkt.size == pkt->size) {
out_pkt.buf = pkt->buf;
@@ -871,6 +1298,11 @@ static int read_from_packet_buffer(AVPacketList **pkt_buffer,
return 0;
}
+static int64_t ts_to_samples(AVStream *st, int64_t ts)
+{
+ return av_rescale(ts, st->time_base.num * st->codec->sample_rate, st->time_base.den);
+}
+
static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
{
int ret = 0, i, got_packet = 0;
@@ -904,33 +1336,40 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
cur_pkt.dts != AV_NOPTS_VALUE &&
cur_pkt.pts < cur_pkt.dts) {
av_log(s, AV_LOG_WARNING,
- "Invalid timestamps stream=%d, pts=%"PRId64", "
- "dts=%"PRId64", size=%d\n",
- cur_pkt.stream_index, cur_pkt.pts,
- cur_pkt.dts, cur_pkt.size);
+ "Invalid timestamps stream=%d, pts=%s, dts=%s, size=%d\n",
+ cur_pkt.stream_index,
+ av_ts2str(cur_pkt.pts),
+ av_ts2str(cur_pkt.dts),
+ cur_pkt.size);
}
if (s->debug & FF_FDEBUG_TS)
av_log(s, AV_LOG_DEBUG,
- "ff_read_packet stream=%d, pts=%"PRId64", dts=%"PRId64", "
- "size=%d, duration=%d, flags=%d\n",
- cur_pkt.stream_index, cur_pkt.pts, cur_pkt.dts,
+ "ff_read_packet stream=%d, pts=%s, dts=%s, size=%d, duration=%d, flags=%d\n",
+ cur_pkt.stream_index,
+ av_ts2str(cur_pkt.pts),
+ av_ts2str(cur_pkt.dts),
cur_pkt.size, cur_pkt.duration, cur_pkt.flags);
if (st->need_parsing && !st->parser && !(s->flags & AVFMT_FLAG_NOPARSE)) {
st->parser = av_parser_init(st->codec->codec_id);
- if (!st->parser)
+ if (!st->parser) {
+ av_log(s, AV_LOG_VERBOSE, "parser not found for codec "
+ "%s, packets or times may be invalid.\n",
+ avcodec_get_name(st->codec->codec_id));
/* no parser available: just output the raw packets */
st->need_parsing = AVSTREAM_PARSE_NONE;
- else if (st->need_parsing == AVSTREAM_PARSE_HEADERS)
+ } else if (st->need_parsing == AVSTREAM_PARSE_HEADERS)
st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
else if (st->need_parsing == AVSTREAM_PARSE_FULL_ONCE)
st->parser->flags |= PARSER_FLAG_ONCE;
+ else if (st->need_parsing == AVSTREAM_PARSE_FULL_RAW)
+ st->parser->flags |= PARSER_FLAG_USE_CODEC_TS;
}
if (!st->need_parsing || !st->parser) {
/* no parsing needed: we just output the packet as is */
*pkt = cur_pkt;
- compute_pkt_fields(s, st, NULL, pkt);
+ compute_pkt_fields(s, st, NULL, pkt, AV_NOPTS_VALUE, AV_NOPTS_VALUE);
if ((s->iformat->flags & AVFMT_GENERIC_INDEX) &&
(pkt->flags & AV_PKT_FLAG_KEY) && pkt->dts != AV_NOPTS_VALUE) {
ff_reduce_index(s, st->index);
@@ -945,11 +1384,65 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
/* free packet */
av_free_packet(&cur_pkt);
}
+ if (pkt->flags & AV_PKT_FLAG_KEY)
+ st->skip_to_keyframe = 0;
+ if (st->skip_to_keyframe) {
+ av_free_packet(&cur_pkt);
+ if (got_packet) {
+ *pkt = cur_pkt;
+ }
+ got_packet = 0;
+ }
}
if (!got_packet && s->internal->parse_queue)
ret = read_from_packet_buffer(&s->internal->parse_queue, &s->internal->parse_queue_end, pkt);
+ if (ret >= 0) {
+ AVStream *st = s->streams[pkt->stream_index];
+ int discard_padding = 0;
+ if (st->first_discard_sample && pkt->pts != AV_NOPTS_VALUE) {
+ int64_t pts = pkt->pts - (is_relative(pkt->pts) ? RELATIVE_TS_BASE : 0);
+ int64_t sample = ts_to_samples(st, pts);
+ int duration = ts_to_samples(st, pkt->duration);
+ int64_t end_sample = sample + duration;
+ if (duration > 0 && end_sample >= st->first_discard_sample &&
+ sample < st->last_discard_sample)
+ discard_padding = FFMIN(end_sample - st->first_discard_sample, duration);
+ }
+ if (st->skip_samples || discard_padding) {
+ uint8_t *p = av_packet_new_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 10);
+ if (p) {
+ AV_WL32(p, st->skip_samples);
+ AV_WL32(p + 4, discard_padding);
+ av_log(s, AV_LOG_DEBUG, "demuxer injecting skip %d\n", st->skip_samples);
+ }
+ st->skip_samples = 0;
+ }
+
+ if (st->inject_global_side_data) {
+ for (i = 0; i < st->nb_side_data; i++) {
+ AVPacketSideData *src_sd = &st->side_data[i];
+ uint8_t *dst_data;
+
+ if (av_packet_get_side_data(pkt, src_sd->type, NULL))
+ continue;
+
+ dst_data = av_packet_new_side_data(pkt, src_sd->type, src_sd->size);
+ if (!dst_data) {
+ av_log(s, AV_LOG_WARNING, "Could not inject global side data\n");
+ continue;
+ }
+
+ memcpy(dst_data, src_sd->data, src_sd->size);
+ }
+ st->inject_global_side_data = 0;
+ }
+
+ if (!(s->flags & AVFMT_FLAG_KEEP_SIDE_DATA))
+ av_packet_merge_side_data(pkt);
+ }
+
av_opt_get_dict_val(s, "metadata", AV_OPT_SEARCH_CHILDREN, &metadata);
if (metadata) {
s->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
@@ -960,9 +1453,11 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
if (s->debug & FF_FDEBUG_TS)
av_log(s, AV_LOG_DEBUG,
- "read_frame_internal stream=%d, pts=%"PRId64", dts=%"PRId64", "
+ "read_frame_internal stream=%d, pts=%s, dts=%s, "
"size=%d, duration=%d, flags=%d\n",
- pkt->stream_index, pkt->pts, pkt->dts,
+ pkt->stream_index,
+ av_ts2str(pkt->pts),
+ av_ts2str(pkt->dts),
pkt->size, pkt->duration, pkt->flags);
return ret;
@@ -972,15 +1467,20 @@ int av_read_frame(AVFormatContext *s, AVPacket *pkt)
{
const int genpts = s->flags & AVFMT_FLAG_GENPTS;
int eof = 0;
+ int ret;
+ AVStream *st;
- if (!genpts)
- return s->internal->packet_buffer
- ? read_from_packet_buffer(&s->internal->packet_buffer,
- &s->internal->packet_buffer_end, pkt)
- : read_frame_internal(s, pkt);
+ if (!genpts) {
+ ret = s->internal->packet_buffer
+ ? read_from_packet_buffer(&s->internal->packet_buffer,
+ &s->internal->packet_buffer_end, pkt)
+ : read_frame_internal(s, pkt);
+ if (ret < 0)
+ return ret;
+ goto return_packet;
+ }
for (;;) {
- int ret;
AVPacketList *pktl = s->internal->packet_buffer;
if (pktl) {
@@ -988,23 +1488,42 @@ int av_read_frame(AVFormatContext *s, AVPacket *pkt)
if (next_pkt->dts != AV_NOPTS_VALUE) {
int wrap_bits = s->streams[next_pkt->stream_index]->pts_wrap_bits;
+ // last dts seen for this stream. if any of packets following
+ // current one had no dts, we will set this to AV_NOPTS_VALUE.
+ int64_t last_dts = next_pkt->dts;
while (pktl && next_pkt->pts == AV_NOPTS_VALUE) {
if (pktl->pkt.stream_index == next_pkt->stream_index &&
- (av_compare_mod(next_pkt->dts, pktl->pkt.dts, 2LL << (wrap_bits - 1)) < 0) &&
- av_compare_mod(pktl->pkt.pts, pktl->pkt.dts, 2LL << (wrap_bits - 1))) {
- // not B-frame
- next_pkt->pts = pktl->pkt.dts;
+ (av_compare_mod(next_pkt->dts, pktl->pkt.dts, 2LL << (wrap_bits - 1)) < 0)) {
+ if (av_compare_mod(pktl->pkt.pts, pktl->pkt.dts, 2LL << (wrap_bits - 1))) {
+ // not B-frame
+ next_pkt->pts = pktl->pkt.dts;
+ }
+ if (last_dts != AV_NOPTS_VALUE) {
+ // Once last dts was set to AV_NOPTS_VALUE, we don't change it.
+ last_dts = pktl->pkt.dts;
+ }
}
pktl = pktl->next;
}
+ if (eof && next_pkt->pts == AV_NOPTS_VALUE && last_dts != AV_NOPTS_VALUE) {
+ // Fixing the last reference frame had none pts issue (For MXF etc).
+ // We only do this when
+ // 1. eof.
+ // 2. we are not able to resolve a pts value for current packet.
+ // 3. the packets for this stream at the end of the files had valid dts.
+ next_pkt->pts = last_dts + next_pkt->duration;
+ }
pktl = s->internal->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->internal->packet_buffer,
+ st = s->streams[next_pkt->stream_index];
+ if (!(next_pkt->pts == AV_NOPTS_VALUE && st->discard < AVDISCARD_ALL &&
+ next_pkt->dts != AV_NOPTS_VALUE && !eof)) {
+ ret = read_from_packet_buffer(&s->internal->packet_buffer,
&s->internal->packet_buffer_end, pkt);
+ goto return_packet;
+ }
}
ret = read_frame_internal(s, pkt);
@@ -1020,11 +1539,28 @@ int av_read_frame(AVFormatContext *s, AVPacket *pkt)
&s->internal->packet_buffer_end)) < 0)
return AVERROR(ENOMEM);
}
+
+return_packet:
+
+ st = s->streams[pkt->stream_index];
+ if ((s->iformat->flags & AVFMT_GENERIC_INDEX) && pkt->flags & AV_PKT_FLAG_KEY) {
+ ff_reduce_index(s, st->index);
+ av_add_index_entry(st, pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME);
+ }
+
+ if (is_relative(pkt->dts))
+ pkt->dts -= RELATIVE_TS_BASE;
+ if (is_relative(pkt->pts))
+ pkt->pts -= RELATIVE_TS_BASE;
+
+ return ret;
}
/* XXX: suppress the packet queue */
static void flush_packet_queue(AVFormatContext *s)
{
+ if (!s->internal)
+ return;
free_packet_buffer(&s->internal->parse_queue, &s->internal->parse_queue_end);
free_packet_buffer(&s->internal->packet_buffer, &s->internal->packet_buffer_end);
free_packet_buffer(&s->internal->raw_packet_buffer, &s->internal->raw_packet_buffer_end);
@@ -1037,23 +1573,36 @@ static void flush_packet_queue(AVFormatContext *s)
int av_find_default_stream_index(AVFormatContext *s)
{
- int first_audio_index = -1;
int i;
AVStream *st;
+ int best_stream = 0;
+ int best_score = -1;
if (s->nb_streams <= 0)
return -1;
for (i = 0; i < s->nb_streams; i++) {
+ int score = 0;
st = s->streams[i];
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
!(st->disposition & AV_DISPOSITION_ATTACHED_PIC)) {
- return i;
+ if (!st->codec->width && !st->codec->height && !st->codec_info_nb_frames)
+ score += 25;
+ else
+ score += 100;
+ }
+ if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
+ if (!st->codec->sample_rate && !st->codec_info_nb_frames)
+ score += 12;
+ else
+ score += 50;
+ }
+
+ if (score > best_score) {
+ best_score = score;
+ best_stream = i;
}
- if (first_audio_index < 0 &&
- st->codec->codec_type == AVMEDIA_TYPE_AUDIO)
- first_audio_index = i;
}
- return first_audio_index >= 0 ? first_audio_index : 0;
+ return best_stream;
}
/** Flush the frame reader. */
@@ -1073,13 +1622,20 @@ void ff_read_frame_flush(AVFormatContext *s)
st->parser = NULL;
}
st->last_IP_pts = AV_NOPTS_VALUE;
- /* We set the current DTS to an unspecified origin. */
- st->cur_dts = AV_NOPTS_VALUE;
+ st->last_dts_for_order_check = AV_NOPTS_VALUE;
+ if (st->first_dts == AV_NOPTS_VALUE)
+ st->cur_dts = RELATIVE_TS_BASE;
+ else
+ /* We set the current DTS to an unspecified origin. */
+ st->cur_dts = AV_NOPTS_VALUE;
st->probe_packets = MAX_PROBE_PACKETS;
for (j = 0; j < MAX_REORDER_DELAY + 1; j++)
st->pts_buffer[j] = AV_NOPTS_VALUE;
+
+ if (s->internal->inject_global_side_data)
+ st->inject_global_side_data = 1;
}
}
@@ -1122,6 +1678,15 @@ int ff_add_index_entry(AVIndexEntry **index_entries,
if ((unsigned) *nb_index_entries + 1 >= UINT_MAX / sizeof(AVIndexEntry))
return -1;
+ if (timestamp == AV_NOPTS_VALUE)
+ return AVERROR(EINVAL);
+
+ if (size < 0 || size > 0x3FFFFFFF)
+ return AVERROR(EINVAL);
+
+ if (is_relative(timestamp)) //FIXME this maintains previous behavior but we should shift by the correct offset once known
+ timestamp -= RELATIVE_TS_BASE;
+
entries = av_fast_realloc(*index_entries,
index_entries_allocated_size,
(*nb_index_entries + 1) *
@@ -1137,7 +1702,7 @@ int ff_add_index_entry(AVIndexEntry **index_entries,
if (index < 0) {
index = (*nb_index_entries)++;
ie = &entries[index];
- assert(index == 0 || ie[-1].timestamp < timestamp);
+ av_assert0(index == 0 || ie[-1].timestamp < timestamp);
} else {
ie = &entries[index];
if (ie->timestamp != timestamp) {
@@ -1163,6 +1728,7 @@ int ff_add_index_entry(AVIndexEntry **index_entries,
int av_add_index_entry(AVStream *st, int64_t pos, int64_t timestamp,
int size, int distance, int flags)
{
+ timestamp = wrap_timestamp(st, timestamp);
return ff_add_index_entry(&st->index_entries, &st->nb_index_entries,
&st->index_entries_allocated_size, pos,
timestamp, size, distance, flags);
@@ -1207,6 +1773,15 @@ int av_index_search_timestamp(AVStream *st, int64_t wanted_timestamp, int flags)
wanted_timestamp, flags);
}
+static int64_t ff_read_timestamp(AVFormatContext *s, int stream_index, int64_t *ppos, int64_t pos_limit,
+ int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t ))
+{
+ int64_t ts = read_timestamp(s, stream_index, ppos, pos_limit);
+ if (stream_index >= 0)
+ ts = wrap_timestamp(s->streams[stream_index], ts);
+ return ts;
+}
+
int ff_seek_frame_binary(AVFormatContext *s, int stream_index,
int64_t target_ts, int flags)
{
@@ -1220,7 +1795,7 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index,
if (stream_index < 0)
return -1;
- av_dlog(s, "read_seek: %d %"PRId64"\n", stream_index, target_ts);
+ av_dlog(s, "read_seek: %d %s\n", stream_index, av_ts2str(target_ts));
ts_max =
ts_min = AV_NOPTS_VALUE;
@@ -1240,23 +1815,23 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index,
if (e->timestamp <= target_ts || e->pos == e->min_distance) {
pos_min = e->pos;
ts_min = e->timestamp;
- av_dlog(s, "using cached pos_min=0x%"PRIx64" dts_min=%"PRId64"\n",
- pos_min, ts_min);
+ av_dlog(s, "using cached pos_min=0x%"PRIx64" dts_min=%s\n",
+ pos_min, av_ts2str(ts_min));
} else {
- assert(index == 0);
+ av_assert1(index == 0);
}
index = av_index_search_timestamp(st, target_ts,
flags & ~AVSEEK_FLAG_BACKWARD);
- assert(index < st->nb_index_entries);
+ av_assert0(index < st->nb_index_entries);
if (index >= 0) {
e = &st->index_entries[index];
- assert(e->timestamp >= target_ts);
+ av_assert1(e->timestamp >= target_ts);
pos_max = e->pos;
ts_max = e->timestamp;
pos_limit = pos_max - e->min_distance;
av_dlog(s, "using cached pos_max=0x%"PRIx64" pos_limit=0x%"PRIx64
- " dts_max=%"PRId64"\n", pos_max, pos_limit, ts_max);
+ " dts_max=%s\n", pos_max, pos_limit, av_ts2str(ts_max));
}
}
@@ -1269,11 +1844,50 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index,
if ((ret = avio_seek(s->pb, pos, SEEK_SET)) < 0)
return ret;
+ ff_read_frame_flush(s);
ff_update_cur_dts(s, st, ts);
return 0;
}
+int ff_find_last_ts(AVFormatContext *s, int stream_index, int64_t *ts, int64_t *pos,
+ int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t ))
+{
+ int64_t step = 1024;
+ int64_t limit, ts_max;
+ int64_t filesize = avio_size(s->pb);
+ int64_t pos_max = filesize - 1;
+ do {
+ limit = pos_max;
+ pos_max = FFMAX(0, (pos_max) - step);
+ ts_max = ff_read_timestamp(s, stream_index,
+ &pos_max, limit, read_timestamp);
+ step += step;
+ } while (ts_max == AV_NOPTS_VALUE && 2*limit > step);
+ if (ts_max == AV_NOPTS_VALUE)
+ return -1;
+
+ for (;;) {
+ int64_t tmp_pos = pos_max + 1;
+ int64_t tmp_ts = ff_read_timestamp(s, stream_index,
+ &tmp_pos, INT64_MAX, read_timestamp);
+ if (tmp_ts == AV_NOPTS_VALUE)
+ break;
+ av_assert0(tmp_pos > pos_max);
+ ts_max = tmp_ts;
+ pos_max = tmp_pos;
+ if (tmp_pos >= filesize)
+ break;
+ }
+
+ if (ts)
+ *ts = ts_max;
+ if (pos)
+ *pos = pos_max;
+
+ return 0;
+}
+
int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts,
int64_t pos_min, int64_t pos_max, int64_t pos_limit,
int64_t ts_min, int64_t ts_max,
@@ -1282,55 +1896,43 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts,
int64_t *, int64_t))
{
int64_t pos, ts;
- int64_t start_pos, filesize;
+ int64_t start_pos;
int no_change;
+ int ret;
- av_dlog(s, "gen_seek: %d %"PRId64"\n", stream_index, target_ts);
+ av_dlog(s, "gen_seek: %d %s\n", stream_index, av_ts2str(target_ts));
if (ts_min == AV_NOPTS_VALUE) {
pos_min = s->internal->data_offset;
- ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
+ ts_min = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp);
if (ts_min == AV_NOPTS_VALUE)
return -1;
}
- if (ts_max == AV_NOPTS_VALUE) {
- int step = 1024;
- filesize = avio_size(s->pb);
- pos_max = filesize - 1;
- do {
- pos_max -= step;
- ts_max = read_timestamp(s, stream_index, &pos_max,
- pos_max + step);
- step += step;
- } while (ts_max == AV_NOPTS_VALUE && pos_max >= step);
- if (ts_max == AV_NOPTS_VALUE)
- return -1;
+ if (ts_min >= target_ts) {
+ *ts_ret = ts_min;
+ return pos_min;
+ }
- for (;;) {
- int64_t tmp_pos = pos_max + 1;
- int64_t tmp_ts = read_timestamp(s, stream_index,
- &tmp_pos, INT64_MAX);
- if (tmp_ts == AV_NOPTS_VALUE)
- break;
- ts_max = tmp_ts;
- pos_max = tmp_pos;
- if (tmp_pos >= filesize)
- break;
- }
+ if (ts_max == AV_NOPTS_VALUE) {
+ if ((ret = ff_find_last_ts(s, stream_index, &ts_max, &pos_max, read_timestamp)) < 0)
+ return ret;
pos_limit = pos_max;
}
- if (ts_min > ts_max)
- return -1;
- else if (ts_min == ts_max)
- pos_limit = pos_min;
+ if (ts_max <= target_ts) {
+ *ts_ret = ts_max;
+ return pos_max;
+ }
+
+ av_assert0(ts_min < ts_max);
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);
- assert(pos_limit <= pos_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));
+ av_assert0(pos_limit <= pos_max);
if (no_change == 0) {
int64_t approximate_keyframe_distance = pos_max - pos_limit;
@@ -1353,20 +1955,20 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts,
start_pos = pos;
// May pass pos_limit instead of -1.
- ts = read_timestamp(s, stream_index, &pos, INT64_MAX);
+ ts = ff_read_timestamp(s, stream_index, &pos, INT64_MAX, read_timestamp);
if (pos == pos_max)
no_change++;
else
no_change = 0;
- av_dlog(s, "%"PRId64" %"PRId64" %"PRId64" / %"PRId64" %"PRId64" %"PRId64
- " target:%"PRId64" limit:%"PRId64" start:%"PRId64" noc:%d\n",
- pos_min, pos, pos_max, ts_min, ts, ts_max, target_ts,
+ av_dlog(s, "%"PRId64" %"PRId64" %"PRId64" / %s %s %s"
+ " target:%s limit:%"PRId64" start:%"PRId64" noc:%d\n",
+ pos_min, pos, pos_max,
+ av_ts2str(ts_min), av_ts2str(ts), av_ts2str(ts_max), av_ts2str(target_ts),
pos_limit, start_pos, no_change);
if (ts == AV_NOPTS_VALUE) {
av_log(s, AV_LOG_ERROR, "read_timestamp() failed in the middle\n");
return -1;
}
- assert(ts != AV_NOPTS_VALUE);
if (target_ts <= ts) {
pos_limit = start_pos - 1;
pos_max = pos;
@@ -1380,12 +1982,14 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts,
pos = (flags & AVSEEK_FLAG_BACKWARD) ? pos_min : pos_max;
ts = (flags & AVSEEK_FLAG_BACKWARD) ? ts_min : ts_max;
+#if 0
pos_min = pos;
- ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
+ ts_min = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp);
pos_min++;
- ts_max = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
- av_dlog(s, "pos=0x%"PRIx64" %"PRId64"<=%"PRId64"<=%"PRId64"\n",
- pos, ts_min, target_ts, ts_max);
+ ts_max = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp);
+ av_dlog(s, "pos=0x%"PRIx64" %s<=%s<=%s\n",
+ pos, av_ts2str(ts_min), av_ts2str(target_ts), av_ts2str(ts_max));
+#endif
*ts_ret = ts;
return pos;
}
@@ -1405,6 +2009,8 @@ static int seek_frame_byte(AVFormatContext *s, int stream_index,
avio_seek(s->pb, pos, SEEK_SET);
+ s->io_repositioned = 1;
+
return 0;
}
@@ -1426,9 +2032,10 @@ static int seek_frame_generic(AVFormatContext *s, int stream_index,
if (index < 0 || index == st->nb_index_entries - 1) {
AVPacket pkt;
+ int nonkey = 0;
if (st->nb_index_entries) {
- assert(st->index_entries);
+ av_assert0(st->index_entries);
ie = &st->index_entries[st->nb_index_entries - 1];
if ((ret = avio_seek(s->pb, ie->pos, SEEK_SET)) < 0)
return ret;
@@ -1445,9 +2052,14 @@ static int seek_frame_generic(AVFormatContext *s, int stream_index,
if (read_status < 0)
break;
av_free_packet(&pkt);
- if (stream_index == pkt.stream_index)
- if ((pkt.flags & AV_PKT_FLAG_KEY) && pkt.dts > timestamp)
+ if (stream_index == pkt.stream_index && pkt.dts > timestamp) {
+ if (pkt.flags & AV_PKT_FLAG_KEY)
+ break;
+ if (nonkey++ > 1000 && st->codec->codec_id != AV_CODEC_ID_CDGRAPHICS) {
+ av_log(s, AV_LOG_ERROR,"seek_frame_generic failed as this stream seems to contain no keyframes after the target timestamp, %d non keyframes found\n", nonkey);
break;
+ }
+ }
}
index = av_index_search_timestamp(st, timestamp, flags);
}
@@ -1513,10 +2125,22 @@ static int seek_frame_internal(AVFormatContext *s, int stream_index,
int av_seek_frame(AVFormatContext *s, int stream_index,
int64_t timestamp, int flags)
{
- int ret = seek_frame_internal(s, stream_index, timestamp, flags);
+ int ret;
+
+ if (s->iformat->read_seek2 && !s->iformat->read_seek) {
+ int64_t min_ts = INT64_MIN, max_ts = INT64_MAX;
+ if ((flags & AVSEEK_FLAG_BACKWARD))
+ max_ts = timestamp;
+ else
+ min_ts = timestamp;
+ return avformat_seek_file(s, stream_index, min_ts, timestamp, max_ts,
+ flags & ~AVSEEK_FLAG_BACKWARD);
+ }
+
+ ret = seek_frame_internal(s, stream_index, timestamp, flags);
if (ret >= 0)
- ret = queue_attached_pictures(s);
+ ret = avformat_queue_attached_pictures(s);
return ret;
}
@@ -1526,15 +2150,33 @@ int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts,
{
if (min_ts > ts || max_ts < ts)
return -1;
+ if (stream_index < -1 || stream_index >= (int)s->nb_streams)
+ return AVERROR(EINVAL);
+
+ if (s->seek2any>0)
+ flags |= AVSEEK_FLAG_ANY;
+ flags &= ~AVSEEK_FLAG_BACKWARD;
if (s->iformat->read_seek2) {
int ret;
ff_read_frame_flush(s);
+
+ if (stream_index == -1 && s->nb_streams == 1) {
+ AVRational time_base = s->streams[0]->time_base;
+ ts = av_rescale_q(ts, AV_TIME_BASE_Q, time_base);
+ min_ts = av_rescale_rnd(min_ts, time_base.den,
+ time_base.num * (int64_t)AV_TIME_BASE,
+ AV_ROUND_UP | AV_ROUND_PASS_MINMAX);
+ max_ts = av_rescale_rnd(max_ts, time_base.den,
+ time_base.num * (int64_t)AV_TIME_BASE,
+ AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX);
+ }
+
ret = s->iformat->read_seek2(s, stream_index, min_ts,
ts, max_ts, flags);
if (ret >= 0)
- ret = queue_attached_pictures(s);
+ ret = avformat_queue_attached_pictures(s);
return ret;
}
@@ -1544,13 +2186,19 @@ int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts,
// Fall back on old API if new is not implemented but old is.
// Note the old API has somewhat different semantics.
- if (s->iformat->read_seek || 1)
- return av_seek_frame(s, stream_index, ts,
- flags | ((uint64_t) ts - min_ts >
- (uint64_t) max_ts - ts
- ? AVSEEK_FLAG_BACKWARD : 0));
+ if (s->iformat->read_seek || 1) {
+ int dir = (ts - (uint64_t)min_ts > (uint64_t)max_ts - ts ? AVSEEK_FLAG_BACKWARD : 0);
+ int ret = av_seek_frame(s, stream_index, ts, flags | dir);
+ if (ret<0 && ts != min_ts && max_ts != ts) {
+ ret = av_seek_frame(s, stream_index, dir ? max_ts : min_ts, flags | dir);
+ if (ret >= 0)
+ ret = av_seek_frame(s, stream_index, ts, flags | (dir^AVSEEK_FLAG_BACKWARD));
+ }
+ return ret;
+ }
// try some generic seek like seek_frame_generic() but with new ts semantics
+ return -1; //unreachable
}
/*******************************************************/
@@ -1582,12 +2230,14 @@ static int has_duration(AVFormatContext *ic)
*/
static void update_stream_timings(AVFormatContext *ic)
{
- int64_t start_time, start_time1, end_time, end_time1;
+ int64_t start_time, start_time1, start_time_text, end_time, end_time1;
int64_t duration, duration1, filesize;
int i;
AVStream *st;
+ AVProgram *p;
start_time = INT64_MAX;
+ start_time_text = INT64_MAX;
end_time = INT64_MIN;
duration = INT64_MIN;
for (i = 0; i < ic->nb_streams; i++) {
@@ -1595,13 +2245,24 @@ static void update_stream_timings(AVFormatContext *ic)
if (st->start_time != AV_NOPTS_VALUE && st->time_base.den) {
start_time1 = av_rescale_q(st->start_time, st->time_base,
AV_TIME_BASE_Q);
- start_time = FFMIN(start_time, start_time1);
+ if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE || st->codec->codec_type == AVMEDIA_TYPE_DATA) {
+ if (start_time1 < start_time_text)
+ start_time_text = start_time1;
+ } else
+ start_time = FFMIN(start_time, start_time1);
+ end_time1 = AV_NOPTS_VALUE;
if (st->duration != AV_NOPTS_VALUE) {
end_time1 = start_time1 +
av_rescale_q(st->duration, st->time_base,
AV_TIME_BASE_Q);
end_time = FFMAX(end_time, end_time1);
}
+ for (p = NULL; (p = av_find_program_from_stream(ic, p, i)); ) {
+ if (p->start_time == AV_NOPTS_VALUE || p->start_time > start_time1)
+ p->start_time = start_time1;
+ if (p->end_time < end_time1)
+ p->end_time = end_time1;
+ }
}
if (st->duration != AV_NOPTS_VALUE) {
duration1 = av_rescale_q(st->duration, st->time_base,
@@ -1609,17 +2270,33 @@ static void update_stream_timings(AVFormatContext *ic)
duration = FFMAX(duration, duration1);
}
}
+ if (start_time == INT64_MAX || (start_time > start_time_text && start_time - start_time_text < AV_TIME_BASE))
+ start_time = start_time_text;
+ else if (start_time > start_time_text)
+ av_log(ic, AV_LOG_VERBOSE, "Ignoring outlier non primary stream starttime %f\n", start_time_text / (float)AV_TIME_BASE);
+
if (start_time != INT64_MAX) {
ic->start_time = start_time;
- if (end_time != INT64_MIN)
- duration = FFMAX(duration, end_time - start_time);
+ if (end_time != INT64_MIN) {
+ if (ic->nb_programs) {
+ for (i = 0; i < ic->nb_programs; i++) {
+ p = ic->programs[i];
+ if (p->start_time != AV_NOPTS_VALUE && p->end_time > p->start_time)
+ duration = FFMAX(duration, p->end_time - p->start_time);
+ }
+ } else
+ duration = FFMAX(duration, end_time - start_time);
+ }
}
- if (duration != INT64_MIN) {
+ if (duration != INT64_MIN && duration > 0 && ic->duration == AV_NOPTS_VALUE) {
ic->duration = duration;
- if (ic->pb && (filesize = avio_size(ic->pb)) > 0)
- /* compute the bitrate */
- ic->bit_rate = (double) filesize * 8.0 * AV_TIME_BASE /
- (double) ic->duration;
+ }
+ if (ic->pb && (filesize = avio_size(ic->pb)) > 0 && ic->duration != AV_NOPTS_VALUE) {
+ /* compute the bitrate */
+ double bitrate = (double) filesize * 8.0 * AV_TIME_BASE /
+ (double) ic->duration;
+ if (bitrate >= 0 && bitrate <= INT_MAX)
+ ic->bit_rate = bitrate;
}
}
@@ -1645,7 +2322,7 @@ static void fill_all_stream_timings(AVFormatContext *ic)
static void estimate_timings_from_bit_rate(AVFormatContext *ic)
{
int64_t filesize, duration;
- int i;
+ int i, show_warning = 0;
AVStream *st;
/* if bit_rate is already set, we believe it */
@@ -1659,6 +2336,11 @@ static void estimate_timings_from_bit_rate(AVFormatContext *ic)
break;
}
bit_rate += st->codec->bit_rate;
+ } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && st->codec_info_nb_frames > 1) {
+ // If we have a videostream with packets but without a bitrate
+ // then consider the sum not known
+ bit_rate = 0;
+ break;
}
}
ic->bit_rate = bit_rate;
@@ -1668,29 +2350,37 @@ static void estimate_timings_from_bit_rate(AVFormatContext *ic)
if (ic->duration == AV_NOPTS_VALUE &&
ic->bit_rate != 0) {
filesize = ic->pb ? avio_size(ic->pb) : 0;
- if (filesize > 0) {
+ if (filesize > ic->internal->data_offset) {
+ filesize -= ic->internal->data_offset;
for (i = 0; i < ic->nb_streams; i++) {
- st = ic->streams[i];
- duration = av_rescale(8 * filesize, st->time_base.den,
- ic->bit_rate *
- (int64_t) st->time_base.num);
- if (st->duration == AV_NOPTS_VALUE)
+ st = ic->streams[i];
+ if ( st->time_base.num <= INT64_MAX / ic->bit_rate
+ && st->duration == AV_NOPTS_VALUE) {
+ duration = av_rescale(8 * filesize, st->time_base.den,
+ ic->bit_rate *
+ (int64_t) st->time_base.num);
st->duration = duration;
+ show_warning = 1;
+ }
}
}
}
+ if (show_warning)
+ av_log(ic, AV_LOG_WARNING,
+ "Estimating duration from bitrate, this may be inaccurate\n");
}
-#define DURATION_MAX_READ_SIZE 250000
-#define DURATION_MAX_RETRY 3
+#define DURATION_MAX_READ_SIZE 250000LL
+#define DURATION_MAX_RETRY 4
/* only usable for MPEG-PS streams */
static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
{
AVPacket pkt1, *pkt = &pkt1;
AVStream *st;
- int read_size, i, ret;
- int64_t end_time;
+ int num, den, read_size, i, ret;
+ int found_duration = 0;
+ int is_end;
int64_t filesize, offset, duration;
int retry = 0;
@@ -1699,9 +2389,11 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
- if (st->start_time == AV_NOPTS_VALUE && st->first_dts == AV_NOPTS_VALUE)
+ if (st->start_time == AV_NOPTS_VALUE &&
+ st->first_dts == AV_NOPTS_VALUE &&
+ st->codec->codec_type != AVMEDIA_TYPE_UNKNOWN)
av_log(st->codec, AV_LOG_WARNING,
- "start time is not set in estimate_timings_from_pts\n");
+ "start time for stream %d is not set in estimate_timings_from_pts\n", i);
if (st->parser) {
av_parser_close(st->parser);
@@ -1709,11 +2401,12 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
}
}
+ av_opt_set(ic, "skip_changes", "1", AV_OPT_SEARCH_CHILDREN);
/* estimate the end time (duration) */
/* XXX: may need to support wrapping */
filesize = ic->pb ? avio_size(ic->pb) : 0;
- end_time = AV_NOPTS_VALUE;
do {
+ is_end = found_duration;
offset = filesize - (DURATION_MAX_READ_SIZE << retry);
if (offset < 0)
offset = 0;
@@ -1734,31 +2427,76 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
if (pkt->pts != AV_NOPTS_VALUE &&
(st->start_time != AV_NOPTS_VALUE ||
st->first_dts != AV_NOPTS_VALUE)) {
- duration = end_time = pkt->pts;
+ if (pkt->duration == 0) {
+ ff_compute_frame_duration(ic, &num, &den, st, st->parser, pkt);
+ if (den && num) {
+ pkt->duration = av_rescale_rnd(1,
+ num * (int64_t) st->time_base.den,
+ den * (int64_t) st->time_base.num,
+ AV_ROUND_DOWN);
+ }
+ }
+ duration = pkt->pts + pkt->duration;
+ found_duration = 1;
if (st->start_time != AV_NOPTS_VALUE)
duration -= st->start_time;
else
duration -= st->first_dts;
- if (duration < 0)
- duration += 1LL << st->pts_wrap_bits;
if (duration > 0) {
- if (st->duration == AV_NOPTS_VALUE || st->duration < duration)
+ if (st->duration == AV_NOPTS_VALUE || st->info->last_duration<= 0 ||
+ (st->duration < duration && FFABS(duration - st->info->last_duration) < 60LL*st->time_base.den / st->time_base.num))
st->duration = duration;
+ st->info->last_duration = duration;
}
}
av_free_packet(pkt);
}
- } while (end_time == AV_NOPTS_VALUE &&
- filesize > (DURATION_MAX_READ_SIZE << retry) &&
+
+ /* check if all audio/video streams have valid duration */
+ if (!is_end) {
+ is_end = 1;
+ for (i = 0; i < ic->nb_streams; i++) {
+ st = ic->streams[i];
+ switch (st->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ case AVMEDIA_TYPE_AUDIO:
+ if (st->duration == AV_NOPTS_VALUE)
+ is_end = 0;
+ }
+ }
+ }
+ } while (!is_end &&
+ offset &&
++retry <= DURATION_MAX_RETRY);
+ av_opt_set(ic, "skip_changes", "0", AV_OPT_SEARCH_CHILDREN);
+
+ /* warn about audio/video streams which duration could not be estimated */
+ for (i = 0; i < ic->nb_streams; i++) {
+ st = ic->streams[i];
+ if (st->duration == AV_NOPTS_VALUE) {
+ switch (st->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ case AVMEDIA_TYPE_AUDIO:
+ if (st->start_time != AV_NOPTS_VALUE || st->first_dts != AV_NOPTS_VALUE) {
+ av_log(ic, AV_LOG_DEBUG, "stream %d : no PTS found at end of file, duration not set\n", i);
+ } else
+ av_log(ic, AV_LOG_DEBUG, "stream %d : no TS found at start of file, duration not set\n", i);
+ }
+ }
+ }
fill_all_stream_timings(ic);
avio_seek(ic->pb, old_offset, SEEK_SET);
for (i = 0; i < ic->nb_streams; i++) {
+ int j;
+
st = ic->streams[i];
st->cur_dts = st->first_dts;
st->last_IP_pts = AV_NOPTS_VALUE;
+ st->last_dts_for_order_check = AV_NOPTS_VALUE;
+ for (j = 0; j < MAX_REORDER_DELAY + 1; j++)
+ st->pts_buffer[j] = AV_NOPTS_VALUE;
}
}
@@ -1779,15 +2517,16 @@ static void estimate_timings(AVFormatContext *ic, int64_t old_offset)
file_size && ic->pb->seekable) {
/* get accurate estimate from the PTSes */
estimate_timings_from_pts(ic, old_offset);
+ ic->duration_estimation_method = AVFMT_DURATION_FROM_PTS;
} else if (has_duration(ic)) {
/* at least one component has timings - we use them for all
* the components */
fill_all_stream_timings(ic);
+ ic->duration_estimation_method = AVFMT_DURATION_FROM_STREAM;
} else {
- av_log(ic, AV_LOG_WARNING,
- "Estimating duration from bitrate, this may be inaccurate\n");
/* less precise: use bitrate info */
estimate_timings_from_bit_rate(ic);
+ ic->duration_estimation_method = AVFMT_DURATION_FROM_BITRATE;
}
update_stream_timings(ic);
@@ -1808,56 +2547,75 @@ static void estimate_timings(AVFormatContext *ic, int64_t old_offset)
}
}
-static int has_codec_parameters(AVStream *st)
+static int has_codec_parameters(AVStream *st, const char **errmsg_ptr)
{
AVCodecContext *avctx = st->codec;
- int val;
+#define FAIL(errmsg) do { \
+ if (errmsg_ptr) \
+ *errmsg_ptr = errmsg; \
+ return 0; \
+ } while (0)
+
+ if ( avctx->codec_id == AV_CODEC_ID_NONE
+ && avctx->codec_type != AVMEDIA_TYPE_DATA)
+ FAIL("unknown codec");
switch (avctx->codec_type) {
case AVMEDIA_TYPE_AUDIO:
- val = avctx->sample_rate && avctx->channels;
+ if (!avctx->frame_size && determinable_frame_size(avctx))
+ FAIL("unspecified frame size");
if (st->info->found_decoder >= 0 &&
avctx->sample_fmt == AV_SAMPLE_FMT_NONE)
- return 0;
+ FAIL("unspecified sample format");
+ if (!avctx->sample_rate)
+ FAIL("unspecified sample rate");
+ if (!avctx->channels)
+ FAIL("unspecified number of channels");
+ if (st->info->found_decoder >= 0 && !st->nb_decoded_frames && avctx->codec_id == AV_CODEC_ID_DTS)
+ FAIL("no decodable DTS frames");
break;
case AVMEDIA_TYPE_VIDEO:
- val = avctx->width;
+ if (!avctx->width)
+ FAIL("unspecified size");
if (st->info->found_decoder >= 0 && avctx->pix_fmt == AV_PIX_FMT_NONE)
- return 0;
+ FAIL("unspecified pixel format");
+ if (st->codec->codec_id == AV_CODEC_ID_RV30 || st->codec->codec_id == AV_CODEC_ID_RV40)
+ if (!st->sample_aspect_ratio.num && !st->codec->sample_aspect_ratio.num && !st->codec_info_nb_frames)
+ FAIL("no frame in rv30/40 and no sar");
break;
- default:
- val = 1;
+ case AVMEDIA_TYPE_SUBTITLE:
+ if (avctx->codec_id == AV_CODEC_ID_HDMV_PGS_SUBTITLE && !avctx->width)
+ FAIL("unspecified size");
break;
+ case AVMEDIA_TYPE_DATA:
+ if (avctx->codec_id == AV_CODEC_ID_NONE) return 1;
}
- return avctx->codec_id != AV_CODEC_ID_NONE && val != 0;
-}
-static int has_decode_delay_been_guessed(AVStream *st)
-{
- return st->codec->codec_id != AV_CODEC_ID_H264 ||
- st->info->nb_decoded_frames >= 6;
+ return 1;
}
/* returns 1 or 0 if or if not decoded data was returned, or a negative error */
-static int try_decode_frame(AVStream *st, AVPacket *avpkt,
+static int try_decode_frame(AVFormatContext *s, AVStream *st, AVPacket *avpkt,
AVDictionary **options)
{
const AVCodec *codec;
int got_picture = 1, ret = 0;
AVFrame *frame = av_frame_alloc();
+ AVSubtitle subtitle;
AVPacket pkt = *avpkt;
if (!frame)
return AVERROR(ENOMEM);
- if (!avcodec_is_open(st->codec) && !st->info->found_decoder) {
+ if (!avcodec_is_open(st->codec) &&
+ st->info->found_decoder <= 0 &&
+ (st->codec->codec_id != -st->info->found_decoder || !st->codec->codec_id)) {
AVDictionary *thread_opt = NULL;
- codec = st->codec->codec ? st->codec->codec
- : avcodec_find_decoder(st->codec->codec_id);
+ codec = find_decoder(s, st, st->codec->codec_id);
if (!codec) {
- st->info->found_decoder = -1;
+ st->info->found_decoder = -st->codec->codec_id;
ret = -1;
goto fail;
}
@@ -1865,11 +2623,13 @@ static int try_decode_frame(AVStream *st, AVPacket *avpkt,
/* Force thread count to 1 since the H.264 decoder will not extract
* SPS and PPS to extradata during multi-threaded decoding. */
av_dict_set(options ? options : &thread_opt, "threads", "1", 0);
+ if (s->codec_whitelist)
+ av_dict_set(options ? options : &thread_opt, "codec_whitelist", s->codec_whitelist, 0);
ret = avcodec_open2(st->codec, codec, options ? options : &thread_opt);
if (!options)
av_dict_free(&thread_opt);
if (ret < 0) {
- st->info->found_decoder = -1;
+ st->info->found_decoder = -st->codec->codec_id;
goto fail;
}
st->info->found_decoder = 1;
@@ -1883,7 +2643,7 @@ static int try_decode_frame(AVStream *st, AVPacket *avpkt,
while ((pkt.size > 0 || (!pkt.data && got_picture)) &&
ret >= 0 &&
- (!has_codec_parameters(st) || !has_decode_delay_been_guessed(st) ||
+ (!has_codec_parameters(st, NULL) || !has_decode_delay_been_guessed(st) ||
(!st->codec_info_nb_frames &&
st->codec->codec->capabilities & CODEC_CAP_CHANNEL_CONF))) {
got_picture = 0;
@@ -1895,18 +2655,26 @@ static int try_decode_frame(AVStream *st, AVPacket *avpkt,
case AVMEDIA_TYPE_AUDIO:
ret = avcodec_decode_audio4(st->codec, frame, &got_picture, &pkt);
break;
+ case AVMEDIA_TYPE_SUBTITLE:
+ ret = avcodec_decode_subtitle2(st->codec, &subtitle,
+ &got_picture, &pkt);
+ ret = pkt.size;
+ break;
default:
break;
}
if (ret >= 0) {
if (got_picture)
- st->info->nb_decoded_frames++;
+ st->nb_decoded_frames++;
pkt.data += ret;
pkt.size -= ret;
ret = got_picture;
}
}
+ if (!pkt.data && !got_picture)
+ ret = -1;
+
fail:
av_frame_free(&frame);
return ret;
@@ -1946,6 +2714,7 @@ enum AVCodecID ff_get_pcm_codec_id(int bps, int flt, int be, int sflags)
return AV_CODEC_ID_NONE;
}
} else {
+ bps += 7;
bps >>= 3;
if (sflags & (1 << (bps - 1))) {
switch (bps) {
@@ -1979,11 +2748,25 @@ enum AVCodecID ff_get_pcm_codec_id(int bps, int flt, int be, int sflags)
unsigned int av_codec_get_tag(const AVCodecTag *const *tags, enum AVCodecID id)
{
+ unsigned int tag;
+ if (!av_codec_get_tag2(tags, id, &tag))
+ return 0;
+ return tag;
+}
+
+int av_codec_get_tag2(const AVCodecTag * const *tags, enum AVCodecID id,
+ unsigned int *tag)
+{
int i;
for (i = 0; tags && tags[i]; i++) {
- int tag = ff_codec_get_tag(tags[i], id);
- if (tag)
- return tag;
+ const AVCodecTag *codec_tags = tags[i];
+ while (codec_tags->id != AV_CODEC_ID_NONE) {
+ if (codec_tags->id == id) {
+ *tag = codec_tags->tag;
+ return 1;
+ }
+ codec_tags++;
+ }
}
return 0;
}
@@ -2025,56 +2808,291 @@ static void compute_chapters_end(AVFormatContext *s)
static int get_std_framerate(int i)
{
- if (i < 60 * 12)
+ if (i < 30*12)
return (i + 1) * 1001;
- else
- return ((const int[]) { 24, 30, 60, 12, 15 })[i - 60 * 12] * 1000 * 12;
+ i -= 30*12;
+
+ if (i < 7)
+ return ((const int[]) { 40, 48, 50, 60, 80, 120, 240})[i] * 1001 * 12;
+
+ i -= 7;
+
+ return ((const int[]) { 24, 30, 60, 12, 15, 48 })[i] * 1000 * 12;
+}
+
+/* Is the time base unreliable?
+ * This is a heuristic to balance between quick acceptance of the values in
+ * the headers vs. some extra checks.
+ * Old DivX and Xvid often have nonsense timebases like 1fps or 2fps.
+ * MPEG-2 commonly misuses field repeat flags to store different framerates.
+ * And there are "variable" fps files this needs to detect as well. */
+static int tb_unreliable(AVCodecContext *c)
+{
+ if (c->time_base.den >= 101LL * c->time_base.num ||
+ c->time_base.den < 5LL * c->time_base.num ||
+ // c->codec_tag == AV_RL32("DIVX") ||
+ // c->codec_tag == AV_RL32("XVID") ||
+ c->codec_tag == AV_RL32("mp4v") ||
+ c->codec_id == AV_CODEC_ID_MPEG2VIDEO ||
+ c->codec_id == AV_CODEC_ID_GIF ||
+ c->codec_id == AV_CODEC_ID_H264)
+ return 1;
+ return 0;
+}
+
+int ff_alloc_extradata(AVCodecContext *avctx, int size)
+{
+ int ret;
+
+ if (size < 0 || size >= INT32_MAX - FF_INPUT_BUFFER_PADDING_SIZE) {
+ avctx->extradata = NULL;
+ avctx->extradata_size = 0;
+ return AVERROR(EINVAL);
+ }
+ avctx->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
+ if (avctx->extradata) {
+ memset(avctx->extradata + size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+ avctx->extradata_size = size;
+ ret = 0;
+ } else {
+ avctx->extradata_size = 0;
+ ret = AVERROR(ENOMEM);
+ }
+ return ret;
+}
+
+int ff_get_extradata(AVCodecContext *avctx, AVIOContext *pb, int size)
+{
+ int ret = ff_alloc_extradata(avctx, size);
+ if (ret < 0)
+ return ret;
+ ret = avio_read(pb, avctx->extradata, size);
+ if (ret != size) {
+ av_freep(&avctx->extradata);
+ avctx->extradata_size = 0;
+ av_log(avctx, AV_LOG_ERROR, "Failed to read extradata of size %d\n", size);
+ return ret < 0 ? ret : AVERROR_INVALIDDATA;
+ }
+
+ return ret;
+}
+
+int ff_rfps_add_frame(AVFormatContext *ic, AVStream *st, int64_t ts)
+{
+ int i, j;
+ int64_t last = st->info->last_dts;
+
+ if ( ts != AV_NOPTS_VALUE && last != AV_NOPTS_VALUE && ts > last
+ && ts - (uint64_t)last < INT64_MAX) {
+ double dts = (is_relative(ts) ? ts - RELATIVE_TS_BASE : ts) * av_q2d(st->time_base);
+ int64_t duration = ts - last;
+
+ if (!st->info->duration_error)
+ st->info->duration_error = av_mallocz(sizeof(st->info->duration_error[0])*2);
+ if (!st->info->duration_error)
+ return AVERROR(ENOMEM);
+
+// if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
+// av_log(NULL, AV_LOG_ERROR, "%f\n", dts);
+ for (i = 0; i<MAX_STD_TIMEBASES; i++) {
+ if (st->info->duration_error[0][1][i] < 1e10) {
+ int framerate = get_std_framerate(i);
+ double sdts = dts*framerate/(1001*12);
+ for (j= 0; j<2; j++) {
+ int64_t ticks = llrint(sdts+j*0.5);
+ double error= sdts - ticks + j*0.5;
+ st->info->duration_error[j][0][i] += error;
+ st->info->duration_error[j][1][i] += error*error;
+ }
+ }
+ }
+ st->info->duration_count++;
+ st->info->rfps_duration_sum += duration;
+
+ if (st->info->duration_count % 10 == 0) {
+ int n = st->info->duration_count;
+ for (i = 0; i<MAX_STD_TIMEBASES; i++) {
+ if (st->info->duration_error[0][1][i] < 1e10) {
+ double a0 = st->info->duration_error[0][0][i] / n;
+ double error0 = st->info->duration_error[0][1][i] / n - a0*a0;
+ double a1 = st->info->duration_error[1][0][i] / n;
+ double error1 = st->info->duration_error[1][1][i] / n - a1*a1;
+ if (error0 > 0.04 && error1 > 0.04) {
+ st->info->duration_error[0][1][i] = 2e10;
+ st->info->duration_error[1][1][i] = 2e10;
+ }
+ }
+ }
+ }
+
+ // ignore the first 4 values, they might have some random jitter
+ if (st->info->duration_count > 3 && is_relative(ts) == is_relative(last))
+ st->info->duration_gcd = av_gcd(st->info->duration_gcd, duration);
+ }
+ if (ts != AV_NOPTS_VALUE)
+ st->info->last_dts = ts;
+
+ return 0;
+}
+
+void ff_rfps_calculate(AVFormatContext *ic)
+{
+ int i, j;
+
+ for (i = 0; i < ic->nb_streams; i++) {
+ AVStream *st = ic->streams[i];
+
+ if (st->codec->codec_type != AVMEDIA_TYPE_VIDEO)
+ continue;
+ // the check for tb_unreliable() is not completely correct, since this is not about handling
+ // a unreliable/inexact time base, but a time base that is finer than necessary, as e.g.
+ // ipmovie.c produces.
+ if (tb_unreliable(st->codec) && st->info->duration_count > 15 && st->info->duration_gcd > FFMAX(1, st->time_base.den/(500LL*st->time_base.num)) && !st->r_frame_rate.num)
+ av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, st->time_base.den, st->time_base.num * st->info->duration_gcd, INT_MAX);
+ if (st->info->duration_count>1 && !st->r_frame_rate.num
+ && tb_unreliable(st->codec)) {
+ int num = 0;
+ double best_error= 0.01;
+ AVRational ref_rate = st->r_frame_rate.num ? st->r_frame_rate : av_inv_q(st->time_base);
+
+ for (j= 0; j<MAX_STD_TIMEBASES; j++) {
+ int k;
+
+ if (st->info->codec_info_duration && st->info->codec_info_duration*av_q2d(st->time_base) < (1001*12.0)/get_std_framerate(j))
+ continue;
+ if (!st->info->codec_info_duration && 1.0 < (1001*12.0)/get_std_framerate(j))
+ continue;
+
+ if (av_q2d(st->time_base) * st->info->rfps_duration_sum / st->info->duration_count < (1001*12.0 * 0.8)/get_std_framerate(j))
+ continue;
+
+ for (k= 0; k<2; k++) {
+ int n = st->info->duration_count;
+ double a= st->info->duration_error[k][0][j] / n;
+ double error= st->info->duration_error[k][1][j]/n - a*a;
+
+ if (error < best_error && best_error> 0.000000001) {
+ best_error= error;
+ num = get_std_framerate(j);
+ }
+ if (error < 0.02)
+ av_log(NULL, AV_LOG_DEBUG, "rfps: %f %f\n", get_std_framerate(j) / 12.0/1001, error);
+ }
+ }
+ // do not increase frame rate by more than 1 % in order to match a standard rate.
+ if (num && (!ref_rate.num || (double)num/(12*1001) < 1.01 * av_q2d(ref_rate)))
+ av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, num, 12*1001, INT_MAX);
+ }
+ if ( !st->avg_frame_rate.num
+ && st->r_frame_rate.num && st->info->rfps_duration_sum
+ && st->info->codec_info_duration <= 0
+ && st->info->duration_count > 2
+ && fabs(1.0 / (av_q2d(st->r_frame_rate) * av_q2d(st->time_base)) - st->info->rfps_duration_sum / (double)st->info->duration_count) <= 1.0
+ ) {
+ av_log(ic, AV_LOG_DEBUG, "Setting avg frame rate based on r frame rate\n");
+ st->avg_frame_rate = st->r_frame_rate;
+ }
+
+ av_freep(&st->info->duration_error);
+ st->info->last_dts = AV_NOPTS_VALUE;
+ st->info->duration_count = 0;
+ st->info->rfps_duration_sum = 0;
+ }
}
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
{
- int i, count, ret, read_size, j;
+ int i, count, ret = 0, j;
+ int64_t read_size;
AVStream *st;
AVPacket pkt1, *pkt;
int64_t old_offset = avio_tell(ic->pb);
// new streams might appear, no options for those
int orig_nb_streams = ic->nb_streams;
+ int flush_codecs;
+ int64_t max_analyze_duration = ic->max_analyze_duration2;
+ int64_t max_stream_analyze_duration;
+ int64_t probesize = ic->probesize2;
+
+ if (!max_analyze_duration)
+ max_analyze_duration = ic->max_analyze_duration;
+ if (ic->probesize)
+ probesize = ic->probesize;
+ flush_codecs = probesize > 0;
+
+ av_opt_set(ic, "skip_clear", "1", AV_OPT_SEARCH_CHILDREN);
+
+ max_stream_analyze_duration = max_analyze_duration;
+ if (!max_analyze_duration) {
+ max_stream_analyze_duration =
+ max_analyze_duration = 5*AV_TIME_BASE;
+ if (!strcmp(ic->iformat->name, "flv"))
+ max_stream_analyze_duration = 30*AV_TIME_BASE;
+ }
+
+ if (ic->pb)
+ av_log(ic, AV_LOG_DEBUG, "Before avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d\n",
+ avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count);
for (i = 0; i < ic->nb_streams; i++) {
const AVCodec *codec;
AVDictionary *thread_opt = NULL;
st = ic->streams[i];
+ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
+ st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+/* if (!st->time_base.num)
+ st->time_base = */
+ if (!st->codec->time_base.num)
+ st->codec->time_base = st->time_base;
+ }
// only for the split stuff
if (!st->parser && !(ic->flags & AVFMT_FLAG_NOPARSE)) {
st->parser = av_parser_init(st->codec->codec_id);
- if (st->need_parsing == AVSTREAM_PARSE_HEADERS && st->parser)
- st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
+ if (st->parser) {
+ if (st->need_parsing == AVSTREAM_PARSE_HEADERS) {
+ st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
+ } else if (st->need_parsing == AVSTREAM_PARSE_FULL_RAW) {
+ st->parser->flags |= PARSER_FLAG_USE_CODEC_TS;
+ }
+ } else if (st->need_parsing) {
+ av_log(ic, AV_LOG_VERBOSE, "parser not found for codec "
+ "%s, packets or times may be invalid.\n",
+ avcodec_get_name(st->codec->codec_id));
+ }
}
- codec = st->codec->codec ? st->codec->codec
- : avcodec_find_decoder(st->codec->codec_id);
+ codec = find_decoder(ic, st, st->codec->codec_id);
/* Force thread count to 1 since the H.264 decoder will not extract
* SPS and PPS to extradata during multi-threaded decoding. */
av_dict_set(options ? &options[i] : &thread_opt, "threads", "1", 0);
+ if (ic->codec_whitelist)
+ av_dict_set(options ? &options[i] : &thread_opt, "codec_whitelist", ic->codec_whitelist, 0);
+
/* Ensure that subtitle_header is properly set. */
if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE
- && codec && !st->codec->codec)
- avcodec_open2(st->codec, codec,
- options ? &options[i] : &thread_opt);
+ && codec && !st->codec->codec) {
+ if (avcodec_open2(st->codec, codec, options ? &options[i] : &thread_opt) < 0)
+ av_log(ic, AV_LOG_WARNING,
+ "Failed to open codec in av_find_stream_info\n");
+ }
// Try to just open decoders, in case this is enough to get parameters.
- if (!has_codec_parameters(st)) {
+ if (!has_codec_parameters(st, NULL) && st->request_probe <= 0) {
if (codec && !st->codec->codec)
- avcodec_open2(st->codec, codec,
- options ? &options[i] : &thread_opt);
+ if (avcodec_open2(st->codec, codec, options ? &options[i] : &thread_opt) < 0)
+ av_log(ic, AV_LOG_WARNING,
+ "Failed to open codec in av_find_stream_info\n");
}
if (!options)
av_dict_free(&thread_opt);
}
for (i = 0; i < ic->nb_streams; i++) {
+#if FF_API_R_FRAME_RATE
+ ic->streams[i]->info->last_dts = AV_NOPTS_VALUE;
+#endif
ic->streams[i]->info->fps_first_dts = AV_NOPTS_VALUE;
ic->streams[i]->info->fps_last_dts = AV_NOPTS_VALUE;
}
@@ -2082,6 +3100,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
count = 0;
read_size = 0;
for (;;) {
+ int analyzed_all_streams;
if (ff_check_interrupt(&ic->interrupt_callback)) {
ret = AVERROR_EXIT;
av_log(ic, AV_LOG_DEBUG, "interrupted\n");
@@ -2093,44 +3112,60 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
int fps_analyze_framecount = 20;
st = ic->streams[i];
- if (!has_codec_parameters(st))
+ if (!has_codec_parameters(st, NULL))
break;
/* If the timebase is coarse (like the usual millisecond precision
* of mkv), we need to analyze more frames to reliably arrive at
* the correct fps. */
if (av_q2d(st->time_base) > 0.0005)
fps_analyze_framecount *= 2;
+ if (!tb_unreliable(st->codec))
+ fps_analyze_framecount = 0;
if (ic->fps_probe_size >= 0)
fps_analyze_framecount = ic->fps_probe_size;
+ if (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
+ fps_analyze_framecount = 0;
/* variable fps and no guess at the real fps */
- if (!st->avg_frame_rate.num &&
- st->codec_info_nb_frames < fps_analyze_framecount &&
+ if (!(st->r_frame_rate.num && st->avg_frame_rate.num) &&
+ st->info->duration_count < fps_analyze_framecount &&
st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
break;
if (st->parser && st->parser->parser->split &&
!st->codec->extradata)
break;
if (st->first_dts == AV_NOPTS_VALUE &&
+ !(ic->iformat->flags & AVFMT_NOTIMESTAMPS) &&
st->codec_info_nb_frames < ic->max_ts_probe &&
(st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
st->codec->codec_type == AVMEDIA_TYPE_AUDIO))
break;
}
+ analyzed_all_streams = 0;
if (i == ic->nb_streams) {
+ analyzed_all_streams = 1;
/* NOTE: If the format has no header, then we need to read some
* packets to get most of the streams, so we cannot stop here. */
if (!(ic->ctx_flags & AVFMTCTX_NOHEADER)) {
/* If we found the info for all the codecs, we can stop. */
ret = count;
av_log(ic, AV_LOG_DEBUG, "All info found\n");
+ flush_codecs = 0;
break;
}
}
/* We did not get all the codec info, but we read too much data. */
- if (read_size >= ic->probesize) {
+ if (read_size >= probesize) {
ret = count;
av_log(ic, AV_LOG_DEBUG,
- "Probe buffer size limit %d reached\n", ic->probesize);
+ "Probe buffer size limit of %"PRId64" bytes reached\n", probesize);
+ for (i = 0; i < ic->nb_streams; i++)
+ if (!ic->streams[i]->r_frame_rate.num &&
+ ic->streams[i]->info->duration_count <= 1 &&
+ ic->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
+ strcmp(ic->iformat->name, "image2"))
+ av_log(ic, AV_LOG_WARNING,
+ "Stream #%d: not enough frames to estimate rate; "
+ "consider increasing probesize\n", i);
break;
}
@@ -2142,56 +3177,32 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
if (ret < 0) {
/* EOF or error*/
- AVPacket empty_pkt = { 0 };
- int err = 0;
- av_init_packet(&empty_pkt);
-
- /* We could not have all the codec parameters before EOF. */
- ret = -1;
- for (i = 0; i < ic->nb_streams; i++) {
- st = ic->streams[i];
-
- /* flush the decoders */
- if (st->info->found_decoder == 1) {
- do {
- err = try_decode_frame(st, &empty_pkt,
- (options && i < orig_nb_streams)
- ? &options[i] : NULL);
- } while (err > 0 && !has_codec_parameters(st));
- }
-
- if (err < 0) {
- av_log(ic, AV_LOG_WARNING,
- "decoding for stream %d failed\n", st->index);
- } else if (!has_codec_parameters(st)) {
- char buf[256];
- avcodec_string(buf, sizeof(buf), st->codec, 0);
- av_log(ic, AV_LOG_WARNING,
- "Could not find codec parameters (%s)\n", buf);
- } else {
- ret = 0;
- }
- }
break;
}
- if (ic->flags & AVFMT_FLAG_NOBUFFER) {
- pkt = &pkt1;
- } else {
+ if (ic->flags & AVFMT_FLAG_NOBUFFER)
+ free_packet_buffer(&ic->internal->packet_buffer,
+ &ic->internal->packet_buffer_end);
+ {
pkt = add_to_pktbuf(&ic->internal->packet_buffer, &pkt1,
&ic->internal->packet_buffer_end);
+ if (!pkt) {
+ ret = AVERROR(ENOMEM);
+ goto find_stream_info_err;
+ }
if ((ret = av_dup_packet(pkt)) < 0)
goto find_stream_info_err;
}
- read_size += pkt->size;
-
st = ic->streams[pkt->stream_index];
+ if (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC))
+ read_size += pkt->size;
+
if (pkt->dts != AV_NOPTS_VALUE && st->codec_info_nb_frames > 1) {
/* check for non-increasing dts */
if (st->info->fps_last_dts != AV_NOPTS_VALUE &&
st->info->fps_last_dts >= pkt->dts) {
- av_log(ic, AV_LOG_WARNING,
+ av_log(ic, AV_LOG_DEBUG,
"Non-increasing DTS in stream %d: packet %d with DTS "
"%"PRId64", packet %d with DTS %"PRId64"\n",
st->index, st->info->fps_last_dts_idx,
@@ -2225,24 +3236,42 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
}
st->info->fps_last_dts = pkt->dts;
st->info->fps_last_dts_idx = st->codec_info_nb_frames;
-
- /* check max_analyze_duration */
- if (av_rescale_q(pkt->dts - st->info->fps_first_dts, st->time_base,
- AV_TIME_BASE_Q) >= ic->max_analyze_duration) {
- av_log(ic, AV_LOG_WARNING, "max_analyze_duration %d reached\n",
- ic->max_analyze_duration);
+ }
+ if (st->codec_info_nb_frames>1) {
+ int64_t t = 0;
+
+ if (st->time_base.den > 0)
+ t = av_rescale_q(st->info->codec_info_duration, st->time_base, AV_TIME_BASE_Q);
+ if (st->avg_frame_rate.num > 0)
+ t = FFMAX(t, av_rescale_q(st->codec_info_nb_frames, av_inv_q(st->avg_frame_rate), AV_TIME_BASE_Q));
+
+ if ( t == 0
+ && st->codec_info_nb_frames>30
+ && st->info->fps_first_dts != AV_NOPTS_VALUE
+ && st->info->fps_last_dts != AV_NOPTS_VALUE)
+ t = FFMAX(t, av_rescale_q(st->info->fps_last_dts - st->info->fps_first_dts, st->time_base, AV_TIME_BASE_Q));
+
+ if (t >= (analyzed_all_streams ? max_analyze_duration : max_stream_analyze_duration)) {
+ av_log(ic, AV_LOG_VERBOSE, "max_analyze_duration %"PRId64" reached at %"PRId64" microseconds\n",
+ max_analyze_duration,
+ t);
if (ic->flags & AVFMT_FLAG_NOBUFFER)
av_packet_unref(pkt);
break;
}
+ if (pkt->duration) {
+ st->info->codec_info_duration += pkt->duration;
+ st->info->codec_info_duration_fields += st->parser && st->need_parsing && st->codec->ticks_per_frame ==2 ? st->parser->repeat_pict + 1 : 2;
+ }
}
+#if FF_API_R_FRAME_RATE
+ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
+ ff_rfps_add_frame(ic, st, pkt->dts);
+#endif
if (st->parser && st->parser->parser->split && !st->codec->extradata) {
int i = st->parser->parser->split(st->codec, pkt->data, pkt->size);
if (i > 0 && i < FF_MAX_EXTRADATA_SIZE) {
- st->codec->extradata_size = i;
- st->codec->extradata = av_mallocz(st->codec->extradata_size +
- FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
+ if (ff_alloc_extradata(st->codec, i))
return AVERROR(ENOMEM);
memcpy(st->codec->extradata, pkt->data,
st->codec->extradata_size);
@@ -2258,7 +3287,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
* least one frame of codec data, this makes sure the codec initializes
* the channel configuration and does not only trust the values from
* the container. */
- try_decode_frame(st, pkt,
+ try_decode_frame(ic, st, pkt,
(options && i < orig_nb_streams) ? &options[i] : NULL);
if (ic->flags & AVFMT_FLAG_NOBUFFER)
@@ -2268,31 +3297,62 @@ 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);
+
+ for (i = 0; i < ic->nb_streams; i++) {
+
+ st = ic->streams[i];
+
+ /* flush the decoders */
+ if (st->info->found_decoder == 1) {
+ do {
+ err = try_decode_frame(ic, st, &empty_pkt,
+ (options && i < orig_nb_streams)
+ ? &options[i] : NULL);
+ } while (err > 0 && !has_codec_parameters(st, NULL));
+
+ if (err < 0) {
+ av_log(ic, AV_LOG_INFO,
+ "decoding for stream %d failed\n", st->index);
+ }
+ }
+ }
+ }
+
// close codecs which were opened in try_decode_frame()
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
avcodec_close(st->codec);
}
+
+ ff_rfps_calculate(ic);
+
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (st->codec->codec_id == AV_CODEC_ID_RAWVIDEO && !st->codec->codec_tag && !st->codec->bits_per_coded_sample) {
+ uint32_t tag= avcodec_pix_fmt_to_codec_tag(st->codec->pix_fmt);
+ if (avpriv_find_pix_fmt(avpriv_get_raw_pix_fmt_tags(), tag) == st->codec->pix_fmt)
+ st->codec->codec_tag= tag;
+ }
+
/* estimate average framerate if not set by demuxer */
- if (!st->avg_frame_rate.num &&
- st->info->fps_last_dts != st->info->fps_first_dts) {
- int64_t delta_dts = st->info->fps_last_dts -
- st->info->fps_first_dts;
- int delta_packets = st->info->fps_last_dts_idx -
- st->info->fps_first_dts_idx;
+ if (st->info->codec_info_duration_fields &&
+ !st->avg_frame_rate.num &&
+ st->info->codec_info_duration) {
int best_fps = 0;
double best_error = 0.01;
- if (delta_dts >= INT64_MAX / st->time_base.num ||
- delta_packets >= INT64_MAX / st->time_base.den ||
- delta_dts < 0)
+ if (st->info->codec_info_duration >= INT64_MAX / st->time_base.num / 2||
+ st->info->codec_info_duration_fields >= INT64_MAX / st->time_base.den ||
+ st->info->codec_info_duration < 0)
continue;
av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
- delta_packets * (int64_t) st->time_base.den,
- delta_dts * (int64_t) st->time_base.num, 60000);
+ st->info->codec_info_duration_fields * (int64_t) st->time_base.den,
+ st->info->codec_info_duration * 2 * (int64_t) st->time_base.num, 60000);
/* Round guessed framerate to a "standard" framerate if it's
* within 1% of the original estimate. */
@@ -2310,6 +3370,22 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
best_fps, 12 * 1001, INT_MAX);
}
+
+ if (!st->r_frame_rate.num) {
+ if ( st->codec->time_base.den * (int64_t) st->time_base.num
+ <= st->codec->time_base.num * st->codec->ticks_per_frame * (int64_t) st->time_base.den) {
+ st->r_frame_rate.num = st->codec->time_base.den;
+ st->r_frame_rate.den = st->codec->time_base.num * st->codec->ticks_per_frame;
+ } else {
+ st->r_frame_rate.num = st->time_base.den;
+ st->r_frame_rate.den = st->time_base.num;
+ }
+ }
+ if (st->display_aspect_ratio.num && st->display_aspect_ratio.den) {
+ AVRational hw_ratio = { st->codec->height, st->codec->width };
+ st->sample_aspect_ratio = av_mul_q(st->display_aspect_ratio,
+ hw_ratio);
+ }
} else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
if (!st->codec->bits_per_coded_sample)
st->codec->bits_per_coded_sample =
@@ -2335,26 +3411,60 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
}
}
+ if (probesize)
estimate_timings(ic, old_offset);
+ av_opt_set(ic, "skip_clear", "0", AV_OPT_SEARCH_CHILDREN);
+
+ if (ret >= 0 && ic->nb_streams)
+ /* We could not have all the codec parameters before EOF. */
+ ret = -1;
+ for (i = 0; i < ic->nb_streams; i++) {
+ const char *errmsg;
+ st = ic->streams[i];
+ if (!has_codec_parameters(st, &errmsg)) {
+ char buf[256];
+ avcodec_string(buf, sizeof(buf), st->codec, 0);
+ av_log(ic, AV_LOG_WARNING,
+ "Could not find codec parameters for stream %d (%s): %s\n"
+ "Consider increasing the value for the 'analyzeduration' and 'probesize' options\n",
+ i, buf, errmsg);
+ } else {
+ ret = 0;
+ }
+ }
+
compute_chapters_end(ic);
find_stream_info_err:
for (i = 0; i < ic->nb_streams; i++) {
- ic->streams[i]->codec->thread_count = 0;
+ st = ic->streams[i];
+ if (ic->streams[i]->codec->codec_type != AVMEDIA_TYPE_AUDIO)
+ ic->streams[i]->codec->thread_count = 0;
+ if (st->info)
+ av_freep(&st->info->duration_error);
av_freep(&ic->streams[i]->info);
}
+ if (ic->pb)
+ av_log(ic, AV_LOG_DEBUG, "After avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d frames:%d\n",
+ avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count, count);
return ret;
}
-static AVProgram *find_program_from_stream(AVFormatContext *ic, int s)
+AVProgram *av_find_program_from_stream(AVFormatContext *ic, AVProgram *last, int s)
{
int i, j;
- for (i = 0; i < ic->nb_programs; i++)
- for (j = 0; j < ic->programs[i]->nb_stream_indexes; j++)
- if (ic->programs[i]->stream_index[j] == s)
- return ic->programs[i];
+ for (i = 0; i < ic->nb_programs; i++) {
+ if (ic->programs[i] == last) {
+ last = NULL;
+ } else {
+ if (!last)
+ for (j = 0; j < ic->programs[i]->nb_stream_indexes; j++)
+ if (ic->programs[i]->stream_index[j] == s)
+ return ic->programs[i];
+ }
+ }
return NULL;
}
@@ -2363,12 +3473,12 @@ int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type,
AVCodec **decoder_ret, int flags)
{
int i, nb_streams = ic->nb_streams;
- int ret = AVERROR_STREAM_NOT_FOUND, best_count = -1;
+ int ret = AVERROR_STREAM_NOT_FOUND, best_count = -1, best_bitrate = -1, best_multiframe = -1, count, bitrate, multiframe;
unsigned *program = NULL;
- AVCodec *decoder = NULL, *best_decoder = NULL;
+ const AVCodec *decoder = NULL, *best_decoder = NULL;
if (related_stream >= 0 && wanted_stream_nb < 0) {
- AVProgram *p = find_program_from_stream(ic, related_stream);
+ AVProgram *p = av_find_program_from_stream(ic, NULL, related_stream);
if (p) {
program = p->stream_index;
nb_streams = p->nb_stream_indexes;
@@ -2382,20 +3492,32 @@ int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type,
continue;
if (wanted_stream_nb >= 0 && real_stream_index != wanted_stream_nb)
continue;
- if (st->disposition & (AV_DISPOSITION_HEARING_IMPAIRED |
+ if (wanted_stream_nb != real_stream_index &&
+ st->disposition & (AV_DISPOSITION_HEARING_IMPAIRED |
AV_DISPOSITION_VISUAL_IMPAIRED))
continue;
+ if (type == AVMEDIA_TYPE_AUDIO && !avctx->channels)
+ continue;
if (decoder_ret) {
- decoder = avcodec_find_decoder(st->codec->codec_id);
+ decoder = find_decoder(ic, st, st->codec->codec_id);
if (!decoder) {
if (ret < 0)
ret = AVERROR_DECODER_NOT_FOUND;
continue;
}
}
- if (best_count >= st->codec_info_nb_frames)
+ count = st->codec_info_nb_frames;
+ bitrate = avctx->bit_rate;
+ if (!bitrate)
+ bitrate = avctx->rc_max_rate;
+ multiframe = FFMIN(5, count);
+ if ((best_multiframe > multiframe) ||
+ (best_multiframe == multiframe && best_bitrate > bitrate) ||
+ (best_multiframe == multiframe && best_bitrate == bitrate && best_count >= count))
continue;
- best_count = st->codec_info_nb_frames;
+ best_count = count;
+ best_bitrate = bitrate;
+ best_multiframe = multiframe;
ret = real_stream_index;
best_decoder = decoder;
if (program && i == nb_streams - 1 && ret < 0) {
@@ -2406,7 +3528,7 @@ int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type,
}
}
if (decoder_ret)
- *decoder_ret = best_decoder;
+ *decoder_ret = (AVCodec*)best_decoder;
return ret;
}
@@ -2430,10 +3552,38 @@ int av_read_pause(AVFormatContext *s)
return AVERROR(ENOSYS);
}
+void ff_free_stream(AVFormatContext *s, AVStream *st) {
+ int j;
+ av_assert0(s->nb_streams>0);
+ av_assert0(s->streams[ s->nb_streams - 1 ] == st);
+
+ for (j = 0; j < st->nb_side_data; j++)
+ av_freep(&st->side_data[j].data);
+ av_freep(&st->side_data);
+ st->nb_side_data = 0;
+
+ if (st->parser) {
+ av_parser_close(st->parser);
+ }
+ if (st->attached_pic.data)
+ av_free_packet(&st->attached_pic);
+ av_dict_free(&st->metadata);
+ av_freep(&st->probe_data.buf);
+ av_freep(&st->index_entries);
+ av_freep(&st->codec->extradata);
+ av_freep(&st->codec->subtitle_header);
+ av_freep(&st->codec);
+ av_freep(&st->priv_data);
+ if (st->info)
+ av_freep(&st->info->duration_error);
+ av_freep(&st->info);
+ av_freep(&st->recommended_encoder_configuration);
+ av_freep(&s->streams[ --s->nb_streams ]);
+}
+
void avformat_free_context(AVFormatContext *s)
{
- int i, j;
- AVStream *st;
+ int i;
if (!s)
return;
@@ -2441,30 +3591,11 @@ void avformat_free_context(AVFormatContext *s)
av_opt_free(s);
if (s->iformat && s->iformat->priv_class && s->priv_data)
av_opt_free(s->priv_data);
+ if (s->oformat && s->oformat->priv_class && s->priv_data)
+ av_opt_free(s->priv_data);
- for (i = 0; i < s->nb_streams; i++) {
- /* free all data in a stream component */
- st = s->streams[i];
-
- for (j = 0; j < st->nb_side_data; j++)
- av_freep(&st->side_data[j].data);
- av_freep(&st->side_data);
- st->nb_side_data = 0;
-
- if (st->parser) {
- av_parser_close(st->parser);
- }
- if (st->attached_pic.data)
- av_free_packet(&st->attached_pic);
- av_dict_free(&st->metadata);
- av_freep(&st->probe_data.buf);
- av_free(st->index_entries);
- av_free(st->codec->extradata);
- av_free(st->codec->subtitle_header);
- av_free(st->codec);
- av_free(st->priv_data);
- av_free(st->info);
- av_free(st);
+ for (i = s->nb_streams - 1; i >= 0; i--) {
+ ff_free_stream(s, s->streams[i]);
}
for (i = s->nb_programs - 1; i >= 0; i--) {
av_dict_free(&s->programs[i]->metadata);
@@ -2475,21 +3606,28 @@ void avformat_free_context(AVFormatContext *s)
av_freep(&s->priv_data);
while (s->nb_chapters--) {
av_dict_free(&s->chapters[s->nb_chapters]->metadata);
- av_free(s->chapters[s->nb_chapters]);
+ av_freep(&s->chapters[s->nb_chapters]);
}
av_freep(&s->chapters);
av_dict_free(&s->metadata);
av_freep(&s->streams);
av_freep(&s->internal);
+ flush_packet_queue(s);
av_free(s);
}
void avformat_close_input(AVFormatContext **ps)
{
- AVFormatContext *s = *ps;
- AVIOContext *pb = s->pb;
+ AVFormatContext *s;
+ AVIOContext *pb;
- if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
+ if (!ps || !*ps)
+ return;
+
+ s = *ps;
+ pb = s->pb;
+
+ if ((s->iformat && strcmp(s->iformat->name, "image2") && s->iformat->flags & AVFMT_NOFILE) ||
(s->flags & AVFMT_FLAG_CUSTOM_IO))
pb = NULL;
@@ -2510,12 +3648,14 @@ AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)
{
AVStream *st;
int i;
+ AVStream **streams;
- if (av_reallocp_array(&s->streams, s->nb_streams + 1,
- sizeof(*s->streams)) < 0) {
- s->nb_streams = 0;
+ if (s->nb_streams >= INT_MAX/sizeof(*streams))
return NULL;
- }
+ streams = av_realloc_array(s->streams, s->nb_streams + 1, sizeof(*streams));
+ if (!streams)
+ return NULL;
+ s->streams = streams;
st = av_mallocz(sizeof(AVStream));
if (!st)
@@ -2524,6 +3664,7 @@ AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)
av_free(st);
return NULL;
}
+ st->info->last_dts = AV_NOPTS_VALUE;
st->codec = avcodec_alloc_context3(c);
if (!st->codec) {
@@ -2546,19 +3687,27 @@ AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)
* but durations get some timestamps, formats with some unknown
* timestamps have their first few packets buffered and the
* timestamps corrected before they are returned to the user */
- st->cur_dts = 0;
+ st->cur_dts = s->iformat ? RELATIVE_TS_BASE : 0;
st->first_dts = AV_NOPTS_VALUE;
st->probe_packets = MAX_PROBE_PACKETS;
+ st->pts_wrap_reference = AV_NOPTS_VALUE;
+ st->pts_wrap_behavior = AV_PTS_WRAP_IGNORE;
st->last_IP_pts = AV_NOPTS_VALUE;
+ st->last_dts_for_order_check = AV_NOPTS_VALUE;
for (i = 0; i < MAX_REORDER_DELAY + 1; i++)
st->pts_buffer[i] = AV_NOPTS_VALUE;
st->sample_aspect_ratio = (AVRational) { 0, 1 };
+#if FF_API_R_FRAME_RATE
+ st->info->last_dts = AV_NOPTS_VALUE;
+#endif
st->info->fps_first_dts = AV_NOPTS_VALUE;
st->info->fps_last_dts = AV_NOPTS_VALUE;
+ st->inject_global_side_data = s->internal->inject_global_side_data;
+
s->streams[s->nb_streams++] = st;
return st;
}
@@ -2582,6 +3731,11 @@ AVProgram *av_new_program(AVFormatContext *ac, int id)
program->discard = AVDISCARD_NONE;
}
program->id = id;
+ program->pts_wrap_reference = AV_NOPTS_VALUE;
+ program->pts_wrap_behavior = AV_PTS_WRAP_IGNORE;
+
+ program->start_time =
+ program->end_time = AV_NOPTS_VALUE;
return program;
}
@@ -2592,6 +3746,11 @@ AVChapter *avpriv_new_chapter(AVFormatContext *s, int id, AVRational time_base,
AVChapter *chapter = NULL;
int i;
+ if (end != AV_NOPTS_VALUE && start > end) {
+ av_log(s, AV_LOG_ERROR, "Chapter end time %"PRId64" before start %"PRId64"\n", end, start);
+ return NULL;
+ }
+
for (i = 0; i < s->nb_chapters; i++)
if (s->chapters[i]->id == id)
chapter = s->chapters[i];
@@ -2615,6 +3774,7 @@ void ff_program_add_stream_index(AVFormatContext *ac, int progid, unsigned idx)
{
int i, j;
AVProgram *program = NULL;
+ void *tmp;
if (idx >= ac->nb_streams) {
av_log(ac, AV_LOG_ERROR, "stream index %d is not valid\n", idx);
@@ -2629,12 +3789,10 @@ void ff_program_add_stream_index(AVFormatContext *ac, int progid, unsigned idx)
if (program->stream_index[j] == idx)
return;
- if (av_reallocp_array(&program->stream_index,
- program->nb_stream_indexes + 1,
- sizeof(*program->stream_index)) < 0) {
- program->nb_stream_indexes = 0;
+ tmp = av_realloc_array(program->stream_index, program->nb_stream_indexes+1, sizeof(unsigned int));
+ if (!tmp)
return;
- }
+ program->stream_index = tmp;
program->stream_index[program->nb_stream_indexes++] = idx;
return;
}
@@ -2673,6 +3831,8 @@ int av_get_frame_filename(char *buf, int buf_size, const char *path, int number)
if (percentd_found)
goto fail;
percentd_found = 1;
+ if (number < 0)
+ nd += 1;
snprintf(buf1, sizeof(buf1), "%0*d", nd, number);
len = strlen(buf1);
if ((q - buf + len) > buf_size - 1)
@@ -2703,7 +3863,7 @@ void av_url_split(char *proto, int proto_size,
char *hostname, int hostname_size,
int *port_ptr, char *path, int path_size, const char *url)
{
- const char *p, *ls, *at, *col, *brk;
+ const char *p, *ls, *ls2, *at, *at2, *col, *brk;
if (port_ptr)
*port_ptr = -1;
@@ -2732,8 +3892,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
@@ -2742,9 +3905,10 @@ void av_url_split(char *proto, int proto_size,
/* the rest is hostname, use that to parse auth/port */
if (ls != p) {
/* authorization (user[:pass]@hostname) */
- if ((at = strchr(p, '@')) && at < ls) {
- av_strlcpy(authorization, p,
- FFMIN(authorization_size, at + 1 - p));
+ at2 = p;
+ while ((at = strchr(p, '@')) && at < ls) {
+ av_strlcpy(authorization, at2,
+ FFMIN(authorization_size, at + 1 - at2));
p = at + 1; /* skip '@' */
}
@@ -2829,11 +3993,13 @@ void avpriv_set_pts_info(AVStream *s, int pts_wrap_bits,
if (new_tb.num <= 0 || new_tb.den <= 0) {
av_log(NULL, AV_LOG_ERROR,
- "Ignoring attempt to set invalid timebase for st:%d\n",
+ "Ignoring attempt to set invalid timebase %d/%d for st:%d\n",
+ new_tb.num, new_tb.den,
s->index);
return;
}
s->time_base = new_tb;
+ av_codec_set_pkt_timebase(s->codec, new_tb);
s->pts_wrap_bits = pts_wrap_bits;
}
@@ -2902,21 +4068,14 @@ int ff_find_stream_index(AVFormatContext *s, int id)
int64_t ff_iso8601_to_unix_time(const char *datestr)
{
-#if HAVE_STRPTIME
struct tm time1 = { 0 }, time2 = { 0 };
char *ret1, *ret2;
- ret1 = strptime(datestr, "%Y - %m - %d %T", &time1);
- ret2 = strptime(datestr, "%Y - %m - %dT%T", &time2);
+ ret1 = av_small_strptime(datestr, "%Y - %m - %d %H:%M:%S", &time1);
+ ret2 = av_small_strptime(datestr, "%Y - %m - %dT%H:%M:%S", &time2);
if (ret2 && !ret1)
return av_timegm(&time2);
else
return av_timegm(&time1);
-#else
- av_log(NULL, AV_LOG_WARNING,
- "strptime() unavailable on this system, cannot convert "
- "the date string.\n");
- return 0;
-#endif
}
int avformat_query_codec(const AVOutputFormat *ofmt, enum AVCodecID codec_id,
@@ -2942,7 +4101,8 @@ int avformat_network_init(void)
ff_network_inited_globally = 1;
if ((ret = ff_network_init()) < 0)
return ret;
- ff_tls_init();
+ if ((ret = ff_tls_init()) < 0)
+ return ret;
#endif
return 0;
}
@@ -2998,6 +4158,136 @@ int ff_add_param_change(AVPacket *pkt, int32_t channels,
return 0;
}
+AVRational av_guess_sample_aspect_ratio(AVFormatContext *format, AVStream *stream, AVFrame *frame)
+{
+ AVRational undef = {0, 1};
+ AVRational stream_sample_aspect_ratio = stream ? stream->sample_aspect_ratio : undef;
+ AVRational codec_sample_aspect_ratio = stream && stream->codec ? stream->codec->sample_aspect_ratio : undef;
+ AVRational frame_sample_aspect_ratio = frame ? frame->sample_aspect_ratio : codec_sample_aspect_ratio;
+
+ av_reduce(&stream_sample_aspect_ratio.num, &stream_sample_aspect_ratio.den,
+ stream_sample_aspect_ratio.num, stream_sample_aspect_ratio.den, INT_MAX);
+ if (stream_sample_aspect_ratio.num <= 0 || stream_sample_aspect_ratio.den <= 0)
+ stream_sample_aspect_ratio = undef;
+
+ av_reduce(&frame_sample_aspect_ratio.num, &frame_sample_aspect_ratio.den,
+ frame_sample_aspect_ratio.num, frame_sample_aspect_ratio.den, INT_MAX);
+ if (frame_sample_aspect_ratio.num <= 0 || frame_sample_aspect_ratio.den <= 0)
+ frame_sample_aspect_ratio = undef;
+
+ if (stream_sample_aspect_ratio.num)
+ return stream_sample_aspect_ratio;
+ else
+ return frame_sample_aspect_ratio;
+}
+
+AVRational av_guess_frame_rate(AVFormatContext *format, AVStream *st, AVFrame *frame)
+{
+ AVRational fr = st->r_frame_rate;
+ AVRational codec_fr = st->codec->framerate;
+ AVRational avg_fr = st->avg_frame_rate;
+
+ if (avg_fr.num > 0 && avg_fr.den > 0 && fr.num > 0 && fr.den > 0 &&
+ av_q2d(avg_fr) < 70 && av_q2d(fr) > 210) {
+ fr = avg_fr;
+ }
+
+
+ if (st->codec->ticks_per_frame > 1) {
+ if ( codec_fr.num > 0 && codec_fr.den > 0 &&
+ (fr.num == 0 || av_q2d(codec_fr) < av_q2d(fr)*0.7 && fabs(1.0 - av_q2d(av_div_q(avg_fr, fr))) > 0.1))
+ fr = codec_fr;
+ }
+
+ return fr;
+}
+
+int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st,
+ const char *spec)
+{
+ if (*spec <= '9' && *spec >= '0') /* opt:index */
+ return strtol(spec, NULL, 0) == st->index;
+ else if (*spec == 'v' || *spec == 'a' || *spec == 's' || *spec == 'd' ||
+ *spec == 't') { /* opt:[vasdt] */
+ enum AVMediaType type;
+
+ switch (*spec++) {
+ case 'v': type = AVMEDIA_TYPE_VIDEO; break;
+ case 'a': type = AVMEDIA_TYPE_AUDIO; break;
+ case 's': type = AVMEDIA_TYPE_SUBTITLE; break;
+ case 'd': type = AVMEDIA_TYPE_DATA; break;
+ case 't': type = AVMEDIA_TYPE_ATTACHMENT; break;
+ default: av_assert0(0);
+ }
+ if (type != st->codec->codec_type)
+ return 0;
+ if (*spec++ == ':') { /* possibly followed by :index */
+ int i, index = strtol(spec, NULL, 0);
+ for (i = 0; i < s->nb_streams; i++)
+ if (s->streams[i]->codec->codec_type == type && index-- == 0)
+ return i == st->index;
+ return 0;
+ }
+ return 1;
+ } else if (*spec == 'p' && *(spec + 1) == ':') {
+ int prog_id, i, j;
+ char *endptr;
+ spec += 2;
+ prog_id = strtol(spec, &endptr, 0);
+ for (i = 0; i < s->nb_programs; i++) {
+ if (s->programs[i]->id != prog_id)
+ continue;
+
+ if (*endptr++ == ':') {
+ int stream_idx = strtol(endptr, NULL, 0);
+ return stream_idx >= 0 &&
+ stream_idx < s->programs[i]->nb_stream_indexes &&
+ st->index == s->programs[i]->stream_index[stream_idx];
+ }
+
+ for (j = 0; j < s->programs[i]->nb_stream_indexes; j++)
+ if (st->index == s->programs[i]->stream_index[j])
+ return 1;
+ }
+ return 0;
+ } else if (*spec == '#' ||
+ (*spec == 'i' && *(spec + 1) == ':')) {
+ int stream_id;
+ char *endptr;
+ spec += 1 + (*spec == 'i');
+ stream_id = strtol(spec, &endptr, 0);
+ if (!*endptr)
+ return stream_id == st->id;
+ } else if (*spec == 'm' && *(spec + 1) == ':') {
+ AVDictionaryEntry *tag;
+ char *key, *val;
+ int ret;
+
+ spec += 2;
+ val = strchr(spec, ':');
+
+ key = val ? av_strndup(spec, val - spec) : av_strdup(spec);
+ if (!key)
+ return AVERROR(ENOMEM);
+
+ tag = av_dict_get(st->metadata, key, NULL, 0);
+ if (tag) {
+ if (!val || !strcmp(tag->value, val + 1))
+ ret = 1;
+ else
+ ret = 0;
+ } else
+ ret = 0;
+
+ av_freep(&key);
+ return ret;
+ } else if (!*spec) /* empty specifier, matches everything */
+ return 1;
+
+ av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec);
+ return AVERROR(EINVAL);
+}
+
int ff_generate_avci_extradata(AVStream *st)
{
static const uint8_t avci100_1080p_extradata[] = {
@@ -3032,6 +4322,21 @@ int ff_generate_avci_extradata(AVStream *st)
0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x33, 0x48,
0xd0
};
+ static const uint8_t avci50_1080p_extradata[] = {
+ // SPS
+ 0x00, 0x00, 0x00, 0x01, 0x67, 0x6e, 0x10, 0x28,
+ 0xa6, 0xd4, 0x20, 0x32, 0x33, 0x0c, 0x71, 0x18,
+ 0x88, 0x62, 0x10, 0x19, 0x19, 0x86, 0x38, 0x8c,
+ 0x44, 0x30, 0x21, 0x02, 0x56, 0x4e, 0x6f, 0x37,
+ 0xcd, 0xf9, 0xbf, 0x81, 0x6b, 0xf3, 0x7c, 0xde,
+ 0x6e, 0x6c, 0xd3, 0x3c, 0x05, 0xa0, 0x22, 0x7e,
+ 0x5f, 0xfc, 0x00, 0x0c, 0x00, 0x13, 0x8c, 0x04,
+ 0x04, 0x05, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00,
+ 0x00, 0x03, 0x00, 0x32, 0x84, 0x00, 0x00, 0x00,
+ // PPS
+ 0x00, 0x00, 0x00, 0x01, 0x68, 0xee, 0x31, 0x12,
+ 0x11
+ };
static const uint8_t avci50_1080i_extradata[] = {
// SPS
0x00, 0x00, 0x00, 0x01, 0x67, 0x6e, 0x10, 0x28,
@@ -3042,8 +4347,8 @@ int ff_generate_avci_extradata(AVStream *st)
0x9c, 0x0b, 0x73, 0xe6, 0xc0, 0xb5, 0x18, 0x63,
0x0d, 0x39, 0xe0, 0x5b, 0x02, 0xd4, 0xc6, 0x19,
0x1a, 0x79, 0x8c, 0x32, 0x34, 0x24, 0xf0, 0x16,
- 0x81, 0x13, 0xf7, 0xff, 0x80, 0x01, 0x80, 0x02,
- 0x71, 0x80, 0x80, 0x80, 0xa0, 0x00, 0x00, 0x03,
+ 0x81, 0x13, 0xf7, 0xff, 0x80, 0x02, 0x00, 0x01,
+ 0xf1, 0x80, 0x80, 0x80, 0xa0, 0x00, 0x00, 0x03,
0x00, 0x20, 0x00, 0x00, 0x06, 0x50, 0x80, 0x00,
// PPS
0x00, 0x00, 0x00, 0x01, 0x68, 0xee, 0x31, 0x12,
@@ -3065,6 +4370,21 @@ int ff_generate_avci_extradata(AVStream *st)
0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x31, 0x12,
0x11
};
+ static const uint8_t avci50_720p_extradata[] = {
+ // SPS
+ 0x00, 0x00, 0x00, 0x01, 0x67, 0x6e, 0x10, 0x20,
+ 0xa6, 0xd4, 0x20, 0x32, 0x33, 0x0c, 0x71, 0x18,
+ 0x88, 0x62, 0x10, 0x19, 0x19, 0x86, 0x38, 0x8c,
+ 0x44, 0x30, 0x21, 0x02, 0x56, 0x4e, 0x6f, 0x37,
+ 0xcd, 0xf9, 0xbf, 0x81, 0x6b, 0xf3, 0x7c, 0xde,
+ 0x6e, 0x6c, 0xd3, 0x3c, 0x0f, 0x01, 0x6e, 0xff,
+ 0xc0, 0x00, 0xc0, 0x01, 0x38, 0xc0, 0x40, 0x40,
+ 0x50, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00,
+ 0x06, 0x48, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // PPS
+ 0x00, 0x00, 0x00, 0x01, 0x68, 0xee, 0x31, 0x12,
+ 0x11
+ };
const uint8_t *data = NULL;
int size = 0;
@@ -3078,24 +4398,28 @@ int ff_generate_avci_extradata(AVStream *st)
size = sizeof(avci100_1080i_extradata);
}
} else if (st->codec->width == 1440) {
- data = avci50_1080i_extradata;
- size = sizeof(avci50_1080i_extradata);
+ if (st->codec->field_order == AV_FIELD_PROGRESSIVE) {
+ data = avci50_1080p_extradata;
+ size = sizeof(avci50_1080p_extradata);
+ } else {
+ data = avci50_1080i_extradata;
+ size = sizeof(avci50_1080i_extradata);
+ }
} else if (st->codec->width == 1280) {
data = avci100_720p_extradata;
size = sizeof(avci100_720p_extradata);
+ } else if (st->codec->width == 960) {
+ data = avci50_720p_extradata;
+ size = sizeof(avci50_720p_extradata);
}
if (!size)
return 0;
av_freep(&st->codec->extradata);
- st->codec->extradata_size = 0;
- st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
+ if (ff_alloc_extradata(st->codec, size))
return AVERROR(ENOMEM);
-
memcpy(st->codec->extradata, data, size);
- st->codec->extradata_size = size;
return 0;
}
diff --git a/libavformat/vc1test.c b/libavformat/vc1test.c
index f302c2b12a..3afe398d8b 100644
--- a/libavformat/vc1test.c
+++ b/libavformat/vc1test.c
@@ -2,20 +2,20 @@
* VC1 Test Bitstreams Format Demuxer
* Copyright (c) 2006, 2008 Konstantin Shishkov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -61,11 +61,8 @@ static int vc1t_read_header(AVFormatContext *s)
st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
st->codec->codec_id = AV_CODEC_ID_WMV3;
- st->codec->extradata = av_malloc(VC1_EXTRADATA_SIZE);
- if (!st->codec->extradata)
+ if (ff_get_extradata(st->codec, pb, VC1_EXTRADATA_SIZE) < 0)
return AVERROR(ENOMEM);
- st->codec->extradata_size = VC1_EXTRADATA_SIZE;
- avio_read(pb, st->codec->extradata, VC1_EXTRADATA_SIZE);
st->codec->height = avio_rl32(pb);
st->codec->width = avio_rl32(pb);
if(avio_rl32(pb) != 0xC)
@@ -94,7 +91,7 @@ static int vc1t_read_packet(AVFormatContext *s,
int keyframe = 0;
uint32_t pts;
- if(pb->eof_reached)
+ if(avio_feof(pb))
return AVERROR(EIO);
frame_size = avio_rl24(pb);
diff --git a/libavformat/vc1testenc.c b/libavformat/vc1testenc.c
index 9d55fee46d..751333a4b8 100644
--- a/libavformat/vc1testenc.c
+++ b/libavformat/vc1testenc.c
@@ -2,20 +2,20 @@
* VC-1 test bitstreams format muxer.
* Copyright (c) 2008 Konstantin Shishkov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "avformat.h"
@@ -82,9 +82,8 @@ static int vc1test_write_trailer(AVFormatContext *s)
}
AVOutputFormat ff_vc1t_muxer = {
- .name = "rcv",
+ .name = "vc1test",
.long_name = NULL_IF_CONFIG_SMALL("VC-1 test bitstream"),
- .mime_type = "",
.extensions = "rcv",
.priv_data_size = sizeof(RCVContext),
.audio_codec = AV_CODEC_ID_NONE,
diff --git a/libavformat/version.h b/libavformat/version.h
index 8ec3a28509..7309972512 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/version.h"
#define LIBAVFORMAT_VERSION_MAJOR 56
-#define LIBAVFORMAT_VERSION_MINOR 11
-#define LIBAVFORMAT_VERSION_MICRO 0
+#define LIBAVFORMAT_VERSION_MINOR 21
+#define LIBAVFORMAT_VERSION_MICRO 100
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
LIBAVFORMAT_VERSION_MINOR, \
@@ -57,5 +57,11 @@
#ifndef FF_API_LAVF_CODEC_TB
#define FF_API_LAVF_CODEC_TB (LIBAVFORMAT_VERSION_MAJOR < 57)
#endif
+#ifndef FF_API_URL_FEOF
+#define FF_API_URL_FEOF (LIBAVFORMAT_VERSION_MAJOR < 57)
+#endif
+#ifndef FF_API_R_FRAME_RATE
+#define FF_API_R_FRAME_RATE 1
+#endif
#endif /* AVFORMAT_VERSION_H */
diff --git a/libavformat/vivo.c b/libavformat/vivo.c
new file mode 100644
index 0000000000..7287379801
--- /dev/null
+++ b/libavformat/vivo.c
@@ -0,0 +1,313 @@
+/*
+ * Vivo stream demuxer
+ * Copyright (c) 2009 Daniel Verkamp <daniel at drv.nu>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * @brief Vivo stream demuxer
+ * @author Daniel Verkamp <daniel at drv.nu>
+ * @sa http://wiki.multimedia.cx/index.php?title=Vivo
+ */
+
+#include "libavutil/parseutils.h"
+#include "avformat.h"
+#include "internal.h"
+
+typedef struct VivoContext {
+ int version;
+
+ int type;
+ int sequence;
+ int length;
+
+ uint8_t text[1024 + 1];
+} VivoContext;
+
+static int vivo_probe(AVProbeData *p)
+{
+ const unsigned char *buf = p->buf;
+ unsigned c, length = 0;
+
+ // stream must start with packet of type 0 and sequence number 0
+ if (*buf++ != 0)
+ return 0;
+
+ // read at most 2 bytes of coded length
+ c = *buf++;
+ length = c & 0x7F;
+ if (c & 0x80) {
+ c = *buf++;
+ length = (length << 7) | (c & 0x7F);
+ }
+ if (c & 0x80 || length > 1024 || length < 21)
+ return 0;
+
+ if (memcmp(buf, "\r\nVersion:Vivo/", 15))
+ return 0;
+ buf += 15;
+
+ if (*buf < '0' && *buf > '2')
+ return 0;
+
+ return AVPROBE_SCORE_MAX;
+}
+
+static int vivo_get_packet_header(AVFormatContext *s)
+{
+ VivoContext *vivo = s->priv_data;
+ AVIOContext *pb = s->pb;
+ unsigned c, get_length = 0;
+
+ if (avio_feof(pb))
+ return AVERROR_EOF;
+
+ c = avio_r8(pb);
+ if (c == 0x82) {
+ get_length = 1;
+ c = avio_r8(pb);
+ }
+
+ vivo->type = c >> 4;
+ vivo->sequence = c & 0xF;
+
+ switch (vivo->type) {
+ case 0: get_length = 1; break;
+ case 1: vivo->length = 128; break;
+ case 2: get_length = 1; break;
+ case 3: vivo->length = 40; break;
+ case 4: vivo->length = 24; break;
+ default:
+ av_log(s, AV_LOG_ERROR, "unknown packet type %d\n", vivo->type);
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (get_length) {
+ c = avio_r8(pb);
+ vivo->length = c & 0x7F;
+ if (c & 0x80) {
+ c = avio_r8(pb);
+ vivo->length = (vivo->length << 7) | (c & 0x7F);
+
+ if (c & 0x80) {
+ av_log(s, AV_LOG_ERROR, "coded length is more than two bytes\n");
+ return AVERROR_INVALIDDATA;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int vivo_read_header(AVFormatContext *s)
+{
+ VivoContext *vivo = s->priv_data;
+ AVRational fps = { 1, 25};
+ AVStream *ast, *vst;
+ unsigned char *line, *line_end, *key, *value;
+ long value_int;
+ int ret, value_used;
+ int64_t duration = 0;
+ char *end_value;
+
+ vst = avformat_new_stream(s, NULL);
+ ast = avformat_new_stream(s, NULL);
+ if (!ast || !vst)
+ return AVERROR(ENOMEM);
+
+ ast->codec->sample_rate = 8000;
+
+ while (1) {
+ if ((ret = vivo_get_packet_header(s)) < 0)
+ return ret;
+
+ // done reading all text header packets?
+ if (vivo->sequence || vivo->type)
+ break;
+
+ if (vivo->length <= 1024) {
+ avio_read(s->pb, vivo->text, vivo->length);
+ vivo->text[vivo->length] = 0;
+ } else {
+ av_log(s, AV_LOG_WARNING, "too big header, skipping\n");
+ avio_skip(s->pb, vivo->length);
+ continue;
+ }
+
+ line = vivo->text;
+ while (*line) {
+ line_end = strstr(line, "\r\n");
+ if (!line_end)
+ break;
+
+ *line_end = 0;
+ key = line;
+ line = line_end + 2; // skip \r\n
+
+ if (line_end == key) // skip blank lines
+ continue;
+
+ value = strchr(key, ':');
+ if (!value) {
+ av_log(s, AV_LOG_WARNING, "missing colon in key:value pair '%s'\n",
+ value);
+ continue;
+ }
+
+ *value++ = 0;
+
+ av_log(s, AV_LOG_DEBUG, "header: '%s' = '%s'\n", key, value);
+
+ value_int = strtol(value, &end_value, 10);
+ value_used = 0;
+ if (*end_value == 0) { // valid integer
+ av_log(s, AV_LOG_DEBUG, "got a valid integer (%ld)\n", value_int);
+ value_used = 1;
+ if (!strcmp(key, "Duration")) {
+ duration = value_int;
+ } else if (!strcmp(key, "Width")) {
+ vst->codec->width = value_int;
+ } else if (!strcmp(key, "Height")) {
+ vst->codec->height = value_int;
+ } else if (!strcmp(key, "TimeUnitNumerator")) {
+ fps.num = value_int / 1000;
+ } else if (!strcmp(key, "TimeUnitDenominator")) {
+ fps.den = value_int;
+ } else if (!strcmp(key, "SamplingFrequency")) {
+ ast->codec->sample_rate = value_int;
+ } else if (!strcmp(key, "NominalBitrate")) {
+ } else if (!strcmp(key, "Length")) {
+ // size of file
+ } else {
+ value_used = 0;
+ }
+ }
+
+ if (!strcmp(key, "Version")) {
+ if (sscanf(value, "Vivo/%d.", &vivo->version) != 1)
+ return AVERROR_INVALIDDATA;
+ value_used = 1;
+ } else if (!strcmp(key, "FPS")) {
+ AVRational tmp;
+
+ value_used = 1;
+ if (!av_parse_ratio(&tmp, value, 10000, AV_LOG_WARNING, s))
+ fps = av_inv_q(tmp);
+ }
+
+ if (!value_used)
+ av_dict_set(&s->metadata, key, value, 0);
+ }
+ }
+
+ avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate);
+ avpriv_set_pts_info(vst, 64, fps.num, fps.den);
+ if (duration)
+ s->duration = av_rescale(duration, 1000, 1);
+
+ vst->start_time = 0;
+ vst->codec->codec_tag = 0;
+ vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+
+ if (vivo->version == 1) {
+ vst->codec->codec_id = AV_CODEC_ID_H263;
+ ast->codec->codec_id = AV_CODEC_ID_G723_1;
+ ast->codec->bits_per_coded_sample = 8;
+ ast->codec->block_align = 24;
+ ast->codec->bit_rate = 6400;
+ }
+
+ ast->start_time = 0;
+ ast->codec->codec_tag = 0;
+ ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+ ast->codec->channels = 1;
+
+ return 0;
+}
+
+static int vivo_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ VivoContext *vivo = s->priv_data;
+ AVIOContext *pb = s->pb;
+ unsigned old_sequence = vivo->sequence, old_type = vivo->type;
+ int stream_index, ret = 0;
+
+restart:
+
+ if (avio_feof(pb))
+ return AVERROR_EOF;
+
+ switch (vivo->type) {
+ case 0:
+ avio_skip(pb, vivo->length);
+ if ((ret = vivo_get_packet_header(s)) < 0)
+ return ret;
+ goto restart;
+ case 1:
+ case 2: // video
+ stream_index = 0;
+ break;
+ case 3:
+ case 4: // audio
+ stream_index = 1;
+ break;
+ default:
+ av_log(s, AV_LOG_ERROR, "unknown packet type %d\n", vivo->type);
+ return AVERROR_INVALIDDATA;
+ }
+
+ if ((ret = av_get_packet(pb, pkt, vivo->length)) < 0)
+ goto fail;
+
+ // get next packet header
+ if ((ret = vivo_get_packet_header(s)) < 0)
+ goto fail;
+
+ while (vivo->sequence == old_sequence &&
+ (((vivo->type - 1) >> 1) == ((old_type - 1) >> 1))) {
+ if (avio_feof(pb)) {
+ ret = AVERROR_EOF;
+ break;
+ }
+
+ if ((ret = av_append_packet(pb, pkt, vivo->length)) < 0)
+ break;
+
+ // get next packet header
+ if ((ret = vivo_get_packet_header(s)) < 0)
+ break;
+ }
+
+ pkt->stream_index = stream_index;
+
+fail:
+ if (ret < 0)
+ av_free_packet(pkt);
+ return ret;
+}
+
+AVInputFormat ff_vivo_demuxer = {
+ .name = "vivo",
+ .long_name = NULL_IF_CONFIG_SMALL("Vivo"),
+ .priv_data_size = sizeof(VivoContext),
+ .read_probe = vivo_probe,
+ .read_header = vivo_read_header,
+ .read_packet = vivo_read_packet,
+ .extensions = "viv",
+};
diff --git a/libavformat/voc.c b/libavformat/voc.c
index 1b7d499278..2a972344d3 100644
--- a/libavformat/voc.c
+++ b/libavformat/voc.c
@@ -2,20 +2,20 @@
* Creative Voice File common data.
* Copyright (c) 2006 Aurelien Jacobs <aurel@gnuage.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/voc.h b/libavformat/voc.h
index d2c0cb46cb..1f9a8be000 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
*/
@@ -27,6 +27,7 @@
typedef struct voc_dec_context {
int64_t remaining_size;
+ int64_t pts;
} VocDecContext;
typedef enum voc_type {
diff --git a/libavformat/vocdec.c b/libavformat/vocdec.c
index 2fb8440931..c49ed163cb 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
*/
@@ -71,11 +71,20 @@ ff_voc_get_packet(AVFormatContext *s, AVPacket *pkt, AVStream *st, int max_size)
int size, tmp_codec=-1;
int sample_rate = 0;
int channels = 1;
+ int64_t duration;
+ int ret;
+
+ av_add_index_entry(st,
+ avio_tell(pb),
+ voc->pts,
+ voc->remaining_size,
+ 0,
+ AVINDEX_KEYFRAME);
while (!voc->remaining_size) {
type = avio_r8(pb);
if (type == VOC_TYPE_EOF)
- return AVERROR(EIO);
+ return AVERROR_EOF;
voc->remaining_size = avio_rl24(pb);
if (!voc->remaining_size) {
if (!s->pb->seekable)
@@ -150,13 +159,23 @@ 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;
size = FFMIN(voc->remaining_size, max_size);
voc->remaining_size -= size;
- return av_get_packet(pb, pkt, size);
+
+ ret = av_get_packet(pb, pkt, size);
+ pkt->dts = pkt->pts = voc->pts;
+
+ duration = av_get_audio_frame_duration(st->codec, size);
+ if (duration > 0 && voc->pts != AV_NOPTS_VALUE)
+ voc->pts += duration;
+ else
+ voc->pts = AV_NOPTS_VALUE;
+
+ return ret;
}
static int voc_read_packet(AVFormatContext *s, AVPacket *pkt)
@@ -164,6 +183,28 @@ static int voc_read_packet(AVFormatContext *s, AVPacket *pkt)
return ff_voc_get_packet(s, pkt, s->streams[0], 0);
}
+static int voc_read_seek(AVFormatContext *s, int stream_index,
+ int64_t timestamp, int flags)
+{
+ VocDecContext *voc = s->priv_data;
+ AVStream *st = s->streams[stream_index];
+ int index = av_index_search_timestamp(st, timestamp, flags);
+
+ if (index >= 0 && index < st->nb_index_entries - 1) {
+ AVIndexEntry *e = &st->index_entries[index];
+ avio_seek(s->pb, e->pos, SEEK_SET);
+ voc->pts = e->timestamp;
+ voc->remaining_size = e->size;
+ return 0;
+ } else if (st->nb_index_entries && st->index_entries[0].timestamp <= timestamp) {
+ AVIndexEntry *e = &st->index_entries[st->nb_index_entries - 1];
+ // prepare context for seek_frame_generic()
+ voc->pts = e->timestamp;
+ voc->remaining_size = e->size;
+ }
+ return -1;
+}
+
AVInputFormat ff_voc_demuxer = {
.name = "voc",
.long_name = NULL_IF_CONFIG_SMALL("Creative Voice"),
@@ -171,5 +212,6 @@ AVInputFormat ff_voc_demuxer = {
.read_probe = voc_probe,
.read_header = voc_read_header,
.read_packet = voc_read_packet,
+ .read_seek = voc_read_seek,
.codec_tag = (const AVCodecTag* const []){ ff_voc_codec_tags, 0 },
};
diff --git a/libavformat/vocenc.c b/libavformat/vocenc.c
index ed10a96f7a..0bb532e0c6 100644
--- a/libavformat/vocenc.c
+++ b/libavformat/vocenc.c
@@ -2,20 +2,20 @@
* Creative Voice File muxer.
* Copyright (c) 2006 Aurelien Jacobs <aurel@gnuage.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -30,6 +30,7 @@ typedef struct voc_enc_context {
static int voc_write_header(AVFormatContext *s)
{
AVIOContext *pb = s->pb;
+ AVCodecContext *enc = s->streams[0]->codec;
const int header_size = 26;
const int version = 0x0114;
@@ -37,6 +38,11 @@ static int voc_write_header(AVFormatContext *s)
|| s->streams[0]->codec->codec_type != AVMEDIA_TYPE_AUDIO)
return AVERROR_PATCHWELCOME;
+ if (!enc->codec_tag && enc->codec_id != AV_CODEC_ID_PCM_U8) {
+ av_log(s, AV_LOG_ERROR, "unsupported codec\n");
+ return AVERROR(EINVAL);
+ }
+
avio_write(pb, ff_voc_magic, sizeof(ff_voc_magic) - 1);
avio_wl16(pb, header_size);
avio_wl16(pb, version);
@@ -52,7 +58,7 @@ static int voc_write_packet(AVFormatContext *s, AVPacket *pkt)
AVIOContext *pb = s->pb;
if (!voc->param_written) {
- if (enc->codec_tag > 0xFF) {
+ if (enc->codec_tag > 3) {
avio_w8(pb, VOC_TYPE_NEW_VOICE_DATA);
avio_wl24(pb, pkt->size + 12);
avio_wl32(pb, enc->sample_rate);
@@ -64,13 +70,13 @@ static int voc_write_packet(AVFormatContext *s, AVPacket *pkt)
if (s->streams[0]->codec->channels > 1) {
avio_w8(pb, VOC_TYPE_EXTENDED);
avio_wl24(pb, 4);
- avio_wl16(pb, 65536-256000000/(enc->sample_rate*enc->channels));
+ avio_wl16(pb, 65536-(256000000 + enc->sample_rate*enc->channels/2)/(enc->sample_rate*enc->channels));
avio_w8(pb, enc->codec_tag);
avio_w8(pb, enc->channels - 1);
}
avio_w8(pb, VOC_TYPE_VOICE_DATA);
avio_wl24(pb, pkt->size + 2);
- avio_w8(pb, 256 - 1000000 / enc->sample_rate);
+ avio_w8(pb, 256 - (1000000 + enc->sample_rate/2) / enc->sample_rate);
avio_w8(pb, enc->codec_tag);
}
voc->param_written = 1;
@@ -95,7 +101,7 @@ AVOutputFormat ff_voc_muxer = {
.mime_type = "audio/x-voc",
.extensions = "voc",
.priv_data_size = sizeof(VocEncContext),
- .audio_codec = AV_CODEC_ID_PCM_U8,
+ .audio_codec = AV_CODEC_ID_PCM_S16LE,
.video_codec = AV_CODEC_ID_NONE,
.write_header = voc_write_header,
.write_packet = voc_write_packet,
diff --git a/libavformat/vorbiscomment.c b/libavformat/vorbiscomment.c
index ee06a57336..cc9b6620ef 100644
--- a/libavformat/vorbiscomment.c
+++ b/libavformat/vorbiscomment.c
@@ -2,20 +2,20 @@
* VorbisComment writer
* Copyright (c) 2009 James Darnley
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -34,6 +34,7 @@ const AVMetadataConv ff_vorbiscomment_metadata_conv[] = {
{ "ALBUMARTIST", "album_artist"},
{ "TRACKNUMBER", "track" },
{ "DISCNUMBER", "disc" },
+ { "DESCRIPTION", "comment" },
{ 0 }
};
diff --git a/libavformat/vorbiscomment.h b/libavformat/vorbiscomment.h
index d9ec099b3f..f4212d2632 100644
--- a/libavformat/vorbiscomment.h
+++ b/libavformat/vorbiscomment.h
@@ -2,20 +2,20 @@
* VorbisComment writer
* Copyright (c) 2009 James Darnley
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/vplayerdec.c b/libavformat/vplayerdec.c
new file mode 100644
index 0000000000..619ccfd4f0
--- /dev/null
+++ b/libavformat/vplayerdec.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2012 Clément Bœsch
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * VPlayer subtitles format demuxer
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "subtitles.h"
+
+typedef struct {
+ FFDemuxSubtitlesQueue q;
+} VPlayerContext;
+
+static int vplayer_probe(AVProbeData *p)
+{
+ char c;
+ const unsigned char *ptr = p->buf;
+
+ if (sscanf(ptr, "%*d:%*d:%*d.%*d%c", &c) == 1 && strchr(": =", c))
+ return AVPROBE_SCORE_MAX;
+ return 0;
+}
+
+static int64_t read_ts(char **line)
+{
+ char c;
+ int hh, mm, ss, ms, len;
+
+ if (sscanf(*line, "%d:%d:%d.%d%c%n",
+ &hh, &mm, &ss, &ms, &c, &len) >= 5) {
+ *line += len;
+ return (hh*3600LL + mm*60LL + ss) * 100LL + ms;
+ }
+ return AV_NOPTS_VALUE;
+}
+
+static int vplayer_read_header(AVFormatContext *s)
+{
+ VPlayerContext *vplayer = s->priv_data;
+ AVStream *st = avformat_new_stream(s, NULL);
+
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 64, 1, 100);
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_VPLAYER;
+
+ while (!avio_feof(s->pb)) {
+ char line[4096];
+ char *p = line;
+ const int64_t pos = avio_tell(s->pb);
+ int len = ff_get_line(s->pb, line, sizeof(line));
+ int64_t pts_start;
+
+ if (!len)
+ break;
+
+ line[strcspn(line, "\r\n")] = 0;
+
+ pts_start = read_ts(&p);
+ if (pts_start != AV_NOPTS_VALUE) {
+ AVPacket *sub;
+
+ sub = ff_subtitles_queue_insert(&vplayer->q, p, strlen(p), 0);
+ if (!sub)
+ return AVERROR(ENOMEM);
+ sub->pos = pos;
+ sub->pts = pts_start;
+ sub->duration = -1;
+ }
+ }
+
+ ff_subtitles_queue_finalize(&vplayer->q);
+ return 0;
+}
+
+static int vplayer_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ VPlayerContext *vplayer = s->priv_data;
+ return ff_subtitles_queue_read_packet(&vplayer->q, pkt);
+}
+
+static int vplayer_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ VPlayerContext *vplayer = s->priv_data;
+ return ff_subtitles_queue_seek(&vplayer->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int vplayer_read_close(AVFormatContext *s)
+{
+ VPlayerContext *vplayer = s->priv_data;
+ ff_subtitles_queue_clean(&vplayer->q);
+ return 0;
+}
+
+AVInputFormat ff_vplayer_demuxer = {
+ .name = "vplayer",
+ .long_name = NULL_IF_CONFIG_SMALL("VPlayer subtitles"),
+ .priv_data_size = sizeof(VPlayerContext),
+ .read_probe = vplayer_probe,
+ .read_header = vplayer_read_header,
+ .read_packet = vplayer_read_packet,
+ .read_seek2 = vplayer_read_seek,
+ .read_close = vplayer_read_close,
+ .extensions = "txt",
+};
diff --git a/libavformat/vqf.c b/libavformat/vqf.c
index c04ffe74ee..ce07ca7851 100644
--- a/libavformat/vqf.c
+++ b/libavformat/vqf.c
@@ -2,20 +2,20 @@
* VQF demuxer
* Copyright (c) 2009 Vitor Sessak
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -43,6 +43,9 @@ static int vqf_probe(AVProbeData *probe_packet)
if (!memcmp(probe_packet->buf + 4, "00052200", 8))
return AVPROBE_SCORE_MAX;
+ if (AV_RL32(probe_packet->buf + 12) > (1<<27))
+ return AVPROBE_SCORE_EXTENSION/2;
+
return AVPROBE_SCORE_EXTENSION;
}
@@ -132,15 +135,16 @@ static int vqf_read_header(AVFormatContext *s)
rate_flag = AV_RB32(comm_chunk + 8);
avio_skip(s->pb, len-12);
+ if (st->codec->channels <= 0) {
+ av_log(s, AV_LOG_ERROR, "Invalid number of channels\n");
+ return AVERROR_INVALIDDATA;
+ }
+
st->codec->bit_rate = read_bitrate*1000;
break;
case MKTAG('D','S','I','Z'): // size of compressed data
{
- char buf[8] = {0};
- int size = avio_rb32(s->pb);
-
- snprintf(buf, sizeof(buf), "%d", size);
- av_dict_set(&s->metadata, "size", buf, 0);
+ av_dict_set_int(&s->metadata, "size", avio_rb32(s->pb), 0);
}
break;
case MKTAG('Y','E','A','R'): // recording date
@@ -158,7 +162,7 @@ static int vqf_read_header(AVFormatContext *s)
header_size -= len;
- } while (header_size >= 0);
+ } while (header_size >= 0 && !avio_feof(s->pb));
switch (rate_flag) {
case -1:
@@ -215,9 +219,8 @@ static int vqf_read_header(AVFormatContext *s)
avpriv_set_pts_info(st, 64, size, st->codec->sample_rate);
/* put first 12 bytes of COMM chunk in extradata */
- if (!(st->codec->extradata = av_malloc(12 + FF_INPUT_BUFFER_PADDING_SIZE)))
+ if (ff_alloc_extradata(st->codec, 12))
return AVERROR(ENOMEM);
- st->codec->extradata_size = 12;
memcpy(st->codec->extradata, comm_chunk, 12);
ff_metadata_conv_ctx(s, NULL, vqf_metadata_conv);
@@ -242,7 +245,7 @@ static int vqf_read_packet(AVFormatContext *s, AVPacket *pkt)
pkt->data[1] = c->last_frame_bits;
ret = avio_read(s->pb, pkt->data+2, size);
- if (ret<=0) {
+ if (ret != size) {
av_free_packet(pkt);
return AVERROR(EIO);
}
diff --git a/libavformat/w64.c b/libavformat/w64.c
new file mode 100644
index 0000000000..ef2d90a600
--- /dev/null
+++ b/libavformat/w64.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009 Daniel Verkamp
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "w64.h"
+
+const uint8_t ff_w64_guid_riff[16] = {
+ 'r', 'i', 'f', 'f',
+ 0x2E, 0x91, 0xCF, 0x11, 0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00
+};
+
+const uint8_t ff_w64_guid_wave[16] = {
+ 'w', 'a', 'v', 'e',
+ 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
+};
+
+const uint8_t ff_w64_guid_fmt [16] = {
+ 'f', 'm', 't', ' ',
+ 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
+};
+
+const uint8_t ff_w64_guid_fact[16] = { 'f', 'a', 'c', 't',
+ 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
+};
+
+const uint8_t ff_w64_guid_data[16] = {
+ 'd', 'a', 't', 'a',
+ 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
+};
+
+const uint8_t ff_w64_guid_summarylist[16] = {
+ 0xBC, 0x94, 0x5F, 0x92,
+ 0x5A, 0x52, 0xD2, 0x11, 0x86, 0xDC, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
+};
diff --git a/libavformat/w64.h b/libavformat/w64.h
new file mode 100644
index 0000000000..0ec3fa9141
--- /dev/null
+++ b/libavformat/w64.h
@@ -0,0 +1,31 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_W64_H
+#define AVFORMAT_W64_H
+
+#include <stdint.h>
+
+extern const uint8_t ff_w64_guid_riff[16];
+extern const uint8_t ff_w64_guid_wave[16];
+extern const uint8_t ff_w64_guid_fmt [16];
+extern const uint8_t ff_w64_guid_fact[16];
+extern const uint8_t ff_w64_guid_data[16];
+extern const uint8_t ff_w64_guid_summarylist[16];
+
+#endif /* AVFORMAT_W64_H */
diff --git a/libavformat/wavdec.c b/libavformat/wavdec.c
index f65a66a006..410c2ec1f9 100644
--- a/libavformat/wavdec.c
+++ b/libavformat/wavdec.c
@@ -6,20 +6,20 @@
* RF64 demuxer
* Copyright (c) 2009 Daniel Verkamp
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -27,6 +27,7 @@
#include "libavutil/avassert.h"
#include "libavutil/dict.h"
+#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
@@ -37,39 +38,61 @@
#include "metadata.h"
#include "pcm.h"
#include "riff.h"
+#include "w64.h"
+#include "spdif.h"
typedef struct WAVDemuxContext {
+ const AVClass *class;
int64_t data_end;
int w64;
+ int64_t smv_data_ofs;
+ int smv_block_size;
+ int smv_frames_per_jpeg;
+ int smv_block;
+ int smv_last_stream;
+ int smv_eof;
+ int audio_eof;
+ int ignore_length;
+ int spdif;
+ int smv_cur_pt;
+ int smv_given_first;
+ int unaligned; // e.g. if an odd number of bytes ID3 tag was prepended
+ int rifx; // RIFX: integer byte order for parameters is big endian
} WAVDemuxContext;
#if CONFIG_WAV_DEMUXER
-static int64_t next_tag(AVIOContext *pb, uint32_t *tag)
+static int64_t next_tag(AVIOContext *pb, uint32_t *tag, int big_endian)
{
*tag = avio_rl32(pb);
- return avio_rl32(pb);
+ if (!big_endian) {
+ return avio_rl32(pb);
+ } else {
+ return avio_rb32(pb);
+ }
}
-/* RIFF chunks are always on a even offset. */
-static int64_t wav_seek_tag(AVIOContext *s, int64_t offset, int whence)
+/* RIFF chunks are always at even offsets relative to where they start. */
+static int64_t wav_seek_tag(WAVDemuxContext * wav, AVIOContext *s, int64_t offset, int whence)
{
- return avio_seek(s, offset + (offset & 1), whence);
+ offset += offset < INT64_MAX && offset + wav->unaligned & 1;
+
+ return avio_seek(s, offset, whence);
}
/* return the size of the found tag */
-static int64_t find_tag(AVIOContext *pb, uint32_t tag1)
+static int64_t find_tag(WAVDemuxContext * wav, AVIOContext *pb, uint32_t tag1)
{
unsigned int tag;
int64_t size;
for (;;) {
- if (pb->eof_reached)
- return -1;
- size = next_tag(pb, &tag);
+ if (avio_feof(pb))
+ return AVERROR_EOF;
+ size = next_tag(pb, &tag, wav->rifx);
if (tag == tag1)
break;
- wav_seek_tag(pb, size, SEEK_CUR);
+ wav_seek_tag(wav, pb, size, SEEK_CUR);
}
return size;
}
@@ -80,7 +103,7 @@ static int wav_probe(AVProbeData *p)
if (p->buf_size <= 32)
return 0;
if (!memcmp(p->buf + 8, "WAVE", 4)) {
- if (!memcmp(p->buf, "RIFF", 4))
+ if (!memcmp(p->buf, "RIFF", 4) || !memcmp(p->buf, "RIFX", 4))
/* Since the ACT demuxer has a standard WAV header at the top of
* its own, the returned score is decreased to avoid a probe
* conflict between ACT and WAV. */
@@ -92,9 +115,18 @@ static int wav_probe(AVProbeData *p)
return 0;
}
+static void handle_stream_probing(AVStream *st)
+{
+ if (st->codec->codec_id == AV_CODEC_ID_PCM_S16LE) {
+ st->request_probe = AVPROBE_SCORE_EXTENSION;
+ st->probe_packets = FFMIN(st->probe_packets, 14);
+ }
+}
+
static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream **st)
{
AVIOContext *pb = s->pb;
+ WAVDemuxContext *wav = s->priv_data;
int ret;
/* parse fmt header */
@@ -102,10 +134,12 @@ static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream **st)
if (!*st)
return AVERROR(ENOMEM);
- ret = ff_get_wav_header(pb, (*st)->codec, size);
+ ret = ff_get_wav_header(pb, (*st)->codec, size, wav->rifx);
if (ret < 0)
return ret;
- (*st)->need_parsing = AVSTREAM_PARSE_FULL;
+ handle_stream_probing(*st);
+
+ (*st)->need_parsing = AVSTREAM_PARSE_FULL_RAW;
avpriv_set_pts_info(*st, 64, 1, (*st)->codec->sample_rate);
@@ -167,7 +201,7 @@ static int wav_parse_bext_tag(AVFormatContext *s, int64_t size)
/* extended UMID */
snprintf(temp, sizeof(temp),
"0x%016"PRIX64"%016"PRIX64"%016"PRIX64"%016"PRIX64
- "0x%016"PRIX64"%016"PRIX64"%016"PRIX64"%016"PRIX64,
+ "%016"PRIX64"%016"PRIX64"%016"PRIX64"%016"PRIX64,
umid_parts[0], umid_parts[1],
umid_parts[2], umid_parts[3],
umid_parts[4], umid_parts[5],
@@ -214,7 +248,8 @@ static int wav_read_header(AVFormatContext *s)
{
int64_t size, av_uninit(data_size);
int64_t sample_count = 0;
- int rf64;
+ int rf64 = 0;
+ char start_code[32];
uint32_t tag;
AVIOContext *pb = s->pb;
AVStream *st = NULL;
@@ -222,22 +257,41 @@ static int wav_read_header(AVFormatContext *s)
int ret, got_fmt = 0;
int64_t next_tag_ofs, data_ofs = -1;
- /* check RIFF header */
- tag = avio_rl32(pb);
+ wav->unaligned = avio_tell(s->pb) & 1;
- rf64 = tag == MKTAG('R', 'F', '6', '4');
- if (!rf64 && tag != MKTAG('R', 'I', 'F', 'F'))
- return AVERROR_INVALIDDATA;
- avio_rl32(pb); /* file size */
+ wav->smv_data_ofs = -1;
+
+ /* read chunk ID */
tag = avio_rl32(pb);
- if (tag != MKTAG('W', 'A', 'V', 'E'))
+ switch (tag) {
+ case MKTAG('R', 'I', 'F', 'F'):
+ break;
+ case MKTAG('R', 'I', 'F', 'X'):
+ wav->rifx = 1;
+ break;
+ case MKTAG('R', 'F', '6', '4'):
+ rf64 = 1;
+ break;
+ default:
+ av_get_codec_tag_string(start_code, sizeof(start_code), tag);
+ av_log(s, AV_LOG_ERROR, "invalid start code %s in RIFF header\n", start_code);
return AVERROR_INVALIDDATA;
+ }
+
+ /* read chunk size */
+ avio_rl32(pb);
+
+ /* read format */
+ if (avio_rl32(pb) != MKTAG('W', 'A', 'V', 'E')) {
+ av_log(s, AV_LOG_ERROR, "invalid format in RIFF header\n");
+ return AVERROR_INVALIDDATA;
+ }
if (rf64) {
if (avio_rl32(pb) != MKTAG('d', 's', '6', '4'))
return AVERROR_INVALIDDATA;
size = avio_rl32(pb);
- if (size < 16)
+ if (size < 24)
return AVERROR_INVALIDDATA;
avio_rl64(pb); /* RIFF size */
@@ -250,20 +304,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 (;;) {
- size = next_tag(pb, &tag);
+ AVStream *vst;
+ size = next_tag(pb, &tag, wav->rifx);
next_tag_ofs = avio_tell(pb) + size;
- if (pb->eof_reached)
+ if (avio_feof(pb))
break;
switch (tag) {
case MKTAG('f', 'm', 't', ' '):
/* only parse the first 'fmt ' tag found */
- if (!got_fmt && (ret = wav_parse_fmt_tag(s, size, &st) < 0)) {
+ if (!got_fmt && (ret = wav_parse_fmt_tag(s, size, &st)) < 0) {
return ret;
} else if (got_fmt)
av_log(s, AV_LOG_WARNING, "found more than one 'fmt ' tag\n");
@@ -294,28 +350,68 @@ static int wav_read_header(AVFormatContext *s)
break;
case MKTAG('f', 'a', 'c', 't'):
if (!sample_count)
- sample_count = avio_rl32(pb);
+ sample_count = (!wav->rifx ? avio_rl32(pb) : avio_rb32(pb));
break;
case MKTAG('b', 'e', 'x', 't'):
if ((ret = wav_parse_bext_tag(s, size)) < 0)
return ret;
break;
+ case MKTAG('S','M','V','0'):
+ if (!got_fmt) {
+ av_log(s, AV_LOG_ERROR, "found no 'fmt ' tag before the 'SMV0' tag\n");
+ return AVERROR_INVALIDDATA;
+ }
+ // SMV file, a wav file with video appended.
+ if (size != MKTAG('0','2','0','0')) {
+ av_log(s, AV_LOG_ERROR, "Unknown SMV version found\n");
+ goto break_loop;
+ }
+ av_log(s, AV_LOG_DEBUG, "Found SMV data\n");
+ wav->smv_given_first = 0;
+ vst = avformat_new_stream(s, NULL);
+ if (!vst)
+ return AVERROR(ENOMEM);
+ avio_r8(pb);
+ vst->id = 1;
+ vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+ vst->codec->codec_id = AV_CODEC_ID_SMVJPEG;
+ vst->codec->width = avio_rl24(pb);
+ vst->codec->height = avio_rl24(pb);
+ if (ff_alloc_extradata(vst->codec, 4)) {
+ av_log(s, AV_LOG_ERROR, "Could not allocate extradata.\n");
+ return AVERROR(ENOMEM);
+ }
+ size = avio_rl24(pb);
+ wav->smv_data_ofs = avio_tell(pb) + (size - 5) * 3;
+ avio_rl24(pb);
+ wav->smv_block_size = avio_rl24(pb);
+ avpriv_set_pts_info(vst, 32, 1, avio_rl24(pb));
+ vst->duration = avio_rl24(pb);
+ avio_rl24(pb);
+ avio_rl24(pb);
+ wav->smv_frames_per_jpeg = avio_rl24(pb);
+ if (wav->smv_frames_per_jpeg > 65536) {
+ av_log(s, AV_LOG_ERROR, "too many frames per jpeg\n");
+ return AVERROR_INVALIDDATA;
+ }
+ AV_WL32(vst->codec->extradata, wav->smv_frames_per_jpeg);
+ wav->smv_cur_pt = 0;
+ goto break_loop;
case MKTAG('L', 'I', 'S', 'T'):
if (size < 4) {
- av_log(s, AV_LOG_ERROR, "too short LIST");
+ av_log(s, AV_LOG_ERROR, "too short LIST tag\n");
return AVERROR_INVALIDDATA;
}
switch (avio_rl32(pb)) {
case MKTAG('I', 'N', 'F', 'O'):
- if ((ret = ff_read_riff_info(s, size - 4)) < 0)
- return ret;
+ ff_read_riff_info(s, size - 4);
}
break;
}
/* seek to next tag unless we know that we'll run into EOF */
if ((avio_size(pb) > 0 && next_tag_ofs >= avio_size(pb)) ||
- wav_seek_tag(pb, next_tag_ofs, SEEK_SET) < 0) {
+ wav_seek_tag(wav, pb, next_tag_ofs, SEEK_SET) < 0) {
break;
}
}
@@ -328,11 +424,21 @@ break_loop:
avio_seek(pb, data_ofs, SEEK_SET);
- if (!sample_count && st->codec->channels &&
- av_get_bits_per_sample(st->codec->codec_id))
- sample_count = (data_size << 3) /
- (st->codec->channels *
- (uint64_t)av_get_bits_per_sample(st->codec->codec_id));
+ if ( data_size > 0 && sample_count && st->codec->channels
+ && data_size / sample_count / st->codec->channels > 8) {
+ av_log(s, AV_LOG_WARNING, "ignoring wrong sample_count %"PRId64"\n", sample_count);
+ sample_count = 0;
+ }
+
+ if (!sample_count || av_get_exact_bits_per_sample(st->codec->codec_id) > 0)
+ if ( st->codec->channels
+ && data_size
+ && av_get_bits_per_sample(st->codec->codec_id)
+ && wav->data_end <= avio_size(pb))
+ sample_count = (data_size << 3)
+ /
+ (st->codec->channels * (uint64_t)av_get_bits_per_sample(st->codec->codec_id));
+
if (sample_count)
st->duration = sample_count;
@@ -351,23 +457,18 @@ static int64_t find_guid(AVIOContext *pb, const uint8_t guid1[16])
uint8_t guid[16];
int64_t size;
- while (!pb->eof_reached) {
+ while (!avio_feof(pb)) {
avio_read(pb, guid, 16);
size = avio_rl64(pb);
if (size <= 24)
- return -1;
+ return AVERROR_INVALIDDATA;
if (!memcmp(guid, guid1, 16))
return size;
avio_skip(pb, FFALIGN(size, INT64_C(8)) - 24);
}
- return -1;
+ return AVERROR_EOF;
}
-static const uint8_t guid_data[16] = {
- 'd', 'a', 't', 'a',
- 0xF3, 0xAC, 0xD3, 0x11,0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
-};
-
#define MAX_SIZE 4096
static int wav_read_packet(AVFormatContext *s, AVPacket *pkt)
@@ -377,16 +478,84 @@ static int wav_read_packet(AVFormatContext *s, AVPacket *pkt)
AVStream *st;
WAVDemuxContext *wav = s->priv_data;
+ if (CONFIG_SPDIF_DEMUXER && wav->spdif == 0 &&
+ s->streams[0]->codec->codec_tag == 1) {
+ enum AVCodecID codec;
+ ret = ff_spdif_probe(s->pb->buffer, s->pb->buf_end - s->pb->buffer,
+ &codec);
+ if (ret > AVPROBE_SCORE_EXTENSION) {
+ s->streams[0]->codec->codec_id = codec;
+ wav->spdif = 1;
+ } else {
+ wav->spdif = -1;
+ }
+ }
+ if (CONFIG_SPDIF_DEMUXER && wav->spdif == 1)
+ return ff_spdif_read_packet(s, pkt);
+
+ if (wav->smv_data_ofs > 0) {
+ int64_t audio_dts, video_dts;
+smv_retry:
+ audio_dts = (int32_t)s->streams[0]->cur_dts;
+ video_dts = (int32_t)s->streams[1]->cur_dts;
+
+ if (audio_dts != AV_NOPTS_VALUE && video_dts != AV_NOPTS_VALUE) {
+ /*We always return a video frame first to get the pixel format first*/
+ wav->smv_last_stream = wav->smv_given_first ?
+ av_compare_ts(video_dts, s->streams[1]->time_base,
+ audio_dts, s->streams[0]->time_base) > 0 : 0;
+ wav->smv_given_first = 1;
+ }
+ wav->smv_last_stream = !wav->smv_last_stream;
+ wav->smv_last_stream |= wav->audio_eof;
+ wav->smv_last_stream &= !wav->smv_eof;
+ if (wav->smv_last_stream) {
+ uint64_t old_pos = avio_tell(s->pb);
+ uint64_t new_pos = wav->smv_data_ofs +
+ wav->smv_block * wav->smv_block_size;
+ if (avio_seek(s->pb, new_pos, SEEK_SET) < 0) {
+ ret = AVERROR_EOF;
+ goto smv_out;
+ }
+ size = avio_rl24(s->pb);
+ ret = av_get_packet(s->pb, pkt, size);
+ if (ret < 0)
+ goto smv_out;
+ pkt->pos -= 3;
+ pkt->pts = wav->smv_block * wav->smv_frames_per_jpeg + wav->smv_cur_pt;
+ wav->smv_cur_pt++;
+ if (wav->smv_frames_per_jpeg > 0)
+ wav->smv_cur_pt %= wav->smv_frames_per_jpeg;
+ if (!wav->smv_cur_pt)
+ wav->smv_block++;
+
+ pkt->stream_index = 1;
+smv_out:
+ avio_seek(s->pb, old_pos, SEEK_SET);
+ if (ret == AVERROR_EOF) {
+ wav->smv_eof = 1;
+ goto smv_retry;
+ }
+ return ret;
+ }
+ }
+
st = s->streams[0];
left = wav->data_end - avio_tell(s->pb);
+ if (wav->ignore_length)
+ left = INT_MAX;
if (left <= 0) {
if (CONFIG_W64_DEMUXER && wav->w64)
- left = find_guid(s->pb, guid_data) - 24;
+ left = find_guid(s->pb, ff_w64_guid_data) - 24;
else
- left = find_tag(s->pb, MKTAG('d', 'a', 't', 'a'));
- if (left < 0)
+ left = find_tag(wav, s->pb, MKTAG('d', 'a', 't', 'a'));
+ if (left < 0) {
+ wav->audio_eof = 1;
+ if (wav->smv_data_ofs > 0 && !wav->smv_eof)
+ goto smv_retry;
return AVERROR_EOF;
+ }
wav->data_end = avio_tell(s->pb) + left;
}
@@ -408,7 +577,21 @@ static int wav_read_packet(AVFormatContext *s, AVPacket *pkt)
static int wav_read_seek(AVFormatContext *s,
int stream_index, int64_t timestamp, int flags)
{
+ WAVDemuxContext *wav = s->priv_data;
AVStream *st;
+ wav->smv_eof = 0;
+ wav->audio_eof = 0;
+ if (wav->smv_data_ofs > 0) {
+ int64_t smv_timestamp = timestamp;
+ if (stream_index == 0)
+ smv_timestamp = av_rescale_q(timestamp, s->streams[0]->time_base, s->streams[1]->time_base);
+ else
+ timestamp = av_rescale_q(smv_timestamp, s->streams[1]->time_base, s->streams[0]->time_base);
+ if (wav->smv_frames_per_jpeg > 0) {
+ wav->smv_block = smv_timestamp / wav->smv_frames_per_jpeg;
+ wav->smv_cur_pt = smv_timestamp % wav->smv_frames_per_jpeg;
+ }
+ }
st = s->streams[0];
switch (st->codec->codec_id) {
@@ -424,6 +607,19 @@ static int wav_read_seek(AVFormatContext *s,
return ff_pcm_read_seek(s, stream_index, timestamp, flags);
}
+#define OFFSET(x) offsetof(WAVDemuxContext, x)
+#define DEC AV_OPT_FLAG_DECODING_PARAM
+static const AVOption demux_options[] = {
+ { "ignore_length", "Ignore length", OFFSET(ignore_length), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, DEC },
+ { NULL },
+};
+
+static const AVClass wav_demuxer_class = {
+ .class_name = "WAV demuxer",
+ .item_name = av_default_item_name,
+ .option = demux_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
AVInputFormat ff_wav_demuxer = {
.name = "wav",
.long_name = NULL_IF_CONFIG_SMALL("WAV / WAVE (Waveform Audio)"),
@@ -434,31 +630,17 @@ AVInputFormat ff_wav_demuxer = {
.read_seek = wav_read_seek,
.flags = AVFMT_GENERIC_INDEX,
.codec_tag = (const AVCodecTag * const []) { ff_codec_wav_tags, 0 },
+ .priv_class = &wav_demuxer_class,
};
#endif /* CONFIG_WAV_DEMUXER */
#if CONFIG_W64_DEMUXER
-static const uint8_t guid_riff[16] = {
- 'r', 'i', 'f', 'f',
- 0x2E, 0x91, 0xCF, 0x11,0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00
-};
-
-static const uint8_t guid_wave[16] = {
- 'w', 'a', 'v', 'e',
- 0xF3, 0xAC, 0xD3, 0x11,0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
-};
-
-static const uint8_t guid_fmt[16] = {
- 'f', 'm', 't', ' ',
- 0xF3, 0xAC, 0xD3, 0x11,0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
-};
-
static int w64_probe(AVProbeData *p)
{
if (p->buf_size <= 40)
return 0;
- if (!memcmp(p->buf, guid_riff, 16) &&
- !memcmp(p->buf + 24, guid_wave, 16))
+ if (!memcmp(p->buf, ff_w64_guid_riff, 16) &&
+ !memcmp(p->buf + 24, ff_w64_guid_wave, 16))
return AVPROBE_SCORE_MAX;
else
return 0;
@@ -466,7 +648,7 @@ static int w64_probe(AVProbeData *p)
static int w64_read_header(AVFormatContext *s)
{
- int64_t size;
+ int64_t size, data_ofs = 0;
AVIOContext *pb = s->pb;
WAVDemuxContext *wav = s->priv_data;
AVStream *st;
@@ -474,7 +656,7 @@ static int w64_read_header(AVFormatContext *s)
int ret;
avio_read(pb, guid, 16);
- if (memcmp(guid, guid_riff, 16))
+ if (memcmp(guid, ff_w64_guid_riff, 16))
return AVERROR_INVALIDDATA;
/* riff + wave + fmt + sizes */
@@ -482,38 +664,91 @@ static int w64_read_header(AVFormatContext *s)
return AVERROR_INVALIDDATA;
avio_read(pb, guid, 16);
- if (memcmp(guid, guid_wave, 16)) {
+ if (memcmp(guid, ff_w64_guid_wave, 16)) {
av_log(s, AV_LOG_ERROR, "could not find wave guid\n");
return AVERROR_INVALIDDATA;
}
- size = find_guid(pb, guid_fmt);
- if (size < 0) {
- av_log(s, AV_LOG_ERROR, "could not find fmt guid\n");
- return AVERROR_INVALIDDATA;
- }
+ wav->w64 = 1;
st = avformat_new_stream(s, NULL);
if (!st)
return AVERROR(ENOMEM);
- /* subtract chunk header size - normal wav file doesn't count it */
- ret = ff_get_wav_header(pb, st->codec, size - 24);
- if (ret < 0)
- return ret;
- avio_skip(pb, FFALIGN(size, INT64_C(8)) - size);
+ while (!avio_feof(pb)) {
+ if (avio_read(pb, guid, 16) != 16)
+ break;
+ size = avio_rl64(pb);
+ if (size <= 24 || INT64_MAX - size < avio_tell(pb))
+ return AVERROR_INVALIDDATA;
- st->need_parsing = AVSTREAM_PARSE_FULL;
+ if (!memcmp(guid, ff_w64_guid_fmt, 16)) {
+ /* subtract chunk header size - normal wav file doesn't count it */
+ ret = ff_get_wav_header(pb, st->codec, size - 24, 0);
+ if (ret < 0)
+ return ret;
+ avio_skip(pb, FFALIGN(size, INT64_C(8)) - size);
- avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+ avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+ } else if (!memcmp(guid, ff_w64_guid_fact, 16)) {
+ int64_t samples;
- size = find_guid(pb, guid_data);
- if (size < 0) {
- av_log(s, AV_LOG_ERROR, "could not find data guid\n");
- return AVERROR_INVALIDDATA;
+ samples = avio_rl64(pb);
+ if (samples > 0)
+ st->duration = samples;
+ } else if (!memcmp(guid, ff_w64_guid_data, 16)) {
+ wav->data_end = avio_tell(pb) + size - 24;
+
+ data_ofs = avio_tell(pb);
+ if (!pb->seekable)
+ break;
+
+ avio_skip(pb, size - 24);
+ } else if (!memcmp(guid, ff_w64_guid_summarylist, 16)) {
+ int64_t start, end, cur;
+ uint32_t count, chunk_size, i;
+
+ start = avio_tell(pb);
+ end = start + FFALIGN(size, INT64_C(8)) - 24;
+ count = avio_rl32(pb);
+
+ for (i = 0; i < count; i++) {
+ char chunk_key[5], *value;
+
+ if (avio_feof(pb) || (cur = avio_tell(pb)) < 0 || cur > end - 8 /* = tag + size */)
+ break;
+
+ chunk_key[4] = 0;
+ avio_read(pb, chunk_key, 4);
+ chunk_size = avio_rl32(pb);
+
+ value = av_mallocz(chunk_size + 1);
+ if (!value)
+ return AVERROR(ENOMEM);
+
+ ret = avio_get_str16le(pb, chunk_size, value, chunk_size);
+ avio_skip(pb, chunk_size - ret);
+
+ av_dict_set(&s->metadata, chunk_key, value, AV_DICT_DONT_STRDUP_VAL);
+ }
+
+ avio_skip(pb, end - avio_tell(pb));
+ } else {
+ av_log(s, AV_LOG_DEBUG, "unknown guid: "FF_PRI_GUID"\n", FF_ARG_GUID(guid));
+ avio_skip(pb, FFALIGN(size, INT64_C(8)) - 24);
+ }
}
- wav->data_end = avio_tell(pb) + size - 24;
- wav->w64 = 1;
+
+ if (!data_ofs)
+ return AVERROR_EOF;
+
+ ff_metadata_conv_ctx(s, NULL, wav_metadata_conv);
+ ff_metadata_conv_ctx(s, NULL, ff_riff_info_conv);
+
+ handle_stream_probing(st);
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
+
+ avio_seek(pb, data_ofs, SEEK_SET);
return 0;
}
diff --git a/libavformat/wavenc.c b/libavformat/wavenc.c
index 25f6ffcde0..2345fc594a 100644
--- a/libavformat/wavenc.c
+++ b/libavformat/wavenc.c
@@ -2,30 +2,43 @@
* WAV muxer
* Copyright (c) 2001, 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * Sony Wave64 muxer
+ * Copyright (c) 2012 Paul B Mahol
*
- * Libav is free software; you can redistribute it and/or
+ * WAV muxer RF64 support
+ * Copyright (c) 2013 Daniel Verkamp <daniel@drv.nu>
+ *
+ * EBU Tech 3285 - Supplement 3 - Peak Envelope Chunk encoder
+ * Copyright (c) 2014 Georg Lippitsch <georg.lippitsch@gmx.at>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdint.h>
#include <string.h>
+#include "libavutil/avstring.h"
#include "libavutil/dict.h"
#include "libavutil/common.h"
+#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
+#include "libavutil/time.h"
+#include "libavutil/time_internal.h"
#include "avformat.h"
#include "avio.h"
@@ -33,16 +46,49 @@
#include "internal.h"
#include "riff.h"
+#define RF64_AUTO (-1)
+#define RF64_NEVER 0
+#define RF64_ALWAYS 1
+
+#define PEAK_BUFFER_SIZE 1024
+
+typedef enum {
+ PEAK_OFF = 0,
+ PEAK_ON,
+ PEAK_ONLY
+} PeakType;
+
+typedef enum {
+ PEAK_FORMAT_UINT8 = 1,
+ PEAK_FORMAT_UINT16
+} PeakFormat;
+
typedef struct WAVMuxContext {
const AVClass *class;
int64_t data;
int64_t fact_pos;
+ int64_t ds64;
int64_t minpts;
int64_t maxpts;
+ int16_t *peak_maxpos, *peak_maxneg;
+ uint32_t peak_num_frames;
+ uint32_t peak_outbuf_size;
+ uint32_t peak_outbuf_bytes;
+ uint32_t peak_pos_pop;
+ uint16_t peak_pop;
+ uint8_t *peak_output;
int last_duration;
int write_bext;
+ int write_peak;
+ int rf64;
+ int peak_block_size;
+ PeakFormat peak_format;
+ int peak_block_pos;
+ int peak_ppv;
+ int peak_bps;
} WAVMuxContext;
+#if CONFIG_WAV_MUXER
static inline void bwf_write_bext_string(AVFormatContext *s, const char *key, int maxlen)
{
AVDictionaryEntry *tag;
@@ -97,25 +143,204 @@ static void bwf_write_bext_chunk(AVFormatContext *s)
ff_end_tag(s->pb, bext);
}
+static av_cold void peak_free_buffers(AVFormatContext *s)
+{
+ WAVMuxContext *wav = s->priv_data;
+
+ av_freep(&wav->peak_maxpos);
+ av_freep(&wav->peak_maxneg);
+ av_freep(&wav->peak_output);
+}
+
+static av_cold int peak_init_writer(AVFormatContext *s)
+{
+ WAVMuxContext *wav = s->priv_data;
+ AVCodecContext *enc = s->streams[0]->codec;
+
+ if (enc->codec_id != AV_CODEC_ID_PCM_S8 &&
+ enc->codec_id != AV_CODEC_ID_PCM_S16LE &&
+ enc->codec_id != AV_CODEC_ID_PCM_U8 &&
+ enc->codec_id != AV_CODEC_ID_PCM_U16LE) {
+ av_log(s, AV_LOG_ERROR, "%s codec not supported for Peak Chunk\n",
+ s->streams[0]->codec->codec ? s->streams[0]->codec->codec->name : "NONE");
+ return -1;
+ }
+
+ wav->peak_bps = av_get_bits_per_sample(enc->codec_id) / 8;
+
+ if (wav->peak_bps == 1 && wav->peak_format == PEAK_FORMAT_UINT16) {
+ av_log(s, AV_LOG_ERROR,
+ "Writing 16 bit peak for 8 bit audio does not make sense\n");
+ return AVERROR(EINVAL);
+ }
+
+ wav->peak_maxpos = av_mallocz_array(enc->channels, sizeof(*wav->peak_maxpos));
+ wav->peak_maxneg = av_mallocz_array(enc->channels, sizeof(*wav->peak_maxneg));
+ wav->peak_output = av_malloc(PEAK_BUFFER_SIZE);
+ if (!wav->peak_maxpos || !wav->peak_maxneg || !wav->peak_output)
+ goto nomem;
+
+ wav->peak_outbuf_size = PEAK_BUFFER_SIZE;
+
+ return 0;
+
+nomem:
+ av_log(s, AV_LOG_ERROR, "Out of memory\n");
+ peak_free_buffers(s);
+ return AVERROR(ENOMEM);
+}
+
+static void peak_write_frame(AVFormatContext *s)
+{
+ WAVMuxContext *wav = s->priv_data;
+ AVCodecContext *enc = s->streams[0]->codec;
+ int peak_of_peaks;
+ int c;
+
+ if (!wav->peak_output)
+ return;
+
+ for (c = 0; c < enc->channels; c++) {
+ wav->peak_maxneg[c] = -wav->peak_maxneg[c];
+
+ if (wav->peak_bps == 2 && wav->peak_format == PEAK_FORMAT_UINT8) {
+ wav->peak_maxpos[c] = wav->peak_maxpos[c] / 256;
+ wav->peak_maxneg[c] = wav->peak_maxneg[c] / 256;
+ }
+
+ if (wav->peak_ppv == 1)
+ wav->peak_maxpos[c] =
+ FFMAX(wav->peak_maxpos[c], wav->peak_maxneg[c]);
+
+ peak_of_peaks = FFMAX3(wav->peak_maxpos[c], wav->peak_maxneg[c],
+ wav->peak_pop);
+ if (peak_of_peaks > wav->peak_pop)
+ wav->peak_pos_pop = wav->peak_num_frames;
+ wav->peak_pop = peak_of_peaks;
+
+ if (wav->peak_outbuf_size - wav->peak_outbuf_bytes <
+ wav->peak_format * wav->peak_ppv) {
+ wav->peak_outbuf_size += PEAK_BUFFER_SIZE;
+ wav->peak_output = av_realloc(wav->peak_output,
+ wav->peak_outbuf_size);
+ if (!wav->peak_output) {
+ av_log(s, AV_LOG_ERROR, "No memory for peak data\n");
+ return;
+ }
+ }
+
+ if (wav->peak_format == PEAK_FORMAT_UINT8) {
+ wav->peak_output[wav->peak_outbuf_bytes++] =
+ wav->peak_maxpos[c];
+ if (wav->peak_ppv == 2) {
+ wav->peak_output[wav->peak_outbuf_bytes++] =
+ wav->peak_maxneg[c];
+ }
+ } else {
+ AV_WL16(wav->peak_output + wav->peak_outbuf_bytes,
+ wav->peak_maxpos[c]);
+ wav->peak_outbuf_bytes += 2;
+ if (wav->peak_ppv == 2) {
+ AV_WL16(wav->peak_output + wav->peak_outbuf_bytes,
+ wav->peak_maxneg[c]);
+ wav->peak_outbuf_bytes += 2;
+ }
+ }
+ wav->peak_maxpos[c] = 0;
+ wav->peak_maxneg[c] = 0;
+ }
+ wav->peak_num_frames++;
+}
+
+static int peak_write_chunk(AVFormatContext *s)
+{
+ WAVMuxContext *wav = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVCodecContext *enc = s->streams[0]->codec;
+ int64_t peak = ff_start_tag(s->pb, "levl");
+ int64_t now0;
+ time_t now_secs;
+ char timestamp[28];
+
+ /* Peak frame of incomplete block at end */
+ if (wav->peak_block_pos)
+ peak_write_frame(s);
+
+ memset(timestamp, 0, sizeof(timestamp));
+ if (!(s->flags & AVFMT_FLAG_BITEXACT)) {
+ struct tm tmpbuf;
+ av_log(s, AV_LOG_INFO, "Writing local time and date to Peak Envelope Chunk\n");
+ now0 = av_gettime();
+ now_secs = now0 / 1000000;
+ if (strftime(timestamp, sizeof(timestamp), "%Y:%m:%d:%H:%M:%S:", localtime_r(&now_secs, &tmpbuf))) {
+ av_strlcatf(timestamp, sizeof(timestamp), "%03d", (int)((now0 / 1000) % 1000));
+ } else {
+ av_log(s, AV_LOG_ERROR, "Failed to write timestamp\n");
+ return -1;
+ }
+ }
+
+ avio_wl32(pb, 1); /* version */
+ avio_wl32(pb, wav->peak_format); /* 8 or 16 bit */
+ avio_wl32(pb, wav->peak_ppv); /* positive and negative */
+ avio_wl32(pb, wav->peak_block_size); /* frames per value */
+ avio_wl32(pb, enc->channels); /* number of channels */
+ avio_wl32(pb, wav->peak_num_frames); /* number of peak frames */
+ avio_wl32(pb, wav->peak_pos_pop); /* audio sample frame index */
+ avio_wl32(pb, 128); /* equal to size of header */
+ avio_write(pb, timestamp, 28); /* ASCII time stamp */
+ ffio_fill(pb, 0, 60);
+
+ avio_write(pb, wav->peak_output, wav->peak_outbuf_bytes);
+
+ ff_end_tag(pb, peak);
+
+ if (!wav->data)
+ wav->data = peak;
+
+ return 0;
+}
+
static int wav_write_header(AVFormatContext *s)
{
WAVMuxContext *wav = s->priv_data;
AVIOContext *pb = s->pb;
int64_t fmt;
- ffio_wfourcc(pb, "RIFF");
- avio_wl32(pb, 0); /* file length */
+ if (s->nb_streams != 1) {
+ av_log(s, AV_LOG_ERROR, "WAVE files have exactly one stream\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (wav->rf64 == RF64_ALWAYS) {
+ ffio_wfourcc(pb, "RF64");
+ avio_wl32(pb, -1); /* RF64 chunk size: use size in ds64 */
+ } else {
+ ffio_wfourcc(pb, "RIFF");
+ avio_wl32(pb, -1); /* file length */
+ }
+
ffio_wfourcc(pb, "WAVE");
- /* format header */
- fmt = ff_start_tag(pb, "fmt ");
- if (ff_put_wav_header(pb, s->streams[0]->codec) < 0) {
- const AVCodecDescriptor *desc = avcodec_descriptor_get(s->streams[0]->codec->codec_id);
- av_log(s, AV_LOG_ERROR, "%s codec not supported in WAVE format\n",
- desc ? desc->name : "unknown");
- return AVERROR(ENOSYS);
+ if (wav->rf64 != RF64_NEVER) {
+ /* write empty ds64 chunk or JUNK chunk to reserve space for ds64 */
+ ffio_wfourcc(pb, wav->rf64 == RF64_ALWAYS ? "ds64" : "JUNK");
+ avio_wl32(pb, 28); /* chunk size */
+ wav->ds64 = avio_tell(pb);
+ ffio_fill(pb, 0, 28);
+ }
+
+ if (wav->write_peak != 2) {
+ /* format header */
+ fmt = ff_start_tag(pb, "fmt ");
+ if (ff_put_wav_header(pb, s->streams[0]->codec, 0) < 0) {
+ const AVCodecDescriptor *desc = avcodec_descriptor_get(s->streams[0]->codec->codec_id);
+ av_log(s, AV_LOG_ERROR, "%s codec not supported in WAVE format\n",
+ desc ? desc->name : "unknown");
+ return AVERROR(ENOSYS);
+ }
+ ff_end_tag(pb, fmt);
}
- ff_end_tag(pb, fmt);
if (s->streams[0]->codec->codec_tag != 0x01 /* hence for all other than PCM */
&& s->pb->seekable) {
@@ -127,15 +352,23 @@ static int wav_write_header(AVFormatContext *s)
if (wav->write_bext)
bwf_write_bext_chunk(s);
+ if (wav->write_peak) {
+ int ret;
+ if ((ret = peak_init_writer(s)) < 0)
+ return ret;
+ }
+
avpriv_set_pts_info(s->streams[0], 64, 1, s->streams[0]->codec->sample_rate);
wav->maxpts = wav->last_duration = 0;
wav->minpts = INT64_MAX;
- /* info header */
- ff_riff_write_info(s);
+ if (wav->write_peak != 2) {
+ /* info header */
+ ff_riff_write_info(s);
- /* data header */
- wav->data = ff_start_tag(pb, "data");
+ /* data header */
+ wav->data = ff_start_tag(pb, "data");
+ }
avio_flush(pb);
@@ -146,7 +379,31 @@ static int wav_write_packet(AVFormatContext *s, AVPacket *pkt)
{
AVIOContext *pb = s->pb;
WAVMuxContext *wav = s->priv_data;
- avio_write(pb, pkt->data, pkt->size);
+
+ if (wav->write_peak != 2)
+ avio_write(pb, pkt->data, pkt->size);
+
+ if (wav->write_peak) {
+ int c = 0;
+ int i;
+ for (i = 0; i < pkt->size; i += wav->peak_bps) {
+ if (wav->peak_bps == 1) {
+ wav->peak_maxpos[c] = FFMAX(wav->peak_maxpos[c], *(int8_t*)(pkt->data + i));
+ wav->peak_maxneg[c] = FFMIN(wav->peak_maxneg[c], *(int8_t*)(pkt->data + i));
+ } else {
+ wav->peak_maxpos[c] = FFMAX(wav->peak_maxpos[c], (int16_t)AV_RL16(pkt->data + i));
+ wav->peak_maxneg[c] = FFMIN(wav->peak_maxneg[c], (int16_t)AV_RL16(pkt->data + i));
+ }
+ if (++c == s->streams[0]->codec->channels) {
+ c = 0;
+ if (++wav->peak_block_pos == wav->peak_block_size) {
+ peak_write_frame(s);
+ wav->peak_block_pos = 0;
+ }
+ }
+ }
+ }
+
if(pkt->pts != AV_NOPTS_VALUE) {
wav->minpts = FFMIN(wav->minpts, pkt->pts);
wav->maxpts = FFMAX(wav->maxpts, pkt->pts);
@@ -160,40 +417,99 @@ static int wav_write_trailer(AVFormatContext *s)
{
AVIOContext *pb = s->pb;
WAVMuxContext *wav = s->priv_data;
- int64_t file_size;
+ int64_t file_size, data_size;
+ int64_t number_of_samples = 0;
+ int rf64 = 0;
+ int ret = 0;
avio_flush(pb);
if (s->pb->seekable) {
- ff_end_tag(pb, wav->data);
+ if (wav->write_peak != 2) {
+ ff_end_tag(pb, wav->data);
+ avio_flush(pb);
+ }
+
+ if (wav->write_peak && wav->peak_output) {
+ ret = peak_write_chunk(s);
+ avio_flush(pb);
+ }
/* update file size */
file_size = avio_tell(pb);
- avio_seek(pb, 4, SEEK_SET);
- avio_wl32(pb, (uint32_t)(file_size - 8));
- avio_seek(pb, file_size, SEEK_SET);
+ data_size = file_size - wav->data;
+ if (wav->rf64 == RF64_ALWAYS || (wav->rf64 == RF64_AUTO && file_size - 8 > UINT32_MAX)) {
+ rf64 = 1;
+ } else {
+ avio_seek(pb, 4, SEEK_SET);
+ avio_wl32(pb, (uint32_t)(file_size - 8));
+ avio_seek(pb, file_size, SEEK_SET);
- avio_flush(pb);
+ avio_flush(pb);
+ }
+
+ number_of_samples = av_rescale(wav->maxpts - wav->minpts + wav->last_duration,
+ s->streams[0]->codec->sample_rate * (int64_t)s->streams[0]->time_base.num,
+ s->streams[0]->time_base.den);
if(s->streams[0]->codec->codec_tag != 0x01) {
/* Update num_samps in fact chunk */
- int number_of_samples;
- number_of_samples = av_rescale(wav->maxpts - wav->minpts + wav->last_duration,
- s->streams[0]->codec->sample_rate * (int64_t)s->streams[0]->time_base.num,
- s->streams[0]->time_base.den);
avio_seek(pb, wav->fact_pos, SEEK_SET);
- avio_wl32(pb, number_of_samples);
+ if (rf64 || (wav->rf64 == RF64_AUTO && number_of_samples > UINT32_MAX)) {
+ rf64 = 1;
+ avio_wl32(pb, -1);
+ } else {
+ avio_wl32(pb, number_of_samples);
+ avio_seek(pb, file_size, SEEK_SET);
+ avio_flush(pb);
+ }
+ }
+
+ if (rf64) {
+ /* overwrite RIFF with RF64 */
+ avio_seek(pb, 0, SEEK_SET);
+ ffio_wfourcc(pb, "RF64");
+ avio_wl32(pb, -1);
+
+ /* write ds64 chunk (overwrite JUNK if rf64 == RF64_AUTO) */
+ avio_seek(pb, wav->ds64 - 8, SEEK_SET);
+ ffio_wfourcc(pb, "ds64");
+ avio_wl32(pb, 28); /* ds64 chunk size */
+ avio_wl64(pb, file_size - 8); /* RF64 chunk size */
+ avio_wl64(pb, data_size); /* data chunk size */
+ avio_wl64(pb, number_of_samples); /* fact chunk number of samples */
+ avio_wl32(pb, 0); /* number of table entries for non-'data' chunks */
+
+ /* write -1 in data chunk size */
+ avio_seek(pb, wav->data - 4, SEEK_SET);
+ avio_wl32(pb, -1);
+
avio_seek(pb, file_size, SEEK_SET);
avio_flush(pb);
}
}
- return 0;
+
+ if (wav->write_peak)
+ peak_free_buffers(s);
+
+ return ret;
}
#define OFFSET(x) offsetof(WAVMuxContext, x)
#define ENC AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
{ "write_bext", "Write BEXT chunk.", OFFSET(write_bext), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, ENC },
+ { "write_peak", "Write Peak Envelope chunk.", OFFSET(write_peak), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, ENC, "peak" },
+ { "off", "Do not write peak chunk.", 0, AV_OPT_TYPE_CONST, { .i64 = PEAK_OFF }, 0, 0, ENC, "peak" },
+ { "on", "Append peak chunk after wav data.", 0, AV_OPT_TYPE_CONST, { .i64 = PEAK_ON }, 0, 0, ENC, "peak" },
+ { "only", "Write only peak chunk, omit wav data.", 0, AV_OPT_TYPE_CONST, { .i64 = PEAK_ONLY }, 0, 0, ENC, "peak" },
+ { "rf64", "Use RF64 header rather than RIFF for large files.", OFFSET(rf64), AV_OPT_TYPE_INT, { .i64 = RF64_NEVER },-1, 1, ENC, "rf64" },
+ { "auto", "Write RF64 header if file grows large enough.", 0, AV_OPT_TYPE_CONST, { .i64 = RF64_AUTO }, 0, 0, ENC, "rf64" },
+ { "always", "Always write RF64 header regardless of file size.", 0, AV_OPT_TYPE_CONST, { .i64 = RF64_ALWAYS }, 0, 0, ENC, "rf64" },
+ { "never", "Never write RF64 header regardless of file size.", 0, AV_OPT_TYPE_CONST, { .i64 = RF64_NEVER }, 0, 0, ENC, "rf64" },
+ { "peak_block_size", "Number of audio samples used to generate each peak frame.", OFFSET(peak_block_size), AV_OPT_TYPE_INT, { .i64 = 256 }, 0, 65536, ENC },
+ { "peak_format", "The format of the peak envelope data (1: uint8, 2: uint16).", OFFSET(peak_format), AV_OPT_TYPE_INT, { .i64 = PEAK_FORMAT_UINT16 }, PEAK_FORMAT_UINT8, PEAK_FORMAT_UINT16, ENC },
+ { "peak_ppv", "Number of peak points per peak value (1 or 2).", OFFSET(peak_ppv), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, 2, ENC },
{ NULL },
};
@@ -219,3 +535,101 @@ AVOutputFormat ff_wav_muxer = {
.codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, 0 },
.priv_class = &wav_muxer_class,
};
+#endif /* CONFIG_WAV_MUXER */
+
+#if CONFIG_W64_MUXER
+#include "w64.h"
+
+static void start_guid(AVIOContext *pb, const uint8_t *guid, int64_t *pos)
+{
+ *pos = avio_tell(pb);
+
+ avio_write(pb, guid, 16);
+ avio_wl64(pb, INT64_MAX);
+}
+
+static void end_guid(AVIOContext *pb, int64_t start)
+{
+ int64_t end, pos = avio_tell(pb);
+
+ end = FFALIGN(pos, 8);
+ ffio_fill(pb, 0, end - pos);
+ avio_seek(pb, start + 16, SEEK_SET);
+ avio_wl64(pb, end - start);
+ avio_seek(pb, end, SEEK_SET);
+}
+
+static int w64_write_header(AVFormatContext *s)
+{
+ WAVMuxContext *wav = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int64_t start;
+ int ret;
+
+ avio_write(pb, ff_w64_guid_riff, sizeof(ff_w64_guid_riff));
+ avio_wl64(pb, -1);
+ avio_write(pb, ff_w64_guid_wave, sizeof(ff_w64_guid_wave));
+ start_guid(pb, ff_w64_guid_fmt, &start);
+ if ((ret = ff_put_wav_header(pb, s->streams[0]->codec, 0)) < 0) {
+ av_log(s, AV_LOG_ERROR, "%s codec not supported\n",
+ s->streams[0]->codec->codec ? s->streams[0]->codec->codec->name : "NONE");
+ return ret;
+ }
+ end_guid(pb, start);
+
+ if (s->streams[0]->codec->codec_tag != 0x01 /* hence for all other than PCM */
+ && s->pb->seekable) {
+ start_guid(pb, ff_w64_guid_fact, &wav->fact_pos);
+ avio_wl64(pb, 0);
+ end_guid(pb, wav->fact_pos);
+ }
+
+ start_guid(pb, ff_w64_guid_data, &wav->data);
+
+ return 0;
+}
+
+static int w64_write_trailer(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ WAVMuxContext *wav = s->priv_data;
+ int64_t file_size;
+
+ if (pb->seekable) {
+ end_guid(pb, wav->data);
+
+ file_size = avio_tell(pb);
+ avio_seek(pb, 16, SEEK_SET);
+ avio_wl64(pb, file_size);
+
+ if (s->streams[0]->codec->codec_tag != 0x01) {
+ int64_t number_of_samples;
+
+ number_of_samples = av_rescale(wav->maxpts - wav->minpts + wav->last_duration,
+ s->streams[0]->codec->sample_rate * (int64_t)s->streams[0]->time_base.num,
+ s->streams[0]->time_base.den);
+ avio_seek(pb, wav->fact_pos + 24, SEEK_SET);
+ avio_wl64(pb, number_of_samples);
+ }
+
+ avio_seek(pb, file_size, SEEK_SET);
+ avio_flush(pb);
+ }
+
+ return 0;
+}
+
+AVOutputFormat ff_w64_muxer = {
+ .name = "w64",
+ .long_name = NULL_IF_CONFIG_SMALL("Sony Wave64"),
+ .extensions = "w64",
+ .priv_data_size = sizeof(WAVMuxContext),
+ .audio_codec = AV_CODEC_ID_PCM_S16LE,
+ .video_codec = AV_CODEC_ID_NONE,
+ .write_header = w64_write_header,
+ .write_packet = wav_write_packet,
+ .write_trailer = w64_write_trailer,
+ .flags = AVFMT_TS_NONSTRICT,
+ .codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, 0 },
+};
+#endif /* CONFIG_W64_MUXER */
diff --git a/libavformat/wc3movie.c b/libavformat/wc3movie.c
index c8942537cb..7bd09e3f17 100644
--- a/libavformat/wc3movie.c
+++ b/libavformat/wc3movie.c
@@ -1,21 +1,21 @@
/*
* Wing Commander III Movie (.mve) File Demuxer
- * Copyright (c) 2003 The ffmpeg Project
+ * Copyright (c) 2003 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -27,6 +27,7 @@
* http://www.pcisys.net/~melanson/codecs/
*/
+#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
@@ -158,7 +159,7 @@ static int wc3_read_header(AVFormatContext *s)
fourcc_tag = avio_rl32(pb);
/* chunk sizes are 16-bit aligned */
size = (avio_rb32(pb) + 1) & (~1);
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR(EIO);
} while (fourcc_tag != BRCH_TAG);
@@ -210,7 +211,7 @@ static int wc3_read_packet(AVFormatContext *s,
fourcc_tag = avio_rl32(pb);
/* chunk sizes are 16-bit aligned */
size = (avio_rb32(pb) + 1) & (~1);
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR(EIO);
switch (fourcc_tag) {
@@ -249,10 +250,16 @@ static int wc3_read_packet(AVFormatContext *s,
else {
int i = 0;
av_log (s, AV_LOG_DEBUG, "Subtitle time!\n");
+ if (i >= size || av_strnlen(&text[i + 1], size - i - 1) >= size - i - 1)
+ return AVERROR_INVALIDDATA;
av_log (s, AV_LOG_DEBUG, " inglish: %s\n", &text[i + 1]);
i += text[i] + 1;
+ if (i >= size || av_strnlen(&text[i + 1], size - i - 1) >= size - i - 1)
+ return AVERROR_INVALIDDATA;
av_log (s, AV_LOG_DEBUG, " doytsch: %s\n", &text[i + 1]);
i += text[i] + 1;
+ if (i >= size || av_strnlen(&text[i + 1], size - i - 1) >= size - i - 1)
+ return AVERROR_INVALIDDATA;
av_log (s, AV_LOG_DEBUG, " fronsay: %s\n", &text[i + 1]);
}
#endif
diff --git a/libavformat/webmdashenc.c b/libavformat/webmdashenc.c
new file mode 100644
index 0000000000..4536b7d5a8
--- /dev/null
+++ b/libavformat/webmdashenc.c
@@ -0,0 +1,386 @@
+/*
+ * WebM DASH Manifest XML muxer
+ * Copyright (c) 2014 Vignesh Venkatasubramanian
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * WebM DASH Specification:
+ * https://sites.google.com/a/webmproject.org/wiki/adaptive-streaming/webm-dash-specification
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "avformat.h"
+#include "avio_internal.h"
+#include "matroska.h"
+
+#include "libavutil/avstring.h"
+#include "libavutil/dict.h"
+#include "libavutil/opt.h"
+
+typedef struct AdaptationSet {
+ char id[10];
+ int *streams;
+ int nb_streams;
+} AdaptationSet;
+
+typedef struct WebMDashMuxContext {
+ const AVClass *class;
+ char *adaptation_sets;
+ AdaptationSet *as;
+ int nb_as;
+ int representation_id;
+} WebMDashMuxContext;
+
+static const char *get_codec_name(int codec_id)
+{
+ switch (codec_id) {
+ case AV_CODEC_ID_VP8:
+ return "vp8";
+ case AV_CODEC_ID_VP9:
+ return "vp9";
+ case AV_CODEC_ID_VORBIS:
+ return "vorbis";
+ case AV_CODEC_ID_OPUS:
+ return "opus";
+ }
+ return NULL;
+}
+
+static double get_duration(AVFormatContext *s)
+{
+ int i = 0;
+ double max = 0.0;
+ for (i = 0; i < s->nb_streams; i++) {
+ AVDictionaryEntry *duration = av_dict_get(s->streams[i]->metadata,
+ DURATION, NULL, 0);
+ if (!duration || atof(duration->value) < 0) continue;
+ if (atof(duration->value) > max) max = atof(duration->value);
+ }
+ return max / 1000;
+}
+
+static void write_header(AVFormatContext *s)
+{
+ double min_buffer_time = 1.0;
+ avio_printf(s->pb, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ avio_printf(s->pb, "<MPD\n");
+ avio_printf(s->pb, " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
+ avio_printf(s->pb, " xmlns=\"urn:mpeg:DASH:schema:MPD:2011\"\n");
+ avio_printf(s->pb, " xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011\"\n");
+ avio_printf(s->pb, " type=\"static\"\n");
+ avio_printf(s->pb, " mediaPresentationDuration=\"PT%gS\"\n",
+ get_duration(s));
+ avio_printf(s->pb, " minBufferTime=\"PT%gS\"\n",
+ min_buffer_time);
+ avio_printf(s->pb, " profiles=\"urn:webm:dash:profile:webm-on-demand:2012\"");
+ avio_printf(s->pb, ">\n");
+}
+
+static void write_footer(AVFormatContext *s)
+{
+ avio_printf(s->pb, "</MPD>\n");
+}
+
+static int subsegment_alignment(AVFormatContext *s, AdaptationSet *as) {
+ int i;
+ AVDictionaryEntry *gold = av_dict_get(s->streams[as->streams[0]]->metadata,
+ CUE_TIMESTAMPS, NULL, 0);
+ if (!gold) return 0;
+ for (i = 1; i < as->nb_streams; i++) {
+ AVDictionaryEntry *ts = av_dict_get(s->streams[as->streams[i]]->metadata,
+ CUE_TIMESTAMPS, NULL, 0);
+ if (!ts || strncmp(gold->value, ts->value, strlen(gold->value))) return 0;
+ }
+ return 1;
+}
+
+static int bitstream_switching(AVFormatContext *s, AdaptationSet *as) {
+ int i;
+ AVDictionaryEntry *gold_track_num = av_dict_get(s->streams[as->streams[0]]->metadata,
+ TRACK_NUMBER, NULL, 0);
+ AVCodecContext *gold_codec = s->streams[as->streams[0]]->codec;
+ if (!gold_track_num) return 0;
+ for (i = 1; i < as->nb_streams; i++) {
+ AVDictionaryEntry *track_num = av_dict_get(s->streams[as->streams[i]]->metadata,
+ TRACK_NUMBER, NULL, 0);
+ AVCodecContext *codec = s->streams[as->streams[i]]->codec;
+ if (!track_num ||
+ strncmp(gold_track_num->value, track_num->value, strlen(gold_track_num->value)) ||
+ gold_codec->codec_id != codec->codec_id ||
+ gold_codec->extradata_size != codec->extradata_size ||
+ memcmp(gold_codec->extradata, codec->extradata, codec->extradata_size)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Writes a Representation within an Adaptation Set. Returns 0 on success and
+ * < 0 on failure.
+ */
+static int write_representation(AVFormatContext *s, AVStream *stream, int id,
+ int output_width, int output_height,
+ int output_sample_rate) {
+ AVDictionaryEntry *irange = av_dict_get(stream->metadata, INITIALIZATION_RANGE, NULL, 0);
+ AVDictionaryEntry *cues_start = av_dict_get(stream->metadata, CUES_START, NULL, 0);
+ AVDictionaryEntry *cues_end = av_dict_get(stream->metadata, CUES_END, NULL, 0);
+ AVDictionaryEntry *filename = av_dict_get(stream->metadata, FILENAME, NULL, 0);
+ AVDictionaryEntry *bandwidth = av_dict_get(stream->metadata, BANDWIDTH, NULL, 0);
+ if (!irange || cues_start == NULL || cues_end == NULL || filename == NULL ||
+ !bandwidth) {
+ return -1;
+ }
+ avio_printf(s->pb, "<Representation id=\"%d\"", id);
+ avio_printf(s->pb, " bandwidth=\"%s\"", bandwidth->value);
+ if (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO && output_width)
+ avio_printf(s->pb, " width=\"%d\"", stream->codec->width);
+ if (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO && output_height)
+ avio_printf(s->pb, " height=\"%d\"", stream->codec->height);
+ if (stream->codec->codec_type = AVMEDIA_TYPE_AUDIO && output_sample_rate)
+ avio_printf(s->pb, " audioSamplingRate=\"%d\"", stream->codec->sample_rate);
+ avio_printf(s->pb, ">\n");
+ avio_printf(s->pb, "<BaseURL>%s</BaseURL>\n", filename->value);
+ avio_printf(s->pb, "<SegmentBase\n");
+ avio_printf(s->pb, " indexRange=\"%s-%s\">\n", cues_start->value, cues_end->value);
+ avio_printf(s->pb, "<Initialization\n");
+ avio_printf(s->pb, " range=\"0-%s\" />\n", irange->value);
+ avio_printf(s->pb, "</SegmentBase>\n");
+ avio_printf(s->pb, "</Representation>\n");
+ return 0;
+}
+
+/*
+ * Checks if width of all streams are the same. Returns 1 if true, 0 otherwise.
+ */
+static int check_matching_width(AVFormatContext *s, AdaptationSet *as) {
+ int first_width, i;
+ if (as->nb_streams < 2) return 1;
+ first_width = s->streams[as->streams[0]]->codec->width;
+ for (i = 1; i < as->nb_streams; i++)
+ if (first_width != s->streams[as->streams[i]]->codec->width)
+ return 0;
+ return 1;
+}
+
+/*
+ * Checks if height of all streams are the same. Returns 1 if true, 0 otherwise.
+ */
+static int check_matching_height(AVFormatContext *s, AdaptationSet *as) {
+ int first_height, i;
+ if (as->nb_streams < 2) return 1;
+ first_height = s->streams[as->streams[0]]->codec->height;
+ for (i = 1; i < as->nb_streams; i++)
+ if (first_height != s->streams[as->streams[i]]->codec->height)
+ return 0;
+ return 1;
+}
+
+/*
+ * Checks if sample rate of all streams are the same. Returns 1 if true, 0 otherwise.
+ */
+static int check_matching_sample_rate(AVFormatContext *s, AdaptationSet *as) {
+ int first_sample_rate, i;
+ if (as->nb_streams < 2) return 1;
+ first_sample_rate = s->streams[as->streams[0]]->codec->sample_rate;
+ for (i = 1; i < as->nb_streams; i++)
+ if (first_sample_rate != s->streams[as->streams[i]]->codec->sample_rate)
+ return 0;
+ return 1;
+}
+
+/*
+ * Writes an Adaptation Set. Returns 0 on success and < 0 on failure.
+ */
+static int write_adaptation_set(AVFormatContext *s, int as_index)
+{
+ WebMDashMuxContext *w = s->priv_data;
+ AdaptationSet *as = &w->as[as_index];
+ AVCodecContext *codec = s->streams[as->streams[0]]->codec;
+ AVDictionaryEntry *lang;
+ int i;
+ static const char boolean[2][6] = { "false", "true" };
+ int subsegmentStartsWithSAP = 1;
+
+ // Width, Height and Sample Rate will go in the AdaptationSet tag if they
+ // are the same for all contained Representations. otherwise, they will go
+ // on their respective Representation tag.
+ int width_in_as = 1, height_in_as = 1, sample_rate_in_as = 1;
+ if (codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ width_in_as = check_matching_width(s, as);
+ height_in_as = check_matching_height(s, as);
+ } else {
+ sample_rate_in_as = check_matching_sample_rate(s, as);
+ }
+
+ avio_printf(s->pb, "<AdaptationSet id=\"%s\"", as->id);
+ avio_printf(s->pb, " mimeType=\"%s/webm\"",
+ codec->codec_type == AVMEDIA_TYPE_VIDEO ? "video" : "audio");
+ avio_printf(s->pb, " codecs=\"%s\"", get_codec_name(codec->codec_id));
+
+ lang = av_dict_get(s->streams[as->streams[0]]->metadata, "language", NULL, 0);
+ if (lang) avio_printf(s->pb, " lang=\"%s\"", lang->value);
+
+ if (codec->codec_type == AVMEDIA_TYPE_VIDEO && width_in_as)
+ avio_printf(s->pb, " width=\"%d\"", codec->width);
+ if (codec->codec_type == AVMEDIA_TYPE_VIDEO && height_in_as)
+ avio_printf(s->pb, " height=\"%d\"", codec->height);
+ if (codec->codec_type == AVMEDIA_TYPE_AUDIO && sample_rate_in_as)
+ avio_printf(s->pb, " audioSamplingRate=\"%d\"", codec->sample_rate);
+
+ avio_printf(s->pb, " bitstreamSwitching=\"%s\"",
+ boolean[bitstream_switching(s, as)]);
+ avio_printf(s->pb, " subsegmentAlignment=\"%s\"",
+ boolean[subsegment_alignment(s, as)]);
+
+ for (i = 0; i < as->nb_streams; i++) {
+ AVDictionaryEntry *kf = av_dict_get(s->streams[as->streams[i]]->metadata,
+ CLUSTER_KEYFRAME, NULL, 0);
+ if (!kf || !strncmp(kf->value, "0", 1)) subsegmentStartsWithSAP = 0;
+ }
+ avio_printf(s->pb, " subsegmentStartsWithSAP=\"%d\"", subsegmentStartsWithSAP);
+ avio_printf(s->pb, ">\n");
+
+ for (i = 0; i < as->nb_streams; i++) {
+ write_representation(s, s->streams[as->streams[i]], w->representation_id++,
+ !width_in_as, !height_in_as, !sample_rate_in_as);
+ }
+ avio_printf(s->pb, "</AdaptationSet>\n");
+ return 0;
+}
+
+static int to_integer(char *p, int len)
+{
+ int ret;
+ char *q = av_malloc(sizeof(char) * len);
+ if (!q) return -1;
+ av_strlcpy(q, p, len);
+ ret = atoi(q);
+ av_free(q);
+ return ret;
+}
+
+static int parse_adaptation_sets(AVFormatContext *s)
+{
+ WebMDashMuxContext *w = s->priv_data;
+ char *p = w->adaptation_sets;
+ char *q;
+ enum { new_set, parsed_id, parsing_streams } state;
+ // syntax id=0,streams=0,1,2 id=1,streams=3,4 and so on
+ state = new_set;
+ while (p < w->adaptation_sets + strlen(w->adaptation_sets)) {
+ if (*p == ' ')
+ continue;
+ else if (state == new_set && !strncmp(p, "id=", 3)) {
+ w->as = av_realloc(w->as, sizeof(*w->as) * ++w->nb_as);
+ if (w->as == NULL) return -1;
+ w->as[w->nb_as - 1].nb_streams = 0;
+ w->as[w->nb_as - 1].streams = NULL;
+ p += 3; // consume "id="
+ q = w->as[w->nb_as - 1].id;
+ while (*p != ',') *q++ = *p++;
+ *q = 0;
+ p++;
+ state = parsed_id;
+ } else if (state == parsed_id && !strncmp(p, "streams=", 8)) {
+ p += 8; // consume "streams="
+ state = parsing_streams;
+ } else if (state == parsing_streams) {
+ struct AdaptationSet *as = &w->as[w->nb_as - 1];
+ q = p;
+ while (*q != '\0' && *q != ',' && *q != ' ') q++;
+ as->streams = av_realloc(as->streams, sizeof(*as->streams) * ++as->nb_streams);
+ if (as->streams == NULL) return -1;
+ as->streams[as->nb_streams - 1] = to_integer(p, q - p + 1);
+ if (as->streams[as->nb_streams - 1] < 0) return -1;
+ if (*q == '\0') break;
+ if (*q == ' ') state = new_set;
+ p = ++q;
+ } else {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int webm_dash_manifest_write_header(AVFormatContext *s)
+{
+ int i;
+ double start = 0.0;
+ WebMDashMuxContext *w = s->priv_data;
+ parse_adaptation_sets(s);
+ write_header(s);
+ avio_printf(s->pb, "<Period id=\"0\"");
+ avio_printf(s->pb, " start=\"PT%gS\"", start);
+ avio_printf(s->pb, " duration=\"PT%gS\"", get_duration(s));
+ avio_printf(s->pb, " >\n");
+
+ for (i = 0; i < w->nb_as; i++) {
+ if (write_adaptation_set(s, i) < 0) return -1;
+ }
+
+ avio_printf(s->pb, "</Period>\n");
+ write_footer(s);
+ return 0;
+}
+
+static int webm_dash_manifest_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ return AVERROR_EOF;
+}
+
+static int webm_dash_manifest_write_trailer(AVFormatContext *s)
+{
+ WebMDashMuxContext *w = s->priv_data;
+ int i;
+ for (i = 0; i < w->nb_as; i++) {
+ av_freep(&w->as[i].streams);
+ }
+ av_freep(&w->as);
+ return 0;
+}
+
+#define OFFSET(x) offsetof(WebMDashMuxContext, x)
+static const AVOption options[] = {
+ { "adaptation_sets", "Adaptation sets. Syntax: id=0,streams=0,1,2 id=1,streams=3,4 and so on", OFFSET(adaptation_sets), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
+ { NULL },
+};
+
+#if CONFIG_WEBM_DASH_MANIFEST_MUXER
+static const AVClass webm_dash_class = {
+ .class_name = "WebM DASH Manifest muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVOutputFormat ff_webm_dash_manifest_muxer = {
+ .name = "webm_dash_manifest",
+ .long_name = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"),
+ .mime_type = "application/xml",
+ .extensions = "xml",
+ .priv_data_size = sizeof(WebMDashMuxContext),
+ .write_header = webm_dash_manifest_write_header,
+ .write_packet = webm_dash_manifest_write_packet,
+ .write_trailer = webm_dash_manifest_write_trailer,
+ .priv_class = &webm_dash_class,
+};
+#endif
diff --git a/libavformat/webpenc.c b/libavformat/webpenc.c
new file mode 100644
index 0000000000..ee110de2dd
--- /dev/null
+++ b/libavformat/webpenc.c
@@ -0,0 +1,169 @@
+/*
+ * webp muxer
+ * Copyright (c) 2014 Michael Niedermayer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "internal.h"
+
+typedef struct WebpContext{
+ AVClass *class;
+ int frame_count;
+ AVPacket last_pkt;
+ int loop;
+} WebpContext;
+
+static int webp_write_header(AVFormatContext *s)
+{
+ AVStream *st;
+
+ if (s->nb_streams != 1) {
+ av_log(s, AV_LOG_ERROR, "Only exactly 1 stream is supported\n");
+ return AVERROR(EINVAL);
+ }
+ st = s->streams[0];
+ if (st->codec->codec_id != AV_CODEC_ID_WEBP) {
+ av_log(s, AV_LOG_ERROR, "Only WebP is supported\n");
+ return AVERROR(EINVAL);
+ }
+ avpriv_set_pts_info(st, 24, 1, 1000);
+
+ avio_write(s->pb, "RIFF\0\0\0\0WEBP", 12);
+
+ return 0;
+}
+
+static int flush(AVFormatContext *s, int trailer, int64_t pts)
+{
+ WebpContext *w = s->priv_data;
+ AVStream *st = s->streams[0];
+
+ if (w->last_pkt.size) {
+ int skip = 0;
+ unsigned flags = 0;
+ int vp8x = 0;
+
+ if (AV_RL32(w->last_pkt.data) == AV_RL32("RIFF"))
+ skip = 12;
+ if (AV_RL32(w->last_pkt.data + skip) == AV_RL32("VP8X")) {
+ flags |= w->last_pkt.data[skip + 4 + 4];
+ vp8x = 1;
+ skip += AV_RL32(w->last_pkt.data + skip + 4) + 8;
+ }
+
+ w->frame_count ++;
+
+ if (w->frame_count == 1) {
+ if (!trailer) {
+ vp8x = 1;
+ flags |= 2 + 16;
+ }
+
+ if (vp8x) {
+ avio_write(s->pb, "VP8X", 4);
+ avio_wl32(s->pb, 10);
+ avio_w8(s->pb, flags);
+ avio_wl24(s->pb, 0);
+ avio_wl24(s->pb, st->codec->width - 1);
+ avio_wl24(s->pb, st->codec->height - 1);
+ }
+ if (!trailer) {
+ avio_write(s->pb, "ANIM", 4);
+ avio_wl32(s->pb, 6);
+ avio_wl32(s->pb, 0xFFFFFFFF);
+ avio_wl16(s->pb, w->loop);
+ }
+ }
+
+ if (w->frame_count > trailer) {
+ avio_write(s->pb, "ANMF", 4);
+ avio_wl32(s->pb, 16 + w->last_pkt.size - skip);
+ avio_wl24(s->pb, 0);
+ avio_wl24(s->pb, 0);
+ avio_wl24(s->pb, st->codec->width - 1);
+ avio_wl24(s->pb, st->codec->height - 1);
+ if (w->last_pkt.pts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE) {
+ avio_wl24(s->pb, pts - w->last_pkt.pts);
+ } else
+ avio_wl24(s->pb, w->last_pkt.duration);
+ avio_w8(s->pb, 0);
+ }
+ avio_write(s->pb, w->last_pkt.data + skip, w->last_pkt.size - skip);
+ av_free_packet(&w->last_pkt);
+ }
+
+ return 0;
+}
+
+static int webp_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ WebpContext *w = s->priv_data;
+ int ret;
+
+ if ((ret = flush(s, 0, pkt->pts)) < 0)
+ return ret;
+
+ av_copy_packet(&w->last_pkt, pkt);
+
+ return 0;
+}
+
+static int webp_write_trailer(AVFormatContext *s)
+{
+ unsigned filesize;
+ int ret;
+
+ if ((ret = flush(s, 1, AV_NOPTS_VALUE)) < 0)
+ return ret;
+
+ filesize = avio_tell(s->pb);
+ avio_seek(s->pb, 4, SEEK_SET);
+ avio_wl32(s->pb, filesize - 8);
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(WebpContext, x)
+#define ENC AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ { "loop", "Number of times to loop the output: 0 - infinite loop", OFFSET(loop),
+ AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 65535, ENC },
+ { NULL },
+};
+
+static const AVClass webp_muxer_class = {
+ .class_name = "WebP muxer",
+ .item_name = av_default_item_name,
+ .version = LIBAVUTIL_VERSION_INT,
+ .option = options,
+};
+AVOutputFormat ff_webp_muxer = {
+ .name = "webp",
+ .long_name = NULL_IF_CONFIG_SMALL("WebP"),
+ .extensions = "webp",
+ .priv_data_size = sizeof(WebpContext),
+ .video_codec = AV_CODEC_ID_WEBP,
+ .write_header = webp_write_header,
+ .write_packet = webp_write_packet,
+ .write_trailer = webp_write_trailer,
+ .priv_class = &webp_muxer_class,
+ .flags = AVFMT_VARIABLE_FPS,
+};
diff --git a/libavformat/webvttdec.c b/libavformat/webvttdec.c
new file mode 100644
index 0000000000..e457e8f6d2
--- /dev/null
+++ b/libavformat/webvttdec.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2012 Clément Bœsch
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * WebVTT subtitle demuxer
+ * @see http://dev.w3.org/html5/webvtt/
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "subtitles.h"
+#include "libavutil/bprint.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+
+typedef struct {
+ const AVClass *class;
+ FFDemuxSubtitlesQueue q;
+ int kind;
+} WebVTTContext;
+
+static int webvtt_probe(AVProbeData *p)
+{
+ const uint8_t *ptr = p->buf;
+
+ if (AV_RB24(ptr) == 0xEFBBBF)
+ ptr += 3; /* skip UTF-8 BOM */
+ if (!strncmp(ptr, "WEBVTT", 6) &&
+ (!ptr[6] || strchr("\n\r\t ", ptr[6])))
+ return AVPROBE_SCORE_MAX;
+ return 0;
+}
+
+static int64_t read_ts(const char *s)
+{
+ int hh, mm, ss, ms;
+ if (sscanf(s, "%u:%u:%u.%u", &hh, &mm, &ss, &ms) == 4) return (hh*3600LL + mm*60LL + ss) * 1000LL + ms;
+ if (sscanf(s, "%u:%u.%u", &mm, &ss, &ms) == 3) return ( mm*60LL + ss) * 1000LL + ms;
+ return AV_NOPTS_VALUE;
+}
+
+static int webvtt_read_header(AVFormatContext *s)
+{
+ WebVTTContext *webvtt = s->priv_data;
+ AVBPrint header, cue;
+ int res = 0;
+ AVStream *st = avformat_new_stream(s, NULL);
+
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 64, 1, 1000);
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codec->codec_id = AV_CODEC_ID_WEBVTT;
+ st->disposition |= webvtt->kind;
+
+ av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED);
+ av_bprint_init(&cue, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+ for (;;) {
+ int i;
+ int64_t pos;
+ AVPacket *sub;
+ const char *p, *identifier, *settings;
+ int identifier_len, settings_len;
+ int64_t ts_start, ts_end;
+
+ ff_subtitles_read_chunk(s->pb, &cue);
+
+ if (!cue.len)
+ break;
+
+ p = identifier = cue.str;
+ pos = avio_tell(s->pb);
+
+ /* ignore header chunk */
+ if (!strncmp(p, "\xEF\xBB\xBFWEBVTT", 9) ||
+ !strncmp(p, "WEBVTT", 6))
+ continue;
+
+ /* optional cue identifier (can be a number like in SRT or some kind of
+ * chaptering id) */
+ for (i = 0; p[i] && p[i] != '\n' && p[i] != '\r'; i++) {
+ if (!strncmp(p + i, "-->", 3)) {
+ identifier = NULL;
+ break;
+ }
+ }
+ if (!identifier)
+ identifier_len = 0;
+ else {
+ identifier_len = strcspn(p, "\r\n");
+ p += identifier_len;
+ if (*p == '\r')
+ p++;
+ if (*p == '\n')
+ p++;
+ }
+
+ /* cue timestamps */
+ if ((ts_start = read_ts(p)) == AV_NOPTS_VALUE)
+ break;
+ if (!(p = strstr(p, "-->")))
+ break;
+ p += 2;
+ do p++; while (*p == ' ' || *p == '\t');
+ if ((ts_end = read_ts(p)) == AV_NOPTS_VALUE)
+ break;
+
+ /* optional cue settings */
+ p += strcspn(p, "\n\t ");
+ while (*p == '\t' || *p == ' ')
+ p++;
+ settings = p;
+ settings_len = strcspn(p, "\r\n");
+ p += settings_len;
+ if (*p == '\r')
+ p++;
+ if (*p == '\n')
+ p++;
+
+ /* create packet */
+ sub = ff_subtitles_queue_insert(&webvtt->q, p, strlen(p), 0);
+ if (!sub) {
+ res = AVERROR(ENOMEM);
+ goto end;
+ }
+ sub->pos = pos;
+ sub->pts = ts_start;
+ sub->duration = ts_end - ts_start;
+
+#define SET_SIDE_DATA(name, type) do { \
+ if (name##_len) { \
+ uint8_t *buf = av_packet_new_side_data(sub, type, name##_len); \
+ if (!buf) { \
+ res = AVERROR(ENOMEM); \
+ goto end; \
+ } \
+ memcpy(buf, name, name##_len); \
+ } \
+} while (0)
+
+ SET_SIDE_DATA(identifier, AV_PKT_DATA_WEBVTT_IDENTIFIER);
+ SET_SIDE_DATA(settings, AV_PKT_DATA_WEBVTT_SETTINGS);
+ }
+
+ ff_subtitles_queue_finalize(&webvtt->q);
+
+end:
+ av_bprint_finalize(&cue, NULL);
+ av_bprint_finalize(&header, NULL);
+ return res;
+}
+
+static int webvtt_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ WebVTTContext *webvtt = s->priv_data;
+ return ff_subtitles_queue_read_packet(&webvtt->q, pkt);
+}
+
+static int webvtt_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ WebVTTContext *webvtt = s->priv_data;
+ return ff_subtitles_queue_seek(&webvtt->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int webvtt_read_close(AVFormatContext *s)
+{
+ WebVTTContext *webvtt = s->priv_data;
+ ff_subtitles_queue_clean(&webvtt->q);
+ return 0;
+}
+
+#define OFFSET(x) offsetof(WebVTTContext, x)
+#define KIND_FLAGS AV_OPT_FLAG_SUBTITLE_PARAM
+
+static const AVOption options[] = {
+ { "kind", "Set kind of WebVTT track", OFFSET(kind), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, KIND_FLAGS, "webvtt_kind" },
+ { "subtitles", "WebVTT subtitles kind", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, 0, "webvtt_kind" },
+ { "captions", "WebVTT captions kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CAPTIONS }, INT_MIN, INT_MAX, 0, "webvtt_kind" },
+ { "descriptions", "WebVTT descriptions kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DESCRIPTIONS }, INT_MIN, INT_MAX, 0, "webvtt_kind" },
+ { "metadata", "WebVTT metadata kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_METADATA }, INT_MIN, INT_MAX, 0, "webvtt_kind" },
+ { NULL }
+};
+
+static const AVClass webvtt_demuxer_class = {
+ .class_name = "WebVTT demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_webvtt_demuxer = {
+ .name = "webvtt",
+ .long_name = NULL_IF_CONFIG_SMALL("WebVTT subtitle"),
+ .priv_data_size = sizeof(WebVTTContext),
+ .read_probe = webvtt_probe,
+ .read_header = webvtt_read_header,
+ .read_packet = webvtt_read_packet,
+ .read_seek2 = webvtt_read_seek,
+ .read_close = webvtt_read_close,
+ .extensions = "vtt",
+ .priv_class = &webvtt_demuxer_class,
+};
diff --git a/libavformat/webvttenc.c b/libavformat/webvttenc.c
new file mode 100644
index 0000000000..b93993d55c
--- /dev/null
+++ b/libavformat/webvttenc.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013 Matthew Heaney
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * WebVTT subtitle muxer
+ * @see http://dev.w3.org/html5/webvtt/
+ */
+
+#include "avformat.h"
+#include "internal.h"
+
+static void webvtt_write_time(AVIOContext *pb, int64_t millisec)
+{
+ int64_t sec, min, hour;
+ sec = millisec / 1000;
+ millisec -= 1000 * sec;
+ min = sec / 60;
+ sec -= 60 * min;
+ hour = min / 60;
+ min -= 60 * hour;
+
+ if (hour > 0)
+ avio_printf(pb, "%"PRId64":", hour);
+
+ avio_printf(pb, "%02"PRId64":%02"PRId64".%03"PRId64"", min, sec, millisec);
+}
+
+static int webvtt_write_header(AVFormatContext *ctx)
+{
+ AVStream *s = ctx->streams[0];
+ AVIOContext *pb = ctx->pb;
+
+ avpriv_set_pts_info(s, 64, 1, 1000);
+
+ avio_printf(pb, "WEBVTT\n");
+ avio_flush(pb);
+
+ return 0;
+}
+
+static int webvtt_write_packet(AVFormatContext *ctx, AVPacket *pkt)
+{
+ AVIOContext *pb = ctx->pb;
+ int id_size, settings_size;
+ uint8_t *id, *settings;
+
+ avio_printf(pb, "\n");
+
+ id = av_packet_get_side_data(pkt, AV_PKT_DATA_WEBVTT_IDENTIFIER,
+ &id_size);
+
+ if (id && id_size > 0)
+ avio_printf(pb, "%.*s\n", id_size, id);
+
+ webvtt_write_time(pb, pkt->pts);
+ avio_printf(pb, " --> ");
+ webvtt_write_time(pb, pkt->pts + pkt->duration);
+
+ settings = av_packet_get_side_data(pkt, AV_PKT_DATA_WEBVTT_SETTINGS,
+ &settings_size);
+
+ if (settings && settings_size > 0)
+ avio_printf(pb, " %.*s", settings_size, settings);
+
+ avio_printf(pb, "\n");
+
+ avio_write(pb, pkt->data, pkt->size);
+ avio_printf(pb, "\n");
+
+ return 0;
+}
+
+AVOutputFormat ff_webvtt_muxer = {
+ .name = "webvtt",
+ .long_name = NULL_IF_CONFIG_SMALL("WebVTT subtitle"),
+ .extensions = "vtt",
+ .mime_type = "text/vtt",
+ .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT,
+ .subtitle_codec = AV_CODEC_ID_WEBVTT,
+ .write_header = webvtt_write_header,
+ .write_packet = webvtt_write_packet,
+};
diff --git a/libavformat/westwood_aud.c b/libavformat/westwood_aud.c
index 611d223238..6d8dbdbd35 100644
--- a/libavformat/westwood_aud.c
+++ b/libavformat/westwood_aud.c
@@ -1,21 +1,21 @@
/*
* Westwood Studios AUD Format Demuxer
- * Copyright (c) 2003 The ffmpeg Project
+ * Copyright (c) 2003 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -69,8 +69,6 @@ static int wsaud_probe(AVProbeData *p)
if (p->buf[10] & 0xFC)
return 0;
- /* note: only check for WS IMA (type 99) right now since there is no
- * support for type 1 */
if (p->buf[11] != 99 && p->buf[11] != 1)
return 0;
@@ -153,7 +151,7 @@ static int wsaud_read_packet(AVFormatContext *s,
Specifically, this is needed to signal when a packet should be
decoding as raw 8-bit pcm or variable-size ADPCM. */
int out_size = AV_RL16(&preamble[2]);
- if ((ret = av_new_packet(pkt, chunk_size + 4)))
+ if ((ret = av_new_packet(pkt, chunk_size + 4)) < 0)
return ret;
if ((ret = avio_read(pb, &pkt->data[4], chunk_size)) != chunk_size)
return ret < 0 ? ret : AVERROR(EIO);
diff --git a/libavformat/westwood_vqa.c b/libavformat/westwood_vqa.c
index 77da375e70..546164c692 100644
--- a/libavformat/westwood_vqa.c
+++ b/libavformat/westwood_vqa.c
@@ -1,21 +1,21 @@
/*
* Westwood Studios VQA Format Demuxer
- * Copyright (c) 2003 The ffmpeg Project
+ * Copyright (c) 2003 The FFmpeg Project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -81,10 +81,10 @@ static int wsvqa_read_header(AVFormatContext *s)
WsVqaDemuxContext *wsvqa = s->priv_data;
AVIOContext *pb = s->pb;
AVStream *st;
- unsigned char *header;
- unsigned char scratch[VQA_PREAMBLE_SIZE];
- unsigned int chunk_tag;
- unsigned int chunk_size;
+ uint8_t *header;
+ uint8_t scratch[VQA_PREAMBLE_SIZE];
+ uint32_t chunk_tag;
+ uint32_t chunk_size;
int fps;
/* initialize the video decoder stream */
@@ -101,13 +101,9 @@ static int wsvqa_read_header(AVFormatContext *s)
avio_seek(pb, 20, SEEK_SET);
/* the VQA header needs to go to the decoder */
- st->codec->extradata_size = VQA_HEADER_SIZE;
- st->codec->extradata = av_mallocz(VQA_HEADER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
- header = (unsigned char *)st->codec->extradata;
- if (avio_read(pb, st->codec->extradata, VQA_HEADER_SIZE) !=
- VQA_HEADER_SIZE) {
- return AVERROR(EIO);
- }
+ if (ff_get_extradata(st->codec, pb, VQA_HEADER_SIZE) < 0)
+ return AVERROR(ENOMEM);
+ header = st->codec->extradata;
st->codec->width = AV_RL16(&header[6]);
st->codec->height = AV_RL16(&header[8]);
fps = header[12];
@@ -130,9 +126,8 @@ static int wsvqa_read_header(AVFormatContext *s)
/* there are 0 or more chunks before the FINF chunk; iterate until
* FINF has been skipped and the file will be ready to be demuxed */
do {
- if (avio_read(pb, scratch, VQA_PREAMBLE_SIZE) != VQA_PREAMBLE_SIZE) {
+ if (avio_read(pb, scratch, VQA_PREAMBLE_SIZE) != VQA_PREAMBLE_SIZE)
return AVERROR(EIO);
- }
chunk_tag = AV_RB32(&scratch[0]);
chunk_size = AV_RB32(&scratch[4]);
@@ -167,26 +162,23 @@ static int wsvqa_read_packet(AVFormatContext *s,
WsVqaDemuxContext *wsvqa = s->priv_data;
AVIOContext *pb = s->pb;
int ret = -1;
- unsigned char preamble[VQA_PREAMBLE_SIZE];
- unsigned int chunk_type;
- unsigned int chunk_size;
+ uint8_t preamble[VQA_PREAMBLE_SIZE];
+ uint32_t chunk_type;
+ uint32_t chunk_size;
int skip_byte;
while (avio_read(pb, preamble, VQA_PREAMBLE_SIZE) == VQA_PREAMBLE_SIZE) {
chunk_type = AV_RB32(&preamble[0]);
chunk_size = AV_RB32(&preamble[4]);
+
skip_byte = chunk_size & 0x01;
if ((chunk_type == SND0_TAG) || (chunk_type == SND1_TAG) ||
(chunk_type == SND2_TAG) || (chunk_type == VQFR_TAG)) {
- if (av_new_packet(pkt, chunk_size))
- return AVERROR(EIO);
- ret = avio_read(pb, pkt->data, chunk_size);
- if (ret != chunk_size) {
- av_free_packet(pkt);
+ ret= av_get_packet(pb, pkt, chunk_size);
+ if (ret<0)
return AVERROR(EIO);
- }
switch (chunk_type) {
case SND0_TAG:
@@ -223,9 +215,7 @@ static int wsvqa_read_packet(AVFormatContext *s,
break;
case SND2_TAG:
st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_WS;
- st->codec->extradata_size = 2;
- st->codec->extradata = av_mallocz(2 + FF_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codec->extradata)
+ if (ff_alloc_extradata(st->codec, 2))
return AVERROR(ENOMEM);
AV_WL16(st->codec->extradata, wsvqa->version);
break;
@@ -236,7 +226,8 @@ static int wsvqa_read_packet(AVFormatContext *s,
switch (chunk_type) {
case SND1_TAG:
/* unpacked size is stored in header */
- pkt->duration = AV_RL16(pkt->data) / wsvqa->channels;
+ if(pkt->data)
+ pkt->duration = AV_RL16(pkt->data) / wsvqa->channels;
break;
case SND2_TAG:
/* 2 samples/byte, 1 or 2 samples per frame depending on stereo */
diff --git a/libavformat/wtv.h b/libavformat/wtv.h
new file mode 100644
index 0000000000..f26ad5efb9
--- /dev/null
+++ b/libavformat/wtv.h
@@ -0,0 +1,60 @@
+/*
+ * Windows Television (WTV)
+ * Copyright (c) 2010-2011 Peter Ross <pross@xvid.org>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_WTV_H
+#define AVFORMAT_WTV_H
+
+#include "riff.h"
+#include "asf.h"
+
+#define WTV_SECTOR_BITS 12
+#define WTV_SECTOR_SIZE (1 << WTV_SECTOR_BITS)
+#define WTV_BIGSECTOR_BITS 18
+#define WTV_PAD8(x) (((x) + 7) & ~7)
+
+extern const uint8_t ff_timeline_le16[16];
+extern const uint8_t ff_timeline_table_0_entries_Events_le16[62];
+extern const uint8_t ff_table_0_entries_legacy_attrib_le16[58];
+extern const uint8_t ff_table_0_entries_time_le16[40];
+
+extern const ff_asf_guid ff_dir_entry_guid;
+extern const ff_asf_guid ff_wtv_guid;
+extern const ff_asf_guid ff_timestamp_guid;
+extern const ff_asf_guid ff_data_guid;
+extern const ff_asf_guid ff_SBE2_STREAM_DESC_EVENT;
+extern const ff_asf_guid ff_stream1_guid;
+extern const ff_asf_guid ff_sync_guid;
+extern const ff_asf_guid ff_index_guid;
+extern const ff_asf_guid ff_mediatype_audio;
+extern const ff_asf_guid ff_mediatype_video;
+extern const ff_asf_guid ff_format_none;
+extern const AVCodecGuid ff_video_guids[];
+
+extern const ff_asf_guid ff_DSATTRIB_TRANSPORT_PROPERTIES;
+extern const ff_asf_guid ff_metadata_guid;
+extern const ff_asf_guid ff_stream2_guid;
+extern const ff_asf_guid ff_mediasubtype_cpfilters_processed;
+extern const ff_asf_guid ff_format_cpfilters_processed;
+extern const ff_asf_guid ff_format_waveformatex;
+extern const ff_asf_guid ff_format_mpeg2_video;
+extern const ff_asf_guid ff_format_videoinfo2;
+
+#endif /* AVFORMAT_WTV_H */
diff --git a/libavformat/wtv_common.c b/libavformat/wtv_common.c
new file mode 100644
index 0000000000..ce4349dce5
--- /dev/null
+++ b/libavformat/wtv_common.c
@@ -0,0 +1,84 @@
+/*
+ * Windows Television (WTV)
+ * Copyright (c) 2010-2011 Peter Ross <pross@xvid.org>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "wtv.h"
+
+/* WTV GUIDs*/
+const ff_asf_guid ff_dir_entry_guid =
+ {0x92,0xB7,0x74,0x91,0x59,0x70,0x70,0x44,0x88,0xDF,0x06,0x3B,0x82,0xCC,0x21,0x3D};
+const ff_asf_guid ff_wtv_guid =
+ {0xB7,0xD8,0x00,0x20,0x37,0x49,0xDA,0x11,0xA6,0x4E,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
+const ff_asf_guid ff_timestamp_guid =
+ {0x5B,0x05,0xE6,0x1B,0x97,0xA9,0x49,0x43,0x88,0x17,0x1A,0x65,0x5A,0x29,0x8A,0x97};
+const ff_asf_guid ff_data_guid =
+ {0x95,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
+const ff_asf_guid ff_SBE2_STREAM_DESC_EVENT =
+ {0xED,0xA4,0x13,0x23,0x2D,0xBF,0x4F,0x45,0xAD,0x8A,0xD9,0x5B,0xA7,0xF9,0x1F,0xEE};
+const ff_asf_guid ff_stream1_guid =
+ {0xA1,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
+const ff_asf_guid ff_sync_guid =
+ {0x97,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
+const ff_asf_guid ff_index_guid =
+ {0x96,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
+const ff_asf_guid ff_mediatype_audio =
+ {'a','u','d','s',FF_MEDIASUBTYPE_BASE_GUID};
+const ff_asf_guid ff_mediatype_video =
+ {'v','i','d','s',FF_MEDIASUBTYPE_BASE_GUID};
+const ff_asf_guid ff_format_none =
+ {0xD6,0x17,0x64,0x0F,0x18,0xC3,0xD0,0x11,0xA4,0x3F,0x00,0xA0,0xC9,0x22,0x31,0x96};
+
+/* declare utf16le strings */
+#define _ , 0,
+const uint8_t ff_timeline_le16[] =
+ {'t'_'i'_'m'_'e'_'l'_'i'_'n'_'e', 0};
+const uint8_t ff_timeline_table_0_entries_Events_le16[] =
+ {'t'_'i'_'m'_'e'_'l'_'i'_'n'_'e'_'.'_'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'E'_'v'_'e'_'n'_'t'_'s', 0};
+const uint8_t ff_table_0_entries_legacy_attrib_le16[] =
+ {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0};
+const uint8_t ff_table_0_entries_time_le16[] =
+ {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'t'_'i'_'m'_'e', 0};
+#undef _
+
+const ff_asf_guid ff_DSATTRIB_TRANSPORT_PROPERTIES =
+ {0x12,0xF6,0x22,0xB6,0xAD,0x47,0x71,0x46,0xAD,0x6C,0x05,0xA9,0x8E,0x65,0xDE,0x3A};
+const ff_asf_guid ff_metadata_guid =
+ {0x5A,0xFE,0xD7,0x6D,0xC8,0x1D,0x8F,0x4A,0x99,0x22,0xFA,0xB1,0x1C,0x38,0x14,0x53};
+const ff_asf_guid ff_stream2_guid =
+ {0xA2,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
+
+/* Media subtypes */
+const ff_asf_guid ff_mediasubtype_cpfilters_processed =
+ {0x28,0xBD,0xAD,0x46,0xD0,0x6F,0x96,0x47,0x93,0xB2,0x15,0x5C,0x51,0xDC,0x04,0x8D};
+
+/* Formats */
+const ff_asf_guid ff_format_cpfilters_processed =
+ {0x6F,0xB3,0x39,0x67,0x5F,0x1D,0xC2,0x4A,0x81,0x92,0x28,0xBB,0x0E,0x73,0xD1,0x6A};
+const ff_asf_guid ff_format_waveformatex =
+ {0x81,0x9F,0x58,0x05,0x56,0xC3,0xCE,0x11,0xBF,0x01,0x00,0xAA,0x00,0x55,0x59,0x5A};
+const ff_asf_guid ff_format_mpeg2_video =
+ {0xE3,0x80,0x6D,0xE0,0x46,0xDB,0xCF,0x11,0xB4,0xD1,0x00,0x80,0x5F,0x6C,0xBB,0xEA};
+const ff_asf_guid ff_format_videoinfo2 =
+ {0xA0,0x76,0x2A,0xF7,0x0A,0xEB,0xD0,0x11,0xAC,0xE4,0x00,0x00,0xC0,0xCC,0x16,0xBA};
+
+const AVCodecGuid ff_video_guids[] = {
+ {AV_CODEC_ID_MPEG2VIDEO, {0x26,0x80,0x6D,0xE0,0x46,0xDB,0xCF,0x11,0xB4,0xD1,0x00,0x80,0x5F,0x6C,0xBB,0xEA}},
+ {AV_CODEC_ID_NONE}
+};
diff --git a/libavformat/wtv.c b/libavformat/wtvdec.c
index 7fe737ed53..a752ee22bd 100644
--- a/libavformat/wtv.c
+++ b/libavformat/wtvdec.c
@@ -2,20 +2,20 @@
* Windows Television (WTV) demuxer
* Copyright (c) 2010-2011 Peter Ross <pross@xvid.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -30,12 +30,10 @@
#include "libavutil/channel_layout.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/intfloat.h"
-#include "libavutil/dict.h"
#include "libavutil/time_internal.h"
#include "avformat.h"
#include "internal.h"
-#include "riff.h"
-#include "asf.h"
+#include "wtv.h"
#include "mpegts.h"
/* Macros for formating GUIDs */
@@ -51,18 +49,12 @@
*
*/
-#define WTV_SECTOR_BITS 12
-#define WTV_SECTOR_SIZE (1 << WTV_SECTOR_BITS)
-#define WTV_BIGSECTOR_BITS 18
-
-#define SHIFT_SECTOR_BITS(a) ((int64_t)(a) << WTV_SECTOR_BITS)
-
typedef struct WtvFile {
- AVIOContext *pb_filesystem; /** file system (AVFormatContext->pb) */
+ AVIOContext *pb_filesystem; /**< file system (AVFormatContext->pb) */
- int sector_bits; /** sector shift bits; used to convert sector number into pb_filesystem offset */
- uint32_t *sectors; /** file allocation table */
- int nb_sectors; /** number of sectors */
+ int sector_bits; /**< sector shift bits; used to convert sector number into pb_filesystem offset */
+ uint32_t *sectors; /**< file allocation table */
+ int nb_sectors; /**< number of sectors */
int error;
int64_t position;
@@ -71,7 +63,7 @@ typedef struct WtvFile {
static int64_t seek_by_sector(AVIOContext *pb, int64_t sector, int64_t offset)
{
- return avio_seek(pb, SHIFT_SECTOR_BITS(sector) + offset, SEEK_SET);
+ return avio_seek(pb, (sector << WTV_SECTOR_BITS) + offset, SEEK_SET);
}
/**
@@ -85,7 +77,7 @@ static int wtvfile_read_packet(void *opaque, uint8_t *buf, int buf_size)
if (wf->error || pb->error)
return -1;
- if (wf->position >= wf->length || pb->eof_reached)
+ if (wf->position >= wf->length || avio_feof(pb))
return 0;
buf_size = FFMIN(buf_size, wf->length - wf->position);
@@ -164,6 +156,7 @@ static AVIOContext * wtvfile_open_sector(int first_sector, uint64_t length, int
AVIOContext *pb;
WtvFile *wf;
uint8_t *buffer;
+ int64_t size;
if (seek_by_sector(s->pb, first_sector, 0) < 0)
return NULL;
@@ -180,7 +173,6 @@ static AVIOContext * wtvfile_open_sector(int first_sector, uint64_t length, int
}
wf->sectors[0] = first_sector;
wf->nb_sectors = 1;
- wf->sector_bits = WTV_SECTOR_BITS;
} else if (depth == 1) {
wf->sectors = av_malloc(WTV_SECTOR_SIZE);
if (!wf->sectors) {
@@ -188,13 +180,12 @@ static AVIOContext * wtvfile_open_sector(int first_sector, uint64_t length, int
return NULL;
}
wf->nb_sectors = read_ints(s->pb, wf->sectors, WTV_SECTOR_SIZE / 4);
- wf->sector_bits = length & (1ULL<<63) ? WTV_SECTOR_BITS : WTV_BIGSECTOR_BITS;
} else if (depth == 2) {
uint32_t sectors1[WTV_SECTOR_SIZE / 4];
int nb_sectors1 = read_ints(s->pb, sectors1, WTV_SECTOR_SIZE / 4);
int i;
- wf->sectors = av_malloc(SHIFT_SECTOR_BITS(nb_sectors1));
+ wf->sectors = av_malloc_array(nb_sectors1, 1 << WTV_SECTOR_BITS);
if (!wf->sectors) {
av_free(wf);
return NULL;
@@ -205,19 +196,23 @@ static AVIOContext * wtvfile_open_sector(int first_sector, uint64_t length, int
break;
wf->nb_sectors += read_ints(s->pb, wf->sectors + i * WTV_SECTOR_SIZE / 4, WTV_SECTOR_SIZE / 4);
}
- wf->sector_bits = length & (1ULL<<63) ? WTV_SECTOR_BITS : WTV_BIGSECTOR_BITS;
} else {
av_log(s, AV_LOG_ERROR, "unsupported file allocation table depth (0x%x)\n", depth);
av_free(wf);
return NULL;
}
+ wf->sector_bits = length & (1ULL<<63) ? WTV_SECTOR_BITS : WTV_BIGSECTOR_BITS;
if (!wf->nb_sectors) {
- av_free(wf->sectors);
- av_free(wf);
+ av_freep(&wf->sectors);
+ av_freep(&wf);
return NULL;
}
+ size = avio_size(s->pb);
+ if (size >= 0 && (int64_t)wf->sectors[wf->nb_sectors - 1] << WTV_SECTOR_BITS > size)
+ av_log(s, AV_LOG_WARNING, "truncated file\n");
+
/* check length */
length &= 0xFFFFFFFFFFFF;
if (length > ((int64_t)wf->nb_sectors << wf->sector_bits)) {
@@ -229,32 +224,29 @@ static AVIOContext * wtvfile_open_sector(int first_sector, uint64_t length, int
/* seek to initial sector */
wf->position = 0;
if (seek_by_sector(s->pb, wf->sectors[0], 0) < 0) {
- av_free(wf->sectors);
- av_free(wf);
+ av_freep(&wf->sectors);
+ av_freep(&wf);
return NULL;
}
wf->pb_filesystem = s->pb;
buffer = av_malloc(1 << wf->sector_bits);
if (!buffer) {
- av_free(wf->sectors);
- av_free(wf);
+ av_freep(&wf->sectors);
+ av_freep(&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);
+ av_freep(&buffer);
+ av_freep(&wf->sectors);
+ av_freep(&wf);
}
return pb;
}
-static const ff_asf_guid dir_entry_guid =
- {0x92,0xB7,0x74,0x91,0x59,0x70,0x70,0x44,0x88,0xDF,0x06,0x3B,0x82,0xCC,0x21,0x3D};
-
/**
* Open file using filename
* @param[in] buf directory buffer
@@ -271,7 +263,7 @@ static AVIOContext * wtvfile_open2(AVFormatContext *s, const uint8_t *buf, int b
int dir_length, name_size, first_sector, depth;
uint64_t file_length;
const uint8_t *name;
- if (ff_guidcmp(buf, dir_entry_guid)) {
+ if (ff_guidcmp(buf, ff_dir_entry_guid)) {
av_log(s, AV_LOG_ERROR, "unknown guid "FF_PRI_GUID", expected dir_entry_guid; "
"remaining directory entries ignored\n", FF_ARG_GUID(buf));
break;
@@ -284,7 +276,7 @@ static AVIOContext * wtvfile_open2(AVFormatContext *s, const uint8_t *buf, int b
"bad filename length, remaining directory entries ignored\n");
break;
}
- if (48 + name_size > buf_end - buf) {
+ if (48 + (int64_t)name_size > buf_end - buf) {
av_log(s, AV_LOG_ERROR, "filename exceeds buffer size; remaining directory entries ignored\n");
break;
}
@@ -312,9 +304,9 @@ static AVIOContext * wtvfile_open2(AVFormatContext *s, const uint8_t *buf, int b
static void wtvfile_close(AVIOContext *pb)
{
WtvFile *wf = pb->opaque;
- av_free(wf->sectors);
- av_free(wf);
- av_free(pb->buffer);
+ av_freep(&wf->sectors);
+ av_freep(&pb->opaque);
+ av_freep(&pb->buffer);
av_free(pb);
}
@@ -329,10 +321,10 @@ typedef struct WtvStream {
} WtvStream;
typedef struct WtvContext {
- AVIOContext *pb; /** timeline file */
+ AVIOContext *pb; /**< timeline file */
int64_t epoch;
- int64_t pts; /** pts for next data chunk */
- int64_t last_valid_pts; /** latest valid pts, used for interative seeking */
+ int64_t pts; /**< pts for next data chunk */
+ int64_t last_valid_pts; /**< latest valid pts, used for interative seeking */
/* maintain private seek index, as the AVIndexEntry->pos is relative to the
start of the 'timeline' file, not the file system (AVFormatContext->pb) */
@@ -342,18 +334,6 @@ typedef struct WtvContext {
} WtvContext;
/* WTV GUIDs */
-static const ff_asf_guid wtv_guid =
- {0xB7,0xD8,0x00,0x20,0x37,0x49,0xDA,0x11,0xA6,0x4E,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
-static const ff_asf_guid metadata_guid =
- {0x5A,0xFE,0xD7,0x6D,0xC8,0x1D,0x8F,0x4A,0x99,0x22,0xFA,0xB1,0x1C,0x38,0x14,0x53};
-static const ff_asf_guid timestamp_guid =
- {0x5B,0x05,0xE6,0x1B,0x97,0xA9,0x49,0x43,0x88,0x17,0x1A,0x65,0x5A,0x29,0x8A,0x97};
-static const ff_asf_guid data_guid =
- {0x95,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
-static const ff_asf_guid stream_guid =
- {0xED,0xA4,0x13,0x23,0x2D,0xBF,0x4F,0x45,0xAD,0x8A,0xD9,0x5B,0xA7,0xF9,0x1F,0xEE};
-static const ff_asf_guid stream2_guid =
- {0xA2,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
static const ff_asf_guid EVENTID_SubtitleSpanningEvent =
{0x48,0xC0,0xCE,0x5D,0xB9,0xD0,0x63,0x41,0x87,0x2C,0x4F,0x32,0x22,0x3B,0xE8,0x8A};
static const ff_asf_guid EVENTID_LanguageSpanningEvent =
@@ -376,10 +356,6 @@ static const ff_asf_guid EVENTID_AudioTypeSpanningEvent =
/* Windows media GUIDs */
/* Media types */
-static const ff_asf_guid mediatype_audio =
- {'a','u','d','s',FF_MEDIASUBTYPE_BASE_GUID};
-static const ff_asf_guid mediatype_video =
- {'v','i','d','s',FF_MEDIASUBTYPE_BASE_GUID};
static const ff_asf_guid mediasubtype_mpeg1payload =
{0x81,0xEB,0x36,0xE4,0x4F,0x52,0xCE,0x11,0x9F,0x53,0x00,0x20,0xAF,0x0B,0xA7,0x70};
static const ff_asf_guid mediatype_mpeg2_sections =
@@ -390,8 +366,6 @@ static const ff_asf_guid mediatype_mstvcaption =
{0x89,0x8A,0x8B,0xB8,0x49,0xB0,0x80,0x4C,0xAD,0xCF,0x58,0x98,0x98,0x5E,0x22,0xC1};
/* Media subtypes */
-static const ff_asf_guid mediasubtype_cpfilters_processed =
- {0x28,0xBD,0xAD,0x46,0xD0,0x6F,0x96,0x47,0x93,0xB2,0x15,0x5C,0x51,0xDC,0x04,0x8D};
static const ff_asf_guid mediasubtype_dvb_subtitle =
{0xC3,0xCB,0xFF,0x34,0xB3,0xD5,0x71,0x41,0x90,0x02,0xD4,0xC6,0x03,0x01,0x69,0x7F};
static const ff_asf_guid mediasubtype_teletext =
@@ -401,71 +375,57 @@ static const ff_asf_guid mediasubtype_dtvccdata =
static const ff_asf_guid mediasubtype_mpeg2_sections =
{0x79,0x85,0x9F,0x4A,0xF8,0x6B,0x92,0x43,0x8A,0x6D,0xD2,0xDD,0x09,0xFA,0x78,0x61};
-/* Formats */
-static const ff_asf_guid format_cpfilters_processed =
- {0x6F,0xB3,0x39,0x67,0x5F,0x1D,0xC2,0x4A,0x81,0x92,0x28,0xBB,0x0E,0x73,0xD1,0x6A};
-static const ff_asf_guid format_waveformatex =
- {0x81,0x9F,0x58,0x05,0x56,0xC3,0xCE,0x11,0xBF,0x01,0x00,0xAA,0x00,0x55,0x59,0x5A};
-static const ff_asf_guid format_videoinfo2 =
- {0xA0,0x76,0x2A,0xF7,0x0A,0xEB,0xD0,0x11,0xAC,0xE4,0x00,0x00,0xC0,0xCC,0x16,0xBA};
-static const ff_asf_guid format_mpeg2_video =
- {0xE3,0x80,0x6D,0xE0,0x46,0xDB,0xCF,0x11,0xB4,0xD1,0x00,0x80,0x5F,0x6C,0xBB,0xEA};
-static const ff_asf_guid format_none =
- {0xD6,0x17,0x64,0x0F,0x18,0xC3,0xD0,0x11,0xA4,0x3F,0x00,0xA0,0xC9,0x22,0x31,0x96};
-
-static const AVCodecGuid video_guids[] = {
- {AV_CODEC_ID_MPEG2VIDEO, {0x26,0x80,0x6D,0xE0,0x46,0xDB,0xCF,0x11,0xB4,0xD1,0x00,0x80,0x5F,0x6C,0xBB,0xEA}},
- {AV_CODEC_ID_NONE}
-};
-
static int read_probe(AVProbeData *p)
{
- return ff_guidcmp(p->buf, wtv_guid) ? 0 : AVPROBE_SCORE_MAX;
+ return ff_guidcmp(p->buf, ff_wtv_guid) ? 0 : AVPROBE_SCORE_MAX;
}
/**
* Convert win32 FILETIME to ISO-8601 string
+ * @return <0 on error
*/
-static void filetime_to_iso8601(char *buf, int buf_size, int64_t value)
+static int filetime_to_iso8601(char *buf, int buf_size, int64_t value)
{
time_t t = (value / 10000000LL) - 11644473600LL;
struct tm tmbuf;
struct tm *tm = gmtime_r(&t, &tmbuf);
- if (tm) {
- if (!strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", tm))
- buf[0] = '\0';
- } else
- buf[0] = '\0';
+ if (!tm)
+ return -1;
+ if (!strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", tm))
+ return -1;
+ return 0;
}
/**
* Convert crazy time (100ns since 1 Jan 0001) to ISO-8601 string
+ * @return <0 on error
*/
-static void crazytime_to_iso8601(char *buf, int buf_size, int64_t value)
+static int crazytime_to_iso8601(char *buf, int buf_size, int64_t value)
{
time_t t = (value / 10000000LL) - 719162LL*86400LL;
struct tm tmbuf;
struct tm *tm = gmtime_r(&t, &tmbuf);
- if (tm) {
- if (!strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", tm))
- buf[0] = '\0';
- } else
- buf[0] = '\0';
+ if (!tm)
+ return -1;
+ if (!strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", tm))
+ return -1;
+ return 0;
}
/**
* Convert OLE DATE to ISO-8601 string
+ * @return <0 on error
*/
-static void oledate_to_iso8601(char *buf, int buf_size, int64_t value)
+static int oledate_to_iso8601(char *buf, int buf_size, int64_t value)
{
- time_t t = 631112400LL + 86400*av_int2double(value);
+ time_t t = (av_int2double(value) - 25569.0) * 86400;
struct tm tmbuf;
- struct tm *tm = gmtime_r(&t, &tmbuf);
- if (tm) {
- if (!strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", tm))
- buf[0] = '\0';
- } else
- buf[0] = '\0';
+ struct tm *tm= gmtime_r(&t, &tmbuf);
+ if (!tm)
+ return -1;
+ if (!strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", tm))
+ return -1;
+ return 0;
}
static void get_attachment(AVFormatContext *s, AVIOContext *pb, int length)
@@ -474,6 +434,7 @@ static void get_attachment(AVFormatContext *s, AVIOContext *pb, int length)
char description[1024];
unsigned int filesize;
AVStream *st;
+ int ret;
int64_t pos = avio_tell(pb);
avio_get_str16le(pb, INT_MAX, mime, sizeof(mime));
@@ -490,22 +451,31 @@ static void get_attachment(AVFormatContext *s, AVIOContext *pb, int length)
if (!st)
goto done;
av_dict_set(&st->metadata, "title", description, 0);
+ st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
st->codec->codec_id = AV_CODEC_ID_MJPEG;
- st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT;
- st->codec->extradata = av_mallocz(filesize);
st->id = -1;
- if (!st->codec->extradata)
+ ret = av_get_packet(pb, &st->attached_pic, filesize);
+ if (ret < 0)
goto done;
- st->codec->extradata_size = filesize;
- avio_read(pb, st->codec->extradata, filesize);
+ st->attached_pic.stream_index = st->index;
+ st->attached_pic.flags |= AV_PKT_FLAG_KEY;
+ st->disposition |= AV_DISPOSITION_ATTACHED_PIC;
done:
avio_seek(pb, pos + length, SEEK_SET);
}
static void get_tag(AVFormatContext *s, AVIOContext *pb, const char *key, int type, int length)
{
- int buf_size = FFMAX(2*length, LEN_PRETTY_GUID) + 1;
- char *buf = av_malloc(buf_size);
+ int buf_size;
+ char *buf;
+
+ if (!strcmp(key, "WM/MediaThumbType")) {
+ avio_skip(pb, length);
+ return;
+ }
+
+ buf_size = FFMAX(2*length, LEN_PRETTY_GUID) + 1;
+ buf = av_malloc(buf_size);
if (!buf)
return;
@@ -522,14 +492,23 @@ static void get_tag(AVFormatContext *s, AVIOContext *pb, const char *key, int ty
} else if (type == 4 && length == 8) {
int64_t num = avio_rl64(pb);
if (!strcmp(key, "WM/EncodingTime") ||
- !strcmp(key, "WM/MediaOriginalBroadcastDateTime"))
- filetime_to_iso8601(buf, buf_size, num);
- else if (!strcmp(key, "WM/WMRVEncodeTime") ||
- !strcmp(key, "WM/WMRVEndTime"))
- crazytime_to_iso8601(buf, buf_size, num);
- else if (!strcmp(key, "WM/WMRVExpirationDate"))
- oledate_to_iso8601(buf, buf_size, num);
- else if (!strcmp(key, "WM/WMRVBitrate"))
+ !strcmp(key, "WM/MediaOriginalBroadcastDateTime")) {
+ if (filetime_to_iso8601(buf, buf_size, num) < 0) {
+ av_free(buf);
+ return;
+ }
+ } else if (!strcmp(key, "WM/WMRVEncodeTime") ||
+ !strcmp(key, "WM/WMRVEndTime")) {
+ if (crazytime_to_iso8601(buf, buf_size, num) < 0) {
+ av_free(buf);
+ return;
+ }
+ } else if (!strcmp(key, "WM/WMRVExpirationDate")) {
+ if (oledate_to_iso8601(buf, buf_size, num) < 0 ) {
+ av_free(buf);
+ return;
+ }
+ } else if (!strcmp(key, "WM/WMRVBitrate"))
snprintf(buf, buf_size, "%f", av_int2double(num));
else
snprintf(buf, buf_size, "%"PRIi64, num);
@@ -561,14 +540,14 @@ static void parse_legacy_attrib(AVFormatContext *s, AVIOContext *pb)
{
ff_asf_guid guid;
int length, type;
- while(!pb->eof_reached) {
+ while(!avio_feof(pb)) {
char key[1024];
ff_get_guid(pb, &guid);
type = avio_rl32(pb);
length = avio_rl32(pb);
if (!length)
break;
- if (ff_guidcmp(&guid, metadata_guid)) {
+ if (ff_guidcmp(&guid, ff_metadata_guid)) {
av_log(s, AV_LOG_WARNING, "unknown guid "FF_PRI_GUID", expected metadata_guid; "
"remaining metadata entries ignored\n", FF_ARG_GUID(guid));
break;
@@ -590,7 +569,7 @@ static int parse_videoinfoheader2(AVFormatContext *s, AVStream *st)
AVIOContext *pb = wtv->pb;
avio_skip(pb, 72); // picture aspect ratio is unreliable
- ff_get_bmp_header(pb, st);
+ st->codec->codec_tag = ff_get_bmp_header(pb, st, NULL);
return 72 + 40;
}
@@ -663,12 +642,12 @@ static AVStream * new_stream(AVFormatContext *s, AVStream *st, int sid, int code
*/
static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid,
ff_asf_guid mediatype, ff_asf_guid subtype,
- ff_asf_guid formattype, int size)
+ ff_asf_guid formattype, uint64_t size)
{
WtvContext *wtv = s->priv_data;
AVIOContext *pb = wtv->pb;
- if (!ff_guidcmp(subtype, mediasubtype_cpfilters_processed) &&
- !ff_guidcmp(formattype, format_cpfilters_processed)) {
+ if (!ff_guidcmp(subtype, ff_mediasubtype_cpfilters_processed) &&
+ !ff_guidcmp(formattype, ff_format_cpfilters_processed)) {
ff_asf_guid actual_subtype;
ff_asf_guid actual_formattype;
@@ -686,16 +665,16 @@ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid,
st = parse_media_type(s, st, sid, mediatype, actual_subtype, actual_formattype, size - 32);
avio_skip(pb, 32);
return st;
- } else if (!ff_guidcmp(mediatype, mediatype_audio)) {
+ } else if (!ff_guidcmp(mediatype, ff_mediatype_audio)) {
st = new_stream(s, st, sid, AVMEDIA_TYPE_AUDIO);
if (!st)
return NULL;
- if (!ff_guidcmp(formattype, format_waveformatex)) {
- int ret = ff_get_wav_header(pb, st->codec, size);
+ if (!ff_guidcmp(formattype, ff_format_waveformatex)) {
+ int ret = ff_get_wav_header(pb, st->codec, size, 0);
if (ret < 0)
return NULL;
} else {
- if (ff_guidcmp(formattype, format_none))
+ if (ff_guidcmp(formattype, ff_format_none))
av_log(s, AV_LOG_WARNING, "unknown formattype:"FF_PRI_GUID"\n", FF_ARG_GUID(formattype));
avio_skip(pb, size);
}
@@ -713,18 +692,19 @@ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid,
av_log(s, AV_LOG_WARNING, "unknown subtype:"FF_PRI_GUID"\n", FF_ARG_GUID(subtype));
}
return st;
- } else if (!ff_guidcmp(mediatype, mediatype_video)) {
+ } else if (!ff_guidcmp(mediatype, ff_mediatype_video)) {
st = new_stream(s, st, sid, AVMEDIA_TYPE_VIDEO);
if (!st)
return NULL;
- if (!ff_guidcmp(formattype, format_videoinfo2)) {
+ if (!ff_guidcmp(formattype, ff_format_videoinfo2)) {
int consumed = parse_videoinfoheader2(s, st);
avio_skip(pb, FFMAX(size - consumed, 0));
- } else if (!ff_guidcmp(formattype, format_mpeg2_video)) {
- int consumed = parse_videoinfoheader2(s, st);
+ } else if (!ff_guidcmp(formattype, ff_format_mpeg2_video)) {
+ uint64_t consumed = parse_videoinfoheader2(s, st);
+ /* ignore extradata; files produced by windows media center contain meaningless mpeg1 sequence header */
avio_skip(pb, FFMAX(size - consumed, 0));
} else {
- if (ff_guidcmp(formattype, format_none))
+ if (ff_guidcmp(formattype, ff_format_none))
av_log(s, AV_LOG_WARNING, "unknown formattype:"FF_PRI_GUID"\n", FF_ARG_GUID(formattype));
avio_skip(pb, size);
}
@@ -732,7 +712,7 @@ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid,
if (!memcmp(subtype + 4, (const uint8_t[]){FF_MEDIASUBTYPE_BASE_GUID}, 12)) {
st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, AV_RL32(subtype));
} else {
- st->codec->codec_id = ff_codec_guid_get_id(video_guids, subtype);
+ st->codec->codec_id = ff_codec_guid_get_id(ff_video_guids, subtype);
}
if (st->codec->codec_id == AV_CODEC_ID_NONE)
av_log(s, AV_LOG_WARNING, "unknown subtype:"FF_PRI_GUID"\n", FF_ARG_GUID(subtype));
@@ -742,7 +722,7 @@ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid,
st = new_stream(s, st, sid, AVMEDIA_TYPE_SUBTITLE);
if (!st)
return NULL;
- if (ff_guidcmp(formattype, format_none))
+ if (ff_guidcmp(formattype, ff_format_none))
av_log(s, AV_LOG_WARNING, "unknown formattype:"FF_PRI_GUID"\n", FF_ARG_GUID(formattype));
avio_skip(pb, size);
st->codec->codec_id = AV_CODEC_ID_DVB_SUBTITLE;
@@ -752,14 +732,14 @@ static AVStream * parse_media_type(AVFormatContext *s, AVStream *st, int sid,
st = new_stream(s, st, sid, AVMEDIA_TYPE_SUBTITLE);
if (!st)
return NULL;
- if (ff_guidcmp(formattype, format_none))
+ if (ff_guidcmp(formattype, ff_format_none))
av_log(s, AV_LOG_WARNING, "unknown formattype:"FF_PRI_GUID"\n", FF_ARG_GUID(formattype));
avio_skip(pb, size);
- st->codec->codec_id = AV_CODEC_ID_DVB_TELETEXT;
+ st->codec->codec_id = !ff_guidcmp(subtype, mediasubtype_teletext) ? AV_CODEC_ID_DVB_TELETEXT : AV_CODEC_ID_EIA_608;
return st;
} else if (!ff_guidcmp(mediatype, mediatype_mpeg2_sections) &&
!ff_guidcmp(subtype, mediasubtype_mpeg2_sections)) {
- if (ff_guidcmp(formattype, format_none))
+ if (ff_guidcmp(formattype, ff_format_none))
av_log(s, AV_LOG_WARNING, "unknown formattype:"FF_PRI_GUID"\n", FF_ARG_GUID(formattype));
avio_skip(pb, size);
return NULL;
@@ -778,6 +758,26 @@ enum {
};
/**
+ * Try to seek over a broken chunk
+ * @return <0 on error
+ */
+static int recover(WtvContext *wtv, uint64_t broken_pos)
+{
+ AVIOContext *pb = wtv->pb;
+ int i;
+ for (i = 0; i < wtv->nb_index_entries; i++) {
+ if (wtv->index_entries[i].pos > broken_pos) {
+ int ret = avio_seek(pb, wtv->index_entries[i].pos, SEEK_SET);
+ if (ret < 0)
+ return ret;
+ wtv->pts = wtv->index_entries[i].timestamp;
+ return 0;
+ }
+ }
+ return AVERROR(EIO);
+}
+
+/**
* Parse WTV chunks
* @param mode SEEK_TO_DATA or SEEK_TO_PTS
* @param seekts timestamp
@@ -788,19 +788,26 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p
{
WtvContext *wtv = s->priv_data;
AVIOContext *pb = wtv->pb;
- while (!pb->eof_reached) {
+ while (!avio_feof(pb)) {
ff_asf_guid g;
int len, sid, consumed;
ff_get_guid(pb, &g);
len = avio_rl32(pb);
- if (len < 32)
- break;
+ if (len < 32) {
+ int ret;
+ if (avio_feof(pb))
+ return AVERROR_EOF;
+ av_log(s, AV_LOG_WARNING, "encountered broken chunk\n");
+ if ((ret = recover(wtv, avio_tell(pb) - 20)) < 0)
+ return ret;
+ continue;
+ }
sid = avio_rl32(pb) & 0x7FFF;
avio_skip(pb, 8);
consumed = 32;
- if (!ff_guidcmp(g, stream_guid)) {
+ if (!ff_guidcmp(g, ff_SBE2_STREAM_DESC_EVENT)) {
if (ff_find_stream_index(s, sid) < 0) {
ff_asf_guid mediatype, subtype, formattype;
int size;
@@ -813,9 +820,9 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p
parse_media_type(s, 0, sid, mediatype, subtype, formattype, size);
consumed += 92 + size;
}
- } else if (!ff_guidcmp(g, stream2_guid)) {
+ } else if (!ff_guidcmp(g, ff_stream2_guid)) {
int stream_index = ff_find_stream_index(s, sid);
- if (stream_index >= 0 && !((WtvStream*)s->streams[stream_index]->priv_data)->seen_data) {
+ if (stream_index >= 0 && s->streams[stream_index]->priv_data && !((WtvStream*)s->streams[stream_index]->priv_data)->seen_data) {
ff_asf_guid mediatype, subtype, formattype;
int size;
avio_skip(pb, 12);
@@ -889,7 +896,7 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p
}
consumed += 15;
}
- } else if (!ff_guidcmp(g, timestamp_guid)) {
+ } else if (!ff_guidcmp(g, ff_timestamp_guid)) {
int stream_index = ff_find_stream_index(s, sid);
if (stream_index >= 0) {
avio_skip(pb, 8);
@@ -902,15 +909,14 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p
if (wtv->epoch == AV_NOPTS_VALUE || wtv->pts < wtv->epoch)
wtv->epoch = wtv->pts;
if (mode == SEEK_TO_PTS && wtv->pts >= seekts) {
-#define WTV_PAD8(x) (((x) + 7) & ~7)
avio_skip(pb, WTV_PAD8(len) - consumed);
return 0;
}
}
}
- } else if (!ff_guidcmp(g, data_guid)) {
+ } else if (!ff_guidcmp(g, ff_data_guid)) {
int stream_index = ff_find_stream_index(s, sid);
- if (mode == SEEK_TO_DATA && stream_index >= 0 && len > 32) {
+ if (mode == SEEK_TO_DATA && stream_index >= 0 && len > 32 && s->streams[stream_index]->priv_data) {
WtvStream *wst = s->streams[stream_index]->priv_data;
wst->seen_data = 1;
if (len_ptr) {
@@ -918,10 +924,15 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p
}
return stream_index;
}
+ } else if (!ff_guidcmp(g, /* DSATTRIB_WMDRMProtectionInfo */ (const ff_asf_guid){0x83,0x95,0x74,0x40,0x9D,0x6B,0xEC,0x4E,0xB4,0x3C,0x67,0xA1,0x80,0x1E,0x1A,0x9B})) {
+ int stream_index = ff_find_stream_index(s, sid);
+ if (stream_index >= 0)
+ av_log(s, AV_LOG_WARNING, "encrypted stream detected (st:%d), decoding will likely fail\n", stream_index);
} else if (
!ff_guidcmp(g, /* DSATTRIB_CAPTURE_STREAMTIME */ (const ff_asf_guid){0x14,0x56,0x1A,0x0C,0xCD,0x30,0x40,0x4F,0xBC,0xBF,0xD0,0x3E,0x52,0x30,0x62,0x07}) ||
+ !ff_guidcmp(g, /* DSATTRIB_PBDATAG_ATTRIBUTE */ (const ff_asf_guid){0x79,0x66,0xB5,0xE0,0xB9,0x12,0xCC,0x43,0xB7,0xDF,0x57,0x8C,0xAA,0x5A,0x7B,0x63}) ||
!ff_guidcmp(g, /* DSATTRIB_PicSampleSeq */ (const ff_asf_guid){0x02,0xAE,0x5B,0x2F,0x8F,0x7B,0x60,0x4F,0x82,0xD6,0xE4,0xEA,0x2F,0x1F,0x4C,0x99}) ||
- !ff_guidcmp(g, /* DSATTRIB_TRANSPORT_PROPERTIES */ (const ff_asf_guid){0x12,0xF6,0x22,0xB6,0xAD,0x47,0x71,0x46,0xAD,0x6C,0x05,0xA9,0x8E,0x65,0xDE,0x3A}) ||
+ !ff_guidcmp(g, /* DSATTRIB_TRANSPORT_PROPERTIES */ ff_DSATTRIB_TRANSPORT_PROPERTIES) ||
!ff_guidcmp(g, /* dvr_ms_vid_frame_rep_data */ (const ff_asf_guid){0xCC,0x32,0x64,0xDD,0x29,0xE2,0xDB,0x40,0x80,0xF6,0xD2,0x63,0x28,0xD2,0x76,0x1F}) ||
!ff_guidcmp(g, /* EVENTID_ChannelChangeSpanningEvent */ (const ff_asf_guid){0xE5,0xC5,0x67,0x90,0x5C,0x4C,0x05,0x42,0x86,0xC8,0x7A,0xFE,0x20,0xFE,0x1E,0xFA}) ||
!ff_guidcmp(g, /* EVENTID_ChannelInfoSpanningEvent */ (const ff_asf_guid){0x80,0x6D,0xF3,0x41,0x32,0x41,0xC2,0x4C,0xB1,0x21,0x01,0xA4,0x32,0x19,0xD8,0x1B}) ||
@@ -934,9 +945,10 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p
!ff_guidcmp(g, (const ff_asf_guid){0x4E,0x7F,0x4C,0x5B,0xC4,0xD0,0x38,0x4B,0xA8,0x3E,0x21,0x7F,0x7B,0xBF,0x52,0xE7}) ||
!ff_guidcmp(g, (const ff_asf_guid){0x63,0x36,0xEB,0xFE,0xA1,0x7E,0xD9,0x11,0x83,0x08,0x00,0x07,0xE9,0x5E,0xAD,0x8D}) ||
!ff_guidcmp(g, (const ff_asf_guid){0x70,0xE9,0xF1,0xF8,0x89,0xA4,0x4C,0x4D,0x83,0x73,0xB8,0x12,0xE0,0xD5,0xF8,0x1E}) ||
- !ff_guidcmp(g, (const ff_asf_guid){0x96,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}) ||
- !ff_guidcmp(g, (const ff_asf_guid){0x97,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D}) ||
- !ff_guidcmp(g, (const ff_asf_guid){0xA1,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D})) {
+ !ff_guidcmp(g, ff_index_guid) ||
+ !ff_guidcmp(g, ff_sync_guid) ||
+ !ff_guidcmp(g, ff_stream1_guid) ||
+ !ff_guidcmp(g, (const ff_asf_guid){0xF7,0x10,0x02,0xB9,0xEE,0x7C,0xED,0x4E,0xBD,0x7F,0x05,0x40,0x35,0x86,0x18,0xA1})) {
//ignore known guids
} else
av_log(s, AV_LOG_WARNING, "unsupported chunk:"FF_PRI_GUID"\n", FF_ARG_GUID(g));
@@ -946,18 +958,6 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p
return AVERROR_EOF;
}
-/* declare utf16le strings */
-#define _ , 0,
-static const uint8_t timeline_le16[] =
- {'t'_'i'_'m'_'e'_'l'_'i'_'n'_'e', 0};
-static const uint8_t table_0_entries_legacy_attrib_le16[] =
- {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0};
-static const uint8_t table_0_entries_time_le16[] =
- {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'t'_'i'_'m'_'e', 0};
-static const uint8_t timeline_table_0_entries_Events_le16[] =
- {'t'_'i'_'m'_'e'_'l'_'i'_'n'_'e'_'.'_'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'E'_'v'_'e'_'n'_'t'_'s', 0};
-#undef _
-
static int read_header(AVFormatContext *s)
{
WtvContext *wtv = s->priv_data;
@@ -989,7 +989,7 @@ static int read_header(AVFormatContext *s)
return AVERROR_INVALIDDATA;
/* parse chunks up until first data chunk */
- wtv->pb = wtvfile_open(s, root, root_size, timeline_le16);
+ wtv->pb = wtvfile_open(s, root, root_size, ff_timeline_le16);
if (!wtv->pb) {
av_log(s, AV_LOG_ERROR, "timeline data missing\n");
return AVERROR_INVALIDDATA;
@@ -1003,7 +1003,7 @@ static int read_header(AVFormatContext *s)
timeline_pos = avio_tell(s->pb); // save before opening another file
/* read metadata */
- pb = wtvfile_open(s, root, root_size, table_0_entries_legacy_attrib_le16);
+ pb = wtvfile_open(s, root, root_size, ff_table_0_entries_legacy_attrib_le16);
if (pb) {
parse_legacy_attrib(s, pb);
wtvfile_close(pb);
@@ -1012,12 +1012,12 @@ static int read_header(AVFormatContext *s)
/* read seek index */
if (s->nb_streams) {
AVStream *st = s->streams[0];
- pb = wtvfile_open(s, root, root_size, table_0_entries_time_le16);
+ pb = wtvfile_open(s, root, root_size, ff_table_0_entries_time_le16);
if (pb) {
while(1) {
uint64_t timestamp = avio_rl64(pb);
uint64_t frame_nb = avio_rl64(pb);
- if (pb->eof_reached)
+ if (avio_feof(pb))
break;
ff_add_index_entry(&wtv->index_entries, &wtv->nb_index_entries, &wtv->index_entries_allocated_size,
0, timestamp, frame_nb, 0, AVINDEX_KEYFRAME);
@@ -1025,13 +1025,13 @@ static int read_header(AVFormatContext *s)
wtvfile_close(pb);
if (wtv->nb_index_entries) {
- pb = wtvfile_open(s, root, root_size, timeline_table_0_entries_Events_le16);
+ pb = wtvfile_open(s, root, root_size, ff_timeline_table_0_entries_Events_le16);
if (pb) {
int i;
while (1) {
uint64_t frame_nb = avio_rl64(pb);
uint64_t position = avio_rl64(pb);
- if (pb->eof_reached)
+ if (avio_feof(pb))
break;
for (i = wtv->nb_index_entries - 1; i >= 0; i--) {
AVIndexEntry *e = wtv->index_entries + i;
@@ -1091,26 +1091,30 @@ static int read_seek(AVFormatContext *s, int stream_index,
i = ff_index_search_timestamp(wtv->index_entries, wtv->nb_index_entries, ts_relative, flags);
if (i < 0) {
- if (wtv->last_valid_pts == AV_NOPTS_VALUE || ts < wtv->last_valid_pts)
- avio_seek(pb, 0, SEEK_SET);
- else if (st->duration != AV_NOPTS_VALUE && ts_relative > st->duration && wtv->nb_index_entries)
- avio_seek(pb, wtv->index_entries[wtv->nb_index_entries - 1].pos, SEEK_SET);
+ if (wtv->last_valid_pts == AV_NOPTS_VALUE || ts < wtv->last_valid_pts) {
+ if (avio_seek(pb, 0, SEEK_SET) < 0)
+ return -1;
+ } else if (st->duration != AV_NOPTS_VALUE && ts_relative > st->duration && wtv->nb_index_entries) {
+ if (avio_seek(pb, wtv->index_entries[wtv->nb_index_entries - 1].pos, SEEK_SET) < 0)
+ return -1;
+ }
if (parse_chunks(s, SEEK_TO_PTS, ts, 0) < 0)
return AVERROR(ERANGE);
return 0;
}
+ if (avio_seek(pb, wtv->index_entries[i].pos, SEEK_SET) < 0)
+ return -1;
wtv->pts = wtv->index_entries[i].timestamp;
if (wtv->epoch != AV_NOPTS_VALUE)
wtv->pts += wtv->epoch;
wtv->last_valid_pts = wtv->pts;
- avio_seek(pb, wtv->index_entries[i].pos, SEEK_SET);
return 0;
}
static int read_close(AVFormatContext *s)
{
WtvContext *wtv = s->priv_data;
- av_free(wtv->index_entries);
+ av_freep(&wtv->index_entries);
wtvfile_close(wtv->pb);
return 0;
}
diff --git a/libavformat/wtvenc.c b/libavformat/wtvenc.c
new file mode 100644
index 0000000000..634545d459
--- /dev/null
+++ b/libavformat/wtvenc.c
@@ -0,0 +1,845 @@
+/*
+ * Windows Television (WTV) muxer
+ * Copyright (c) 2011 Zhentan Feng <spyfeng at gmail dot com>
+ * Copyright (c) 2011 Peter Ross <pross@xvid.org>
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Windows Television (WTV) demuxer
+ * @author Zhentan Feng <spyfeng at gmail dot com>
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/avassert.h"
+#include "avformat.h"
+#include "avio_internal.h"
+#include "internal.h"
+#include "mpegts.h"
+#include "wtv.h"
+
+#define WTV_BIGSECTOR_SIZE (1 << WTV_BIGSECTOR_BITS)
+#define INDEX_BASE 0x2
+#define MAX_NB_INDEX 10
+
+/* declare utf16le strings */
+#define _ , 0,
+static const uint8_t timeline_table_0_header_events[] =
+ {'t'_'i'_'m'_'e'_'l'_'i'_'n'_'e'_'.'_'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'h'_'e'_'a'_'d'_'e'_'r'_'.'_'E'_'v'_'e'_'n'_'t'_'s', 0};
+static const uint8_t table_0_header_legacy_attrib[] =
+ {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'h'_'e'_'a'_'d'_'e'_'r'_'.'_'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0};
+static const uint8_t table_0_redirector_legacy_attrib[] =
+ {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'r'_'e'_'d'_'i'_'r'_'e'_'c'_'t'_'o'_'r'_'.'_'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0};
+static const uint8_t table_0_header_time[] =
+ {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'h'_'e'_'a'_'d'_'e'_'r'_'.'_'t'_'i'_'m'_'e', 0};
+static const uint8_t legacy_attrib[] =
+ {'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0};
+#undef _
+
+static const ff_asf_guid sub_wtv_guid =
+ {0x8C,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
+
+enum WtvFileIndex {
+ WTV_TIMELINE_TABLE_0_HEADER_EVENTS = 0,
+ WTV_TIMELINE_TABLE_0_ENTRIES_EVENTS,
+ WTV_TIMELINE,
+ WTV_TABLE_0_HEADER_LEGACY_ATTRIB,
+ WTV_TABLE_0_ENTRIES_LEGACY_ATTRIB,
+ WTV_TABLE_0_REDIRECTOR_LEGACY_ATTRIB,
+ WTV_TABLE_0_HEADER_TIME,
+ WTV_TABLE_0_ENTRIES_TIME,
+ WTV_FILES
+};
+
+typedef struct {
+ int64_t length;
+ const void *header;
+ int depth;
+ int first_sector;
+} WtvFile;
+
+typedef struct {
+ int64_t pos;
+ int64_t serial;
+ const ff_asf_guid * guid;
+ int stream_id;
+} WtvChunkEntry;
+
+typedef struct {
+ int64_t serial;
+ int64_t value;
+} WtvSyncEntry;
+
+typedef struct {
+ int64_t timeline_start_pos;
+ WtvFile file[WTV_FILES];
+ int64_t serial; /**< chunk serial number */
+ int64_t last_chunk_pos; /**< last chunk position */
+ int64_t last_timestamp_pos; /**< last timestamp chunk position */
+ int64_t first_index_pos; /**< first index_chunk position */
+
+ WtvChunkEntry index[MAX_NB_INDEX];
+ int nb_index;
+ int first_video_flag;
+
+ WtvSyncEntry *st_pairs; /* (serial, timestamp) pairs */
+ int nb_st_pairs;
+ WtvSyncEntry *sp_pairs; /* (serial, position) pairs */
+ int nb_sp_pairs;
+
+ int64_t last_pts;
+ int64_t last_serial;
+
+ AVPacket thumbnail;
+} WtvContext;
+
+
+static void add_serial_pair(WtvSyncEntry ** list, int * count, int64_t serial, int64_t value)
+{
+ int new_count = *count + 1;
+ WtvSyncEntry *new_list = av_realloc(*list, new_count * sizeof(WtvSyncEntry));
+ if (!new_list)
+ return;
+ new_list[*count] = (WtvSyncEntry){serial, value};
+ *list = new_list;
+ *count = new_count;
+}
+
+typedef int WTVHeaderWriteFunc(AVIOContext *pb);
+
+typedef struct {
+ const uint8_t *header;
+ int header_size;
+ WTVHeaderWriteFunc *write_header;
+} WTVRootEntryTable;
+
+#define write_pad(pb, size) ffio_fill(pb, 0, size)
+
+/**
+ * Write chunk header. If header chunk (0x80000000 set) then add to list of header chunks
+ */
+static void write_chunk_header(AVFormatContext *s, const ff_asf_guid *guid, int length, int stream_id)
+{
+ WtvContext *wctx = s->priv_data;
+ AVIOContext *pb = s->pb;
+
+ wctx->last_chunk_pos = avio_tell(pb) - wctx->timeline_start_pos;
+ ff_put_guid(pb, guid);
+ avio_wl32(pb, 32 + length);
+ avio_wl32(pb, stream_id);
+ avio_wl64(pb, wctx->serial);
+
+ if ((stream_id & 0x80000000) && guid != &ff_index_guid) {
+ WtvChunkEntry *t = wctx->index + wctx->nb_index;
+ av_assert0(wctx->nb_index < MAX_NB_INDEX);
+ t->pos = wctx->last_chunk_pos;
+ t->serial = wctx->serial;
+ t->guid = guid;
+ t->stream_id = stream_id & 0x3FFFFFFF;
+ wctx->nb_index++;
+ }
+}
+
+static void write_chunk_header2(AVFormatContext *s, const ff_asf_guid *guid, int stream_id)
+{
+ WtvContext *wctx = s->priv_data;
+ AVIOContext *pb = s->pb;
+
+ int64_t last_chunk_pos = wctx->last_chunk_pos;
+ write_chunk_header(s, guid, 0, stream_id); // length updated later
+ avio_wl64(pb, last_chunk_pos);
+}
+
+static void finish_chunk_noindex(AVFormatContext *s)
+{
+ WtvContext *wctx = s->priv_data;
+ AVIOContext *pb = s->pb;
+
+ // update the chunk_len field and pad.
+ int64_t chunk_len = avio_tell(pb) - (wctx->last_chunk_pos + wctx->timeline_start_pos);
+ avio_seek(pb, -(chunk_len - 16), SEEK_CUR);
+ avio_wl32(pb, chunk_len);
+ avio_seek(pb, chunk_len - (16 + 4), SEEK_CUR);
+
+ write_pad(pb, WTV_PAD8(chunk_len) - chunk_len);
+ wctx->serial++;
+}
+
+static void write_index(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ WtvContext *wctx = s->priv_data;
+ int i;
+
+ write_chunk_header2(s, &ff_index_guid, 0x80000000);
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+
+ for (i = 0; i < wctx->nb_index; i++) {
+ WtvChunkEntry *t = wctx->index + i;
+ ff_put_guid(pb, t->guid);
+ avio_wl64(pb, t->pos);
+ avio_wl32(pb, t->stream_id);
+ avio_wl32(pb, 0); // checksum?
+ avio_wl64(pb, t->serial);
+ }
+ wctx->nb_index = 0; // reset index
+ finish_chunk_noindex(s);
+
+ if (!wctx->first_index_pos)
+ wctx->first_index_pos = wctx->last_chunk_pos;
+}
+
+static void finish_chunk(AVFormatContext *s)
+{
+ WtvContext *wctx = s->priv_data;
+ finish_chunk_noindex(s);
+ if (wctx->nb_index == MAX_NB_INDEX)
+ write_index(s);
+}
+
+static void put_videoinfoheader2(AVIOContext *pb, AVStream *st)
+{
+ AVRational dar = av_mul_q(st->sample_aspect_ratio, (AVRational){st->codec->width, st->codec->height});
+ unsigned int num, den;
+ av_reduce(&num, &den, dar.num, dar.den, 0xFFFFFFFF);
+
+ /* VIDEOINFOHEADER2 */
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+ avio_wl32(pb, st->codec->width);
+ avio_wl32(pb, st->codec->height);
+
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+
+ avio_wl32(pb, st->codec->bit_rate);
+ avio_wl32(pb, 0);
+ avio_wl64(pb, st->avg_frame_rate.num && st->avg_frame_rate.den ? INT64_C(10000000) / av_q2d(st->avg_frame_rate) : 0);
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+
+ avio_wl32(pb, num);
+ avio_wl32(pb, den);
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+
+ ff_put_bmp_header(pb, st->codec, ff_codec_bmp_tags, 0, 1);
+
+ if (st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
+ int padding = (st->codec->extradata_size & 3) ? 4 - (st->codec->extradata_size & 3) : 0;
+ /* MPEG2VIDEOINFO */
+ avio_wl32(pb, 0);
+ avio_wl32(pb, st->codec->extradata_size + padding);
+ avio_wl32(pb, -1);
+ avio_wl32(pb, -1);
+ avio_wl32(pb, 0);
+ avio_write(pb, st->codec->extradata, st->codec->extradata_size);
+ ffio_fill(pb, 0, padding);
+ }
+}
+
+static int write_stream_codec_info(AVFormatContext *s, AVStream *st)
+{
+ const ff_asf_guid *g, *media_type, *format_type;
+ const AVCodecTag *tags;
+ AVIOContext *pb = s->pb;
+ int64_t hdr_pos_start;
+ int hdr_size = 0;
+
+ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ g = get_codec_guid(st->codec->codec_id, ff_video_guids);
+ media_type = &ff_mediatype_video;
+ format_type = st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO ? &ff_format_mpeg2_video : &ff_format_videoinfo2;
+ tags = ff_codec_bmp_tags;
+ } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
+ g = get_codec_guid(st->codec->codec_id, ff_codec_wav_guids);
+ media_type = &ff_mediatype_audio;
+ format_type = &ff_format_waveformatex;
+ tags = ff_codec_wav_tags;
+ } else {
+ av_log(s, AV_LOG_ERROR, "unknown codec_type (0x%x)\n", st->codec->codec_type);
+ return -1;
+ }
+
+ ff_put_guid(pb, media_type); // mediatype
+ ff_put_guid(pb, &ff_mediasubtype_cpfilters_processed); // subtype
+ write_pad(pb, 12);
+ ff_put_guid(pb,&ff_format_cpfilters_processed); // format type
+ avio_wl32(pb, 0); // size
+
+ hdr_pos_start = avio_tell(pb);
+ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ put_videoinfoheader2(pb, st);
+ } else {
+ if (ff_put_wav_header(pb, st->codec, 0) < 0)
+ format_type = &ff_format_none;
+ }
+ hdr_size = avio_tell(pb) - hdr_pos_start;
+
+ // seek back write hdr_size
+ avio_seek(pb, -(hdr_size + 4), SEEK_CUR);
+ avio_wl32(pb, hdr_size + 32);
+ avio_seek(pb, hdr_size, SEEK_CUR);
+ if (g) {
+ ff_put_guid(pb, g); // actual_subtype
+ } else {
+ int tag = ff_codec_get_tag(tags, st->codec->codec_id);
+ if (!tag) {
+ av_log(s, AV_LOG_ERROR, "unsupported codec_id (0x%x)\n", st->codec->codec_id);
+ return -1;
+ }
+ avio_wl32(pb, tag);
+ avio_write(pb, (const uint8_t[]){FF_MEDIASUBTYPE_BASE_GUID}, 12);
+ }
+ ff_put_guid(pb, format_type); // actual_formattype
+
+ return 0;
+}
+
+static int write_stream_codec(AVFormatContext *s, AVStream * st)
+{
+ AVIOContext *pb = s->pb;
+ int ret;
+ write_chunk_header2(s, &ff_stream1_guid, 0x80000000 | 0x01);
+
+ avio_wl32(pb, 0x01);
+ write_pad(pb, 4);
+ write_pad(pb, 4);
+
+ ret = write_stream_codec_info(s, st);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "write stream codec info failed codec_type(0x%x)\n", st->codec->codec_type);
+ return -1;
+ }
+
+ finish_chunk(s);
+ return 0;
+}
+
+static void write_sync(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ WtvContext *wctx = s->priv_data;
+ int64_t last_chunk_pos = wctx->last_chunk_pos;
+
+ write_chunk_header(s, &ff_sync_guid, 0x18, 0);
+ avio_wl64(pb, wctx->first_index_pos);
+ avio_wl64(pb, wctx->last_timestamp_pos);
+ avio_wl64(pb, 0);
+
+ finish_chunk(s);
+ add_serial_pair(&wctx->sp_pairs, &wctx->nb_sp_pairs, wctx->serial, wctx->last_chunk_pos);
+
+ wctx->last_chunk_pos = last_chunk_pos;
+}
+
+static int write_stream_data(AVFormatContext *s, AVStream *st)
+{
+ AVIOContext *pb = s->pb;
+ int ret;
+
+ write_chunk_header2(s, &ff_SBE2_STREAM_DESC_EVENT, 0x80000000 | (st->index + INDEX_BASE));
+ avio_wl32(pb, 0x00000001);
+ avio_wl32(pb, st->index + INDEX_BASE); //stream_id
+ avio_wl32(pb, 0x00000001);
+ write_pad(pb, 8);
+
+ ret = write_stream_codec_info(s, st);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "write stream codec info failed codec_type(0x%x)\n", st->codec->codec_type);
+ return -1;
+ }
+ finish_chunk(s);
+
+ avpriv_set_pts_info(st, 64, 1, 10000000);
+
+ return 0;
+}
+
+static int write_header(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ WtvContext *wctx = s->priv_data;
+ int i, pad, ret;
+ AVStream *st;
+
+ wctx->last_chunk_pos = -1;
+ wctx->last_timestamp_pos = -1;
+
+ ff_put_guid(pb, &ff_wtv_guid);
+ ff_put_guid(pb, &sub_wtv_guid);
+
+ avio_wl32(pb, 0x01);
+ avio_wl32(pb, 0x02);
+ avio_wl32(pb, 1 << WTV_SECTOR_BITS);
+ avio_wl32(pb, 1 << WTV_BIGSECTOR_BITS);
+
+ //write initial root fields
+ avio_wl32(pb, 0); // root_size, update later
+ write_pad(pb, 4);
+ avio_wl32(pb, 0); // root_sector, update it later.
+
+ write_pad(pb, 32);
+ avio_wl32(pb, 0); // file ends pointer, update it later.
+
+ pad = (1 << WTV_SECTOR_BITS) - avio_tell(pb);
+ write_pad(pb, pad);
+
+ wctx->timeline_start_pos = avio_tell(pb);
+
+ wctx->serial = 1;
+ wctx->last_chunk_pos = -1;
+ wctx->first_video_flag = 1;
+
+ for (i = 0; i < s->nb_streams; i++) {
+ st = s->streams[i];
+ if (st->codec->codec_id == AV_CODEC_ID_MJPEG)
+ continue;
+ ret = write_stream_codec(s, st);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "write stream codec failed codec_type(0x%x)\n", st->codec->codec_type);
+ return -1;
+ }
+ if (!i)
+ write_sync(s);
+ }
+
+ for (i = 0; i < s->nb_streams; i++) {
+ st = s->streams[i];
+ if (st->codec->codec_id == AV_CODEC_ID_MJPEG)
+ continue;
+ ret = write_stream_data(s, st);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "write stream data failed codec_type(0x%x)\n", st->codec->codec_type);
+ return -1;
+ }
+ }
+
+ if (wctx->nb_index)
+ write_index(s);
+
+ return 0;
+}
+
+static void write_timestamp(AVFormatContext *s, AVPacket *pkt)
+{
+ AVIOContext *pb = s->pb;
+ WtvContext *wctx = s->priv_data;
+ AVCodecContext *enc = s->streams[pkt->stream_index]->codec;
+
+ write_chunk_header(s, &ff_timestamp_guid, 56, 0x40000000 | (INDEX_BASE + pkt->stream_index));
+ write_pad(pb, 8);
+ avio_wl64(pb, pkt->pts == AV_NOPTS_VALUE ? -1 : pkt->pts);
+ avio_wl64(pb, pkt->pts == AV_NOPTS_VALUE ? -1 : pkt->pts);
+ avio_wl64(pb, pkt->pts == AV_NOPTS_VALUE ? -1 : pkt->pts);
+ avio_wl64(pb, 0);
+ avio_wl64(pb, enc->codec_type == AVMEDIA_TYPE_VIDEO && (pkt->flags & AV_PKT_FLAG_KEY) ? 1 : 0);
+ avio_wl64(pb, 0);
+
+ wctx->last_timestamp_pos = wctx->last_chunk_pos;
+}
+
+static int write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVIOContext *pb = s->pb;
+ WtvContext *wctx = s->priv_data;
+ AVStream *st = s->streams[pkt->stream_index];
+
+ if (st->codec->codec_id == AV_CODEC_ID_MJPEG && !wctx->thumbnail.size) {
+ av_copy_packet(&wctx->thumbnail, pkt);
+ return 0;
+ } else if (st->codec->codec_id == AV_CODEC_ID_H264) {
+ int ret = ff_check_h264_startcode(s, st, pkt);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* emit sync chunk and 'timeline.table.0.entries.Event' record every 50 frames */
+ if (wctx->serial - (wctx->nb_sp_pairs ? wctx->sp_pairs[wctx->nb_sp_pairs - 1].serial : 0) >= 50)
+ write_sync(s);
+
+ /* emit 'table.0.entries.time' record every 500ms */
+ if (pkt->pts != AV_NOPTS_VALUE && pkt->pts - (wctx->nb_st_pairs ? wctx->st_pairs[wctx->nb_st_pairs - 1].value : 0) >= 5000000)
+ add_serial_pair(&wctx->st_pairs, &wctx->nb_st_pairs, wctx->serial, pkt->pts);
+
+ if (pkt->pts != AV_NOPTS_VALUE && pkt->pts > wctx->last_pts) {
+ wctx->last_pts = pkt->pts;
+ wctx->last_serial = wctx->serial;
+ }
+
+ // write timestamp chunk
+ write_timestamp(s, pkt);
+
+ write_chunk_header(s, &ff_data_guid, pkt->size, INDEX_BASE + pkt->stream_index);
+ avio_write(pb, pkt->data, pkt->size);
+ write_pad(pb, WTV_PAD8(pkt->size) - pkt->size);
+
+ wctx->serial++;
+ return 0;
+}
+
+static int write_table0_header_events(AVIOContext *pb)
+{
+ avio_wl32(pb, 0x10);
+ write_pad(pb, 84);
+ avio_wl64(pb, 0x32);
+ return 96;
+}
+
+static int write_table0_header_legacy_attrib(AVIOContext *pb)
+{
+ int pad = 0;
+ avio_wl32(pb, 0xFFFFFFFF);
+ write_pad(pb, 12);
+ avio_write(pb, legacy_attrib, sizeof(legacy_attrib));
+ pad = WTV_PAD8(sizeof(legacy_attrib)) - sizeof(legacy_attrib);
+ write_pad(pb, pad);
+ write_pad(pb, 32);
+ return 48 + WTV_PAD8(sizeof(legacy_attrib));
+}
+
+static int write_table0_header_time(AVIOContext *pb)
+{
+ avio_wl32(pb, 0x10);
+ write_pad(pb, 76);
+ avio_wl64(pb, 0x40);
+ return 88;
+}
+
+static const WTVRootEntryTable wtv_root_entry_table[] = {
+ { timeline_table_0_header_events, sizeof(timeline_table_0_header_events), write_table0_header_events},
+ { ff_timeline_table_0_entries_Events_le16, sizeof(ff_timeline_table_0_entries_Events_le16), NULL},
+ { ff_timeline_le16, sizeof(ff_timeline_le16), NULL},
+ { table_0_header_legacy_attrib, sizeof(table_0_header_legacy_attrib), write_table0_header_legacy_attrib},
+ { ff_table_0_entries_legacy_attrib_le16, sizeof(ff_table_0_entries_legacy_attrib_le16), NULL},
+ { table_0_redirector_legacy_attrib, sizeof(table_0_redirector_legacy_attrib), NULL},
+ { table_0_header_time, sizeof(table_0_header_time), write_table0_header_time},
+ { ff_table_0_entries_time_le16, sizeof(ff_table_0_entries_time_le16), NULL},
+};
+
+static int write_root_table(AVFormatContext *s, int64_t sector_pos)
+{
+ AVIOContext *pb = s->pb;
+ WtvContext *wctx = s->priv_data;
+ int size, pad;
+ int i;
+
+ const WTVRootEntryTable *h = wtv_root_entry_table;
+ for (i = 0; i < sizeof(wtv_root_entry_table)/sizeof(WTVRootEntryTable); i++, h++) {
+ WtvFile *w = &wctx->file[i];
+ int filename_padding = WTV_PAD8(h->header_size) - h->header_size;
+ WTVHeaderWriteFunc *write = h->write_header;
+ int len = 0;
+ int64_t len_pos;
+
+ ff_put_guid(pb, &ff_dir_entry_guid);
+ len_pos = avio_tell(pb);
+ avio_wl16(pb, 40 + h->header_size + filename_padding + 8); // maybe updated later
+ write_pad(pb, 6);
+ avio_wl64(pb, write ? 0 : w->length);// maybe update later
+ avio_wl32(pb, (h->header_size + filename_padding) >> 1);
+ write_pad(pb, 4);
+
+ avio_write(pb, h->header, h->header_size);
+ write_pad(pb, filename_padding);
+
+ if (write) {
+ len = write(pb);
+ // update length field
+ avio_seek(pb, len_pos, SEEK_SET);
+ avio_wl64(pb, 40 + h->header_size + filename_padding + len);
+ avio_wl64(pb, len |(1ULL<<62) | (1ULL<<60));
+ avio_seek(pb, 8 + h->header_size + filename_padding + len, SEEK_CUR);
+ } else {
+ avio_wl32(pb, w->first_sector);
+ avio_wl32(pb, w->depth);
+ }
+ }
+
+ // caculate root table size
+ size = avio_tell(pb) - sector_pos;
+ pad = WTV_SECTOR_SIZE- size;
+ write_pad(pb, pad);
+
+ return size;
+}
+
+static void write_fat(AVIOContext *pb, int start_sector, int nb_sectors, int shift)
+{
+ int i;
+ for (i = 0; i < nb_sectors; i++) {
+ avio_wl32(pb, start_sector + (i << shift));
+ }
+ // pad left sector pointer size
+ write_pad(pb, WTV_SECTOR_SIZE - ((nb_sectors << 2) % WTV_SECTOR_SIZE));
+}
+
+static int64_t write_fat_sector(AVFormatContext *s, int64_t start_pos, int nb_sectors, int sector_bits, int depth)
+{
+ int64_t start_sector = start_pos >> WTV_SECTOR_BITS;
+ int shift = sector_bits - WTV_SECTOR_BITS;
+
+ int64_t fat = avio_tell(s->pb);
+ write_fat(s->pb, start_sector, nb_sectors, shift);
+
+ if (depth == 2) {
+ int64_t start_sector1 = fat >> WTV_SECTOR_BITS;
+ int nb_sectors1 = ((nb_sectors << 2) + WTV_SECTOR_SIZE - 1) / WTV_SECTOR_SIZE;
+ int64_t fat1 = avio_tell(s->pb);
+
+ write_fat(s->pb, start_sector1, nb_sectors1, 0);
+ return fat1;
+ }
+
+ return fat;
+}
+
+static void write_table_entries_events(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ WtvContext *wctx = s->priv_data;
+ int i;
+ for (i = 0; i < wctx->nb_sp_pairs; i++) {
+ avio_wl64(pb, wctx->sp_pairs[i].serial);
+ avio_wl64(pb, wctx->sp_pairs[i].value);
+ }
+}
+
+static void write_table_entries_time(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ WtvContext *wctx = s->priv_data;
+ int i;
+ for (i = 0; i < wctx->nb_st_pairs; i++) {
+ avio_wl64(pb, wctx->st_pairs[i].value);
+ avio_wl64(pb, wctx->st_pairs[i].serial);
+ }
+ avio_wl64(pb, wctx->last_pts);
+ avio_wl64(pb, wctx->last_serial);
+}
+
+static void write_metadata_header(AVIOContext *pb, int type, const char *key, int value_size)
+{
+ ff_put_guid(pb, &ff_metadata_guid);
+ avio_wl32(pb, type);
+ avio_wl32(pb, value_size);
+ avio_put_str16le(pb, key);
+}
+
+static int metadata_header_size(const char *key)
+{
+ return 16 + 4 + 4 + strlen(key)*2 + 2;
+}
+
+static void write_tag_int32(AVIOContext *pb, const char *key, int value)
+{
+ write_metadata_header(pb, 0, key, 4);
+ avio_wl32(pb, value);
+}
+
+static void write_tag(AVIOContext *pb, const char *key, const char *value)
+{
+ write_metadata_header(pb, 1, key, strlen(value)*2 + 2);
+ avio_put_str16le(pb, value);
+}
+
+static int attachment_value_size(const AVPacket *pkt, const AVDictionaryEntry *e)
+{
+ return strlen("image/jpeg")*2 + 2 + 1 + (e ? strlen(e->value)*2 : 0) + 2 + 4 + pkt->size;
+}
+
+static void write_table_entries_attrib(AVFormatContext *s)
+{
+ WtvContext *wctx = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVDictionaryEntry *tag = 0;
+
+ //FIXME: translate special tags (e.g. WM/Bitrate) to binary representation
+ ff_metadata_conv(&s->metadata, ff_asf_metadata_conv, NULL);
+ while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)))
+ write_tag(pb, tag->key, tag->value);
+
+ if (wctx->thumbnail.size) {
+ AVStream *st = s->streams[wctx->thumbnail.stream_index];
+ tag = av_dict_get(st->metadata, "title", NULL, 0);
+ write_metadata_header(pb, 2, "WM/Picture", attachment_value_size(&wctx->thumbnail, tag));
+
+ avio_put_str16le(pb, "image/jpeg");
+ avio_w8(pb, 0x10);
+ avio_put_str16le(pb, tag ? tag->value : "");
+
+ avio_wl32(pb, wctx->thumbnail.size);
+ avio_write(pb, wctx->thumbnail.data, wctx->thumbnail.size);
+
+ write_tag_int32(pb, "WM/MediaThumbType", 2);
+ }
+}
+
+static void write_table_redirector_legacy_attrib(AVFormatContext *s)
+{
+ WtvContext *wctx = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVDictionaryEntry *tag = 0;
+ int64_t pos = 0;
+
+ //FIXME: translate special tags to binary representation
+ while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
+ avio_wl64(pb, pos);
+ pos += metadata_header_size(tag->key) + strlen(tag->value)*2 + 2;
+ }
+
+ if (wctx->thumbnail.size) {
+ AVStream *st = s->streams[wctx->thumbnail.stream_index];
+ avio_wl64(pb, pos);
+ pos += metadata_header_size("WM/Picture") + attachment_value_size(&wctx->thumbnail, av_dict_get(st->metadata, "title", NULL, 0));
+
+ avio_wl64(pb, pos);
+ pos += metadata_header_size("WM/MediaThumbType") + 4;
+ }
+}
+
+/**
+ * Pad the remainder of a file
+ * Write out fat table
+ * @return <0 on error
+ */
+static int finish_file(AVFormatContext *s, enum WtvFileIndex index, int64_t start_pos)
+{
+ WtvContext *wctx = s->priv_data;
+ AVIOContext *pb = s->pb;
+ WtvFile *w = &wctx->file[index];
+ int64_t end_pos = avio_tell(pb);
+ int sector_bits, nb_sectors, pad;
+
+ av_assert0(index < WTV_FILES);
+
+ w->length = (end_pos - start_pos);
+
+ // determine optimal fat table depth, sector_bits, nb_sectors
+ if (w->length <= WTV_SECTOR_SIZE) {
+ w->depth = 0;
+ sector_bits = WTV_SECTOR_BITS;
+ } else if (w->length <= (WTV_SECTOR_SIZE / 4) * WTV_SECTOR_SIZE) {
+ w->depth = 1;
+ sector_bits = WTV_SECTOR_BITS;
+ } else if (w->length <= (WTV_SECTOR_SIZE / 4) * WTV_BIGSECTOR_SIZE) {
+ w->depth = 1;
+ sector_bits = WTV_BIGSECTOR_BITS;
+ } else if (w->length <= (int64_t)(WTV_SECTOR_SIZE / 4) * (WTV_SECTOR_SIZE / 4) * WTV_SECTOR_SIZE) {
+ w->depth = 2;
+ sector_bits = WTV_SECTOR_BITS;
+ } else if (w->length <= (int64_t)(WTV_SECTOR_SIZE / 4) * (WTV_SECTOR_SIZE / 4) * WTV_BIGSECTOR_SIZE) {
+ w->depth = 2;
+ sector_bits = WTV_BIGSECTOR_BITS;
+ } else {
+ av_log(s, AV_LOG_ERROR, "unsupported file allocation table depth (%"PRIi64" bytes)\n", w->length);
+ return -1;
+ }
+
+ // determine the nb_sectors
+ nb_sectors = (int)(w->length >> sector_bits);
+
+ // pad sector of timeline
+ pad = (1 << sector_bits) - (w->length % (1 << sector_bits));
+ if (pad) {
+ nb_sectors++;
+ write_pad(pb, pad);
+ }
+
+ //write fat table
+ if (w->depth > 0) {
+ w->first_sector = write_fat_sector(s, start_pos, nb_sectors, sector_bits, w->depth) >> WTV_SECTOR_BITS;
+ } else {
+ w->first_sector = start_pos >> WTV_SECTOR_BITS;
+ }
+
+ w->length |= 1ULL<<60;
+ if (sector_bits == WTV_SECTOR_BITS)
+ w->length |= 1ULL<<63;
+
+ return 0;
+}
+
+static int write_trailer(AVFormatContext *s)
+{
+ WtvContext *wctx = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int root_size;
+ int64_t sector_pos;
+ int64_t start_pos, file_end_pos;
+
+ if (finish_file(s, WTV_TIMELINE, wctx->timeline_start_pos) < 0)
+ return -1;
+
+ start_pos = avio_tell(pb);
+ write_table_entries_events(s);
+ if (finish_file(s, WTV_TIMELINE_TABLE_0_ENTRIES_EVENTS, start_pos) < 0)
+ return -1;
+
+ start_pos = avio_tell(pb);
+ write_table_entries_attrib(s);
+ if (finish_file(s, WTV_TABLE_0_ENTRIES_LEGACY_ATTRIB, start_pos) < 0)
+ return -1;
+
+ start_pos = avio_tell(pb);
+ write_table_redirector_legacy_attrib(s);
+ if (finish_file(s, WTV_TABLE_0_REDIRECTOR_LEGACY_ATTRIB, start_pos) < 0)
+ return -1;
+
+ start_pos = avio_tell(pb);
+ write_table_entries_time(s);
+ if (finish_file(s, WTV_TABLE_0_ENTRIES_TIME, start_pos) < 0)
+ return -1;
+
+ // write root table
+ sector_pos = avio_tell(pb);
+ root_size = write_root_table(s, sector_pos);
+
+ file_end_pos = avio_tell(pb);
+ // update root value
+ avio_seek(pb, 0x30, SEEK_SET);
+ avio_wl32(pb, root_size);
+ avio_seek(pb, 4, SEEK_CUR);
+ avio_wl32(pb, sector_pos >> WTV_SECTOR_BITS);
+ avio_seek(pb, 0x5c, SEEK_SET);
+ avio_wl32(pb, file_end_pos >> WTV_SECTOR_BITS);
+
+ avio_flush(pb);
+
+ av_free(wctx->sp_pairs);
+ av_free(wctx->st_pairs);
+ av_free_packet(&wctx->thumbnail);
+ return 0;
+}
+
+AVOutputFormat ff_wtv_muxer = {
+ .name = "wtv",
+ .long_name = NULL_IF_CONFIG_SMALL("Windows Television (WTV)"),
+ .extensions = "wtv",
+ .priv_data_size = sizeof(WtvContext),
+ .audio_codec = AV_CODEC_ID_AC3,
+ .video_codec = AV_CODEC_ID_MPEG2VIDEO,
+ .write_header = write_header,
+ .write_packet = write_packet,
+ .write_trailer = write_trailer,
+ .codec_tag = (const AVCodecTag* const []){ ff_codec_bmp_tags,
+ ff_codec_wav_tags, 0 },
+};
diff --git a/libavformat/wv.c b/libavformat/wv.c
index 724256b19c..0f4f80761a 100644
--- a/libavformat/wv.c
+++ b/libavformat/wv.c
@@ -1,20 +1,20 @@
/*
* WavPack shared functions
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/wv.h b/libavformat/wv.h
index ef285d2ebc..07d2e1bc0a 100644
--- a/libavformat/wv.h
+++ b/libavformat/wv.h
@@ -1,20 +1,20 @@
/*
* WavPack shared functions
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/wvdec.c b/libavformat/wvdec.c
index 75eddd2f50..4e58512393 100644
--- a/libavformat/wvdec.c
+++ b/libavformat/wvdec.c
@@ -2,20 +2,20 @@
* WavPack demuxer
* Copyright (c) 2006,2011 Konstantin Shishkov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -64,8 +64,11 @@ static int wv_probe(AVProbeData *p)
/* check file header */
if (p->buf_size <= 32)
return 0;
- if (p->buf[0] == 'w' && p->buf[1] == 'v' &&
- p->buf[2] == 'p' && p->buf[3] == 'k')
+ if (AV_RL32(&p->buf[0]) == MKTAG('w', 'v', 'p', 'k') &&
+ AV_RL32(&p->buf[4]) >= 24 &&
+ AV_RL32(&p->buf[4]) <= WV_BLOCK_LIMIT &&
+ AV_RL16(&p->buf[8]) >= 0x402 &&
+ AV_RL16(&p->buf[8]) <= 0x410)
return AVPROBE_SCORE_MAX;
else
return 0;
@@ -121,7 +124,7 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb)
"Cannot determine additional parameters\n");
return AVERROR_INVALIDDATA;
}
- while (avio_tell(pb) < block_end) {
+ while (avio_tell(pb) < block_end && !avio_feof(pb)) {
int id, size;
id = avio_r8(pb);
size = (id & 0x80) ? avio_rl24(pb) : avio_r8(pb);
@@ -235,7 +238,8 @@ static int wv_read_header(AVFormatContext *s)
st->codec->bits_per_coded_sample = wc->bpp;
avpriv_set_pts_info(st, 64, 1, wc->rate);
st->start_time = 0;
- st->duration = wc->header.total_samples;
+ if (wc->header.total_samples != 0xFFFFFFFFu)
+ st->duration = wc->header.total_samples;
if (s->pb->seekable) {
int64_t cur = avio_tell(s->pb);
@@ -256,7 +260,7 @@ static int wv_read_packet(AVFormatContext *s, AVPacket *pkt)
int64_t pos;
uint32_t block_samples;
- if (s->pb->eof_reached)
+ if (avio_feof(s->pb))
return AVERROR_EOF;
if (wc->block_parsed) {
if ((ret = wv_read_block_header(s, s->pb)) < 0)
diff --git a/libavformat/wvenc.c b/libavformat/wvenc.c
index 2e150e1a7a..b0d74caaec 100644
--- a/libavformat/wvenc.c
+++ b/libavformat/wvenc.c
@@ -1,18 +1,22 @@
/*
- * This file is part of Libav.
+ * WavPack muxer
+ * Copyright (c) 2013 Konstantin Shishkov <kostya.shishkov@gmail.com>
+ * Copyright (c) 2012 Paul B Mahol
*
- * Libav is free software; you can redistribute it and/or
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/xa.c b/libavformat/xa.c
index 57a36bcbe2..43661dea43 100644
--- a/libavformat/xa.c
+++ b/libavformat/xa.c
@@ -2,20 +2,20 @@
* Maxis XA (.xa) File Demuxer
* Copyright (c) 2008 Robert Marston
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -84,6 +84,9 @@ static int xa_read_header(AVFormatContext *s)
avio_skip(pb, 2); /* Skip block align */
avio_skip(pb, 2); /* Skip bits-per-sample */
+ if (!st->codec->channels || !st->codec->sample_rate)
+ return AVERROR_INVALIDDATA;
+
st->codec->bit_rate = av_clip(15LL * st->codec->channels * 8 *
st->codec->sample_rate / 28, 0, INT_MAX);
diff --git a/libavformat/xmv.c b/libavformat/xmv.c
index bcb17f8893..45c24642cc 100644
--- a/libavformat/xmv.c
+++ b/libavformat/xmv.c
@@ -3,20 +3,20 @@
* Copyright (c) 2011 Sven Hesse <drmccoy@drmccoy.de>
* Copyright (c) 2011 Matthew Hoops <clone2727@gmail.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -32,83 +32,81 @@
#include "avformat.h"
#include "internal.h"
#include "riff.h"
+#include "libavutil/avassert.h"
+/** The min size of an XMV header. */
#define XMV_MIN_HEADER_SIZE 36
+/** Audio flag: ADPCM'd 5.1 stream, front left / right channels */
#define XMV_AUDIO_ADPCM51_FRONTLEFTRIGHT 1
+/** Audio flag: ADPCM'd 5.1 stream, front center / low frequency channels */
#define XMV_AUDIO_ADPCM51_FRONTCENTERLOW 2
+/** Audio flag: ADPCM'd 5.1 stream, rear left / right channels */
#define XMV_AUDIO_ADPCM51_REARLEFTRIGHT 4
+/** Audio flag: Any of the ADPCM'd 5.1 stream flags. */
#define XMV_AUDIO_ADPCM51 (XMV_AUDIO_ADPCM51_FRONTLEFTRIGHT | \
XMV_AUDIO_ADPCM51_FRONTCENTERLOW | \
XMV_AUDIO_ADPCM51_REARLEFTRIGHT)
#define XMV_BLOCK_ALIGN_SIZE 36
-typedef struct XMVAudioTrack {
- uint16_t compression;
- uint16_t channels;
- uint32_t sample_rate;
- uint16_t bits_per_sample;
- uint32_t bit_rate;
- uint16_t flags;
- uint16_t block_align;
- uint16_t block_samples;
-
- enum AVCodecID codec_id;
-} XMVAudioTrack;
-
+/** A video packet with an XMV file. */
typedef struct XMVVideoPacket {
- /* The decoder stream index for this video packet. */
- int stream_index;
+ 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
+ unsigned block_align; ///< Bytes per compressed block.
+ uint16_t block_samples; ///< Decompressed samples per compressed block.
- uint32_t data_size;
- uint32_t data_offset;
+ enum AVCodecID codec_id; ///< The codec ID of the compression scheme.
- uint32_t frame_size;
+ uint32_t data_size; ///< The size of the remaining audio data.
+ uint64_t data_offset; ///< The offset of the audio data within the file.
- uint32_t block_count;
+ uint32_t frame_size; ///< Number of bytes to put into an audio frame.
+
+ uint64_t block_count; ///< Running counter of decompressed audio block.
} XMVAudioPacket;
+/** Context for demuxing an XMV file. */
typedef struct XMVDemuxContext {
- uint16_t audio_track_count;
+ 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)
@@ -132,8 +130,7 @@ static int xmv_read_close(AVFormatContext *s)
{
XMVDemuxContext *xmv = s->priv_data;
- av_free(xmv->audio);
- av_free(xmv->audio_tracks);
+ av_freep(&xmv->audio);
return 0;
}
@@ -185,36 +182,30 @@ static int xmv_read_header(AVFormatContext *s)
avio_skip(pb, 2); /* Unknown (padding?) */
- xmv->audio_tracks = av_malloc(xmv->audio_track_count * sizeof(XMVAudioTrack));
- if (!xmv->audio_tracks)
- return AVERROR(ENOMEM);
-
- xmv->audio = av_malloc(xmv->audio_track_count * sizeof(XMVAudioPacket));
+ xmv->audio = av_malloc_array(xmv->audio_track_count, sizeof(XMVAudioPacket));
if (!xmv->audio) {
ret = AVERROR(ENOMEM);
goto fail;
}
for (audio_track = 0; audio_track < xmv->audio_track_count; audio_track++) {
- XMVAudioTrack *track = &xmv->audio_tracks[audio_track];
- XMVAudioPacket *packet = &xmv->audio [audio_track];
+ XMVAudioPacket *packet = &xmv->audio[audio_track];
AVStream *ast = NULL;
- track->compression = avio_rl16(pb);
- track->channels = avio_rl16(pb);
- track->sample_rate = avio_rl32(pb);
- track->bits_per_sample = avio_rl16(pb);
- track->flags = avio_rl16(pb);
-
- track->bit_rate = track->bits_per_sample *
- track->sample_rate *
- track->channels;
- track->block_align = XMV_BLOCK_ALIGN_SIZE * track->channels;
- track->block_samples = 64;
- track->codec_id = ff_wav_codec_get_id(track->compression,
- track->bits_per_sample);
-
- packet->track = track;
+ packet->compression = avio_rl16(pb);
+ packet->channels = avio_rl16(pb);
+ packet->sample_rate = avio_rl32(pb);
+ packet->bits_per_sample = avio_rl16(pb);
+ packet->flags = avio_rl16(pb);
+
+ packet->bit_rate = packet->bits_per_sample *
+ packet->sample_rate *
+ packet->channels;
+ packet->block_align = XMV_BLOCK_ALIGN_SIZE * packet->channels;
+ packet->block_samples = 64;
+ packet->codec_id = ff_wav_codec_get_id(packet->compression,
+ packet->bits_per_sample);
+
packet->stream_index = -1;
packet->frame_size = 0;
@@ -222,12 +213,12 @@ static int xmv_read_header(AVFormatContext *s)
/* TODO: ADPCM'd 5.1 sound is encoded in three separate streams.
* Those need to be interleaved to a proper 5.1 stream. */
- if (track->flags & XMV_AUDIO_ADPCM51)
+ if (packet->flags & XMV_AUDIO_ADPCM51)
av_log(s, AV_LOG_WARNING, "Unsupported 5.1 ADPCM audio stream "
- "(0x%04X)\n", track->flags);
+ "(0x%04X)\n", packet->flags);
- if (!track->channels || !track->sample_rate ||
- track->channels >= UINT16_MAX / XMV_BLOCK_ALIGN_SIZE) {
+ if (!packet->channels || !packet->sample_rate ||
+ packet->channels >= UINT16_MAX / XMV_BLOCK_ALIGN_SIZE) {
av_log(s, AV_LOG_ERROR, "Invalid parameters for audio track %"PRIu16".\n",
audio_track);
ret = AVERROR_INVALIDDATA;
@@ -241,15 +232,15 @@ static int xmv_read_header(AVFormatContext *s)
}
ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
- ast->codec->codec_id = track->codec_id;
- ast->codec->codec_tag = track->compression;
- ast->codec->channels = track->channels;
- ast->codec->sample_rate = track->sample_rate;
- ast->codec->bits_per_coded_sample = track->bits_per_sample;
- ast->codec->bit_rate = track->bit_rate;
- ast->codec->block_align = 36 * track->channels;
+ ast->codec->codec_id = packet->codec_id;
+ ast->codec->codec_tag = packet->compression;
+ ast->codec->channels = packet->channels;
+ ast->codec->sample_rate = packet->sample_rate;
+ ast->codec->bits_per_coded_sample = packet->bits_per_sample;
+ ast->codec->bit_rate = packet->bit_rate;
+ ast->codec->block_align = 36 * packet->channels;
- avpriv_set_pts_info(ast, 32, track->block_samples, track->sample_rate);
+ avpriv_set_pts_info(ast, 32, packet->block_samples, packet->sample_rate);
packet->stream_index = ast->index;
@@ -257,7 +248,7 @@ static int xmv_read_header(AVFormatContext *s)
}
- /** Initialize the packet context */
+ /* Initialize the packet context */
xmv->next_packet_offset = avio_tell(pb);
xmv->next_packet_size = this_packet_size - xmv->next_packet_offset;
@@ -303,10 +294,11 @@ static int xmv_process_packet_header(AVFormatContext *s)
{
XMVDemuxContext *xmv = s->priv_data;
AVIOContext *pb = s->pb;
+ int ret;
uint8_t data[8];
uint16_t audio_track;
- uint32_t data_offset;
+ uint64_t data_offset;
/* Next packet size */
xmv->next_packet_size = avio_rl32(pb);
@@ -337,7 +329,7 @@ static int xmv_process_packet_header(AVFormatContext *s)
xmv->current_stream = 0;
if (!xmv->video.frame_count) {
xmv->video.frame_count = 1;
- xmv->current_stream = 1;
+ xmv->current_stream = xmv->stream_count > 1;
}
/* Packet audio header */
@@ -357,9 +349,9 @@ static int xmv_process_packet_header(AVFormatContext *s)
*/
packet->data_size = xmv->audio[audio_track - 1].data_size;
- /** Carve up the audio data in frame_count slices */
+ /* Carve up the audio data in frame_count slices */
packet->frame_size = packet->data_size / xmv->video.frame_count;
- packet->frame_size -= packet->frame_size % packet->track->block_align;
+ packet->frame_size -= packet->frame_size % packet->block_align;
}
/* Packet data offsets */
@@ -387,14 +379,13 @@ 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);
+ av_freep(&vst->codec->extradata);
- vst->codec->extradata =
- av_malloc(4 + FF_INPUT_BUFFER_PADDING_SIZE);
- vst->codec->extradata_size = 4;
+ if ((ret = ff_alloc_extradata(vst->codec, 4)) < 0)
+ return ret;
}
memcpy(vst->codec->extradata, xmv->video.extradata, 4);
@@ -411,6 +402,9 @@ static int xmv_fetch_new_packet(AVFormatContext *s)
AVIOContext *pb = s->pb;
int result;
+ if (xmv->this_packet_offset == xmv->next_packet_offset)
+ return AVERROR_EOF;
+
/* Seek to it */
xmv->this_packet_offset = xmv->next_packet_offset;
if (avio_seek(pb, xmv->this_packet_offset, SEEK_SET) != xmv->this_packet_offset)
@@ -463,7 +457,7 @@ static int xmv_fetch_audio_packet(AVFormatContext *s,
/* Calculate the PTS */
- block_count = data_size / audio->track->block_align;
+ block_count = data_size / audio->block_align;
pkt->duration = block_count;
pkt->pts = audio->block_count;
@@ -488,7 +482,7 @@ static int xmv_fetch_video_packet(AVFormatContext *s,
int result;
uint32_t frame_header;
uint32_t frame_size, frame_timestamp;
- uint32_t i;
+ uint8_t *data, *end;
/* Seek to it */
if (avio_seek(pb, video->data_offset, SEEK_SET) != video->data_offset)
@@ -503,17 +497,17 @@ static int xmv_fetch_video_packet(AVFormatContext *s,
if ((frame_size + 4) > video->data_size)
return AVERROR(EIO);
- /* Create the packet */
- result = av_new_packet(pkt, frame_size);
- if (result)
+ /* Get the packet data */
+ result = av_get_packet(pb, pkt, frame_size);
+ if (result != frame_size)
return result;
/* Contrary to normal WMV2 video, the bit stream in XMV's
* WMV2 is little-endian.
* TODO: This manual swap is of course suboptimal.
*/
- for (i = 0; i < frame_size; i += 4)
- AV_WB32(pkt->data + i, avio_rl32(pb));
+ for (data = pkt->data, end = pkt->data + frame_size; data < end; data += 4)
+ AV_WB32(data, AV_RL32(data));
pkt->stream_index = video->stream_index;
diff --git a/libavformat/xwma.c b/libavformat/xwma.c
index 4a30001f76..075a85a2e7 100644
--- a/libavformat/xwma.c
+++ b/libavformat/xwma.c
@@ -2,20 +2,20 @@
* xWMA demuxer
* Copyright (c) 2011 Max Horn
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -46,7 +46,7 @@ static int xwma_read_header(AVFormatContext *s)
int64_t size;
int ret = 0;
uint32_t dpds_table_size = 0;
- uint32_t *dpds_table = 0;
+ uint32_t *dpds_table = NULL;
unsigned int tag;
AVIOContext *pb = s->pb;
AVStream *st;
@@ -75,7 +75,7 @@ static int xwma_read_header(AVFormatContext *s)
if (!st)
return AVERROR(ENOMEM);
- ret = ff_get_wav_header(pb, st->codec, size);
+ ret = ff_get_wav_header(pb, st->codec, size, 0);
if (ret < 0)
return ret;
st->need_parsing = AVSTREAM_PARSE_NONE;
@@ -131,7 +131,7 @@ static int xwma_read_header(AVFormatContext *s)
/* parse the remaining RIFF chunks */
for (;;) {
if (pb->eof_reached) {
- ret = AVERROR_INVALIDDATA;
+ ret = AVERROR_EOF;
goto fail;
}
/* read next chunk tag */
@@ -167,7 +167,7 @@ static int xwma_read_header(AVFormatContext *s)
if (dpds_table_size == 0 || dpds_table_size >= INT_MAX / 4) {
av_log(s, AV_LOG_ERROR,
"dpds chunk size %"PRId64" invalid\n", size);
- return -1;
+ return AVERROR_INVALIDDATA;
}
/* Allocate some temporary storage to keep the dpds data around.
diff --git a/libavformat/yop.c b/libavformat/yop.c
index 08fcc19e3c..81b3cc2b32 100644
--- a/libavformat/yop.c
+++ b/libavformat/yop.c
@@ -5,20 +5,20 @@
* derived from the code by
* Copyright (C) 2009 Thomas P. Higdon <thomas.p.higdon@gmail.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -39,10 +39,15 @@ typedef struct yop_dec_context {
static int yop_probe(AVProbeData *probe_packet)
{
if (AV_RB16(probe_packet->buf) == AV_RB16("YO") &&
+ probe_packet->buf[2]<10 &&
+ probe_packet->buf[3]<10 &&
probe_packet->buf[6] &&
probe_packet->buf[7] &&
!(probe_packet->buf[8] & 1) &&
- !(probe_packet->buf[10] & 1))
+ !(probe_packet->buf[10] & 1) &&
+ AV_RL16(probe_packet->buf + 12 + 6) >= 920 &&
+ AV_RL16(probe_packet->buf + 12 + 6) < probe_packet->buf[12] * 3 + 4 + probe_packet->buf[7] * 2048
+ )
return AVPROBE_SCORE_MAX * 3 / 4;
return 0;
@@ -60,14 +65,12 @@ static int yop_read_header(AVFormatContext *s)
audio_stream = avformat_new_stream(s, NULL);
video_stream = avformat_new_stream(s, NULL);
+ if (!audio_stream || !video_stream)
+ return AVERROR(ENOMEM);
// Extra data that will be passed to the decoder
-
- video_stream->codec->extradata = av_mallocz(8 + FF_INPUT_BUFFER_PADDING_SIZE);
-
- if (!video_stream->codec->extradata)
+ if (ff_alloc_extradata(video_stream->codec, 8))
return AVERROR(ENOMEM);
- video_stream->codec->extradata_size = 8;
// Audio
audio_dec = audio_stream->codec;
@@ -98,6 +101,8 @@ static int yop_read_header(AVFormatContext *s)
yop->palette_size = video_dec->extradata[0] * 3 + 4;
yop->audio_block_length = AV_RL16(video_dec->extradata + 6);
+ video_dec->bit_rate = 8 * (yop->frame_size - yop->audio_block_length) * frame_rate;
+
// 1840 samples per frame, 1 nibble per sample; hence 1840/2 = 920
if (yop->audio_block_length < 920 ||
yop->audio_block_length + yop->palette_size >= yop->frame_size) {
diff --git a/libavformat/yuv4mpeg.h b/libavformat/yuv4mpeg.h
index 7891ed60a7..750f498407 100644
--- a/libavformat/yuv4mpeg.h
+++ b/libavformat/yuv4mpeg.h
@@ -1,20 +1,20 @@
/*
* YUV4MPEG common definitions
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/yuv4mpegdec.c b/libavformat/yuv4mpegdec.c
index dd81358608..7613c3cdc3 100644
--- a/libavformat/yuv4mpegdec.c
+++ b/libavformat/yuv4mpegdec.c
@@ -2,20 +2,20 @@
* YUV4MPEG demuxer
* Copyright (c) 2001, 2002, 2003 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -79,20 +79,52 @@ static int yuv4_read_header(AVFormatContext *s)
} else if (strncmp("420paldv", tokstart, 8) == 0) {
pix_fmt = AV_PIX_FMT_YUV420P;
chroma_sample_location = AVCHROMA_LOC_TOPLEFT;
+ } else if (strncmp("420p16", tokstart, 6) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV420P16;
+ } else if (strncmp("422p16", tokstart, 6) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV422P16;
+ } else if (strncmp("444p16", tokstart, 6) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV444P16;
+ } else if (strncmp("420p14", tokstart, 6) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV420P14;
+ } else if (strncmp("422p14", tokstart, 6) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV422P14;
+ } else if (strncmp("444p14", tokstart, 6) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV444P14;
+ } else if (strncmp("420p12", tokstart, 6) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV420P12;
+ } else if (strncmp("422p12", tokstart, 6) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV422P12;
+ } else if (strncmp("444p12", tokstart, 6) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV444P12;
+ } else if (strncmp("420p10", tokstart, 6) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV420P10;
+ } else if (strncmp("422p10", tokstart, 6) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV422P10;
+ } else if (strncmp("444p10", tokstart, 6) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV444P10;
+ } else if (strncmp("420p9", tokstart, 5) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV420P9;
+ } else if (strncmp("422p9", tokstart, 5) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV422P9;
+ } else if (strncmp("444p9", tokstart, 5) == 0) {
+ pix_fmt = AV_PIX_FMT_YUV444P9;
} else if (strncmp("420", tokstart, 3) == 0) {
pix_fmt = AV_PIX_FMT_YUV420P;
chroma_sample_location = AVCHROMA_LOC_CENTER;
- } else if (strncmp("411", tokstart, 3) == 0)
+ } else if (strncmp("411", tokstart, 3) == 0) {
pix_fmt = AV_PIX_FMT_YUV411P;
- else if (strncmp("422", tokstart, 3) == 0)
+ } else if (strncmp("422", tokstart, 3) == 0) {
pix_fmt = AV_PIX_FMT_YUV422P;
- else if (strncmp("444alpha", tokstart, 8) == 0 ) {
+ } else if (strncmp("444alpha", tokstart, 8) == 0 ) {
av_log(s, AV_LOG_ERROR, "Cannot handle 4:4:4:4 "
"YUV4MPEG stream.\n");
return -1;
- } else if (strncmp("444", tokstart, 3) == 0)
+ } else if (strncmp("444", tokstart, 3) == 0) {
pix_fmt = AV_PIX_FMT_YUV444P;
- else if (strncmp("mono", tokstart, 4) == 0) {
+ } else if (strncmp("mono16", tokstart, 6) == 0) {
+ pix_fmt = AV_PIX_FMT_GRAY16;
+ } else if (strncmp("mono", tokstart, 4) == 0) {
pix_fmt = AV_PIX_FMT_GRAY8;
} else {
av_log(s, AV_LOG_ERROR, "YUV4MPEG stream contains an unknown "
@@ -119,10 +151,9 @@ static int yuv4_read_header(AVFormatContext *s)
case 'm':
av_log(s, AV_LOG_ERROR, "YUV4MPEG stream contains mixed "
"interlaced and non-interlaced frames.\n");
- return -1;
default:
av_log(s, AV_LOG_ERROR, "YUV4MPEG has invalid header.\n");
- return -1;
+ return AVERROR(EINVAL);
}
break;
case 'F': // Frame rate
@@ -145,6 +176,36 @@ static int yuv4_read_header(AVFormatContext *s)
alt_pix_fmt = AV_PIX_FMT_YUV420P;
else if (strncmp("420PALDV", tokstart, 8) == 0)
alt_pix_fmt = AV_PIX_FMT_YUV420P;
+ else if (strncmp("420P9", tokstart, 5) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV420P9;
+ else if (strncmp("422P9", tokstart, 5) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV422P9;
+ else if (strncmp("444P9", tokstart, 5) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV444P9;
+ else if (strncmp("420P10", tokstart, 6) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV420P10;
+ else if (strncmp("422P10", tokstart, 6) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV422P10;
+ else if (strncmp("444P10", tokstart, 6) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV444P10;
+ else if (strncmp("420P12", tokstart, 6) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV420P12;
+ else if (strncmp("422P12", tokstart, 6) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV422P12;
+ else if (strncmp("444P12", tokstart, 6) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV444P12;
+ else if (strncmp("420P14", tokstart, 6) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV420P14;
+ else if (strncmp("422P14", tokstart, 6) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV422P14;
+ else if (strncmp("444P14", tokstart, 6) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV444P14;
+ else if (strncmp("420P16", tokstart, 6) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV420P16;
+ else if (strncmp("422P16", tokstart, 6) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV422P16;
+ else if (strncmp("444P16", tokstart, 6) == 0)
+ alt_pix_fmt = AV_PIX_FMT_YUV444P16;
else if (strncmp("411", tokstart, 3) == 0)
alt_pix_fmt = AV_PIX_FMT_YUV411P;
else if (strncmp("422", tokstart, 3) == 0)
diff --git a/libavformat/yuv4mpegenc.c b/libavformat/yuv4mpegenc.c
index 2caa364908..cc954fcc63 100644
--- a/libavformat/yuv4mpegenc.c
+++ b/libavformat/yuv4mpegenc.c
@@ -2,20 +2,20 @@
* YUV4MPEG muxer
* Copyright (c) 2001, 2002, 2003 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -49,7 +49,9 @@ static int yuv4_generate_header(AVFormatContext *s, char* buf)
aspectd = 0; // 0:0 means unknown
switch (st->codec->field_order) {
+ case AV_FIELD_TB:
case AV_FIELD_TT: inter = 't'; break;
+ case AV_FIELD_BT:
case AV_FIELD_BB: inter = 'b'; break;
default: inter = 'p'; break;
}
@@ -58,6 +60,9 @@ static int yuv4_generate_header(AVFormatContext *s, char* buf)
case AV_PIX_FMT_GRAY8:
colorspace = " Cmono";
break;
+ case AV_PIX_FMT_GRAY16:
+ colorspace = " Cmono16";
+ break;
case AV_PIX_FMT_YUV411P:
colorspace = " C411 XYSCSS=411";
break;
@@ -74,6 +79,51 @@ static int yuv4_generate_header(AVFormatContext *s, char* buf)
case AV_PIX_FMT_YUV444P:
colorspace = " C444 XYSCSS=444";
break;
+ case AV_PIX_FMT_YUV420P9:
+ colorspace = " C420p9 XYSCSS=420P9";
+ break;
+ case AV_PIX_FMT_YUV422P9:
+ colorspace = " C422p9 XYSCSS=422P9";
+ break;
+ case AV_PIX_FMT_YUV444P9:
+ colorspace = " C444p9 XYSCSS=444P9";
+ break;
+ case AV_PIX_FMT_YUV420P10:
+ colorspace = " C420p10 XYSCSS=420P10";
+ break;
+ case AV_PIX_FMT_YUV422P10:
+ colorspace = " C422p10 XYSCSS=422P10";
+ break;
+ case AV_PIX_FMT_YUV444P10:
+ colorspace = " C444p10 XYSCSS=444P10";
+ break;
+ case AV_PIX_FMT_YUV420P12:
+ colorspace = " C420p12 XYSCSS=420P12";
+ break;
+ case AV_PIX_FMT_YUV422P12:
+ colorspace = " C422p12 XYSCSS=422P12";
+ break;
+ case AV_PIX_FMT_YUV444P12:
+ colorspace = " C444p12 XYSCSS=444P12";
+ break;
+ case AV_PIX_FMT_YUV420P14:
+ colorspace = " C420p14 XYSCSS=420P14";
+ break;
+ case AV_PIX_FMT_YUV422P14:
+ colorspace = " C422p14 XYSCSS=422P14";
+ break;
+ case AV_PIX_FMT_YUV444P14:
+ colorspace = " C444p14 XYSCSS=444P14";
+ break;
+ case AV_PIX_FMT_YUV420P16:
+ colorspace = " C420p16 XYSCSS=420P16";
+ break;
+ case AV_PIX_FMT_YUV422P16:
+ colorspace = " C422p16 XYSCSS=422P16";
+ break;
+ case AV_PIX_FMT_YUV444P16:
+ colorspace = " C444p16 XYSCSS=444P16";
+ break;
}
/* construct stream header, if this is the first frame */
@@ -88,7 +138,7 @@ static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt)
{
AVStream *st = s->streams[pkt->stream_index];
AVIOContext *pb = s->pb;
- AVPicture *picture;
+ AVPicture *picture, picture_tmp;
int* first_pkt = s->priv_data;
int width, height, h_chroma_shift, v_chroma_shift;
int i;
@@ -96,7 +146,8 @@ static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt)
char buf1[20];
uint8_t *ptr, *ptr1, *ptr2;
- picture = (AVPicture *)pkt->data;
+ memcpy(&picture_tmp, pkt->data, sizeof(AVPicture));
+ picture = &picture_tmp;
/* for the first packet we have to output the header as well */
if (*first_pkt) {
@@ -119,18 +170,50 @@ static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt)
height = st->codec->height;
ptr = picture->data[0];
+
+ switch (st->codec->pix_fmt) {
+ case AV_PIX_FMT_GRAY8:
+ case AV_PIX_FMT_YUV411P:
+ case AV_PIX_FMT_YUV420P:
+ case AV_PIX_FMT_YUV422P:
+ case AV_PIX_FMT_YUV444P:
+ break;
+ case AV_PIX_FMT_GRAY16:
+ case AV_PIX_FMT_YUV420P9:
+ case AV_PIX_FMT_YUV422P9:
+ case AV_PIX_FMT_YUV444P9:
+ case AV_PIX_FMT_YUV420P10:
+ case AV_PIX_FMT_YUV422P10:
+ case AV_PIX_FMT_YUV444P10:
+ case AV_PIX_FMT_YUV420P12:
+ case AV_PIX_FMT_YUV422P12:
+ case AV_PIX_FMT_YUV444P12:
+ case AV_PIX_FMT_YUV420P14:
+ case AV_PIX_FMT_YUV422P14:
+ case AV_PIX_FMT_YUV444P14:
+ case AV_PIX_FMT_YUV420P16:
+ case AV_PIX_FMT_YUV422P16:
+ case AV_PIX_FMT_YUV444P16:
+ width *= 2;
+ break;
+ default:
+ av_log(s, AV_LOG_ERROR, "The pixel format '%s' is not supported.\n",
+ av_get_pix_fmt_name(st->codec->pix_fmt));
+ return AVERROR(EINVAL);
+ }
+
for (i = 0; i < height; i++) {
avio_write(pb, ptr, width);
ptr += picture->linesize[0];
}
- if (st->codec->pix_fmt != AV_PIX_FMT_GRAY8) {
+ if (st->codec->pix_fmt != AV_PIX_FMT_GRAY8 &&
+ st->codec->pix_fmt != AV_PIX_FMT_GRAY16) {
// Adjust for smaller Cb and Cr planes
av_pix_fmt_get_chroma_sub_sample(st->codec->pix_fmt, &h_chroma_shift,
&v_chroma_shift);
- // Shift right, rounding up
- width = -(-width >> h_chroma_shift);
- height = -(-height >> v_chroma_shift);
+ width = FF_CEIL_RSHIFT(width, h_chroma_shift);
+ height = FF_CEIL_RSHIFT(height, v_chroma_shift);
ptr1 = picture->data[1];
ptr2 = picture->data[2];
@@ -143,6 +226,7 @@ static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt)
ptr2 += picture->linesize[2];
}
}
+
return 0;
}
@@ -158,15 +242,50 @@ static int yuv4_write_header(AVFormatContext *s)
return AVERROR_INVALIDDATA;
}
- if (s->streams[0]->codec->pix_fmt == AV_PIX_FMT_YUV411P) {
- av_log(s, AV_LOG_ERROR, "Warning: generating rarely used 4:1:1 YUV "
+ switch (s->streams[0]->codec->pix_fmt) {
+ case AV_PIX_FMT_YUV411P:
+ av_log(s, AV_LOG_WARNING, "Warning: generating rarely used 4:1:1 YUV "
"stream, some mjpegtools might not work.\n");
- } else if ((s->streams[0]->codec->pix_fmt != AV_PIX_FMT_YUV420P) &&
- (s->streams[0]->codec->pix_fmt != AV_PIX_FMT_YUV422P) &&
- (s->streams[0]->codec->pix_fmt != AV_PIX_FMT_GRAY8) &&
- (s->streams[0]->codec->pix_fmt != AV_PIX_FMT_YUV444P)) {
- av_log(s, AV_LOG_ERROR, "ERROR: yuv4mpeg only handles yuv444p, "
- "yuv422p, yuv420p, yuv411p and gray pixel formats. "
+ break;
+ case AV_PIX_FMT_GRAY8:
+ case AV_PIX_FMT_GRAY16:
+ case AV_PIX_FMT_YUV420P:
+ case AV_PIX_FMT_YUV422P:
+ case AV_PIX_FMT_YUV444P:
+ break;
+ case AV_PIX_FMT_YUV420P9:
+ case AV_PIX_FMT_YUV422P9:
+ case AV_PIX_FMT_YUV444P9:
+ case AV_PIX_FMT_YUV420P10:
+ case AV_PIX_FMT_YUV422P10:
+ case AV_PIX_FMT_YUV444P10:
+ case AV_PIX_FMT_YUV420P12:
+ case AV_PIX_FMT_YUV422P12:
+ case AV_PIX_FMT_YUV444P12:
+ case AV_PIX_FMT_YUV420P14:
+ case AV_PIX_FMT_YUV422P14:
+ case AV_PIX_FMT_YUV444P14:
+ case AV_PIX_FMT_YUV420P16:
+ case AV_PIX_FMT_YUV422P16:
+ case AV_PIX_FMT_YUV444P16:
+ if (s->strict_std_compliance >= FF_COMPLIANCE_NORMAL) {
+ av_log(s, AV_LOG_ERROR, "'%s' is not a official yuv4mpegpipe pixel format. "
+ "Use '-strict -1' to encode to this pixel format.\n",
+ av_get_pix_fmt_name(s->streams[0]->codec->pix_fmt));
+ return AVERROR(EINVAL);
+ }
+ av_log(s, AV_LOG_WARNING, "Warning: generating non standard YUV stream. "
+ "Mjpegtools will not work.\n");
+ break;
+ default:
+ av_log(s, AV_LOG_ERROR, "ERROR: yuv4mpeg can only handle "
+ "yuv444p, yuv422p, yuv420p, yuv411p and gray8 pixel formats. "
+ "And using 'strict -1' also yuv444p9, yuv422p9, yuv420p9, "
+ "yuv444p10, yuv422p10, yuv420p10, "
+ "yuv444p12, yuv422p12, yuv420p12, "
+ "yuv444p14, yuv422p14, yuv420p14, "
+ "yuv444p16, yuv422p16, yuv420p16 "
+ "and gray16 pixel formats. "
"Use -pix_fmt to select one.\n");
return AVERROR(EIO);
}
@@ -178,7 +297,6 @@ static int yuv4_write_header(AVFormatContext *s)
AVOutputFormat ff_yuv4mpegpipe_muxer = {
.name = "yuv4mpegpipe",
.long_name = NULL_IF_CONFIG_SMALL("YUV4MPEG pipe"),
- .mime_type = "",
.extensions = "y4m",
.priv_data_size = sizeof(int),
.audio_codec = AV_CODEC_ID_NONE,