summaryrefslogtreecommitdiffstats
path: root/libavformat
diff options
context:
space:
mode:
Diffstat (limited to 'libavformat')
-rw-r--r--libavformat/3dostr.c168
-rw-r--r--libavformat/4xm.c50
-rw-r--r--libavformat/Makefile271
-rw-r--r--libavformat/a64.c9
-rw-r--r--libavformat/aacdec.c72
-rw-r--r--libavformat/aadec.c310
-rw-r--r--libavformat/ac3dec.c77
-rw-r--r--libavformat/acm.c75
-rw-r--r--libavformat/act.c207
-rw-r--r--libavformat/adp.c98
-rw-r--r--libavformat/ads.c89
-rw-r--r--libavformat/adtsenc.c62
-rw-r--r--libavformat/adxdec.c41
-rw-r--r--libavformat/aea.c29
-rw-r--r--libavformat/afc.c79
-rw-r--r--libavformat/aiff.h16
-rw-r--r--libavformat/aiffdec.c152
-rw-r--r--libavformat/aiffenc.c200
-rw-r--r--libavformat/aixdec.c140
-rw-r--r--libavformat/allformats.c174
-rw-r--r--libavformat/amr.c30
-rw-r--r--libavformat/anm.c10
-rw-r--r--libavformat/apc.c21
-rw-r--r--libavformat/ape.c64
-rw-r--r--libavformat/apetag.c100
-rw-r--r--libavformat/apetag.h13
-rw-r--r--libavformat/apngdec.c443
-rw-r--r--libavformat/apngenc.c305
-rw-r--r--libavformat/aqtitledec.c148
-rw-r--r--libavformat/asf.c21
-rw-r--r--libavformat/asf.h21
-rw-r--r--libavformat/asfcrypt.c8
-rw-r--r--libavformat/asfcrypt.h8
-rw-r--r--libavformat/asfdec_f.c1705
-rw-r--r--libavformat/asfdec_o.c (renamed from libavformat/asfdec.c)34
-rw-r--r--libavformat/asfenc.c460
-rw-r--r--libavformat/assdec.c264
-rw-r--r--libavformat/assenc.c212
-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/async.c699
-rw-r--r--libavformat/au.c229
-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.h1088
-rw-r--r--libavformat/avformatres.rc55
-rw-r--r--libavformat/avi.h15
-rw-r--r--libavformat/avidec.c633
-rw-r--r--libavformat/avienc.c530
-rw-r--r--libavformat/avio.c380
-rw-r--r--libavformat/avio.h370
-rw-r--r--libavformat/avio_internal.h39
-rw-r--r--libavformat/aviobuf.c554
-rw-r--r--libavformat/avisynth.c68
-rw-r--r--libavformat/avlanguage.c19
-rw-r--r--libavformat/avlanguage.h15
-rw-r--r--libavformat/avr.c99
-rw-r--r--libavformat/avs.c13
-rw-r--r--libavformat/bethsoftvid.c17
-rw-r--r--libavformat/bfi.c26
-rw-r--r--libavformat/bink.c85
-rw-r--r--libavformat/bintext.c388
-rw-r--r--libavformat/bit.c168
-rw-r--r--libavformat/bluray.c235
-rw-r--r--libavformat/bmv.c8
-rw-r--r--libavformat/boadec.c85
-rw-r--r--libavformat/brstm.c483
-rw-r--r--libavformat/c93.c10
-rw-r--r--libavformat/cache.c330
-rw-r--r--libavformat/caf.c70
-rw-r--r--libavformat/caf.h8
-rw-r--r--libavformat/cafdec.c93
-rw-r--r--libavformat/cafenc.c280
-rw-r--r--libavformat/cavsvideodec.c19
-rw-r--r--libavformat/cdg.c22
-rw-r--r--libavformat/cdxl.c75
-rw-r--r--libavformat/chromaprint.c190
-rw-r--r--libavformat/cinedec.c329
-rw-r--r--libavformat/concat.c33
-rw-r--r--libavformat/concatdec.c780
-rw-r--r--libavformat/crcenc.c9
-rw-r--r--libavformat/crypto.c139
-rw-r--r--libavformat/cutils.c29
-rw-r--r--libavformat/dash.c157
-rw-r--r--libavformat/dash.h39
-rw-r--r--libavformat/dashdec.c1848
-rw-r--r--libavformat/dashenc.c298
-rw-r--r--libavformat/data_uri.c118
-rw-r--r--libavformat/dauddec.c12
-rw-r--r--libavformat/daudenc.c8
-rw-r--r--libavformat/dcstr.c87
-rw-r--r--libavformat/dfa.c26
-rw-r--r--libavformat/diracdec.c23
-rw-r--r--libavformat/dnxhddec.c15
-rw-r--r--libavformat/dsfdec.c165
-rw-r--r--libavformat/dsicin.c13
-rw-r--r--libavformat/dss.c90
-rw-r--r--libavformat/dtsdec.c107
-rw-r--r--libavformat/dtshddec.c172
-rw-r--r--libavformat/dump.c146
-rw-r--r--libavformat/dv.c229
-rw-r--r--libavformat/dv.h10
-rw-r--r--libavformat/dvbsub.c70
-rw-r--r--libavformat/dvbtxt.c50
-rw-r--r--libavformat/dvenc.c126
-rw-r--r--libavformat/dxa.c44
-rw-r--r--libavformat/eacdata.c17
-rw-r--r--libavformat/electronicarts.c257
-rw-r--r--libavformat/epafdec.c105
-rw-r--r--libavformat/ffm.h62
-rw-r--r--libavformat/ffmdec.c878
-rw-r--r--libavformat/ffmenc.c362
-rw-r--r--libavformat/ffmeta.h8
-rw-r--r--libavformat/ffmetadec.c26
-rw-r--r--libavformat/ffmetaenc.c8
-rw-r--r--libavformat/fifo.c667
-rw-r--r--libavformat/file.c223
-rw-r--r--libavformat/filmstripdec.c19
-rw-r--r--libavformat/filmstripenc.c10
-rw-r--r--libavformat/fitsdec.c231
-rw-r--r--libavformat/fitsenc.c183
-rw-r--r--libavformat/flac_picture.c33
-rw-r--r--libavformat/flac_picture.h11
-rw-r--r--libavformat/flacdec.c177
-rw-r--r--libavformat/flacenc.c45
-rw-r--r--libavformat/flacenc.h8
-rw-r--r--libavformat/flacenc_header.c8
-rw-r--r--libavformat/flic.c29
-rw-r--r--libavformat/flv.h32
-rw-r--r--libavformat/flvdec.c654
-rw-r--r--libavformat/flvenc.c726
-rw-r--r--libavformat/format.c234
-rw-r--r--libavformat/framecrcenc.c53
-rw-r--r--libavformat/framehash.c27
-rw-r--r--libavformat/frmdec.c111
-rw-r--r--libavformat/fsb.c210
-rw-r--r--libavformat/ftp.c1127
-rw-r--r--libavformat/g722.c11
-rw-r--r--libavformat/g723_1.c10
-rw-r--r--libavformat/g726.c105
-rw-r--r--libavformat/g729dec.c10
-rw-r--r--libavformat/gdv.c201
-rw-r--r--libavformat/genh.c195
-rw-r--r--libavformat/gif.c419
-rw-r--r--libavformat/gifdec.c346
-rw-r--r--libavformat/golomb_tab.c1
-rw-r--r--libavformat/gopher.c12
-rw-r--r--libavformat/gsmdec.c31
-rw-r--r--libavformat/gxf.c87
-rw-r--r--libavformat/gxf.h8
-rw-r--r--libavformat/gxfenc.c190
-rw-r--r--libavformat/h261dec.c48
-rw-r--r--libavformat/h263dec.c31
-rw-r--r--libavformat/h264dec.c66
-rw-r--r--libavformat/hashenc.c262
-rw-r--r--libavformat/hdsenc.c38
-rw-r--r--libavformat/hevc.c38
-rw-r--r--libavformat/hevc.h10
-rw-r--r--libavformat/hevcdec.c8
-rw-r--r--libavformat/hls.c1971
-rw-r--r--libavformat/hlsenc.c1792
-rw-r--r--libavformat/hlsproto.c25
-rw-r--r--libavformat/hnm.c12
-rw-r--r--libavformat/http.c890
-rw-r--r--libavformat/http.h12
-rw-r--r--libavformat/httpauth.c16
-rw-r--r--libavformat/httpauth.h10
-rw-r--r--libavformat/icecast.c75
-rw-r--r--libavformat/icodec.c222
-rw-r--r--libavformat/icoenc.c203
-rw-r--r--libavformat/id3v1.c26
-rw-r--r--libavformat/id3v1.h8
-rw-r--r--libavformat/id3v2.c589
-rw-r--r--libavformat/id3v2.h45
-rw-r--r--libavformat/id3v2enc.c158
-rw-r--r--libavformat/idcin.c54
-rw-r--r--libavformat/idroqdec.c20
-rw-r--r--libavformat/idroqenc.c37
-rw-r--r--libavformat/iff.c736
-rw-r--r--libavformat/ilbc.c8
-rw-r--r--libavformat/img2.c29
-rw-r--r--libavformat/img2.h78
-rw-r--r--libavformat/img2_alias_pix.c73
-rw-r--r--libavformat/img2_brender_pix.c57
-rw-r--r--libavformat/img2dec.c826
-rw-r--r--libavformat/img2enc.c209
-rw-r--r--libavformat/ingenientdec.c34
-rw-r--r--libavformat/internal.h276
-rw-r--r--libavformat/ipmovie.c288
-rw-r--r--libavformat/ircam.c47
-rw-r--r--libavformat/ircam.h30
-rw-r--r--libavformat/ircamdec.c118
-rw-r--r--libavformat/ircamenc.c62
-rw-r--r--libavformat/isom.c195
-rw-r--r--libavformat/isom.h112
-rw-r--r--libavformat/iss.c28
-rw-r--r--libavformat/iv8.c8
-rw-r--r--libavformat/ivfdec.c8
-rw-r--r--libavformat/ivfenc.c66
-rw-r--r--libavformat/jacosubdec.c274
-rw-r--r--libavformat/jacosubenc.c42
-rw-r--r--libavformat/jvdec.c22
-rw-r--r--libavformat/latmenc.c122
-rw-r--r--libavformat/libavformat.v13
-rw-r--r--libavformat/libgme.c201
-rw-r--r--libavformat/libmodplug.c382
-rw-r--r--libavformat/libopenmpt.c212
-rw-r--r--libavformat/librtmp.c28
-rw-r--r--libavformat/libsmbclient.c383
-rw-r--r--libavformat/libssh.c507
-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.c153
-rw-r--r--libavformat/lvfdec.c152
-rw-r--r--libavformat/lxfdec.c22
-rw-r--r--libavformat/m4vdec.c17
-rw-r--r--libavformat/matroska.c59
-rw-r--r--libavformat/matroska.h105
-rw-r--r--libavformat/matroskadec.c1878
-rw-r--r--libavformat/matroskaenc.c1770
-rw-r--r--libavformat/md5enc.c133
-rw-r--r--libavformat/md5proto.c14
-rw-r--r--libavformat/metadata.c10
-rw-r--r--libavformat/metadata.h8
-rw-r--r--libavformat/mgsts.c106
-rw-r--r--libavformat/microdvddec.c204
-rw-r--r--libavformat/microdvdenc.c67
-rw-r--r--libavformat/mj2kdec.c54
-rw-r--r--libavformat/mkvtimestamp_v2.c50
-rw-r--r--libavformat/mlpdec.c88
-rw-r--r--libavformat/mlvdec.c479
-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.c90
-rw-r--r--libavformat/mmst.c38
-rw-r--r--libavformat/mov.c3866
-rw-r--r--libavformat/mov_chan.c17
-rw-r--r--libavformat/mov_chan.h8
-rw-r--r--libavformat/movenc.c2911
-rw-r--r--libavformat/movenc.h62
-rw-r--r--libavformat/movenccenc.c415
-rw-r--r--libavformat/movenccenc.h86
-rw-r--r--libavformat/movenchint.c23
-rw-r--r--libavformat/mp3dec.c398
-rw-r--r--libavformat/mp3enc.c181
-rw-r--r--libavformat/mpc.c19
-rw-r--r--libavformat/mpc8.c75
-rw-r--r--libavformat/mpeg.c654
-rw-r--r--libavformat/mpeg.h11
-rw-r--r--libavformat/mpegenc.c152
-rw-r--r--libavformat/mpegts.c1162
-rw-r--r--libavformat/mpegts.h27
-rw-r--r--libavformat/mpegtsenc.c1050
-rw-r--r--libavformat/mpegvideodec.c61
-rw-r--r--libavformat/mpjpeg.c41
-rw-r--r--libavformat/mpjpegdec.c297
-rw-r--r--libavformat/mpl2dec.c154
-rw-r--r--libavformat/mpsubdec.c144
-rw-r--r--libavformat/msf.c110
-rw-r--r--libavformat/msnwc_tcp.c14
-rw-r--r--libavformat/mtaf.c81
-rw-r--r--libavformat/mtv.c58
-rw-r--r--libavformat/musx.c177
-rw-r--r--libavformat/mux.c904
-rw-r--r--libavformat/mvdec.c42
-rw-r--r--libavformat/mvi.c12
-rw-r--r--libavformat/mxf.c31
-rw-r--r--libavformat/mxf.h46
-rw-r--r--libavformat/mxfdec.c1618
-rw-r--r--libavformat/mxfenc.c1246
-rw-r--r--libavformat/mxg.c26
-rw-r--r--libavformat/ncdec.c10
-rw-r--r--libavformat/network.c85
-rw-r--r--libavformat/network.h53
-rw-r--r--libavformat/nistspheredec.c138
-rw-r--r--libavformat/nsvdec.c97
-rw-r--r--libavformat/nullenc.c10
-rw-r--r--libavformat/nut.c236
-rw-r--r--libavformat/nut.h21
-rw-r--r--libavformat/nutdec.c417
-rw-r--r--libavformat/nutenc.c508
-rw-r--r--libavformat/nuv.c109
-rw-r--r--libavformat/oggdec.c448
-rw-r--r--libavformat/oggdec.h14
-rw-r--r--libavformat/oggenc.c260
-rw-r--r--libavformat/oggparsecelt.c34
-rw-r--r--libavformat/oggparsedaala.c257
-rw-r--r--libavformat/oggparsedirac.c14
-rw-r--r--libavformat/oggparseflac.c77
-rw-r--r--libavformat/oggparseogm.c33
-rw-r--r--libavformat/oggparseopus.c104
-rw-r--r--libavformat/oggparseskeleton.c25
-rw-r--r--libavformat/oggparsespeex.c21
-rw-r--r--libavformat/oggparsetheora.c81
-rw-r--r--libavformat/oggparsevorbis.c122
-rw-r--r--libavformat/oggparsevp8.c14
-rw-r--r--libavformat/oma.c18
-rw-r--r--libavformat/oma.h10
-rw-r--r--libavformat/omadec.c181
-rw-r--r--libavformat/omaenc.c15
-rw-r--r--libavformat/options.c61
-rw-r--r--libavformat/options_table.h58
-rw-r--r--libavformat/os_support.c15
-rw-r--r--libavformat/os_support.h69
-rw-r--r--libavformat/paf.c21
-rw-r--r--libavformat/pcm.c26
-rw-r--r--libavformat/pcm.h9
-rw-r--r--libavformat/pcmdec.c104
-rw-r--r--libavformat/pcmenc.c8
-rw-r--r--libavformat/pjsdec.c138
-rw-r--r--libavformat/pmpdec.c84
-rw-r--r--libavformat/prompeg.c481
-rw-r--r--libavformat/protocols.c23
-rw-r--r--libavformat/psxstr.c65
-rw-r--r--libavformat/pva.c44
-rw-r--r--libavformat/pvfdec.c77
-rw-r--r--libavformat/qcp.c27
-rw-r--r--libavformat/qtpalette.c116
-rw-r--r--libavformat/qtpalette.h61
-rw-r--r--libavformat/r3d.c19
-rw-r--r--libavformat/rawdec.c209
-rw-r--r--libavformat/rawdec.h42
-rw-r--r--libavformat/rawenc.c174
-rw-r--r--libavformat/rawenc.h8
-rw-r--r--libavformat/rawutils.c67
-rw-r--r--libavformat/rawvideodec.c59
-rw-r--r--libavformat/rdt.c60
-rw-r--r--libavformat/rdt.h8
-rw-r--r--libavformat/realtextdec.c156
-rw-r--r--libavformat/redspark.c162
-rw-r--r--libavformat/replaygain.c8
-rw-r--r--libavformat/replaygain.h8
-rw-r--r--libavformat/riff.c142
-rw-r--r--libavformat/riff.h51
-rw-r--r--libavformat/riffdec.c144
-rw-r--r--libavformat/riffenc.c141
-rw-r--r--libavformat/rl2.c42
-rw-r--r--libavformat/rm.c9
-rw-r--r--libavformat/rm.h10
-rw-r--r--libavformat/rmdec.c607
-rw-r--r--libavformat/rmenc.c62
-rw-r--r--libavformat/rmsipr.c8
-rw-r--r--libavformat/rmsipr.h8
-rw-r--r--libavformat/rpl.c43
-rw-r--r--libavformat/rsd.c221
-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.c13
-rw-r--r--libavformat/rtmpcrypt.h8
-rw-r--r--libavformat/rtmpdh.c35
-rw-r--r--libavformat/rtmpdh.h13
-rw-r--r--libavformat/rtmphttp.c21
-rw-r--r--libavformat/rtmppkt.c133
-rw-r--r--libavformat/rtmppkt.h8
-rw-r--r--libavformat/rtmpproto.c174
-rw-r--r--libavformat/rtp.c11
-rw-r--r--libavformat/rtp.h8
-rw-r--r--libavformat/rtpdec.c40
-rw-r--r--libavformat/rtpdec.h12
-rw-r--r--libavformat/rtpdec_ac3.c8
-rw-r--r--libavformat/rtpdec_amr.c8
-rw-r--r--libavformat/rtpdec_asf.c33
-rw-r--r--libavformat/rtpdec_dv.c8
-rw-r--r--libavformat/rtpdec_formats.h16
-rw-r--r--libavformat/rtpdec_g726.c16
-rw-r--r--libavformat/rtpdec_h261.c27
-rw-r--r--libavformat/rtpdec_h263.c8
-rw-r--r--libavformat/rtpdec_h263_rfc2190.c27
-rw-r--r--libavformat/rtpdec_h264.c20
-rw-r--r--libavformat/rtpdec_hevc.c40
-rw-r--r--libavformat/rtpdec_ilbc.c8
-rw-r--r--libavformat/rtpdec_jpeg.c31
-rw-r--r--libavformat/rtpdec_latm.c39
-rw-r--r--libavformat/rtpdec_mpa_robust.c10
-rw-r--r--libavformat/rtpdec_mpeg12.c8
-rw-r--r--libavformat/rtpdec_mpeg4.c37
-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.c41
-rw-r--r--libavformat/rtpdec_rfc4175.c236
-rw-r--r--libavformat/rtpdec_svq3.c13
-rw-r--r--libavformat/rtpdec_vc2hq.c225
-rw-r--r--libavformat/rtpdec_vp8.c8
-rw-r--r--libavformat/rtpdec_vp9.c8
-rw-r--r--libavformat/rtpdec_xiph.c28
-rw-r--r--libavformat/rtpenc.c58
-rw-r--r--libavformat/rtpenc.h14
-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.c52
-rw-r--r--libavformat/rtpenc_h264_hevc.c8
-rw-r--r--libavformat/rtpenc_jpeg.c109
-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_vc2hq.c134
-rw-r--r--libavformat/rtpenc_vp8.c8
-rw-r--r--libavformat/rtpenc_vp9.c54
-rw-r--r--libavformat/rtpenc_xiph.c11
-rw-r--r--libavformat/rtpproto.c133
-rw-r--r--libavformat/rtpproto.h8
-rw-r--r--libavformat/rtsp.c238
-rw-r--r--libavformat/rtsp.h23
-rw-r--r--libavformat/rtspcodes.h125
-rw-r--r--libavformat/rtspdec.c62
-rw-r--r--libavformat/rtspenc.c17
-rw-r--r--libavformat/s337m.c206
-rw-r--r--libavformat/samidec.c146
-rw-r--r--libavformat/sapdec.c28
-rw-r--r--libavformat/sapenc.c38
-rw-r--r--libavformat/sauce.c10
-rw-r--r--libavformat/sauce.h8
-rw-r--r--libavformat/sbgdec.c1514
-rw-r--r--libavformat/sccdec.c180
-rw-r--r--libavformat/sccenc.c123
-rw-r--r--libavformat/sctp.c18
-rw-r--r--libavformat/sdp.c52
-rw-r--r--libavformat/sdr2.c121
-rw-r--r--libavformat/sdsdec.c165
-rw-r--r--libavformat/sdxdec.c90
-rw-r--r--libavformat/segafilm.c38
-rw-r--r--libavformat/segment.c1064
-rw-r--r--libavformat/shortendec.c71
-rw-r--r--libavformat/sierravmd.c111
-rw-r--r--libavformat/siff.c30
-rw-r--r--libavformat/smacker.c72
-rw-r--r--libavformat/smjpeg.c8
-rw-r--r--libavformat/smjpeg.h8
-rw-r--r--libavformat/smjpegdec.c14
-rw-r--r--libavformat/smjpegenc.c11
-rw-r--r--libavformat/smoothstreamingenc.c70
-rw-r--r--libavformat/smush.c18
-rw-r--r--libavformat/sol.c15
-rw-r--r--libavformat/sox.h8
-rw-r--r--libavformat/soxdec.c40
-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.c25
-rw-r--r--libavformat/srtdec.c240
-rw-r--r--libavformat/srtenc.c115
-rw-r--r--libavformat/srtp.c8
-rw-r--r--libavformat/srtp.h8
-rw-r--r--libavformat/srtpproto.c12
-rw-r--r--libavformat/stldec.c141
-rw-r--r--libavformat/subfile.c149
-rw-r--r--libavformat/subtitles.c425
-rw-r--r--libavformat/subtitles.h212
-rw-r--r--libavformat/subviewer1dec.c124
-rw-r--r--libavformat/subviewerdec.c194
-rw-r--r--libavformat/supdec.c109
-rw-r--r--libavformat/supenc.c96
-rw-r--r--libavformat/svag.c78
-rw-r--r--libavformat/swf.c8
-rw-r--r--libavformat/swf.h89
-rw-r--r--libavformat/swfdec.c319
-rw-r--r--libavformat/swfenc.c31
-rw-r--r--libavformat/takdec.c62
-rw-r--r--libavformat/tcp.c122
-rw-r--r--libavformat/tedcaptionsdec.c366
-rw-r--r--libavformat/tee.c618
-rw-r--r--libavformat/tee_common.c68
-rw-r--r--libavformat/tee_common.h30
-rw-r--r--libavformat/teeproto.c147
-rw-r--r--libavformat/tests/.gitignore2
-rw-r--r--libavformat/tests/fifo_muxer.c447
-rw-r--r--libavformat/tests/movenc.c78
-rw-r--r--libavformat/tests/noproxy.c8
-rw-r--r--libavformat/tests/rtmpdh.c8
-rw-r--r--libavformat/tests/seek.c53
-rw-r--r--libavformat/tests/srtp.c8
-rw-r--r--libavformat/tests/url.c8
-rw-r--r--libavformat/thp.c54
-rw-r--r--libavformat/tiertexseq.c10
-rw-r--r--libavformat/tls.c52
-rw-r--r--libavformat/tls.h20
-rw-r--r--libavformat/tls_gnutls.c44
-rw-r--r--libavformat/tls_openssl.c165
-rw-r--r--libavformat/tls_schannel.c608
-rw-r--r--libavformat/tls_securetransport.c406
-rw-r--r--libavformat/tmv.c10
-rw-r--r--libavformat/tta.c96
-rw-r--r--libavformat/ttaenc.c149
-rw-r--r--libavformat/tty.c43
-rw-r--r--libavformat/txd.c33
-rw-r--r--libavformat/udp.c547
-rw-r--r--libavformat/uncodedframecrcenc.c177
-rw-r--r--libavformat/unix.c13
-rw-r--r--libavformat/url.c26
-rw-r--r--libavformat/url.h89
-rw-r--r--libavformat/urldecode.c8
-rw-r--r--libavformat/urldecode.h8
-rw-r--r--libavformat/utils.c3266
-rw-r--r--libavformat/v210.c130
-rw-r--r--libavformat/vag.c83
-rw-r--r--libavformat/vc1dec.c81
-rw-r--r--libavformat/vc1test.c15
-rw-r--r--libavformat/vc1testenc.c11
-rw-r--r--libavformat/version.h49
-rw-r--r--libavformat/vivo.c313
-rw-r--r--libavformat/voc.c8
-rw-r--r--libavformat/voc.h9
-rw-r--r--libavformat/voc_packet.c41
-rw-r--r--libavformat/vocdec.c39
-rw-r--r--libavformat/vocenc.c22
-rw-r--r--libavformat/vorbiscomment.c19
-rw-r--r--libavformat/vorbiscomment.h10
-rw-r--r--libavformat/vpcc.c103
-rw-r--r--libavformat/vpcc.h47
-rw-r--r--libavformat/vpk.c117
-rw-r--r--libavformat/vplayerdec.c129
-rw-r--r--libavformat/vqf.c35
-rw-r--r--libavformat/w64.c50
-rw-r--r--libavformat/w64.h31
-rw-r--r--libavformat/wavdec.c551
-rw-r--r--libavformat/wavenc.c492
-rw-r--r--libavformat/wc3movie.c29
-rw-r--r--libavformat/webm_chunk.c280
-rw-r--r--libavformat/webmdashenc.c585
-rw-r--r--libavformat/webpenc.c218
-rw-r--r--libavformat/webvttdec.c224
-rw-r--r--libavformat/webvttenc.c106
-rw-r--r--libavformat/westwood_aud.c18
-rw-r--r--libavformat/westwood_vqa.c59
-rw-r--r--libavformat/wsddec.c173
-rw-r--r--libavformat/wtv.h60
-rw-r--r--libavformat/wtv_common.c84
-rw-r--r--libavformat/wtvdec.c (renamed from libavformat/wtv.c)375
-rw-r--r--libavformat/wtvenc.c846
-rw-r--r--libavformat/wv.c8
-rw-r--r--libavformat/wv.h8
-rw-r--r--libavformat/wvdec.c60
-rw-r--r--libavformat/wvedec.c62
-rw-r--r--libavformat/wvenc.c12
-rw-r--r--libavformat/xa.c13
-rw-r--r--libavformat/xmv.c289
-rw-r--r--libavformat/xvag.c113
-rw-r--r--libavformat/xwma.c38
-rw-r--r--libavformat/yop.c25
-rw-r--r--libavformat/yuv4mpeg.h9
-rw-r--r--libavformat/yuv4mpegdec.c133
-rw-r--r--libavformat/yuv4mpegenc.c163
560 files changed, 79727 insertions, 12953 deletions
diff --git a/libavformat/3dostr.c b/libavformat/3dostr.c
new file mode 100644
index 0000000..3668e5f
--- /dev/null
+++ b/libavformat/3dostr.c
@@ -0,0 +1,168 @@
+/*
+ * 3DO STR demuxer
+ * Copyright (c) 2015 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"
+
+static int threedostr_probe(AVProbeData *p)
+{
+ if (memcmp(p->buf, "CTRL", 4) &&
+ memcmp(p->buf, "SHDR", 4) &&
+ memcmp(p->buf, "SNDS", 4))
+ return 0;
+
+ return AVPROBE_SCORE_MAX / 3 * 2;
+}
+
+static int threedostr_read_header(AVFormatContext *s)
+{
+ unsigned chunk, codec = 0, size, ctrl_size = -1, found_shdr = 0;
+ AVStream *st;
+
+ while (!avio_feof(s->pb) && !found_shdr) {
+ chunk = avio_rl32(s->pb);
+ size = avio_rb32(s->pb);
+
+ if (size < 8)
+ return AVERROR_INVALIDDATA;
+ size -= 8;
+
+ switch (chunk) {
+ case MKTAG('C','T','R','L'):
+ ctrl_size = size;
+ break;
+ case MKTAG('S','N','D','S'):
+ if (size < 56)
+ return AVERROR_INVALIDDATA;
+ avio_skip(s->pb, 8);
+ if (avio_rl32(s->pb) != MKTAG('S','H','D','R'))
+ return AVERROR_INVALIDDATA;
+ avio_skip(s->pb, 24);
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->sample_rate = avio_rb32(s->pb);
+ st->codecpar->channels = avio_rb32(s->pb);
+ if (st->codecpar->channels <= 0)
+ return AVERROR_INVALIDDATA;
+ codec = avio_rl32(s->pb);
+ avio_skip(s->pb, 4);
+ if (ctrl_size == 20 || ctrl_size == 3 || ctrl_size == -1)
+ st->duration = (avio_rb32(s->pb) - 1) / st->codecpar->channels;
+ else
+ st->duration = avio_rb32(s->pb) * 16 / st->codecpar->channels;
+ size -= 56;
+ found_shdr = 1;
+ break;
+ case MKTAG('S','H','D','R'):
+ if (size > 0x78) {
+ avio_skip(s->pb, 0x74);
+ size -= 0x78;
+ if (avio_rl32(s->pb) == MKTAG('C','T','R','L') && size > 4) {
+ ctrl_size = avio_rb32(s->pb);
+ size -= 4;
+ }
+ }
+ break;
+ default:
+ av_log(s, AV_LOG_DEBUG, "skipping unknown chunk: %X\n", chunk);
+ break;
+ }
+
+ avio_skip(s->pb, size);
+ }
+
+ switch (codec) {
+ case MKTAG('S','D','X','2'):
+ st->codecpar->codec_id = AV_CODEC_ID_SDX2_DPCM;
+ st->codecpar->block_align = 1 * st->codecpar->channels;
+ break;
+ default:
+ avpriv_request_sample(s, "codec %X", codec);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ return 0;
+}
+
+static int threedostr_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ unsigned chunk, size, found_ssmp = 0;
+ AVStream *st = s->streams[0];
+ int64_t pos;
+ int ret = 0;
+
+ while (!found_ssmp) {
+ if (avio_feof(s->pb))
+ return AVERROR_EOF;
+
+ pos = avio_tell(s->pb);
+ chunk = avio_rl32(s->pb);
+ size = avio_rb32(s->pb);
+
+ if (!size)
+ continue;
+
+ if (size < 8)
+ return AVERROR_INVALIDDATA;
+ size -= 8;
+
+ switch (chunk) {
+ case MKTAG('S','N','D','S'):
+ if (size <= 16)
+ return AVERROR_INVALIDDATA;
+ avio_skip(s->pb, 8);
+ if (avio_rl32(s->pb) != MKTAG('S','S','M','P'))
+ return AVERROR_INVALIDDATA;
+ avio_skip(s->pb, 4);
+ size -= 16;
+ ret = av_get_packet(s->pb, pkt, size);
+ pkt->pos = pos;
+ pkt->stream_index = 0;
+ pkt->duration = size / st->codecpar->channels;
+ size = 0;
+ found_ssmp = 1;
+ break;
+ default:
+ av_log(s, AV_LOG_DEBUG, "skipping unknown chunk: %X\n", chunk);
+ break;
+ }
+
+ avio_skip(s->pb, size);
+ }
+
+ return ret;
+}
+
+AVInputFormat ff_threedostr_demuxer = {
+ .name = "3dostr",
+ .long_name = NULL_IF_CONFIG_SMALL("3DO STR"),
+ .read_probe = threedostr_probe,
+ .read_header = threedostr_read_header,
+ .read_packet = threedostr_read_packet,
+ .extensions = "str",
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/4xm.c b/libavformat/4xm.c
index 1597b3b..ead6d2b 100644
--- a/libavformat/4xm.c
+++ b/libavformat/4xm.c
@@ -2,20 +2,20 @@
* 4X Technologies .4xm File Demuxer (no muxer)
* Copyright (c) 2003 The FFmpeg project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -29,6 +29,7 @@
#include "libavutil/intreadwrite.h"
#include "libavutil/intfloat.h"
+#include "libavcodec/internal.h"
#include "avformat.h"
#include "internal.h"
@@ -77,7 +78,7 @@ typedef struct FourxmDemuxContext {
AudioTrack *tracks;
int64_t video_pts;
- float fps;
+ AVRational fps;
} FourxmDemuxContext;
static int fourxm_probe(AVProbeData *p)
@@ -104,7 +105,7 @@ static int parse_vtrk(AVFormatContext *s,
if (!st)
return AVERROR(ENOMEM);
- avpriv_set_pts_info(st, 60, 1, fourxm->fps);
+ avpriv_set_pts_info(st, 60, fourxm->fps.den, fourxm->fps.num);
fourxm->video_stream_index = st->index;
@@ -134,8 +135,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);
@@ -150,11 +154,24 @@ static int parse_strk(AVFormatContext *s,
fourxm->tracks[track].audio_pts = 0;
if (fourxm->tracks[track].channels <= 0 ||
+ fourxm->tracks[track].channels > FF_SANE_NB_CHANNELS ||
fourxm->tracks[track].sample_rate <= 0 ||
- fourxm->tracks[track].bits <= 0) {
+ fourxm->tracks[track].bits <= 0 ||
+ fourxm->tracks[track].bits > INT_MAX / FF_SANE_NB_CHANNELS) {
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;
+ }
+
+ if (fourxm->tracks[track].sample_rate > INT64_MAX / fourxm->tracks[track].bits / fourxm->tracks[track].channels) {
+ av_log(s, AV_LOG_ERROR, "Overflow during bit rate calculation %d * %d * %d\n",
+ fourxm->tracks[track].sample_rate, fourxm->tracks[track].bits, fourxm->tracks[track].channels);
+ return AVERROR_INVALIDDATA;
+ }
+
/* allocate a new AVStream */
st = avformat_new_stream(s, NULL);
if (!st)
@@ -170,7 +187,7 @@ static int parse_strk(AVFormatContext *s,
st->codecpar->channels = fourxm->tracks[track].channels;
st->codecpar->sample_rate = fourxm->tracks[track].sample_rate;
st->codecpar->bits_per_coded_sample = fourxm->tracks[track].bits;
- st->codecpar->bit_rate = st->codecpar->channels *
+ st->codecpar->bit_rate = (int64_t)st->codecpar->channels *
st->codecpar->sample_rate *
st->codecpar->bits_per_coded_sample;
st->codecpar->block_align = st->codecpar->channels *
@@ -198,7 +215,7 @@ static int fourxm_read_header(AVFormatContext *s)
fourxm->track_count = 0;
fourxm->tracks = NULL;
- fourxm->fps = 1.0;
+ fourxm->fps = (AVRational){1,1};
/* skip the first 3 32-bit numbers */
avio_skip(pb, 12);
@@ -222,13 +239,18 @@ 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;
}
- fourxm->fps = av_int2float(AV_RL32(&header[i + 12]));
+ fourxm->fps = av_d2q(av_int2float(AV_RL32(&header[i + 12])), 10000);
} else if (fourcc_tag == vtrk_TAG) {
if ((ret = parse_vtrk(s, fourxm, header + i, size,
header_size - i)) < 0)
@@ -280,7 +302,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 +331,10 @@ static int fourxm_read_packet(AVFormatContext *s,
if (ret < 0) {
av_packet_unref(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 f363955..d955a8b 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -1,5 +1,5 @@
NAME = avformat
-DESC = Libav container format library
+DESC = FFmpeg container format library
HEADERS = avformat.h \
avio.h \
@@ -13,11 +13,11 @@ OBJS = allformats.o \
format.o \
id3v1.o \
id3v2.o \
- log2_tab.o \
metadata.o \
mux.o \
options.o \
os_support.o \
+ qtpalette.o \
protocols.o \
riff.o \
sdp.o \
@@ -54,75 +54,119 @@ OBJS-$(CONFIG_RTPDEC) += rdt.o \
rtpdec_qcelp.o \
rtpdec_qdm2.o \
rtpdec_qt.o \
+ rtpdec_rfc4175.o \
rtpdec_svq3.o \
+ rtpdec_vc2hq.o \
rtpdec_vp8.o \
rtpdec_vp9.o \
rtpdec_xiph.o
OBJS-$(CONFIG_RTPENC_CHAIN) += rtpenc_chain.o rtp.o
+OBJS-$(CONFIG_SHARED) += log2_tab.o golomb_tab.o
OBJS-$(CONFIG_SRTP) += srtp.o
# muxers/demuxers
OBJS-$(CONFIG_A64_MUXER) += a64.o rawenc.o
-OBJS-$(CONFIG_AAC_DEMUXER) += aacdec.o rawdec.o
+OBJS-$(CONFIG_AA_DEMUXER) += aadec.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_ADTS_MUXER) += adtsenc.o
+OBJS-$(CONFIG_ACM_DEMUXER) += acm.o rawdec.o
+OBJS-$(CONFIG_ACT_DEMUXER) += act.o
+OBJS-$(CONFIG_ADF_DEMUXER) += bintext.o sauce.o
+OBJS-$(CONFIG_ADP_DEMUXER) += adp.o
+OBJS-$(CONFIG_ADS_DEMUXER) += ads.o
+OBJS-$(CONFIG_ADTS_MUXER) += adtsenc.o apetag.o img2.o \
+ id3v2enc.o
OBJS-$(CONFIG_ADX_DEMUXER) += adxdec.o
OBJS-$(CONFIG_ADX_MUXER) += rawenc.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 id3v2enc.o
+OBJS-$(CONFIG_AIX_DEMUXER) += aixdec.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_ASF_DEMUXER) += asfdec.o asf.o asfcrypt.o \
+OBJS-$(CONFIG_APNG_DEMUXER) += apngdec.o
+OBJS-$(CONFIG_APNG_MUXER) += apngenc.o
+OBJS-$(CONFIG_AQTITLE_DEMUXER) += aqtitledec.o subtitles.o
+OBJS-$(CONFIG_ASF_DEMUXER) += asfdec_f.o asf.o asfcrypt.o \
avlanguage.o
-OBJS-$(CONFIG_ASF_MUXER) += asfenc.o asf.o
-OBJS-$(CONFIG_ASS_DEMUXER) += assdec.o
+OBJS-$(CONFIG_ASF_O_DEMUXER) += asfdec_o.o asf.o asfcrypt.o \
+ avlanguage.o
+OBJS-$(CONFIG_ASF_MUXER) += asfenc.o asf.o avlanguage.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 rawutils.o
OBJS-$(CONFIG_AVM2_MUXER) += swfenc.o swf.o
-OBJS-$(CONFIG_AVS_DEMUXER) += avs.o voc_packet.o voc.o
+OBJS-$(CONFIG_AVR_DEMUXER) += avr.o pcm.o
+OBJS-$(CONFIG_AVS_DEMUXER) += avs.o voc_packet.o vocdec.o voc.o
OBJS-$(CONFIG_BETHSOFTVID_DEMUXER) += bethsoftvid.o
OBJS-$(CONFIG_BFI_DEMUXER) += bfi.o
OBJS-$(CONFIG_BINK_DEMUXER) += bink.o
+OBJS-$(CONFIG_BINTEXT_DEMUXER) += bintext.o sauce.o
+OBJS-$(CONFIG_BIT_DEMUXER) += bit.o
+OBJS-$(CONFIG_BIT_MUXER) += bit.o
OBJS-$(CONFIG_BMV_DEMUXER) += bmv.o
-OBJS-$(CONFIG_C93_DEMUXER) += c93.o voc_packet.o voc.o
+OBJS-$(CONFIG_BOA_DEMUXER) += boadec.o
+OBJS-$(CONFIG_BFSTM_DEMUXER) += brstm.o
+OBJS-$(CONFIG_BRSTM_DEMUXER) += brstm.o
+OBJS-$(CONFIG_C93_DEMUXER) += c93.o voc_packet.o vocdec.o voc.o
OBJS-$(CONFIG_CAF_DEMUXER) += cafdec.o caf.o mov.o mov_chan.o \
replaygain.o
+OBJS-$(CONFIG_CAF_MUXER) += cafenc.o caf.o riff.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_DASH_MUXER) += dashenc.o
+OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o
+OBJS-$(CONFIG_DATA_MUXER) += rawenc.o
+OBJS-$(CONFIG_DASH_MUXER) += dash.o dashenc.o
+OBJS-$(CONFIG_DASH_DEMUXER) += dash.o dashdec.o
OBJS-$(CONFIG_DAUD_DEMUXER) += dauddec.o
OBJS-$(CONFIG_DAUD_MUXER) += daudenc.o
+OBJS-$(CONFIG_DCSTR_DEMUXER) += dcstr.o
OBJS-$(CONFIG_DFA_DEMUXER) += dfa.o
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_DSS_DEMUXER) += dss.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_DVBTXT_DEMUXER) += dvbtxt.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_FIFO_MUXER) += fifo.o
OBJS-$(CONFIG_FILMSTRIP_DEMUXER) += filmstripdec.o
OBJS-$(CONFIG_FILMSTRIP_MUXER) += filmstripenc.o
+OBJS-$(CONFIG_FITS_DEMUXER) += fitsdec.o
+OBJS-$(CONFIG_FITS_MUXER) += fitsenc.o
OBJS-$(CONFIG_FLAC_DEMUXER) += flacdec.o rawdec.o \
flac_picture.o \
oggparsevorbis.o \
@@ -132,32 +176,48 @@ 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_FRAMEHASH_MUXER) += hashenc.o framehash.o
+OBJS-$(CONFIG_FRAMEMD5_MUXER) += hashenc.o framehash.o
+OBJS-$(CONFIG_FRM_DEMUXER) += frmdec.o
+OBJS-$(CONFIG_FSB_DEMUXER) += fsb.o
+OBJS-$(CONFIG_GIF_MUXER) += gif.o
+OBJS-$(CONFIG_GIF_DEMUXER) += gifdec.o
+OBJS-$(CONFIG_GSM_DEMUXER) += gsmdec.o
+OBJS-$(CONFIG_GSM_MUXER) += rawenc.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_G726_DEMUXER) += g726.o
+OBJS-$(CONFIG_G726_MUXER) += rawenc.o
+OBJS-$(CONFIG_G726LE_DEMUXER) += g726.o
+OBJS-$(CONFIG_G726LE_MUXER) += rawenc.o
OBJS-$(CONFIG_G729_DEMUXER) += g729dec.o
-OBJS-$(CONFIG_GIF_MUXER) += gif.o
-OBJS-$(CONFIG_GSM_DEMUXER) += gsmdec.o
-OBJS-$(CONFIG_GXF_DEMUXER) += gxf.o
-OBJS-$(CONFIG_GXF_MUXER) += gxfenc.o audiointerleave.o
+OBJS-$(CONFIG_GDV_DEMUXER) += gdv.o
+OBJS-$(CONFIG_GENH_DEMUXER) += genh.o
OBJS-$(CONFIG_H261_DEMUXER) += h261dec.o rawdec.o
OBJS-$(CONFIG_H261_MUXER) += rawenc.o
OBJS-$(CONFIG_H263_DEMUXER) += h263dec.o rawdec.o
OBJS-$(CONFIG_H263_MUXER) += rawenc.o
OBJS-$(CONFIG_H264_DEMUXER) += h264dec.o rawdec.o
OBJS-$(CONFIG_H264_MUXER) += rawenc.o
+OBJS-$(CONFIG_HASH_MUXER) += hashenc.o
OBJS-$(CONFIG_HDS_MUXER) += hdsenc.o
OBJS-$(CONFIG_HEVC_DEMUXER) += hevcdec.o rawdec.o
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
@@ -165,37 +225,77 @@ 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_DDS_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_PAM_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_PBM_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_PCX_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_PGMYUV_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_PGM_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_PPM_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_PSD_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_QDRAW_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_SGI_PIPE_DEMUXER) += img2dec.o img2.o
+OBJS-$(CONFIG_IMAGE_SVG_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_IMAGE_XPM_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_IVR_DEMUXER) += rmdec.o rm.o rmsipr.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 \
- rmsipr.o \
+ rmsipr.o flac_picture.o \
oggparsevorbis.o vorbiscomment.o \
flac_picture.o replaygain.o
OBJS-$(CONFIG_MATROSKA_MUXER) += matroskaenc.o matroska.o \
avc.o hevc.o \
- flacenc_header.o avlanguage.o vorbiscomment.o wv.o
-OBJS-$(CONFIG_MD5_MUXER) += md5enc.o
+ flacenc_header.o avlanguage.o vorbiscomment.o wv.o \
+ webmdashenc.o webm_chunk.o
+OBJS-$(CONFIG_MD5_MUXER) += hashenc.o
+OBJS-$(CONFIG_MGSTS_DEMUXER) += mgsts.o
+OBJS-$(CONFIG_MICRODVD_DEMUXER) += microdvddec.o subtitles.o
+OBJS-$(CONFIG_MICRODVD_MUXER) += microdvdenc.o
+OBJS-$(CONFIG_MJPEG_2000_DEMUXER) += rawdec.o mj2kdec.o
OBJS-$(CONFIG_MJPEG_DEMUXER) += rawdec.o
OBJS-$(CONFIG_MJPEG_MUXER) += rawenc.o
-OBJS-$(CONFIG_MLP_DEMUXER) += rawdec.o
+OBJS-$(CONFIG_MLP_DEMUXER) += rawdec.o mlpdec.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 mov_chan.o replaygain.o
-OBJS-$(CONFIG_MOV_MUXER) += movenc.o avc.o hevc.o \
- movenchint.o mov_chan.o
+OBJS-$(CONFIG_MOV_MUXER) += movenc.o avc.o hevc.o vpcc.o \
+ movenchint.o mov_chan.o rtp.o \
+ movenccenc.o rawutils.o
OBJS-$(CONFIG_MP2_MUXER) += rawenc.o
OBJS-$(CONFIG_MP3_DEMUXER) += mp3dec.o replaygain.o
OBJS-$(CONFIG_MP3_MUXER) += mp3enc.o rawenc.o id3v2enc.o
@@ -214,21 +314,28 @@ OBJS-$(CONFIG_MPEGTS_MUXER) += mpegtsenc.o
OBJS-$(CONFIG_MPEGVIDEO_DEMUXER) += mpegvideodec.o rawdec.o
OBJS-$(CONFIG_MPJPEG_DEMUXER) += mpjpegdec.o
OBJS-$(CONFIG_MPJPEG_MUXER) += mpjpeg.o
+OBJS-$(CONFIG_MPL2_DEMUXER) += mpl2dec.o subtitles.o
+OBJS-$(CONFIG_MSF_DEMUXER) += msf.o
+OBJS-$(CONFIG_MPSUB_DEMUXER) += mpsubdec.o subtitles.o
OBJS-$(CONFIG_MSNWC_TCP_DEMUXER) += msnwc_tcp.o
+OBJS-$(CONFIG_MTAF_DEMUXER) += mtaf.o
OBJS-$(CONFIG_MTV_DEMUXER) += mtv.o
+OBJS-$(CONFIG_MUSX_DEMUXER) += musx.o
OBJS-$(CONFIG_MV_DEMUXER) += mvdec.o
OBJS-$(CONFIG_MVI_DEMUXER) += mvi.o
OBJS-$(CONFIG_MXF_DEMUXER) += mxfdec.o mxf.o
OBJS-$(CONFIG_MXF_MUXER) += mxfenc.o mxf.o audiointerleave.o
OBJS-$(CONFIG_MXG_DEMUXER) += mxg.o
OBJS-$(CONFIG_NC_DEMUXER) += ncdec.o
+OBJS-$(CONFIG_NISTSPHERE_DEMUXER) += nistspheredec.o pcm.o
OBJS-$(CONFIG_NSV_DEMUXER) += nsvdec.o
OBJS-$(CONFIG_NULL_MUXER) += nullenc.o
-OBJS-$(CONFIG_NUT_DEMUXER) += nutdec.o nut.o
+OBJS-$(CONFIG_NUT_DEMUXER) += nutdec.o nut.o isom.o
OBJS-$(CONFIG_NUT_MUXER) += nutenc.o nut.o
OBJS-$(CONFIG_NUV_DEMUXER) += nuv.o
OBJS-$(CONFIG_OGG_DEMUXER) += oggdec.o \
oggparsecelt.o \
+ oggparsedaala.o \
oggparsedirac.o \
oggparseflac.o \
oggparseogm.o \
@@ -241,10 +348,16 @@ 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_OGV_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
@@ -286,17 +399,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_RPL_DEMUXER) += rpl.o
OBJS-$(CONFIG_RSO_DEMUXER) += rsodec.o rso.o pcm.o
OBJS-$(CONFIG_RSO_MUXER) += rsoenc.o rso.o
@@ -312,20 +430,31 @@ OBJS-$(CONFIG_RTP_MUXER) += rtp.o \
rtpenc_jpeg.o \
rtpenc_mpv.o \
rtpenc.o \
+ rtpenc_vc2hq.o \
rtpenc_vp8.o \
+ rtpenc_vp9.o \
rtpenc_xiph.o \
avc.o hevc.o
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_S337M_DEMUXER) += s337m.o spdif.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_SCC_DEMUXER) += sccdec.o subtitles.o
+OBJS-$(CONFIG_SCC_MUXER) += sccenc.o subtitles.o
OBJS-$(CONFIG_SDP_DEMUXER) += rtsp.o
+OBJS-$(CONFIG_SDR2_DEMUXER) += sdr2.o
+OBJS-$(CONFIG_SDS_DEMUXER) += sdsdec.o
+OBJS-$(CONFIG_SDX_DEMUXER) += sdxdec.o
OBJS-$(CONFIG_SEGAFILM_DEMUXER) += segafilm.o
OBJS-$(CONFIG_SEGMENT_MUXER) += segment.o
-OBJS-$(CONFIG_SHORTEN_DEMUXER) += rawdec.o
+OBJS-$(CONFIG_SHORTEN_DEMUXER) += shortendec.o rawdec.o
OBJS-$(CONFIG_SIFF_DEMUXER) += siff.o
+OBJS-$(CONFIG_SINGLEJPEG_MUXER) += rawenc.o
OBJS-$(CONFIG_SMACKER_DEMUXER) += smacker.o
OBJS-$(CONFIG_SMJPEG_DEMUXER) += smjpegdec.o smjpeg.o
OBJS-$(CONFIG_SMJPEG_MUXER) += smjpegenc.o smjpeg.o
@@ -333,46 +462,81 @@ OBJS-$(CONFIG_SMOOTHSTREAMING_MUXER) += smoothstreamingenc.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_SUP_MUXER) += supenc.o
+OBJS-$(CONFIG_SVAG_DEMUXER) += svag.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 tee_common.o
OBJS-$(CONFIG_THP_DEMUXER) += thp.o
+OBJS-$(CONFIG_THREEDOSTR_DEMUXER) += 3dostr.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_DEMUXER) += rawdec.o mlpdec.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_TTA_MUXER) += ttaenc.o apetag.o img2.o
OBJS-$(CONFIG_TTY_DEMUXER) += tty.o sauce.o
OBJS-$(CONFIG_TXD_DEMUXER) += txd.o
-OBJS-$(CONFIG_VC1_DEMUXER) += rawdec.o
+OBJS-$(CONFIG_UNCODEDFRAMECRC_MUXER) += uncodedframecrcenc.o framehash.o
+OBJS-$(CONFIG_V210_DEMUXER) += v210.o
+OBJS-$(CONFIG_V210X_DEMUXER) += v210.o
+OBJS-$(CONFIG_VAG_DEMUXER) += vag.o
+OBJS-$(CONFIG_VC1_DEMUXER) += rawdec.o vc1dec.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_packet.o voc.o
OBJS-$(CONFIG_VOC_MUXER) += vocenc.o voc.o
+OBJS-$(CONFIG_VPK_DEMUXER) += vpk.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 \
avc.o hevc.o \
flacenc_header.o avlanguage.o \
- wv.o vorbiscomment.o
+ wv.o vorbiscomment.o \
+ webmdashenc.o webm_chunk.o
+OBJS-$(CONFIG_WEBM_DASH_MANIFEST_MUXER) += webmdashenc.o matroska.o
+OBJS-$(CONFIG_WEBM_CHUNK_MUXER) += webm_chunk.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_WSD_DEMUXER) += wsddec.o rawdec.o
OBJS-$(CONFIG_WSVQA_DEMUXER) += westwood_vqa.o
-OBJS-$(CONFIG_WTV_DEMUXER) += wtv.o asfdec.o asf.o asfcrypt.o \
- avlanguage.o
+OBJS-$(CONFIG_WTV_DEMUXER) += wtvdec.o wtv_common.o \
+ asf.o
+OBJS-$(CONFIG_WTV_MUXER) += wtvenc.o wtv_common.o \
+ asf.o
OBJS-$(CONFIG_WV_DEMUXER) += wvdec.o wv.o apetag.o img2.o
+OBJS-$(CONFIG_WVE_DEMUXER) += wvedec.o pcm.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_XVAG_DEMUXER) += xvag.o
OBJS-$(CONFIG_XWMA_DEMUXER) += xwma.o
OBJS-$(CONFIG_YOP_DEMUXER) += yop.o
OBJS-$(CONFIG_YUV4MPEGPIPE_DEMUXER) += yuv4mpegdec.o
@@ -380,15 +544,26 @@ OBJS-$(CONFIG_YUV4MPEGPIPE_MUXER) += yuv4mpegenc.o
# external libraries
OBJS-$(CONFIG_AVISYNTH_DEMUXER) += avisynth.o
+OBJS-$(CONFIG_CHROMAPRINT_MUXER) += chromaprint.o
+OBJS-$(CONFIG_LIBGME_DEMUXER) += libgme.o
+OBJS-$(CONFIG_LIBMODPLUG_DEMUXER) += libmodplug.o
+OBJS-$(CONFIG_LIBOPENMPT_DEMUXER) += libopenmpt.o
OBJS-$(CONFIG_LIBRTMP) += librtmp.o
+OBJS-$(CONFIG_LIBSSH_PROTOCOL) += libssh.o
+OBJS-$(CONFIG_LIBSMBCLIENT_PROTOCOL) += libsmbclient.o
# protocols I/O
+OBJS-$(CONFIG_ASYNC_PROTOCOL) += async.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
@@ -399,6 +574,7 @@ OBJS-$(CONFIG_MD5_PROTOCOL) += md5proto.o
OBJS-$(CONFIG_MMSH_PROTOCOL) += mmsh.o mms.o asf.o
OBJS-$(CONFIG_MMST_PROTOCOL) += mmst.o mms.o asf.o
OBJS-$(CONFIG_PIPE_PROTOCOL) += file.o
+OBJS-$(CONFIG_PROMPEG_PROTOCOL) += prompeg.o
OBJS-$(CONFIG_RTMP_PROTOCOL) += rtmpproto.o rtmppkt.o
OBJS-$(CONFIG_RTMPE_PROTOCOL) += rtmpproto.o rtmppkt.o
OBJS-$(CONFIG_RTMPS_PROTOCOL) += rtmpproto.o rtmppkt.o
@@ -408,18 +584,32 @@ 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_TEE_PROTOCOL) += teeproto.o tee_common.o
OBJS-$(CONFIG_TCP_PROTOCOL) += tcp.o
OBJS-$(CONFIG_TLS_GNUTLS_PROTOCOL) += tls_gnutls.o tls.o
OBJS-$(CONFIG_TLS_OPENSSL_PROTOCOL) += tls_openssl.o tls.o
+OBJS-$(CONFIG_TLS_SCHANNEL_PROTOCOL) += tls_schannel.o tls.o
+OBJS-$(CONFIG_TLS_SECURETRANSPORT_PROTOCOL) += tls_securetransport.o tls.o
OBJS-$(CONFIG_UDP_PROTOCOL) += udp.o
+OBJS-$(CONFIG_UDPLITE_PROTOCOL) += udp.o
OBJS-$(CONFIG_UNIX_PROTOCOL) += unix.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
TESTPROGS = seek \
url \
+# async \
+FIFO-MUXER-TESTPROGS-$(CONFIG_NETWORK) += fifo_muxer
+TESTPROGS-$(CONFIG_FIFO_MUXER) += $(FIFO-MUXER-TESTPROGS-yes)
TESTPROGS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh
TESTPROGS-$(CONFIG_MOV_MUXER) += movenc
TESTPROGS-$(CONFIG_NETWORK) += noproxy
@@ -429,4 +619,5 @@ TOOLS = aviocat \
ismindex \
pktdumper \
probetest \
+ seek_print \
sidxindex \
diff --git a/libavformat/a64.c b/libavformat/a64.c
index fdd6f62..2a0489d 100644
--- a/libavformat/a64.c
+++ b/libavformat/a64.c
@@ -2,25 +2,24 @@
* a64 muxer
* Copyright (c) 2009 Tobias Bindhammer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libavcodec/avcodec.h"
-#include "libavcodec/a64enc.h"
#include "libavcodec/bytestream.h"
#include "avformat.h"
#include "rawenc.h"
diff --git a/libavformat/aacdec.c b/libavformat/aacdec.c
index 2f20792..364b334 100644
--- a/libavformat/aacdec.c
+++ b/libavformat/aacdec.c
@@ -3,37 +3,39 @@
* 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
*/
#include "libavutil/intreadwrite.h"
#include "avformat.h"
#include "internal.h"
-#include "rawdec.h"
#include "id3v1.h"
+#include "apetag.h"
+
+#define ADTS_HEADER_SIZE 7
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 +57,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);
@@ -68,7 +71,7 @@ static int adts_aac_probe(AVProbeData *p)
return AVPROBE_SCORE_EXTENSION;
else if (max_frames >= 3)
return AVPROBE_SCORE_EXTENSION / 2;
- else if (max_frames >= 1)
+ else if (first_frames >= 1)
return 1;
else
return 0;
@@ -77,6 +80,7 @@ static int adts_aac_probe(AVProbeData *p)
static int adts_aac_read_header(AVFormatContext *s)
{
AVStream *st;
+ uint16_t state;
st = avformat_new_stream(s, NULL);
if (!st)
@@ -84,9 +88,27 @@ static int adts_aac_read_header(AVFormatContext *s)
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->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 & AVIO_SEEKABLE_NORMAL) &&
+ !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);
+ }
+
+ // skip data until the first ADTS frame is found
+ state = avio_r8(s->pb);
+ while (!avio_feof(s->pb) && avio_tell(s->pb) < s->probesize) {
+ state = (state << 8) | avio_r8(s->pb);
+ if ((state >> 4) != 0xFFF)
+ continue;
+ avio_seek(s->pb, -2, SEEK_CUR);
+ break;
+ }
+ if ((state >> 4) != 0xFFF)
+ return AVERROR_INVALIDDATA;
// LCM of all possible ADTS sample rates
avpriv_set_pts_info(st, 64, 1, 28224000);
@@ -94,12 +116,38 @@ static int adts_aac_read_header(AVFormatContext *s)
return 0;
}
+static int adts_aac_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ int ret, fsize;
+
+ ret = av_get_packet(s->pb, pkt, ADTS_HEADER_SIZE);
+ if (ret < 0)
+ return ret;
+ if (ret < ADTS_HEADER_SIZE) {
+ av_packet_unref(pkt);
+ return AVERROR(EIO);
+ }
+
+ if ((AV_RB16(pkt->data) >> 4) != 0xfff) {
+ av_packet_unref(pkt);
+ return AVERROR_INVALIDDATA;
+ }
+
+ fsize = (AV_RB32(pkt->data + 3) >> 13) & 0x1FFF;
+ if (fsize < ADTS_HEADER_SIZE) {
+ av_packet_unref(pkt);
+ return AVERROR_INVALIDDATA;
+ }
+
+ return av_append_packet(s->pb, pkt, fsize - ADTS_HEADER_SIZE);
+}
+
AVInputFormat ff_aac_demuxer = {
.name = "aac",
.long_name = NULL_IF_CONFIG_SMALL("raw ADTS AAC (Advanced Audio Coding)"),
.read_probe = adts_aac_probe,
.read_header = adts_aac_read_header,
- .read_packet = ff_raw_read_partial_packet,
+ .read_packet = adts_aac_read_packet,
.flags = AVFMT_GENERIC_INDEX,
.extensions = "aac",
.mime_type = "audio/aac,audio/aacp,audio/x-aac",
diff --git a/libavformat/aadec.c b/libavformat/aadec.c
new file mode 100644
index 0000000..8d39b1d
--- /dev/null
+++ b/libavformat/aadec.c
@@ -0,0 +1,310 @@
+/*
+ * Audible AA demuxer
+ * Copyright (c) 2015 Vesselin Bontchev
+ *
+ * Header parsing is borrowed from https://github.com/jteeuwen/audible project.
+ * Copyright (c) 2001-2014, Jim Teeuwen
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "libavutil/dict.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/tea.h"
+#include "libavutil/opt.h"
+
+#define AA_MAGIC 1469084982 /* this identifies an audible .aa file */
+#define MAX_CODEC_SECOND_SIZE 3982
+#define MAX_TOC_ENTRIES 16
+#define MAX_DICTIONARY_ENTRIES 128
+#define TEA_BLOCK_SIZE 8
+
+typedef struct AADemuxContext {
+ AVClass *class;
+ uint8_t *aa_fixed_key;
+ int aa_fixed_key_len;
+ int codec_second_size;
+ int current_codec_second_size;
+ int chapter_idx;
+ struct AVTEA *tea_ctx;
+ uint8_t file_key[16];
+ int64_t current_chapter_size;
+} AADemuxContext;
+
+static int get_second_size(char *codec_name)
+{
+ int result = -1;
+
+ if (!strcmp(codec_name, "mp332")) {
+ result = 3982;
+ } else if (!strcmp(codec_name, "acelp16")) {
+ result = 2000;
+ } else if (!strcmp(codec_name, "acelp85")) {
+ result = 1045;
+ }
+
+ return result;
+}
+
+static int aa_read_header(AVFormatContext *s)
+{
+ int i, j, idx, largest_idx = -1;
+ uint32_t nkey, nval, toc_size, npairs, header_seed = 0, start;
+ char key[128], val[128], codec_name[64] = {0};
+ uint8_t output[24], dst[8], src[8];
+ int64_t largest_size = -1, current_size = -1;
+ struct toc_entry {
+ uint32_t offset;
+ uint32_t size;
+ } TOC[MAX_TOC_ENTRIES];
+ uint32_t header_key_part[4];
+ uint8_t header_key[16] = {0};
+ AADemuxContext *c = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVStream *st;
+
+ /* parse .aa header */
+ avio_skip(pb, 4); // file size
+ avio_skip(pb, 4); // magic string
+ toc_size = avio_rb32(pb); // TOC size
+ avio_skip(pb, 4); // unidentified integer
+ if (toc_size > MAX_TOC_ENTRIES)
+ return AVERROR_INVALIDDATA;
+ for (i = 0; i < toc_size; i++) { // read TOC
+ avio_skip(pb, 4); // TOC entry index
+ TOC[i].offset = avio_rb32(pb); // block offset
+ TOC[i].size = avio_rb32(pb); // block size
+ }
+ avio_skip(pb, 24); // header termination block (ignored)
+ npairs = avio_rb32(pb); // read dictionary entries
+ if (npairs > MAX_DICTIONARY_ENTRIES)
+ return AVERROR_INVALIDDATA;
+ for (i = 0; i < npairs; i++) {
+ memset(val, 0, sizeof(val));
+ memset(key, 0, sizeof(key));
+ avio_skip(pb, 1); // unidentified integer
+ nkey = avio_rb32(pb); // key string length
+ nval = avio_rb32(pb); // value string length
+ avio_get_str(pb, nkey, key, sizeof(key));
+ avio_get_str(pb, nval, val, sizeof(val));
+ if (!strcmp(key, "codec")) {
+ av_log(s, AV_LOG_DEBUG, "Codec is <%s>\n", val);
+ strncpy(codec_name, val, sizeof(codec_name) - 1);
+ } else if (!strcmp(key, "HeaderSeed")) {
+ av_log(s, AV_LOG_DEBUG, "HeaderSeed is <%s>\n", val);
+ header_seed = atoi(val);
+ } else if (!strcmp(key, "HeaderKey")) { // this looks like "1234567890 1234567890 1234567890 1234567890"
+ av_log(s, AV_LOG_DEBUG, "HeaderKey is <%s>\n", val);
+ sscanf(val, "%"SCNu32"%"SCNu32"%"SCNu32"%"SCNu32,
+ &header_key_part[0], &header_key_part[1], &header_key_part[2], &header_key_part[3]);
+ for (idx = 0; idx < 4; idx++) {
+ AV_WB32(&header_key[idx * 4], header_key_part[idx]); // convert each part to BE!
+ }
+ av_log(s, AV_LOG_DEBUG, "Processed HeaderKey is ");
+ for (i = 0; i < 16; i++)
+ av_log(s, AV_LOG_DEBUG, "%02x", header_key[i]);
+ av_log(s, AV_LOG_DEBUG, "\n");
+ } else {
+ av_dict_set(&s->metadata, key, val, 0);
+ }
+ }
+
+ /* verify fixed key */
+ if (c->aa_fixed_key_len != 16) {
+ av_log(s, AV_LOG_ERROR, "aa_fixed_key value needs to be 16 bytes!\n");
+ return AVERROR(EINVAL);
+ }
+
+ /* verify codec */
+ if ((c->codec_second_size = get_second_size(codec_name)) == -1) {
+ av_log(s, AV_LOG_ERROR, "unknown codec <%s>!\n", codec_name);
+ return AVERROR(EINVAL);
+ }
+
+ /* decryption key derivation */
+ c->tea_ctx = av_tea_alloc();
+ if (!c->tea_ctx)
+ return AVERROR(ENOMEM);
+ av_tea_init(c->tea_ctx, c->aa_fixed_key, 16);
+ output[0] = output[1] = 0; // purely for padding purposes
+ memcpy(output + 2, header_key, 16);
+ idx = 0;
+ for (i = 0; i < 3; i++) { // TEA CBC with weird mixed endianness
+ AV_WB32(src, header_seed);
+ AV_WB32(src + 4, header_seed + 1);
+ header_seed += 2;
+ av_tea_crypt(c->tea_ctx, dst, src, 1, NULL, 0); // TEA ECB encrypt
+ for (j = 0; j < TEA_BLOCK_SIZE && idx < 18; j+=1, idx+=1) {
+ output[idx] = output[idx] ^ dst[j];
+ }
+ }
+ memcpy(c->file_key, output + 2, 16); // skip first 2 bytes of output
+ av_log(s, AV_LOG_DEBUG, "File key is ");
+ for (i = 0; i < 16; i++)
+ av_log(s, AV_LOG_DEBUG, "%02x", c->file_key[i]);
+ av_log(s, AV_LOG_DEBUG, "\n");
+
+ /* decoder setup */
+ st = avformat_new_stream(s, NULL);
+ if (!st) {
+ av_freep(&c->tea_ctx);
+ return AVERROR(ENOMEM);
+ }
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ if (!strcmp(codec_name, "mp332")) {
+ st->codecpar->codec_id = AV_CODEC_ID_MP3;
+ st->codecpar->sample_rate = 22050;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
+ st->start_time = 0;
+ } else if (!strcmp(codec_name, "acelp85")) {
+ st->codecpar->codec_id = AV_CODEC_ID_SIPR;
+ st->codecpar->block_align = 19;
+ st->codecpar->channels = 1;
+ st->codecpar->sample_rate = 8500;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
+ } else if (!strcmp(codec_name, "acelp16")) {
+ st->codecpar->codec_id = AV_CODEC_ID_SIPR;
+ st->codecpar->block_align = 20;
+ st->codecpar->channels = 1;
+ st->codecpar->sample_rate = 16000;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
+ }
+
+ /* determine, and jump to audio start offset */
+ for (i = 1; i < toc_size; i++) { // skip the first entry!
+ current_size = TOC[i].size;
+ if (current_size > largest_size) {
+ largest_idx = i;
+ largest_size = current_size;
+ }
+ }
+ start = TOC[largest_idx].offset;
+ avio_seek(pb, start, SEEK_SET);
+ c->current_chapter_size = 0;
+
+ return 0;
+}
+
+static int aa_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ uint8_t dst[TEA_BLOCK_SIZE];
+ uint8_t src[TEA_BLOCK_SIZE];
+ int i;
+ int trailing_bytes;
+ int blocks;
+ uint8_t buf[MAX_CODEC_SECOND_SIZE * 2];
+ int written = 0;
+ int ret;
+ AADemuxContext *c = s->priv_data;
+
+ // are we at the start of a chapter?
+ if (c->current_chapter_size == 0) {
+ c->current_chapter_size = avio_rb32(s->pb);
+ if (c->current_chapter_size == 0) {
+ return AVERROR_EOF;
+ }
+ av_log(s, AV_LOG_DEBUG, "Chapter %d (%" PRId64 " bytes)\n", c->chapter_idx, c->current_chapter_size);
+ c->chapter_idx = c->chapter_idx + 1;
+ avio_skip(s->pb, 4); // data start offset
+ c->current_codec_second_size = c->codec_second_size;
+ }
+
+ // is this the last block in this chapter?
+ if (c->current_chapter_size / c->current_codec_second_size == 0) {
+ c->current_codec_second_size = c->current_chapter_size % c->current_codec_second_size;
+ }
+
+ // decrypt c->current_codec_second_size bytes
+ blocks = c->current_codec_second_size / TEA_BLOCK_SIZE;
+ for (i = 0; i < blocks; i++) {
+ avio_read(s->pb, src, TEA_BLOCK_SIZE);
+ av_tea_init(c->tea_ctx, c->file_key, 16);
+ av_tea_crypt(c->tea_ctx, dst, src, 1, NULL, 1);
+ memcpy(buf + written, dst, TEA_BLOCK_SIZE);
+ written = written + TEA_BLOCK_SIZE;
+ }
+ trailing_bytes = c->current_codec_second_size % TEA_BLOCK_SIZE;
+ if (trailing_bytes != 0) { // trailing bytes are left unencrypted!
+ avio_read(s->pb, src, trailing_bytes);
+ memcpy(buf + written, src, trailing_bytes);
+ written = written + trailing_bytes;
+ }
+
+ // update state
+ c->current_chapter_size = c->current_chapter_size - c->current_codec_second_size;
+ if (c->current_chapter_size <= 0)
+ c->current_chapter_size = 0;
+
+ ret = av_new_packet(pkt, written);
+ if (ret < 0)
+ return ret;
+ memcpy(pkt->data, buf, written);
+
+ return 0;
+}
+
+static int aa_probe(AVProbeData *p)
+{
+ uint8_t *buf = p->buf;
+
+ // first 4 bytes are file size, next 4 bytes are the magic
+ if (AV_RB32(buf+4) != AA_MAGIC)
+ return 0;
+
+ return AVPROBE_SCORE_MAX / 2;
+}
+
+static int aa_read_close(AVFormatContext *s)
+{
+ AADemuxContext *c = s->priv_data;
+
+ av_freep(&c->tea_ctx);
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(AADemuxContext, x)
+static const AVOption aa_options[] = {
+ { "aa_fixed_key", // extracted from libAAX_SDK.so and AAXSDKWin.dll files!
+ "Fixed key used for handling Audible AA files", OFFSET(aa_fixed_key),
+ AV_OPT_TYPE_BINARY, {.str="77214d4b196a87cd520045fd2a51d673"},
+ .flags = AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+static const AVClass aa_class = {
+ .class_name = "aa",
+ .item_name = av_default_item_name,
+ .option = aa_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_aa_demuxer = {
+ .name = "aa",
+ .long_name = NULL_IF_CONFIG_SMALL("Audible AA format files"),
+ .priv_class = &aa_class,
+ .priv_data_size = sizeof(AADemuxContext),
+ .extensions = "aa",
+ .read_probe = aa_probe,
+ .read_header = aa_read_header,
+ .read_packet = aa_read_packet,
+ .read_close = aa_read_close,
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/ac3dec.c b/libavformat/ac3dec.c
index 4ceffa5..e85b0ac 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 ] = buf2[i+1];
+ buf3[i+1] = buf2[i ];
+ }
+ init_get_bits(&gbc, buf3, 54);
+ }else
+ init_get_bits(&gbc, buf2, 54);
+ if(avpriv_ac3_parse_header(&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 ] = buf2[i+1];
+ buf3[i+1] = buf2[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>=7) 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/acm.c b/libavformat/acm.c
new file mode 100644
index 0000000..08dd928
--- /dev/null
+++ b/libavformat/acm.c
@@ -0,0 +1,75 @@
+/*
+ * ACM demuxer
+ * Copyright (c) 2015 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 "rawdec.h"
+#include "internal.h"
+
+static int acm_probe(AVProbeData *p)
+{
+ if (AV_RB32(p->buf) != 0x97280301)
+ return 0;
+
+ return AVPROBE_SCORE_MAX / 3 * 2;
+}
+
+static int acm_read_header(AVFormatContext *s)
+{
+ AVStream *st;
+ int ret;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_INTERPLAY_ACM;
+
+ ff_alloc_extradata(st->codecpar, 14);
+ if (!st->codecpar->extradata)
+ return AVERROR(ENOMEM);
+ ret = avio_read(s->pb, st->codecpar->extradata, 14);
+ if (ret < 10)
+ return ret < 0 ? ret : AVERROR_EOF;
+
+ st->codecpar->channels = AV_RL16(st->codecpar->extradata + 8);
+ st->codecpar->sample_rate = AV_RL16(st->codecpar->extradata + 10);
+ if (st->codecpar->channels <= 0 || st->codecpar->sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+ st->start_time = 0;
+ st->duration = AV_RL32(st->codecpar->extradata + 4) / st->codecpar->channels;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ return 0;
+}
+
+AVInputFormat ff_acm_demuxer = {
+ .name = "acm",
+ .long_name = NULL_IF_CONFIG_SMALL("Interplay ACM"),
+ .read_probe = acm_probe,
+ .read_header = acm_read_header,
+ .read_packet = ff_raw_read_partial_packet,
+ .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS,
+ .extensions = "acm",
+ .raw_codec_id = AV_CODEC_ID_INTERPLAY_ACM,
+};
diff --git a/libavformat/act.c b/libavformat/act.c
new file mode 100644
index 0000000..fe67411
--- /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(s, pb, st->codecpar, size, 0);
+
+ /*
+ 8000Hz (Fine-rec) file format has 10 bytes long
+ packets with 10ms of sound data in them
+ */
+ if (st->codecpar->sample_rate != 8000) {
+ av_log(s, AV_LOG_ERROR, "Sample rate %d is not supported.\n", st->codecpar->sample_rate);
+ return AVERROR_INVALIDDATA;
+ }
+
+ st->codecpar->frame_size=80;
+ st->codecpar->channels=1;
+ avpriv_set_pts_info(st, 64, 1, 100);
+
+ st->codecpar->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->codecpar->sample_rate, 1000 * st->codecpar->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]->codecpar->sample_rate==8000?10:22;
+
+
+ if(s->streams[0]->codecpar->sample_rate==8000)
+ ret=av_new_packet(pkt, 10);
+ else
+ ret=av_new_packet(pkt, 11);
+
+ if(ret)
+ return ret;
+
+ if(s->streams[0]->codecpar->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]->codecpar->sample_rate==4400 && ctx->second_packet)
+ {
+ pkt->data[0]=ctx->audio_buffer[5];
+ pkt->data[1]=ctx->audio_buffer[17];
+ pkt->data[2]=ctx->audio_buffer[6];
+ pkt->data[3]=ctx->audio_buffer[18];
+ pkt->data[4]=ctx->audio_buffer[7];
+ pkt->data[5]=ctx->audio_buffer[19];
+ pkt->data[6]=ctx->audio_buffer[8];
+ pkt->data[7]=ctx->audio_buffer[20];
+ pkt->data[8]=ctx->audio_buffer[9];
+ pkt->data[9]=ctx->audio_buffer[21];
+ pkt->data[10]=ctx->audio_buffer[10];
+
+ ctx->second_packet=0;
+ }
+ else // 8000 Hz
+ {
+ ret = avio_read(pb, ctx->audio_buffer, frame_size);
+
+ if(ret<0)
+ return ret;
+ if(ret!=frame_size)
+ return AVERROR(EIO);
+
+ pkt->data[0]=ctx->audio_buffer[5];
+ pkt->data[1]=ctx->audio_buffer[0];
+ pkt->data[2]=ctx->audio_buffer[6];
+ pkt->data[3]=ctx->audio_buffer[1];
+ pkt->data[4]=ctx->audio_buffer[7];
+ pkt->data[5]=ctx->audio_buffer[2];
+ pkt->data[6]=ctx->audio_buffer[8];
+ pkt->data[7]=ctx->audio_buffer[3];
+ pkt->data[8]=ctx->audio_buffer[9];
+ pkt->data[9]=ctx->audio_buffer[4];
+ }
+
+ ctx->bytes_left_in_chunk -= frame_size;
+
+ if(ctx->bytes_left_in_chunk < frame_size)
+ {
+ avio_skip(pb, ctx->bytes_left_in_chunk);
+ ctx->bytes_left_in_chunk=CHUNK_SIZE;
+ }
+
+ pkt->duration=1;
+
+ return ret;
+}
+
+AVInputFormat ff_act_demuxer = {
+ .name = "act",
+ .long_name = "ACT Voice file format",
+ .priv_data_size = sizeof(ACTContext),
+ .read_probe = probe,
+ .read_header = read_header,
+ .read_packet = read_packet,
+};
diff --git a/libavformat/adp.c b/libavformat/adp.c
new file mode 100644
index 0000000..3914857
--- /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;
+ uint8_t 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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_DTK;
+ st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
+ st->codecpar->channels = 2;
+ st->codecpar->sample_rate = 48000;
+ st->start_time = 0;
+ if (s->pb->seekable & AVIO_SEEKABLE_NORMAL)
+ st->duration = av_get_audio_frame_duration2(st->codecpar, avio_size(s->pb));
+
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->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_packet_unref(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/ads.c b/libavformat/ads.c
new file mode 100644
index 0000000..73ea7c7
--- /dev/null
+++ b/libavformat/ads.c
@@ -0,0 +1,89 @@
+/*
+ * ADS/SS2 demuxer
+ * Copyright (c) 2015 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"
+
+static int ads_probe(AVProbeData *p)
+{
+ if (memcmp(p->buf, "SShd", 4) ||
+ memcmp(p->buf+32, "SSbd", 4))
+ return 0;
+
+ return AVPROBE_SCORE_MAX / 3 * 2;
+}
+
+static int ads_read_header(AVFormatContext *s)
+{
+ int align, codec, size;
+ AVStream *st;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avio_skip(s->pb, 8);
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ codec = avio_rl32(s->pb);
+ st->codecpar->sample_rate = avio_rl32(s->pb);
+ if (st->codecpar->sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->channels = avio_rl32(s->pb);
+ if (st->codecpar->channels <= 0)
+ return AVERROR_INVALIDDATA;
+ align = avio_rl32(s->pb);
+ if (align <= 0 || align > INT_MAX / st->codecpar->channels)
+ return AVERROR_INVALIDDATA;
+
+ if (codec == 1)
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE_PLANAR;
+ else
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_PSX;
+
+ st->codecpar->block_align = st->codecpar->channels * align;
+ avio_skip(s->pb, 12);
+ size = avio_rl32(s->pb);
+ if (st->codecpar->codec_id == AV_CODEC_ID_ADPCM_PSX)
+ st->duration = (size - 0x40) / 16 / st->codecpar->channels * 28;
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ return 0;
+}
+
+static int ads_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ int ret;
+
+ ret = av_get_packet(s->pb, pkt, par->block_align);
+ pkt->stream_index = 0;
+ return ret;
+}
+
+AVInputFormat ff_ads_demuxer = {
+ .name = "ads",
+ .long_name = NULL_IF_CONFIG_SMALL("Sony PS2 ADS"),
+ .read_probe = ads_probe,
+ .read_header = ads_read_header,
+ .read_packet = ads_read_packet,
+ .extensions = "ads,ss2",
+};
diff --git a/libavformat/adtsenc.c b/libavformat/adtsenc.c
index e7c72d8..a046c2f 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;
AVCodecParameters *par = s->streams[0]->codecpar;
+ if (adts->id3v2tag)
+ ff_id3v2_write_simple(s, 4, ID3v2_DEFAULT_MAGIC);
if (par->extradata_size > 0)
return adts_decode_extradata(s, adts, par->extradata,
par->extradata_size);
@@ -141,11 +149,28 @@ static int adts_write_frame_header(ADTSContext *ctx,
static int adts_write_packet(AVFormatContext *s, AVPacket *pkt)
{
ADTSContext *adts = s->priv_data;
+ AVCodecParameters *par = s->streams[0]->codecpar;
AVIOContext *pb = s->pb;
uint8_t buf[ADTS_HEADER_SIZE];
if (!pkt->size)
return 0;
+ if (!par->extradata_size) {
+ uint8_t *side_data;
+ int side_data_size = 0, ret;
+
+ side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
+ &side_data_size);
+ if (side_data_size) {
+ ret = adts_decode_extradata(s, adts, side_data, side_data_size);
+ if (ret < 0)
+ return ret;
+ ret = ff_alloc_extradata(par, side_data_size);
+ if (ret < 0)
+ return ret;
+ memcpy(par->extradata, side_data, side_data_size);
+ }
+ }
if (adts->write_adts) {
int err = adts_write_frame_header(adts, buf, pkt->size,
adts->pce_size);
@@ -162,6 +187,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_BOOL, {.i64 = 0}, 0, 1, ENC},
+ { "write_apetag", "Enable APE tag writing", OFFSET(apetag), AV_OPT_TYPE_BOOL, {.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 +222,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 8162c69..a271e2a 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
*/
@@ -34,12 +34,30 @@ typedef struct ADXDemuxerContext {
int header_size;
} ADXDemuxerContext;
+static int adx_probe(AVProbeData *p)
+{
+ int offset;
+ if (AV_RB16(p->buf) != 0x8000)
+ return 0;
+ offset = AV_RB16(&p->buf[2]);
+ if ( offset < 8
+ || offset > p->buf_size - 4
+ || memcmp(p->buf + offset - 2, "(c)CRI", 6))
+ return 0;
+ return AVPROBE_SCORE_MAX * 3 / 4;
+}
+
static int adx_read_packet(AVFormatContext *s, AVPacket *pkt)
{
ADXDemuxerContext *c = s->priv_data;
AVCodecParameters *par = s->streams[0]->codecpar;
int ret, size;
+ if (par->channels <= 0) {
+ av_log(s, AV_LOG_ERROR, "invalid number of channels %d\n", par->channels);
+ return AVERROR_INVALIDDATA;
+ }
+
size = BLOCK_SIZE * par->channels;
pkt->pos = avio_tell(s->pb);
@@ -76,14 +94,8 @@ static int adx_read_header(AVFormatContext *s)
c->header_size = avio_rb16(s->pb) + 4;
avio_seek(s->pb, -4, SEEK_CUR);
- par->extradata = av_mallocz(c->header_size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!par->extradata)
+ if (ff_get_extradata(s, par, s->pb, c->header_size) < 0)
return AVERROR(ENOMEM);
- if (avio_read(s->pb, par->extradata, c->header_size) < c->header_size) {
- av_freep(&par->extradata);
- return AVERROR(EIO);
- }
- par->extradata_size = c->header_size;
if (par->extradata_size < 12) {
av_log(s, AV_LOG_ERROR, "Invalid extradata size.\n");
@@ -97,8 +109,14 @@ static int adx_read_header(AVFormatContext *s)
return AVERROR_INVALIDDATA;
}
+ if (par->sample_rate <= 0) {
+ av_log(s, AV_LOG_ERROR, "Invalid sample rate %d\n", par->sample_rate);
+ return AVERROR_INVALIDDATA;
+ }
+
par->codec_type = AVMEDIA_TYPE_AUDIO;
par->codec_id = s->iformat->raw_codec_id;
+ par->bit_rate = (int64_t)par->sample_rate * par->channels * BLOCK_SIZE * 8LL / BLOCK_SAMPLES;
avpriv_set_pts_info(st, 64, BLOCK_SAMPLES, par->sample_rate);
@@ -108,6 +126,7 @@ static int adx_read_header(AVFormatContext *s)
AVInputFormat ff_adx_demuxer = {
.name = "adx",
.long_name = NULL_IF_CONFIG_SMALL("CRI ADX"),
+ .read_probe = adx_probe,
.priv_data_size = sizeof(ADXDemuxerContext),
.read_header = adx_read_header,
.read_packet = adx_read_packet,
diff --git a/libavformat/aea.c b/libavformat/aea.c
index fc914f0..f795495 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 0000000..542cb16
--- /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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_AFC;
+ st->codecpar->channels = 2;
+ st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
+
+ if (ff_alloc_extradata(st->codecpar, 1))
+ return AVERROR(ENOMEM);
+ st->codecpar->extradata[0] = 8 * st->codecpar->channels;
+
+ c->data_end = avio_rb32(s->pb) + 32LL;
+ st->duration = avio_rb32(s->pb);
+ st->codecpar->sample_rate = avio_rb16(s->pb);
+ avio_skip(s->pb, 22);
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->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 4ed135b..0ed10a2 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,22 +33,28 @@
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_G722, MKTAG('G','7','2','2') },
- { AV_CODEC_ID_ADPCM_G726, MKTAG('G','7','2','6') },
+ { 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') },
+ { AV_CODEC_ID_QDMC, MKTAG('Q','D','M','C') },
{ AV_CODEC_ID_QDM2, MKTAG('Q','D','M','2') },
{ AV_CODEC_ID_QCELP, MKTAG('Q','c','l','p') },
+ { AV_CODEC_ID_SDX2_DPCM, MKTAG('S','D','X','2') },
+ { AV_CODEC_ID_ADPCM_IMA_WS, MKTAG('A','D','P','4') },
{ AV_CODEC_ID_NONE, 0 },
};
diff --git a/libavformat/aiffdec.c b/libavformat/aiffdec.c
index 481a92d..99e05c7 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,23 +74,24 @@ 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 */
-static unsigned int get_aiff_header(AVFormatContext *s, int size,
+static int get_aiff_header(AVFormatContext *s, int size,
unsigned version)
{
AVIOContext *pb = s->pb;
@@ -94,7 +99,7 @@ static unsigned int get_aiff_header(AVFormatContext *s, int size,
AIFFInputContext *aiff = s->priv_data;
int exp;
uint64_t val;
- double sample_rate;
+ int sample_rate;
unsigned int num_frames;
if (size & 1)
@@ -104,16 +109,28 @@ static unsigned int get_aiff_header(AVFormatContext *s, int size,
num_frames = avio_rb32(pb);
par->bits_per_coded_sample = avio_rb16(pb);
- exp = avio_rb16(pb);
+ exp = avio_rb16(pb) - 16383 - 63;
val = avio_rb64(pb);
- sample_rate = ldexp(val, exp - 16383 - 63);
+ if (exp <-63 || exp >63) {
+ av_log(s, AV_LOG_ERROR, "exp %d is out of range\n", exp);
+ return AVERROR_INVALIDDATA;
+ }
+ if (exp >= 0)
+ sample_rate = val << exp;
+ else
+ sample_rate = (val + (1ULL<<(-exp-1))) >> -exp;
par->sample_rate = sample_rate;
size -= 18;
/* get codec id for AIFF-C */
- if (version == AIFF_C_VERSION1) {
+ if (size < 4) {
+ version = AIFF;
+ } else if (version == AIFF_C_VERSION1) {
par->codec_tag = avio_rl32(pb);
par->codec_id = ff_codec_get_id(ff_codec_aiff_tags, par->codec_tag);
+ if (par->codec_id == AV_CODEC_ID_NONE)
+ avpriv_request_sample(s, "unknown or unsupported codec tag: %s",
+ av_fourcc2str(par->codec_tag));
size -= 4;
}
@@ -136,17 +153,19 @@ static unsigned int get_aiff_header(AVFormatContext *s, int size,
case AV_CODEC_ID_MACE3:
par->block_align = 2 * par->channels;
break;
+ case AV_CODEC_ID_ADPCM_G726LE:
+ par->bits_per_coded_sample = 5;
+ case AV_CODEC_ID_ADPCM_IMA_WS:
case AV_CODEC_ID_ADPCM_G722:
case AV_CODEC_ID_MACE6:
+ case AV_CODEC_ID_SDX2_DPCM:
par->block_align = 1 * par->channels;
break;
case AV_CODEC_ID_GSM:
par->block_align = 33;
break;
- case AV_CODEC_ID_QCELP:
- par->block_align = 35;
- break;
default:
+ aiff->block_duration = 1;
break;
}
if (par->block_align > 0)
@@ -157,10 +176,10 @@ 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 (!par->block_align)
- par->block_align = (par->bits_per_coded_sample * par->channels) >> 3;
+ par->block_align = (av_get_bits_per_sample(par->codec_id) * par->channels) >> 3;
if (aiff->block_duration) {
- par->bit_rate = par->sample_rate * (par->block_align << 3) /
+ par->bit_rate = (int64_t)par->sample_rate * (par->block_align << 3) /
aiff->block_duration;
}
@@ -186,13 +205,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);
@@ -215,6 +235,11 @@ static int aiff_read_header(AVFormatContext *s)
while (filesize > 0) {
/* parse different chunks */
size = get_tag(pb, &tag);
+
+ if (size == AVERROR_EOF && offset > 0 && st->codecpar->block_align) {
+ av_log(s, AV_LOG_WARNING, "header parser hit EOF\n");
+ goto got_sound;
+ }
if (size < 0)
return size;
@@ -229,6 +254,19 @@ 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 ||
+ (ret = ff_id3v2_parse_chapters(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;
@@ -249,7 +287,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->codecpar->block_align) /* Assume COMM already parsed */
+ if (st->codecpar->block_align && !(pb->seekable & AVIO_SEEKABLE_NORMAL)) /* Assume COMM already parsed */
goto got_sound;
if (!(pb->seekable & AVIO_SEEKABLE_NORMAL)) {
av_log(s, AV_LOG_ERROR, "file is not seekable\n");
@@ -260,12 +298,36 @@ static int aiff_read_header(AVFormatContext *s)
case MKTAG('w', 'a', 'v', 'e'):
if ((uint64_t)size > (1<<30))
return -1;
- st->codecpar->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
+ if (ff_get_extradata(s, st->codecpar, pb, size) < 0)
return AVERROR(ENOMEM);
- st->codecpar->extradata_size = size;
- avio_read(pb, st->codecpar->extradata, size);
+ if ( (st->codecpar->codec_id == AV_CODEC_ID_QDMC || st->codecpar->codec_id == AV_CODEC_ID_QDM2)
+ && size>=12*4 && !st->codecpar->block_align) {
+ st->codecpar->block_align = AV_RB32(st->codecpar->extradata+11*4);
+ aiff->block_duration = AV_RB32(st->codecpar->extradata+9*4);
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_QCELP) {
+ char rate = 0;
+ if (size >= 25)
+ rate = st->codecpar->extradata[24];
+ switch (rate) {
+ case 'H': // RATE_HALF
+ st->codecpar->block_align = 17;
+ break;
+ case 'F': // RATE_FULL
+ default:
+ st->codecpar->block_align = 35;
+ }
+ aiff->block_duration = 160;
+ st->codecpar->bit_rate = (int64_t)st->codecpar->sample_rate * (st->codecpar->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;
+ case 0:
+ if (offset > 0 && st->codecpar->block_align) // COMM && SSND
+ goto got_sound;
default: /* Jump */
avio_skip(pb, size);
}
@@ -278,7 +340,10 @@ static int aiff_read_header(AVFormatContext *s)
}
got_sound:
- if (!st->codecpar->block_align) {
+ if (!st->codecpar->block_align && st->codecpar->codec_id == AV_CODEC_ID_QCELP) {
+ av_log(s, AV_LOG_WARNING, "qcelp without wave chunk, assuming full rate\n");
+ st->codecpar->block_align = 35;
+ } else if (!st->codecpar->block_align) {
av_log(s, AV_LOG_ERROR, "could not find COMM tag or invalid block_align value\n");
return -1;
}
@@ -309,16 +374,29 @@ static int aiff_read_packet(AVFormatContext *s,
if (max_size <= 0)
return AVERROR_EOF;
+ if (!st->codecpar->block_align) {
+ av_log(s, AV_LOG_ERROR, "block_align not set\n");
+ return AVERROR_INVALIDDATA;
+ }
+
/* Now for that packet */
- if (st->codecpar->block_align >= 33) // GSM, QCLP, IMA4
+ switch (st->codecpar->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->codecpar->block_align;
- else
- size = (MAX_SIZE / st->codecpar->block_align) * st->codecpar->block_align;
+ break;
+ default:
+ size = st->codecpar->block_align ? (MAX_SIZE / st->codecpar->block_align) * st->codecpar->block_align : MAX_SIZE;
+ }
size = FFMIN(max_size, size);
res = av_get_packet(s->pb, pkt, size);
if (res < 0)
return res;
+ if (size >= st->codecpar->block_align)
+ pkt->flags &= ~AV_PKT_FLAG_CORRUPT;
/* Only one stream in an AIFF file */
pkt->stream_index = 0;
pkt->duration = (res / st->codecpar->block_align) * aiff->block_duration;
diff --git a/libavformat/aiffenc.c b/libavformat/aiffenc.c
index 191e746..aab3741 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 & AVIO_SEEKABLE_NORMAL)
+ 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;
- AVCodecParameters *par = s->streams[0]->codecpar;
+ AVCodecParameters *par;
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->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ aiff->audio_stream_idx = i;
+ } else if (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
+ av_log(s, AV_LOG_ERROR, "AIFF allows only one audio stream and a picture.\n");
+ return AVERROR(EINVAL);
+ }
+ }
+ if (aiff->audio_stream_idx < 0) {
+ av_log(s, AV_LOG_ERROR, "No audio stream present.\n");
+ return AVERROR(EINVAL);
+ }
+
+ par = s->streams[aiff->audio_stream_idx]->codecpar;
/* First verify if format is ok */
if (!par->codec_tag)
@@ -54,7 +136,6 @@ static int aiff_write_header(AVFormatContext *s)
ffio_wfourcc(pb, aifc ? "AIFC" : "AIFF");
if (aifc) { // compressed audio
- par->bits_per_coded_sample = 16;
if (!par->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 (par->channels > 2 && par->channel_layout) {
+ ffio_wfourcc(pb, "CHAN");
+ avio_wb32(pb, 12);
+ ff_mov_write_chan(pb, par->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,13 @@ static int aiff_write_header(AVFormatContext *s)
avio_wb16(pb, 0);
}
+ if ( (par->codec_tag == MKTAG('Q','D','M','2')
+ || par->codec_tag == MKTAG('Q','c','l','p')) && par->extradata_size) {
+ ffio_wfourcc(pb, "wave");
+ avio_wb32(pb, par->extradata_size);
+ avio_write(pb, par->extradata, par->extradata_size);
+ }
+
/* Sound data chunk */
ffio_wfourcc(pb, "SSND");
aiff->ssnd = avio_tell(pb); /* Sound chunk size */
@@ -100,7 +199,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]->codecpar->sample_rate);
+ avpriv_set_pts_info(s->streams[aiff->audio_stream_idx], 64, 1,
+ s->streams[aiff->audio_stream_idx]->codecpar->sample_rate);
/* Data is starting here */
avio_flush(pb);
@@ -110,16 +210,55 @@ 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]->codecpar->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);
+
+ ret = av_packet_ref(&pict_list->pkt, pkt);
+ if (ret < 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;
- AVCodecParameters *par = s->streams[0]->codecpar;
+ AVPacketList *pict_list = aiff->pict_list;
+ AVCodecParameters *par = s->streams[aiff->audio_stream_idx]->codecpar;
/* Chunks sizes must be even */
int64_t file_size, end_size;
@@ -130,10 +269,6 @@ static int aiff_write_trailer(AVFormatContext *s)
}
if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) {
- /* 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) / par->block_align);
@@ -145,12 +280,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_packet_unref(&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_BOOL, {.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 +327,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/aixdec.c b/libavformat/aixdec.c
new file mode 100644
index 0000000..cad8a1e
--- /dev/null
+++ b/libavformat/aixdec.c
@@ -0,0 +1,140 @@
+/*
+ * AIX demuxer
+ * Copyright (c) 2016 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 aix_probe(AVProbeData *p)
+{
+ if (AV_RL32(p->buf) != MKTAG('A','I','X','F') ||
+ AV_RB32(p->buf + 8) != 0x01000014 ||
+ AV_RB32(p->buf + 12) != 0x00000800)
+ return 0;
+
+ return AVPROBE_SCORE_MAX;
+}
+
+static int aix_read_header(AVFormatContext *s)
+{
+ unsigned nb_streams, first_offset, nb_segments;
+ unsigned stream_list_offset;
+ unsigned segment_list_offset = 0x20;
+ unsigned segment_list_entry_size = 0x10;
+ unsigned size;
+ int i;
+
+ avio_skip(s->pb, 4);
+ first_offset = avio_rb32(s->pb) + 8;
+ avio_skip(s->pb, 16);
+ nb_segments = avio_rb16(s->pb);
+ if (nb_segments == 0)
+ return AVERROR_INVALIDDATA;
+ stream_list_offset = segment_list_offset + segment_list_entry_size * nb_segments + 0x10;
+ if (stream_list_offset >= first_offset)
+ return AVERROR_INVALIDDATA;
+ avio_seek(s->pb, stream_list_offset, SEEK_SET);
+ nb_streams = avio_r8(s->pb);
+ if (nb_streams == 0)
+ return AVERROR_INVALIDDATA;
+ avio_skip(s->pb, 7);
+ for (i = 0; i < nb_streams; i++) {
+ AVStream *st = avformat_new_stream(s, NULL);
+
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_ADX;
+ st->codecpar->sample_rate = avio_rb32(s->pb);
+ st->codecpar->channels = avio_r8(s->pb);
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+ avio_skip(s->pb, 3);
+ }
+
+ avio_seek(s->pb, first_offset, SEEK_SET);
+ for (i = 0; i < nb_streams; i++) {
+ if (avio_rl32(s->pb) != MKTAG('A','I','X','P'))
+ return AVERROR_INVALIDDATA;
+ size = avio_rb32(s->pb);
+ if (size <= 8)
+ return AVERROR_INVALIDDATA;
+ avio_skip(s->pb, 8);
+ ff_get_extradata(s, s->streams[i]->codecpar, s->pb, size - 8);
+ }
+
+ return 0;
+}
+
+static int aix_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ unsigned size, index, duration, chunk;
+ int64_t pos;
+ int sequence, ret, i;
+
+ pos = avio_tell(s->pb);
+ if (avio_feof(s->pb))
+ return AVERROR_EOF;
+ chunk = avio_rl32(s->pb);
+ size = avio_rb32(s->pb);
+ if (chunk == MKTAG('A','I','X','E')) {
+ avio_skip(s->pb, size);
+ for (i = 0; i < s->nb_streams; i++) {
+ if (avio_feof(s->pb))
+ return AVERROR_EOF;
+ chunk = avio_rl32(s->pb);
+ size = avio_rb32(s->pb);
+ avio_skip(s->pb, size);
+ }
+ pos = avio_tell(s->pb);
+ chunk = avio_rl32(s->pb);
+ size = avio_rb32(s->pb);
+ }
+
+ if (chunk != MKTAG('A','I','X','P'))
+ return AVERROR_INVALIDDATA;
+ if (size <= 8)
+ return AVERROR_INVALIDDATA;
+ index = avio_r8(s->pb);
+ if (avio_r8(s->pb) != s->nb_streams || index >= s->nb_streams)
+ return AVERROR_INVALIDDATA;
+ duration = avio_rb16(s->pb);
+ sequence = avio_rb32(s->pb);
+ if (sequence < 0) {
+ avio_skip(s->pb, size - 8);
+ return 0;
+ }
+
+ ret = av_get_packet(s->pb, pkt, size - 8);
+ pkt->stream_index = index;
+ pkt->duration = duration;
+ pkt->pos = pos;
+ return ret;
+}
+
+AVInputFormat ff_aix_demuxer = {
+ .name = "aix",
+ .long_name = NULL_IF_CONFIG_SMALL("CRI AIX"),
+ .read_probe = aix_probe,
+ .read_header = aix_read_header,
+ .read_packet = aix_read_packet,
+ .extensions = "aix",
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index f51c4e7..405ddb5 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -2,23 +2,24 @@
* Register all the formats and protocols
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/thread.h"
#include "avformat.h"
#include "rtp.h"
#include "rdt.h"
@@ -41,103 +42,150 @@
#define REGISTER_MUXDEMUX(X, x) REGISTER_MUXER(X, x); REGISTER_DEMUXER(X, x)
-void av_register_all(void)
+static void register_all(void)
{
- static int initialized;
-
- if (initialized)
- return;
- initialized = 1;
-
avcodec_register_all();
/* (de)muxers */
REGISTER_MUXER (A64, a64);
+ REGISTER_DEMUXER (AA, aa);
REGISTER_DEMUXER (AAC, aac);
REGISTER_MUXDEMUX(AC3, ac3);
+ REGISTER_DEMUXER (ACM, acm);
+ REGISTER_DEMUXER (ACT, act);
+ REGISTER_DEMUXER (ADF, adf);
+ REGISTER_DEMUXER (ADP, adp);
+ REGISTER_DEMUXER (ADS, ads);
REGISTER_MUXER (ADTS, adts);
REGISTER_MUXDEMUX(ADX, adx);
REGISTER_DEMUXER (AEA, aea);
+ REGISTER_DEMUXER (AFC, afc);
REGISTER_MUXDEMUX(AIFF, aiff);
+ REGISTER_DEMUXER (AIX, aix);
REGISTER_MUXDEMUX(AMR, amr);
REGISTER_DEMUXER (ANM, anm);
REGISTER_DEMUXER (APC, apc);
REGISTER_DEMUXER (APE, ape);
+ REGISTER_MUXDEMUX(APNG, apng);
+ REGISTER_DEMUXER (AQTITLE, aqtitle);
REGISTER_MUXDEMUX(ASF, asf);
+ REGISTER_DEMUXER (ASF_O, asf_o);
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 (BFSTM, bfstm);
+ 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(DASH, dash);
+ REGISTER_MUXDEMUX(DATA, data);
REGISTER_MUXDEMUX(DAUD, daud);
+ REGISTER_DEMUXER (DCSTR, dcstr);
REGISTER_DEMUXER (DFA, dfa);
REGISTER_MUXDEMUX(DIRAC, dirac);
REGISTER_MUXDEMUX(DNXHD, dnxhd);
+ REGISTER_DEMUXER (DSF, dsf);
REGISTER_DEMUXER (DSICIN, dsicin);
REGISTER_DEMUXER (DSS, dss);
REGISTER_MUXDEMUX(DTS, dts);
+ REGISTER_DEMUXER (DTSHD, dtshd);
REGISTER_MUXDEMUX(DV, dv);
+ REGISTER_DEMUXER (DVBSUB, dvbsub);
+ REGISTER_DEMUXER (DVBTXT, dvbtxt);
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_MUXER (FIFO, fifo);
REGISTER_MUXDEMUX(FILMSTRIP, filmstrip);
+ REGISTER_MUXDEMUX(FITS, fits);
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 (FRAMEHASH, framehash);
REGISTER_MUXER (FRAMEMD5, framemd5);
+ REGISTER_DEMUXER (FRM, frm);
+ REGISTER_DEMUXER (FSB, fsb);
REGISTER_MUXDEMUX(G722, g722);
REGISTER_MUXDEMUX(G723_1, g723_1);
+ REGISTER_MUXDEMUX(G726, g726);
+ REGISTER_MUXDEMUX(G726LE, g726le);
REGISTER_DEMUXER (G729, g729);
- REGISTER_MUXER (GIF, gif);
- REGISTER_DEMUXER (GSM, gsm);
+ REGISTER_DEMUXER (GDV, gdv);
+ REGISTER_DEMUXER (GENH, genh);
+ REGISTER_MUXDEMUX(GIF, gif);
+ REGISTER_MUXDEMUX(GSM, gsm);
REGISTER_MUXDEMUX(GXF, gxf);
REGISTER_MUXDEMUX(H261, h261);
REGISTER_MUXDEMUX(H263, h263);
REGISTER_MUXDEMUX(H264, h264);
+ REGISTER_MUXER (HASH, hash);
REGISTER_MUXER (HDS, hds);
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_DEMUXER (IVR, ivr);
+ REGISTER_MUXDEMUX(JACOSUB, jacosub);
REGISTER_DEMUXER (JV, jv);
- REGISTER_MUXDEMUX(LATM, latm);
+ REGISTER_MUXER (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_DEMUXER (MJPEG_2000, mjpeg_2000);
REGISTER_MUXDEMUX(MLP, mlp);
+ REGISTER_DEMUXER (MLV, mlv);
REGISTER_DEMUXER (MM, mm);
REGISTER_MUXDEMUX(MMF, mmf);
REGISTER_MUXDEMUX(MOV, mov);
@@ -158,20 +206,28 @@ void av_register_all(void)
REGISTER_DEMUXER (MPEGTSRAW, mpegtsraw);
REGISTER_DEMUXER (MPEGVIDEO, mpegvideo);
REGISTER_MUXDEMUX(MPJPEG, mpjpeg);
+ REGISTER_DEMUXER (MPL2, mpl2);
+ REGISTER_DEMUXER (MPSUB, mpsub);
+ REGISTER_DEMUXER (MSF, msf);
REGISTER_DEMUXER (MSNWC_TCP, msnwc_tcp);
+ REGISTER_DEMUXER (MTAF, mtaf);
REGISTER_DEMUXER (MTV, mtv);
+ REGISTER_DEMUXER (MUSX, musx);
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);
REGISTER_DEMUXER (NUV, nuv);
REGISTER_MUXER (OGA, oga);
REGISTER_MUXDEMUX(OGG, ogg);
+ REGISTER_MUXER (OGV, ogv);
REGISTER_MUXDEMUX(OMA, oma);
REGISTER_MUXER (OPUS, opus);
REGISTER_DEMUXER (PAF, paf);
@@ -195,30 +251,45 @@ 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 (S337M, s337m);
+ REGISTER_DEMUXER (SAMI, sami);
REGISTER_MUXDEMUX(SAP, sap);
+ REGISTER_DEMUXER (SBG, sbg);
+ REGISTER_MUXDEMUX(SCC, scc);
REGISTER_DEMUXER (SDP, sdp);
+ REGISTER_DEMUXER (SDR2, sdr2);
+ REGISTER_DEMUXER (SDS, sds);
+ REGISTER_DEMUXER (SDX, sdx);
#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_MUXER (SINGLEJPEG, singlejpeg);
+ REGISTER_DEMUXER (SLN, sln);
REGISTER_DEMUXER (SMACKER, smacker);
REGISTER_MUXDEMUX(SMJPEG, smjpeg);
REGISTER_MUXER (SMOOTHSTREAMING, smoothstreaming);
@@ -229,33 +300,96 @@ 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_MUXDEMUX(SUP, sup);
+ REGISTER_DEMUXER (SVAG, svag);
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 (THREEDOSTR, threedostr);
REGISTER_DEMUXER (TIERTEXSEQ, tiertexseq);
+ REGISTER_MUXER (MKVTIMESTAMP_V2, mkvtimestamp_v2);
REGISTER_DEMUXER (TMV, tmv);
REGISTER_MUXDEMUX(TRUEHD, truehd);
- REGISTER_DEMUXER (TTA, tta);
+ REGISTER_MUXDEMUX(TTA, tta);
REGISTER_DEMUXER (TXD, txd);
REGISTER_DEMUXER (TTY, tty);
- REGISTER_DEMUXER (VC1, vc1);
+ REGISTER_MUXER (UNCODEDFRAMECRC, uncodedframecrc);
+ REGISTER_DEMUXER (V210, v210);
+ REGISTER_DEMUXER (V210X, v210x);
+ REGISTER_DEMUXER (VAG, vag);
+ 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 (VPK, vpk);
+ 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 (WEBM_CHUNK, webm_chunk);
+ REGISTER_MUXER (WEBP, webp);
+ REGISTER_MUXDEMUX(WEBVTT, webvtt);
REGISTER_DEMUXER (WSAUD, wsaud);
+ REGISTER_DEMUXER (WSD, wsd);
REGISTER_DEMUXER (WSVQA, wsvqa);
- REGISTER_DEMUXER (WTV, wtv);
+ REGISTER_MUXDEMUX(WTV, wtv);
+ REGISTER_DEMUXER (WVE, wve);
REGISTER_MUXDEMUX(WV, wv);
REGISTER_DEMUXER (XA, xa);
+ REGISTER_DEMUXER (XBIN, xbin);
REGISTER_DEMUXER (XMV, xmv);
+ REGISTER_DEMUXER (XVAG, xvag);
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_DDS_PIPE, image_dds_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_PAM_PIPE, image_pam_pipe);
+ REGISTER_DEMUXER (IMAGE_PBM_PIPE, image_pbm_pipe);
+ REGISTER_DEMUXER (IMAGE_PCX_PIPE, image_pcx_pipe);
+ REGISTER_DEMUXER (IMAGE_PGMYUV_PIPE, image_pgmyuv_pipe);
+ REGISTER_DEMUXER (IMAGE_PGM_PIPE, image_pgm_pipe);
+ REGISTER_DEMUXER (IMAGE_PICTOR_PIPE, image_pictor_pipe);
+ REGISTER_DEMUXER (IMAGE_PNG_PIPE, image_png_pipe);
+ REGISTER_DEMUXER (IMAGE_PPM_PIPE, image_ppm_pipe);
+ REGISTER_DEMUXER (IMAGE_PSD_PIPE, image_psd_pipe);
+ REGISTER_DEMUXER (IMAGE_QDRAW_PIPE, image_qdraw_pipe);
+ REGISTER_DEMUXER (IMAGE_SGI_PIPE, image_sgi_pipe);
+ REGISTER_DEMUXER (IMAGE_SVG_PIPE, image_svg_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);
+ REGISTER_DEMUXER (IMAGE_XPM_PIPE, image_xpm_pipe);
+
+ /* external libraries */
+ REGISTER_MUXER (CHROMAPRINT, chromaprint);
+ REGISTER_DEMUXER (LIBGME, libgme);
+ REGISTER_DEMUXER (LIBMODPLUG, libmodplug);
+ REGISTER_DEMUXER (LIBOPENMPT, libopenmpt);
+}
+
+void av_register_all(void)
+{
+ static AVOnce control = AV_ONCE_INIT;
+
+ ff_thread_once(&control, register_all);
}
diff --git a/libavformat/amr.c b/libavformat/amr.c
index cdba27d..b5194a2 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,9 +115,10 @@ static int amr_read_packet(AVFormatContext *s, AVPacket *pkt)
AVCodecParameters *par = s->streams[0]->codecpar;
int read, size = 0, toc, mode;
int64_t pos = avio_tell(s->pb);
+ AMRContext *amr = s->priv_data;
- if (s->pb->eof_reached) {
- return AVERROR(EIO);
+ if (avio_feof(s->pb)) {
+ return AVERROR_EOF;
}
// FIXME this is wrong, this should rather be in an AVParser
@@ -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]->codecpar->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]->codecpar->bit_rate = amr->cumulated_size / ++amr->block_count * 8 * 50;
+ }
pkt->stream_index = 0;
pkt->pos = pos;
@@ -149,6 +156,8 @@ static int amr_read_packet(AVFormatContext *s, AVPacket *pkt)
if (read != size - 1) {
av_packet_unref(pkt);
+ if (read < 0)
+ return read;
return AVERROR(EIO);
}
@@ -159,6 +168,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 2ab6712..b31757a 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 40ecdf2..b180a50 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->codecpar->sample_rate = avio_rl32(pb);
- st->codecpar->extradata_size = 2 * 4;
- st->codecpar->extradata = av_malloc(st->codecpar->extradata_size +
- AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
- return AVERROR(ENOMEM);
-
/* initial predictor values for adpcm decoder */
- avio_read(pb, st->codecpar->extradata, 2 * 4);
+ if (ff_get_extradata(s, st->codecpar, pb, 2 * 4) < 0)
+ return AVERROR(ENOMEM);
if (avio_rl32(pb)) {
st->codecpar->channels = 2;
@@ -69,7 +65,7 @@ static int apc_read_header(AVFormatContext *s)
}
st->codecpar->bits_per_coded_sample = 4;
- st->codecpar->bit_rate = st->codecpar->bits_per_coded_sample * st->codecpar->channels
+ st->codecpar->bit_rate = (int64_t)st->codecpar->bits_per_coded_sample * st->codecpar->channels
* st->codecpar->sample_rate;
st->codecpar->block_align = 1;
@@ -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 010b795..c06db78 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 {
@@ -59,7 +57,6 @@ typedef struct APEContext {
APEFrame *frames;
/* Info from Descriptor Block */
- char magic[4];
int16_t fileversion;
int16_t padding1;
uint32_t descriptorlength;
@@ -88,19 +85,22 @@ 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;
}
-#ifdef DEBUG
static void ape_dumpinfo(AVFormatContext * s, APEContext * ape_ctx)
{
+#ifdef DEBUG
int i;
av_log(s, AV_LOG_DEBUG, "Descriptor Block:\n\n");
- av_log(s, AV_LOG_DEBUG, "magic = \"%c%c%c%c\"\n", ape_ctx->magic[0], ape_ctx->magic[1], ape_ctx->magic[2], ape_ctx->magic[3]);
av_log(s, AV_LOG_DEBUG, "fileversion = %"PRId16"\n", ape_ctx->fileversion);
av_log(s, AV_LOG_DEBUG, "descriptorlength = %"PRIu32"\n", ape_ctx->descriptorlength);
av_log(s, AV_LOG_DEBUG, "headerlength = %"PRIu32"\n", ape_ctx->headerlength);
@@ -154,10 +154,8 @@ static void ape_dumpinfo(AVFormatContext * s, APEContext * ape_ctx)
av_log(s, AV_LOG_DEBUG, "junklength = %"PRIu32"\n", ape_ctx->junklength);
av_log(s, AV_LOG_DEBUG, "firstframe = %"PRIu32"\n", ape_ctx->firstframe);
av_log(s, AV_LOG_DEBUG, "totalsamples = %"PRIu32"\n", ape_ctx->totalsamples);
-}
-#else
-#define ape_dumpinfo(s, ape_ctx) do {} while(0)
#endif
+}
static int ape_read_header(AVFormatContext * s)
{
@@ -174,14 +172,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) {
@@ -260,15 +258,15 @@ 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;
}
- ape->frames = av_malloc(ape->totalframes * sizeof(APEFrame));
+ ape->frames = av_malloc_array(ape->totalframes, sizeof(APEFrame));
if(!ape->frames)
return AVERROR(ENOMEM);
ape->firstframe = ape->junklength + ape->descriptorlength + ape->headerlength + ape->seektablelength + ape->wavheaderlength;
@@ -282,18 +280,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;
@@ -335,14 +335,14 @@ static int ape_read_header(AVFormatContext * s)
ape_dumpinfo(s, ape);
- av_log(s, AV_LOG_DEBUG, "Decoding file - v%d.%02d, compression level %"PRIu16"\n",
+ av_log(s, AV_LOG_VERBOSE, "Decoding file - v%d.%02d, compression level %"PRIu16"\n",
ape->fileversion / 1000, (ape->fileversion % 1000) / 10,
ape->compressiontype);
/* 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;
@@ -355,11 +355,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->codecpar->extradata = av_malloc(APE_EXTRADATA_SIZE);
- st->codecpar->extradata_size = APE_EXTRADATA_SIZE;
+ if (ff_alloc_extradata(st->codecpar, APE_EXTRADATA_SIZE))
+ return AVERROR(ENOMEM);
AV_WL16(st->codecpar->extradata + 0, ape->fileversion);
AV_WL16(st->codecpar->extradata + 2, ape->compressiontype);
AV_WL16(st->codecpar->extradata + 4, ape->formatflags);
@@ -368,7 +368,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 */
@@ -387,7 +387,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;
@@ -415,6 +415,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_packet_unref(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 a7cf853..cdc602e 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,9 +29,6 @@
#include "apetag.h"
#include "internal.h"
-#define APE_TAG_VERSION 2000
-#define APE_TAG_FOOTER_BYTES 32
-#define APE_TAG_HEADER_BYTES 32
#define APE_TAG_FLAG_CONTAINS_HEADER (1 << 31)
#define APE_TAG_FLAG_LACKS_FOOTER (1 << 30)
#define APE_TAG_FLAG_IS_HEADER (1 << 29)
@@ -65,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);
@@ -95,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->codecpar->extradata = av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
+ if (ff_get_extradata(s, st->codecpar, s->pb, size) < 0)
return AVERROR(ENOMEM);
- if (avio_read(pb, st->codecpar->extradata, size) != size) {
- av_freep(&st->codecpar->extradata);
- return AVERROR(EIO);
- }
- st->codecpar->extradata_size = size;
st->codecpar->codec_type = AVMEDIA_TYPE_ATTACHMENT;
}
} else {
@@ -135,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;
}
@@ -181,42 +176,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;
+ int size, ret, count = 0;
+ AVIOContext *dyn_bc = NULL;
+ uint8_t *dyn_buf = NULL;
- if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL))
- return 0;
+ if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0)
+ goto end;
+
+ ff_standardize_creation_time(s);
+ while ((e = av_dict_get(s->metadata, "", e, AV_DICT_IGNORE_SUFFIX))) {
+ int val_len;
+
+ 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;
- start = avio_tell(s->pb);
+ size = avio_close_dyn_buf(dyn_bc, &dyn_buf);
+ if (size <= 0)
+ goto end;
+ size += APE_TAG_FOOTER_BYTES;
// 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
+ avio_wl32(s->pb, APE_TAG_VERSION); // version
+ avio_wl32(s->pb, size);
+ avio_wl32(s->pb, count);
// flags
avio_wl32(s->pb, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_IS_HEADER);
ffio_fill(s->pb, 0, 8); // reserved
- while ((e = av_dict_get(s->metadata, "", e, AV_DICT_IGNORE_SUFFIX))) {
- int val_len = strlen(e->value);
-
- 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
- count++;
- }
-
- size = avio_tell(s->pb) - start;
+ avio_write(s->pb, dyn_buf, size - APE_TAG_FOOTER_BYTES);
// 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
@@ -224,12 +238,10 @@ int ff_ape_write_tag(AVFormatContext *s)
avio_wl32(s->pb, APE_TAG_FLAG_CONTAINS_HEADER);
ffio_fill(s->pb, 0, 8); // reserved
- // update values in the header
- end = avio_tell(s->pb);
- avio_seek(s->pb, start + 12, SEEK_SET);
- avio_wl32(s->pb, size);
- avio_wl32(s->pb, count);
- avio_seek(s->pb, end, SEEK_SET);
+end:
+ if (dyn_bc && !dyn_buf)
+ avio_close_dyn_buf(dyn_bc, &dyn_buf);
+ av_freep(&dyn_buf);
- return 0;
+ return ret;
}
diff --git a/libavformat/apetag.h b/libavformat/apetag.h
index 36e3211..49cd10e 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,11 @@
#include "avformat.h"
+#define APE_TAG_PREAMBLE "APETAGEX"
+#define APE_TAG_VERSION 2000
+#define APE_TAG_FOOTER_BYTES 32
+#define APE_TAG_HEADER_BYTES 32
+
/**
* Read and parse an APE tag
*
diff --git a/libavformat/apngdec.c b/libavformat/apngdec.c
new file mode 100644
index 0000000..ffff037
--- /dev/null
+++ b/libavformat/apngdec.c
@@ -0,0 +1,443 @@
+/*
+ * 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(AVCodecParameters *par, AVIOContext *pb, int len)
+{
+ int previous_size = par->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(par->extradata, new_size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!new_extradata)
+ return AVERROR(ENOMEM);
+ par->extradata = new_extradata;
+ par->extradata_size = new_size;
+
+ if ((ret = avio_read(pb, par->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 acTL_found = 0;
+ int64_t ret = AVERROR_INVALIDDATA;
+
+ /* 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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_id = AV_CODEC_ID_APNG;
+ st->codecpar->width = avio_rb32(pb);
+ st->codecpar->height = avio_rb32(pb);
+ if ((ret = av_image_check_size(st->codecpar->width, st->codecpar->height, 0, s)) < 0)
+ return ret;
+
+ /* extradata will contain every chunk up to the first fcTL (excluded) */
+ st->codecpar->extradata = av_malloc(len + 12 + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!st->codecpar->extradata)
+ return AVERROR(ENOMEM);
+ st->codecpar->extradata_size = len + 12;
+ AV_WB32(st->codecpar->extradata, len);
+ AV_WL32(st->codecpar->extradata+4, tag);
+ AV_WB32(st->codecpar->extradata+8, st->codecpar->width);
+ AV_WB32(st->codecpar->extradata+12, st->codecpar->height);
+ if ((ret = avio_read(pb, st->codecpar->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->codecpar, pb, len + 12)) < 0)
+ goto fail;
+ acTL_found = 1;
+ ctx->num_frames = AV_RB32(st->codecpar->extradata + ret + 8);
+ ctx->num_play = AV_RB32(st->codecpar->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->codecpar, pb, len + 12)) < 0)
+ goto fail;
+ }
+ }
+
+fail:
+ if (st->codecpar->extradata_size) {
+ av_freep(&st->codecpar->extradata);
+ st->codecpar->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 || (ctx->max_fps && 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]->codecpar->width ||
+ height != s->streams[0]->codecpar->height ||
+ x_offset != 0 ||
+ y_offset != 0) {
+ if (sequence_number == 0 ||
+ x_offset >= s->streams[0]->codecpar->width ||
+ width > s->streams[0]->codecpar->width - x_offset ||
+ y_offset >= s->streams[0]->codecpar->height ||
+ height > s->streams[0]->codecpar->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;
+ int64_t 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]->codecpar->extradata_size + 8, SEEK_SET)) < 0)
+ return ret;
+ return 0;
+ default:
+ avpriv_request_sample(s, "In-stream tag=%s (0x%08"PRIX32") len=%"PRIu32,
+ av_fourcc2str(tag), 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_BOOL, { .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 = 0 }, 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/apngenc.c b/libavformat/apngenc.c
new file mode 100644
index 0000000..77c1c91
--- /dev/null
+++ b/libavformat/apngenc.c
@@ -0,0 +1,305 @@
+/*
+ * APNG muxer
+ * Copyright (c) 2015 Donny Yang
+ *
+ * first version by Donny Yang <work@kota.moe>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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/avassert.h"
+#include "libavutil/crc.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/log.h"
+#include "libavutil/opt.h"
+#include "libavcodec/png.h"
+#include "libavcodec/apng.h"
+
+typedef struct APNGMuxContext {
+ AVClass *class;
+
+ uint32_t plays;
+ AVRational last_delay;
+
+ uint64_t acTL_offset;
+ uint32_t frame_number;
+
+ AVPacket *prev_packet;
+ AVRational prev_delay;
+
+ int framerate_warned;
+
+ uint8_t *extra_data;
+ int extra_data_size;
+} APNGMuxContext;
+
+static uint8_t *apng_find_chunk(uint32_t tag, uint8_t *buf, size_t length)
+{
+ size_t b;
+ for (b = 0; b < length; b += AV_RB32(buf + b) + 12)
+ if (AV_RB32(&buf[b + 4]) == tag)
+ return &buf[b];
+ return NULL;
+}
+
+static void apng_write_chunk(AVIOContext *io_context, uint32_t tag,
+ uint8_t *buf, size_t length)
+{
+ const AVCRC *crc_table = av_crc_get_table(AV_CRC_32_IEEE_LE);
+ uint32_t crc = ~0U;
+ uint8_t tagbuf[4];
+
+ av_assert0(crc_table);
+
+ avio_wb32(io_context, length);
+ AV_WB32(tagbuf, tag);
+ crc = av_crc(crc_table, crc, tagbuf, 4);
+ avio_wb32(io_context, tag);
+ if (length > 0) {
+ crc = av_crc(crc_table, crc, buf, length);
+ avio_write(io_context, buf, length);
+ }
+ avio_wb32(io_context, ~crc);
+}
+
+static int apng_write_header(AVFormatContext *format_context)
+{
+ APNGMuxContext *apng = format_context->priv_data;
+ AVCodecParameters *par = format_context->streams[0]->codecpar;
+
+ if (format_context->nb_streams != 1 ||
+ format_context->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO ||
+ format_context->streams[0]->codecpar->codec_id != AV_CODEC_ID_APNG) {
+ av_log(format_context, AV_LOG_ERROR,
+ "APNG muxer supports only a single video APNG stream.\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (apng->last_delay.num > USHRT_MAX || apng->last_delay.den > USHRT_MAX) {
+ av_reduce(&apng->last_delay.num, &apng->last_delay.den,
+ apng->last_delay.num, apng->last_delay.den, USHRT_MAX);
+ av_log(format_context, AV_LOG_WARNING,
+ "Last frame delay is too precise. Reducing to %d/%d (%f).\n",
+ apng->last_delay.num, apng->last_delay.den, (double)apng->last_delay.num / apng->last_delay.den);
+ }
+
+ avio_wb64(format_context->pb, PNGSIG);
+ // Remaining headers are written when they are copied from the encoder
+
+ if (par->extradata_size) {
+ apng->extra_data = av_mallocz(par->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!apng->extra_data)
+ return AVERROR(ENOMEM);
+ apng->extra_data_size = par->extradata_size;
+ memcpy(apng->extra_data, par->extradata, par->extradata_size);
+ }
+
+ return 0;
+}
+
+static int flush_packet(AVFormatContext *format_context, AVPacket *packet)
+{
+ APNGMuxContext *apng = format_context->priv_data;
+ AVIOContext *io_context = format_context->pb;
+ AVStream *codec_stream = format_context->streams[0];
+ uint8_t *side_data = NULL;
+ int side_data_size = 0;
+
+ av_assert0(apng->prev_packet);
+
+ side_data = av_packet_get_side_data(apng->prev_packet, AV_PKT_DATA_NEW_EXTRADATA, &side_data_size);
+
+ if (side_data_size) {
+ av_freep(&apng->extra_data);
+ apng->extra_data = av_mallocz(side_data_size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!apng->extra_data)
+ return AVERROR(ENOMEM);
+ apng->extra_data_size = side_data_size;
+ memcpy(apng->extra_data, side_data, apng->extra_data_size);
+ }
+
+ if (apng->frame_number == 0 && !packet) {
+ uint8_t *existing_acTL_chunk;
+ uint8_t *existing_fcTL_chunk;
+
+ av_log(format_context, AV_LOG_INFO, "Only a single frame so saving as a normal PNG.\n");
+
+ // Write normal PNG headers without acTL chunk
+ existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), apng->extra_data, apng->extra_data_size);
+ if (existing_acTL_chunk) {
+ uint8_t *chunk_after_acTL = existing_acTL_chunk + AV_RB32(existing_acTL_chunk) + 12;
+ avio_write(io_context, apng->extra_data, existing_acTL_chunk - apng->extra_data);
+ avio_write(io_context, chunk_after_acTL, apng->extra_data + apng->extra_data_size - chunk_after_acTL);
+ } else {
+ avio_write(io_context, apng->extra_data, apng->extra_data_size);
+ }
+
+ // Write frame data without fcTL chunk
+ existing_fcTL_chunk = apng_find_chunk(MKBETAG('f', 'c', 'T', 'L'), apng->prev_packet->data, apng->prev_packet->size);
+ if (existing_fcTL_chunk) {
+ uint8_t *chunk_after_fcTL = existing_fcTL_chunk + AV_RB32(existing_fcTL_chunk) + 12;
+ avio_write(io_context, apng->prev_packet->data, existing_fcTL_chunk - apng->prev_packet->data);
+ avio_write(io_context, chunk_after_fcTL, apng->prev_packet->data + apng->prev_packet->size - chunk_after_fcTL);
+ } else {
+ avio_write(io_context, apng->prev_packet->data, apng->prev_packet->size);
+ }
+ } else {
+ uint8_t *existing_fcTL_chunk;
+
+ if (apng->frame_number == 0) {
+ uint8_t *existing_acTL_chunk;
+
+ // Write normal PNG headers
+ avio_write(io_context, apng->extra_data, apng->extra_data_size);
+
+ existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), apng->extra_data, apng->extra_data_size);
+ if (!existing_acTL_chunk) {
+ uint8_t buf[8];
+ // Write animation control header
+ apng->acTL_offset = avio_tell(io_context);
+ AV_WB32(buf, UINT_MAX); // number of frames (filled in later)
+ AV_WB32(buf + 4, apng->plays);
+ apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8);
+ }
+ }
+
+ existing_fcTL_chunk = apng_find_chunk(MKBETAG('f', 'c', 'T', 'L'), apng->prev_packet->data, apng->prev_packet->size);
+ if (existing_fcTL_chunk) {
+ AVRational delay;
+
+ existing_fcTL_chunk += 8;
+ delay.num = AV_RB16(existing_fcTL_chunk + 20);
+ delay.den = AV_RB16(existing_fcTL_chunk + 22);
+
+ if (delay.num == 0 && delay.den == 0) {
+ if (packet) {
+ int64_t delay_num_raw = (packet->dts - apng->prev_packet->dts) * codec_stream->time_base.num;
+ int64_t delay_den_raw = codec_stream->time_base.den;
+ if (!av_reduce(&delay.num, &delay.den, delay_num_raw, delay_den_raw, USHRT_MAX) &&
+ !apng->framerate_warned) {
+ av_log(format_context, AV_LOG_WARNING,
+ "Frame rate is too high or specified too precisely. Unable to copy losslessly.\n");
+ apng->framerate_warned = 1;
+ }
+ } else if (apng->last_delay.num > 0) {
+ delay = apng->last_delay;
+ } else {
+ delay = apng->prev_delay;
+ }
+
+ // Update frame control header with new delay
+ AV_WB16(existing_fcTL_chunk + 20, delay.num);
+ AV_WB16(existing_fcTL_chunk + 22, delay.den);
+ AV_WB32(existing_fcTL_chunk + 26, ~av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE), ~0U, existing_fcTL_chunk - 4, 26 + 4));
+ }
+ apng->prev_delay = delay;
+ }
+
+ // Write frame data
+ avio_write(io_context, apng->prev_packet->data, apng->prev_packet->size);
+ }
+ ++apng->frame_number;
+
+ av_packet_unref(apng->prev_packet);
+ if (packet)
+ av_packet_ref(apng->prev_packet, packet);
+ return 0;
+}
+
+static int apng_write_packet(AVFormatContext *format_context, AVPacket *packet)
+{
+ APNGMuxContext *apng = format_context->priv_data;
+ int ret;
+
+ if (!apng->prev_packet) {
+ apng->prev_packet = av_packet_alloc();
+ if (!apng->prev_packet)
+ return AVERROR(ENOMEM);
+
+ av_packet_ref(apng->prev_packet, packet);
+ } else {
+ ret = flush_packet(format_context, packet);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int apng_write_trailer(AVFormatContext *format_context)
+{
+ APNGMuxContext *apng = format_context->priv_data;
+ AVIOContext *io_context = format_context->pb;
+ uint8_t buf[8];
+ int ret;
+
+ if (apng->prev_packet) {
+ ret = flush_packet(format_context, NULL);
+ av_freep(&apng->prev_packet);
+ if (ret < 0)
+ return ret;
+ }
+
+ apng_write_chunk(io_context, MKBETAG('I', 'E', 'N', 'D'), NULL, 0);
+
+ if (apng->acTL_offset && (io_context->seekable & AVIO_SEEKABLE_NORMAL)) {
+ avio_seek(io_context, apng->acTL_offset, SEEK_SET);
+
+ AV_WB32(buf, apng->frame_number);
+ AV_WB32(buf + 4, apng->plays);
+ apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8);
+ }
+
+ av_freep(&apng->extra_data);
+ apng->extra_data = 0;
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(APNGMuxContext, x)
+#define ENC AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ { "plays", "Number of times to play the output: 0 - infinite loop, 1 - no loop", OFFSET(plays),
+ AV_OPT_TYPE_INT, { .i64 = 1 }, 0, UINT_MAX, ENC },
+ { "final_delay", "Force delay after the last frame", OFFSET(last_delay),
+ AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, USHRT_MAX, ENC },
+ { NULL },
+};
+
+static const AVClass apng_muxer_class = {
+ .class_name = "APNG muxer",
+ .item_name = av_default_item_name,
+ .version = LIBAVUTIL_VERSION_INT,
+ .option = options,
+};
+
+AVOutputFormat ff_apng_muxer = {
+ .name = "apng",
+ .long_name = NULL_IF_CONFIG_SMALL("Animated Portable Network Graphics"),
+ .mime_type = "image/png",
+ .extensions = "apng",
+ .priv_data_size = sizeof(APNGMuxContext),
+ .audio_codec = AV_CODEC_ID_NONE,
+ .video_codec = AV_CODEC_ID_APNG,
+ .write_header = apng_write_header,
+ .write_packet = apng_write_packet,
+ .write_trailer = apng_write_trailer,
+ .priv_class = &apng_muxer_class,
+ .flags = AVFMT_VARIABLE_FPS,
+};
diff --git a/libavformat/aqtitledec.c b/libavformat/aqtitledec.c
new file mode 100644
index 0000000..f0e840b
--- /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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->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(s, &aqt->q);
+ return 0;
+}
+
+static int aqt_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AQTitleContext *aqt = s->priv_data;
+ return ff_subtitles_queue_read_packet(&aqt->q, pkt);
+}
+
+static int aqt_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ AQTitleContext *aqt = s->priv_data;
+ return ff_subtitles_queue_seek(&aqt->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int aqt_read_close(AVFormatContext *s)
+{
+ AQTitleContext *aqt = s->priv_data;
+ ff_subtitles_queue_clean(&aqt->q);
+ return 0;
+}
+
+#define OFFSET(x) offsetof(AQTitleContext, x)
+#define SD AV_OPT_FLAG_SUBTITLE_PARAM|AV_OPT_FLAG_DECODING_PARAM
+static const AVOption aqt_options[] = {
+ { "subfps", "set the movie frame rate", OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, {.dbl=25}, 0, INT_MAX, SD },
+ { NULL }
+};
+
+static const AVClass aqt_class = {
+ .class_name = "aqtdec",
+ .item_name = av_default_item_name,
+ .option = aqt_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_aqtitle_demuxer = {
+ .name = "aqtitle",
+ .long_name = NULL_IF_CONFIG_SMALL("AQTitle subtitles"),
+ .priv_data_size = sizeof(AQTitleContext),
+ .read_probe = aqt_probe,
+ .read_header = aqt_read_header,
+ .read_packet = aqt_read_packet,
+ .read_seek2 = aqt_read_seek,
+ .read_close = aqt_read_close,
+ .extensions = "aqt",
+ .priv_class = &aqt_class,
+};
diff --git a/libavformat/asf.c b/libavformat/asf.c
index ec34b50..719cae9 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
*/
@@ -143,6 +143,18 @@ const ff_asf_guid ff_asf_digital_signature = {
0xfc, 0xb3, 0x11, 0x22, 0x23, 0xbd, 0xd2, 0x11, 0xb4, 0xb7, 0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e
};
+const ff_asf_guid ff_asf_extended_stream_properties_object = {
+ 0xcb, 0xa5, 0xe6, 0x14, 0x72, 0xc6, 0x32, 0x43, 0x83, 0x99, 0xa9, 0x69, 0x52, 0x06, 0x5b, 0x5a
+};
+
+const ff_asf_guid ff_asf_group_mutual_exclusion_object = {
+ 0x40, 0x5a, 0x46, 0xd1, 0x79, 0x5a, 0x38, 0x43, 0xb7, 0x1b, 0xe3, 0x6b, 0x8f, 0xd6, 0xc2, 0x49
+};
+
+const ff_asf_guid ff_asf_mutex_language = {
+ 0x00, 0x2a, 0xe2, 0xd6, 0xda, 0x35, 0xd1, 0x11, 0x90, 0x34, 0x00, 0xa0, 0xc9, 0x03, 0x49, 0xbe
+};
+
/* List of official tags at http://msdn.microsoft.com/en-us/library/dd743066(VS.85).aspx */
const AVMetadataConv ff_asf_metadata_conv[] = {
{ "WM/AlbumArtist", "album_artist" },
@@ -159,7 +171,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 6130e7a..1a5b338 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
*/
@@ -26,7 +26,15 @@
#include "metadata.h"
#include "riff.h"
-#define PACKET_SIZE 3200
+typedef enum ASFDataType {
+ ASF_UNICODE = 0,
+ ASF_BYTE_ARRAY = 1,
+ ASF_BOOL = 2,
+ ASF_DWORD = 3,
+ ASF_QWORD = 4,
+ ASF_WORD = 5,
+ ASF_GUID = 6,
+}ASFDataType;
typedef struct ASFMainHeader {
ff_asf_guid guid; ///< generated by client computer
@@ -91,6 +99,9 @@ extern const ff_asf_guid ff_asf_language_guid;
extern const ff_asf_guid ff_asf_content_encryption;
extern const ff_asf_guid ff_asf_ext_content_encryption;
extern const ff_asf_guid ff_asf_digital_signature;
+extern const ff_asf_guid ff_asf_extended_stream_properties_object;
+extern const ff_asf_guid ff_asf_group_mutual_exclusion_object;
+extern const ff_asf_guid ff_asf_mutex_language;
extern const AVMetadataConv ff_asf_metadata_conv[];
diff --git a/libavformat/asfcrypt.c b/libavformat/asfcrypt.c
index 891d4db..221a8a8 100644
--- a/libavformat/asfcrypt.c
+++ b/libavformat/asfcrypt.c
@@ -3,20 +3,20 @@
* Copyright (c) 2007 Reimar Doeffinger
* This is a rewrite of code contained in freeme/freeme2
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/asfcrypt.h b/libavformat/asfcrypt.h
index 53388b4..8b80d63 100644
--- a/libavformat/asfcrypt.h
+++ b/libavformat/asfcrypt.h
@@ -2,20 +2,20 @@
* ASF decryption
* Copyright (c) 2007 Reimar Doeffinger
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/asfdec_f.c b/libavformat/asfdec_f.c
new file mode 100644
index 0000000..64a0b9d
--- /dev/null
+++ b/libavformat/asfdec_f.c
@@ -0,0 +1,1705 @@
+/*
+ * ASF compatible demuxer
+ * Copyright (c) 2000, 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 <inttypes.h>
+
+#include "libavutil/attributes.h"
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/bswap.h"
+#include "libavutil/common.h"
+#include "libavutil/dict.h"
+#include "libavutil/internal.h"
+#include "libavutil/mathematics.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "avio_internal.h"
+#include "avlanguage.h"
+#include "id3v2.h"
+#include "internal.h"
+#include "riff.h"
+#include "asf.h"
+#include "asfcrypt.h"
+
+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;
+ int ds_chunk_size;
+
+ int64_t packet_pos;
+
+ uint16_t stream_language_index;
+
+ int palette_changed;
+ uint32_t palette[256];
+
+ int payload_ext_ct;
+ ASFPayload payload[8];
+} ASFStream;
+
+typedef struct ASFContext {
+ const AVClass *class;
+ int asfid2avid[128]; ///< conversion table from asf ID 2 AVStream ID
+ ASFStream streams[128]; ///< it's max number and it's not that big
+ uint32_t stream_bitrates[128]; ///< max number of streams, bitrate for each (for streaming)
+ AVRational dar[128];
+ char stream_languages[128][6]; ///< max number of streams, language for each (RFC1766, e.g. en-US)
+ /* non streamed additonnal info */
+ /* packet filling */
+ int packet_size_left;
+ /* only for reading */
+ uint64_t data_offset; ///< beginning of the first data packet
+ uint64_t data_object_offset; ///< data object offset (excl. GUID & size)
+ uint64_t data_object_size; ///< size of the data object
+ int index_read;
+
+ ASFMainHeader hdr;
+
+ int packet_flags;
+ int packet_property;
+ int packet_timestamp;
+ int packet_segsizetype;
+ int packet_segments;
+ int packet_seq;
+ int packet_replic_size;
+ int packet_key_frame;
+ int packet_padsize;
+ 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_time_delta;
+ int packet_time_start;
+ int64_t packet_pos;
+
+ int stream_index;
+
+ ASFStream *asf_st; ///< currently decoded stream
+
+ int no_resync_search;
+ int export_xmp;
+
+ int uses_std_ecc;
+} ASFContext;
+
+static const AVOption options[] = {
+ { "no_resync_search", "Don't try to resynchronize by looking for a certain optional start code", offsetof(ASFContext, no_resync_search), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
+ { "export_xmp", "Export full XMP metadata", offsetof(ASFContext, export_xmp), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+static const AVClass asf_class = {
+ .class_name = "asf demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+#undef NDEBUG
+#include <assert.h>
+
+#define ASF_MAX_STREAMS 127
+#define FRAME_HEADER_SIZE 6
+// Fix Me! FRAME_HEADER_SIZE may be different.
+// (7 is known to be too large for GipsyGuitar.wmv)
+
+#ifdef DEBUG
+static const ff_asf_guid stream_bitrate_guid = { /* (http://get.to/sdp) */
+ 0xce, 0x75, 0xf8, 0x7b, 0x8d, 0x46, 0xd1, 0x11, 0x8d, 0x82, 0x00, 0x60, 0x97, 0xc9, 0xa2, 0xb2
+};
+
+#define PRINT_IF_GUID(g, cmp) \
+ if (!ff_guidcmp(g, &cmp)) \
+ av_log(NULL, AV_LOG_TRACE, "(GUID: %s) ", # cmp)
+
+static void print_guid(ff_asf_guid *g)
+{
+ int i;
+ PRINT_IF_GUID(g, ff_asf_header);
+ else PRINT_IF_GUID(g, ff_asf_file_header);
+ else PRINT_IF_GUID(g, ff_asf_stream_header);
+ else PRINT_IF_GUID(g, ff_asf_audio_stream);
+ else PRINT_IF_GUID(g, ff_asf_audio_conceal_none);
+ else PRINT_IF_GUID(g, ff_asf_video_stream);
+ else PRINT_IF_GUID(g, ff_asf_video_conceal_none);
+ else PRINT_IF_GUID(g, ff_asf_command_stream);
+ else PRINT_IF_GUID(g, ff_asf_comment_header);
+ 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, 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);
+ else PRINT_IF_GUID(g, ff_asf_ext_stream_header);
+ else PRINT_IF_GUID(g, ff_asf_extended_content_header);
+ else PRINT_IF_GUID(g, ff_asf_ext_stream_embed_stream_header);
+ else PRINT_IF_GUID(g, ff_asf_ext_stream_audio_stream);
+ else PRINT_IF_GUID(g, ff_asf_metadata_header);
+ else PRINT_IF_GUID(g, ff_asf_metadata_library_header);
+ else PRINT_IF_GUID(g, ff_asf_marker_header);
+ else PRINT_IF_GUID(g, stream_bitrate_guid);
+ else PRINT_IF_GUID(g, ff_asf_language_guid);
+ else
+ av_log(NULL, AV_LOG_TRACE, "(GUID: unknown) ");
+ for (i = 0; i < 16; i++)
+ av_log(NULL, AV_LOG_TRACE, " 0x%02x,", (*g)[i]);
+ av_log(NULL, AV_LOG_TRACE, "}\n");
+}
+#undef PRINT_IF_GUID
+#else
+#define print_guid(g) while(0)
+#endif
+
+static int asf_probe(AVProbeData *pd)
+{
+ /* check file header */
+ if (!ff_guidcmp(pd->buf, &ff_asf_header))
+ return AVPROBE_SCORE_MAX;
+ else
+ return 0;
+}
+
+/* size of type 2 (BOOL) is 32bit for "Extended Content Description Object"
+ * but 16 bit for "Metadata Object" and "Metadata Library Object" */
+static int get_value(AVIOContext *pb, int type, int type2_size)
+{
+ switch (type) {
+ case 2:
+ return (type2_size == 32) ? avio_rl32(pb) : avio_rl16(pb);
+ case 3:
+ return avio_rl32(pb);
+ case 4:
+ return avio_rl64(pb);
+ case 5:
+ return avio_rl16(pb);
+ default:
+ return INT_MIN;
+ }
+}
+
+/* MSDN claims that this should be "compatible with the ID3 frame, APIC",
+ * but in reality this is only loosely similar */
+static int asf_read_picture(AVFormatContext *s, int len)
+{
+ AVPacket pkt = { 0 };
+ const CodecMime *mime = ff_id3v2_mime_tags;
+ enum AVCodecID id = AV_CODEC_ID_NONE;
+ char mimetype[64];
+ uint8_t *desc = NULL;
+ AVStream *st = NULL;
+ int ret, type, picsize, desc_len;
+
+ /* type + picsize + mime + desc */
+ if (len < 1 + 4 + 2 + 2) {
+ av_log(s, AV_LOG_ERROR, "Invalid attached picture size: %d.\n", len);
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* picture type */
+ type = avio_r8(s->pb);
+ len--;
+ if (type >= FF_ARRAY_ELEMS(ff_id3v2_picture_types) || type < 0) {
+ av_log(s, AV_LOG_WARNING, "Unknown attached picture type: %d.\n", type);
+ type = 0;
+ }
+
+ /* picture data size */
+ picsize = avio_rl32(s->pb);
+ len -= 4;
+
+ /* picture MIME type */
+ len -= avio_get_str16le(s->pb, len, mimetype, sizeof(mimetype));
+ while (mime->id != AV_CODEC_ID_NONE) {
+ if (!strncmp(mime->str, mimetype, sizeof(mimetype))) {
+ id = mime->id;
+ break;
+ }
+ mime++;
+ }
+ if (id == AV_CODEC_ID_NONE) {
+ av_log(s, AV_LOG_ERROR, "Unknown attached picture mimetype: %s.\n",
+ mimetype);
+ return 0;
+ }
+
+ if (picsize >= len) {
+ av_log(s, AV_LOG_ERROR, "Invalid attached picture data size: %d >= %d.\n",
+ picsize, len);
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* picture description */
+ desc_len = (len - picsize) * 2 + 1;
+ desc = av_malloc(desc_len);
+ if (!desc)
+ return AVERROR(ENOMEM);
+ len -= avio_get_str16le(s->pb, len - picsize, desc, desc_len);
+
+ ret = av_get_packet(s->pb, &pkt, picsize);
+ if (ret < 0)
+ goto fail;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ st->disposition |= AV_DISPOSITION_ATTACHED_PIC;
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_id = id;
+ st->attached_pic = pkt;
+ st->attached_pic.stream_index = st->index;
+ st->attached_pic.flags |= AV_PKT_FLAG_KEY;
+
+ if (*desc)
+ av_dict_set(&st->metadata, "title", desc, AV_DICT_DONT_STRDUP_VAL);
+ else
+ av_freep(&desc);
+
+ av_dict_set(&st->metadata, "comment", ff_id3v2_picture_types[type], 0);
+
+ return 0;
+
+fail:
+ av_freep(&desc);
+ av_packet_unref(&pkt);
+ return ret;
+}
+
+static void get_id3_tag(AVFormatContext *s, int len)
+{
+ ID3v2ExtraMeta *id3v2_extra_meta = NULL;
+
+ 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_parse_chapters(s, &id3v2_extra_meta);
+ }
+ ff_id3v2_free_extra_meta(&id3v2_extra_meta);
+}
+
+static void get_tag(AVFormatContext *s, const char *key, int type, int len, int type2_size)
+{
+ ASFContext *asf = s->priv_data;
+ char *value = NULL;
+ int64_t off = avio_tell(s->pb);
+#define LEN 22
+
+ if ((unsigned)len >= (UINT_MAX - LEN) / 2)
+ return;
+
+ if (!asf->export_xmp && !strncmp(key, "xmp", 3))
+ goto finish;
+
+ value = av_malloc(2 * len + LEN);
+ if (!value)
+ goto finish;
+
+ switch (type) {
+ case ASF_UNICODE:
+ avio_get_str16le(s->pb, len, value, 2 * len + 1);
+ break;
+ case -1: // ASCI
+ avio_read(s->pb, value, len);
+ value[len]=0;
+ break;
+ case ASF_BYTE_ARRAY:
+ if (!strcmp(key, "WM/Picture")) { // handle cover art
+ asf_read_picture(s, len);
+ } else if (!strcmp(key, "ID3")) { // handle ID3 tag
+ get_id3_tag(s, len);
+ } else {
+ av_log(s, AV_LOG_VERBOSE, "Unsupported byte array in tag %s.\n", key);
+ }
+ goto finish;
+ case ASF_BOOL:
+ case ASF_DWORD:
+ case ASF_QWORD:
+ case ASF_WORD: {
+ uint64_t num = get_value(s->pb, type, type2_size);
+ snprintf(value, LEN, "%"PRIu64, num);
+ break;
+ }
+ case ASF_GUID:
+ av_log(s, AV_LOG_DEBUG, "Unsupported GUID value in tag %s.\n", key);
+ goto finish;
+ default:
+ av_log(s, AV_LOG_DEBUG,
+ "Unsupported value type %d in tag %s.\n", type, key);
+ goto finish;
+ }
+ if (*value)
+ av_dict_set(&s->metadata, key, value, 0);
+
+finish:
+ av_freep(&value);
+ avio_seek(s->pb, off + len, SEEK_SET);
+}
+
+static int asf_read_file_properties(AVFormatContext *s, int64_t size)
+{
+ ASFContext *asf = s->priv_data;
+ AVIOContext *pb = s->pb;
+
+ ff_get_guid(pb, &asf->hdr.guid);
+ asf->hdr.file_size = avio_rl64(pb);
+ asf->hdr.create_time = avio_rl64(pb);
+ avio_rl64(pb); /* number of packets */
+ asf->hdr.play_time = avio_rl64(pb);
+ asf->hdr.send_time = avio_rl64(pb);
+ asf->hdr.preroll = avio_rl32(pb);
+ asf->hdr.ignore = avio_rl32(pb);
+ asf->hdr.flags = avio_rl32(pb);
+ asf->hdr.min_pktsize = avio_rl32(pb);
+ asf->hdr.max_pktsize = avio_rl32(pb);
+ if (asf->hdr.min_pktsize >= (1U << 29))
+ return AVERROR_INVALIDDATA;
+ asf->hdr.max_bitrate = avio_rl32(pb);
+ s->packet_size = asf->hdr.max_pktsize;
+
+ return 0;
+}
+
+static int asf_read_stream_properties(AVFormatContext *s, int64_t size)
+{
+ ASFContext *asf = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVStream *st;
+ ASFStream *asf_st;
+ ff_asf_guid g;
+ enum AVMediaType type;
+ int type_specific_size, sizeX;
+ unsigned int tag1;
+ int64_t pos1, pos2, start_time;
+ int test_for_ext_stream_audio, is_dvr_ms_audio = 0;
+
+ if (s->nb_streams == ASF_MAX_STREAMS) {
+ av_log(s, AV_LOG_ERROR, "too many streams\n");
+ return AVERROR(EINVAL);
+ }
+
+ pos1 = avio_tell(pb);
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
+ start_time = asf->hdr.preroll;
+
+ if (!(asf->hdr.flags & 0x01)) { // if we aren't streaming...
+ int64_t fsize = avio_size(pb);
+ if (fsize <= 0 || (int64_t)asf->hdr.file_size <= 0 ||
+ 20*FFABS(fsize - (int64_t)asf->hdr.file_size) < FFMIN(fsize, asf->hdr.file_size))
+ st->duration = asf->hdr.play_time /
+ (10000000 / 1000) - start_time;
+ }
+ ff_get_guid(pb, &g);
+
+ test_for_ext_stream_audio = 0;
+ if (!ff_guidcmp(&g, &ff_asf_audio_stream)) {
+ type = AVMEDIA_TYPE_AUDIO;
+ } else if (!ff_guidcmp(&g, &ff_asf_video_stream)) {
+ type = AVMEDIA_TYPE_VIDEO;
+ } else if (!ff_guidcmp(&g, &ff_asf_jfif_media)) {
+ type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_id = AV_CODEC_ID_MJPEG;
+ } else if (!ff_guidcmp(&g, &ff_asf_command_stream)) {
+ type = AVMEDIA_TYPE_DATA;
+ } else if (!ff_guidcmp(&g, &ff_asf_ext_stream_embed_stream_header)) {
+ test_for_ext_stream_audio = 1;
+ type = AVMEDIA_TYPE_UNKNOWN;
+ } else {
+ return -1;
+ }
+ ff_get_guid(pb, &g);
+ avio_skip(pb, 8); /* total_size */
+ type_specific_size = avio_rl32(pb);
+ avio_rl32(pb);
+ 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);
+
+ if (test_for_ext_stream_audio) {
+ ff_get_guid(pb, &g);
+ if (!ff_guidcmp(&g, &ff_asf_ext_stream_audio_stream)) {
+ type = AVMEDIA_TYPE_AUDIO;
+ is_dvr_ms_audio = 1;
+ ff_get_guid(pb, &g);
+ avio_rl32(pb);
+ avio_rl32(pb);
+ avio_rl32(pb);
+ ff_get_guid(pb, &g);
+ avio_rl32(pb);
+ }
+ }
+
+ st->codecpar->codec_type = type;
+ if (type == AVMEDIA_TYPE_AUDIO) {
+ int ret = ff_get_wav_header(s, pb, st->codecpar, 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->request_probe = 1;
+ st->codecpar->codec_tag = 0;
+ }
+ if (st->codecpar->codec_id == AV_CODEC_ID_AAC)
+ st->need_parsing = AVSTREAM_PARSE_NONE;
+ else
+ st->need_parsing = AVSTREAM_PARSE_FULL;
+ /* We have to init the frame size at some point .... */
+ pos2 = avio_tell(pb);
+ if (size >= (pos2 + 8 - pos1 + 24)) {
+ asf_st->ds_span = avio_r8(pb);
+ asf_st->ds_packet_size = avio_rl16(pb);
+ asf_st->ds_chunk_size = avio_rl16(pb);
+ avio_rl16(pb); // ds_data_size
+ avio_r8(pb); // ds_silence_data
+ }
+ if (asf_st->ds_span > 1) {
+ if (!asf_st->ds_chunk_size ||
+ (asf_st->ds_packet_size / asf_st->ds_chunk_size <= 1) ||
+ asf_st->ds_packet_size % asf_st->ds_chunk_size)
+ asf_st->ds_span = 0; // disable descrambling
+ }
+ } else if (type == AVMEDIA_TYPE_VIDEO &&
+ size - (avio_tell(pb) - pos1 + 24) >= 51) {
+ avio_rl32(pb);
+ avio_rl32(pb);
+ avio_r8(pb);
+ avio_rl16(pb); /* size */
+ sizeX = avio_rl32(pb); /* size */
+ st->codecpar->width = avio_rl32(pb);
+ st->codecpar->height = avio_rl32(pb);
+ /* not available for asf */
+ avio_rl16(pb); /* panes */
+ st->codecpar->bits_per_coded_sample = avio_rl16(pb); /* depth */
+ tag1 = avio_rl32(pb);
+ avio_skip(pb, 20);
+ if (sizeX > 40) {
+ st->codecpar->extradata_size = ffio_limit(pb, sizeX - 40);
+ st->codecpar->extradata = av_mallocz(st->codecpar->extradata_size +
+ AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!st->codecpar->extradata)
+ return AVERROR(ENOMEM);
+ avio_read(pb, st->codecpar->extradata, st->codecpar->extradata_size);
+ }
+
+ /* Extract palette from extradata if bpp <= 8 */
+ /* This code assumes that extradata contains only palette */
+ /* This is true for all paletted codecs implemented in libavcodec */
+ if (st->codecpar->extradata_size && (st->codecpar->bits_per_coded_sample <= 8)) {
+#if HAVE_BIGENDIAN
+ int i;
+ for (i = 0; i < FFMIN(st->codecpar->extradata_size, AVPALETTE_SIZE) / 4; i++)
+ asf_st->palette[i] = av_bswap32(((uint32_t *)st->codecpar->extradata)[i]);
+#else
+ memcpy(asf_st->palette, st->codecpar->extradata,
+ FFMIN(st->codecpar->extradata_size, AVPALETTE_SIZE));
+#endif
+ asf_st->palette_changed = 1;
+ }
+
+ st->codecpar->codec_tag = tag1;
+ st->codecpar->codec_id = ff_codec_get_id(ff_codec_bmp_tags, tag1);
+ if (tag1 == MKTAG('D', 'V', 'R', ' ')) {
+ st->need_parsing = AVSTREAM_PARSE_FULL;
+ /* issue658 contains wrong w/h and MS even puts a fake seq header
+ * with wrong w/h in extradata while a correct one is in the stream.
+ * maximum lameness */
+ st->codecpar->width =
+ st->codecpar->height = 0;
+ av_freep(&st->codecpar->extradata);
+ st->codecpar->extradata_size = 0;
+ }
+ if (st->codecpar->codec_id == AV_CODEC_ID_H264)
+ st->need_parsing = AVSTREAM_PARSE_FULL_ONCE;
+ if (st->codecpar->codec_id == AV_CODEC_ID_MPEG4)
+ st->need_parsing = AVSTREAM_PARSE_FULL_ONCE;
+ }
+ pos2 = avio_tell(pb);
+ avio_skip(pb, size - (pos2 - pos1 + 24));
+
+ return 0;
+}
+
+static int asf_read_ext_stream_properties(AVFormatContext *s, int64_t size)
+{
+ ASFContext *asf = s->priv_data;
+ AVIOContext *pb = s->pb;
+ ff_asf_guid g;
+ int ext_len, payload_ext_ct, stream_ct, i;
+ uint32_t leak_rate, stream_num;
+ unsigned int stream_languageid_index;
+
+ avio_rl64(pb); // starttime
+ avio_rl64(pb); // endtime
+ leak_rate = avio_rl32(pb); // leak-datarate
+ avio_rl32(pb); // bucket-datasize
+ avio_rl32(pb); // init-bucket-fullness
+ avio_rl32(pb); // alt-leak-datarate
+ avio_rl32(pb); // alt-bucket-datasize
+ avio_rl32(pb); // alt-init-bucket-fullness
+ avio_rl32(pb); // max-object-size
+ avio_rl32(pb); // flags (reliable,seekable,no_cleanpoints?,resend-live-cleanpoints, rest of bits reserved)
+ stream_num = avio_rl16(pb); // stream-num
+
+ stream_languageid_index = avio_rl16(pb); // stream-language-id-index
+ if (stream_num < 128)
+ asf->streams[stream_num].stream_language_index = stream_languageid_index;
+
+ avio_rl64(pb); // avg frametime in 100ns units
+ stream_ct = avio_rl16(pb); // stream-name-count
+ payload_ext_ct = avio_rl16(pb); // payload-extension-system-count
+
+ 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);
+ ext_len = avio_rl16(pb);
+ avio_skip(pb, ext_len);
+ }
+
+ for (i = 0; i < payload_ext_ct; i++) {
+ int size;
+ ff_get_guid(pb, &g);
+ 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;
+}
+
+static int asf_read_content_desc(AVFormatContext *s, int64_t size)
+{
+ AVIOContext *pb = s->pb;
+ int len1, len2, len3, len4, len5;
+
+ len1 = avio_rl16(pb);
+ len2 = avio_rl16(pb);
+ len3 = avio_rl16(pb);
+ len4 = avio_rl16(pb);
+ len5 = avio_rl16(pb);
+ get_tag(s, "title", 0, len1, 32);
+ get_tag(s, "author", 0, len2, 32);
+ get_tag(s, "copyright", 0, len3, 32);
+ get_tag(s, "comment", 0, len4, 32);
+ avio_skip(pb, len5);
+
+ return 0;
+}
+
+static int asf_read_ext_content_desc(AVFormatContext *s, int64_t size)
+{
+ AVIOContext *pb = s->pb;
+ ASFContext *asf = s->priv_data;
+ int desc_count, i, ret;
+
+ desc_count = avio_rl16(pb);
+ for (i = 0; i < desc_count; i++) {
+ int name_len, value_type, value_len;
+ char name[1024];
+
+ name_len = avio_rl16(pb);
+ if (name_len % 2) // must be even, broken lavf versions wrote len-1
+ name_len += 1;
+ if ((ret = avio_get_str16le(pb, name_len, name, sizeof(name))) < name_len)
+ avio_skip(pb, name_len - ret);
+ value_type = avio_rl16(pb);
+ value_len = avio_rl16(pb);
+ if (!value_type && value_len % 2)
+ value_len += 1;
+ /* My sample has that stream set to 0 maybe that mean the container.
+ * ASF stream count starts at 1. I am using 0 to the container value
+ * since it's unused. */
+ if (!strcmp(name, "AspectRatioX"))
+ asf->dar[0].num = get_value(s->pb, value_type, 32);
+ else if (!strcmp(name, "AspectRatioY"))
+ asf->dar[0].den = get_value(s->pb, value_type, 32);
+ else
+ get_tag(s, name, value_type, value_len, 32);
+ }
+
+ return 0;
+}
+
+static int asf_read_language_list(AVFormatContext *s, int64_t size)
+{
+ AVIOContext *pb = s->pb;
+ ASFContext *asf = s->priv_data;
+ int j, ret;
+ int stream_count = avio_rl16(pb);
+ for (j = 0; j < stream_count; j++) {
+ char lang[6];
+ unsigned int lang_len = avio_r8(pb);
+ if ((ret = avio_get_str16le(pb, lang_len, lang,
+ sizeof(lang))) < lang_len)
+ avio_skip(pb, lang_len - ret);
+ if (j < 128)
+ av_strlcpy(asf->stream_languages[j], lang,
+ sizeof(*asf->stream_languages));
+ }
+
+ return 0;
+}
+
+static int asf_read_metadata(AVFormatContext *s, int64_t size)
+{
+ AVIOContext *pb = s->pb;
+ ASFContext *asf = s->priv_data;
+ int n, stream_num, name_len_utf16, name_len_utf8, value_len;
+ int ret, i;
+ n = avio_rl16(pb);
+
+ for (i = 0; i < n; i++) {
+ uint8_t *name;
+ int value_type;
+
+ avio_rl16(pb); // lang_list_index
+ stream_num = avio_rl16(pb);
+ name_len_utf16 = avio_rl16(pb);
+ value_type = avio_rl16(pb); /* value_type */
+ value_len = avio_rl32(pb);
+
+ name_len_utf8 = 2*name_len_utf16 + 1;
+ name = av_malloc(name_len_utf8);
+ if (!name)
+ return AVERROR(ENOMEM);
+
+ if ((ret = avio_get_str16le(pb, name_len_utf16, name, name_len_utf8)) < name_len_utf16)
+ avio_skip(pb, name_len_utf16 - ret);
+ av_log(s, AV_LOG_TRACE, "%d stream %d name_len %2d type %d len %4d <%s>\n",
+ i, stream_num, name_len_utf16, value_type, value_len, name);
+
+ if (!strcmp(name, "AspectRatioX")){
+ int aspect_x = get_value(s->pb, value_type, 16);
+ if(stream_num < 128)
+ asf->dar[stream_num].num = aspect_x;
+ } else if(!strcmp(name, "AspectRatioY")){
+ int aspect_y = get_value(s->pb, value_type, 16);
+ if(stream_num < 128)
+ asf->dar[stream_num].den = aspect_y;
+ } else {
+ get_tag(s, name, value_type, value_len, 16);
+ }
+ av_freep(&name);
+ }
+
+ return 0;
+}
+
+static int asf_read_marker(AVFormatContext *s, int64_t size)
+{
+ AVIOContext *pb = s->pb;
+ ASFContext *asf = s->priv_data;
+ int i, count, name_len, ret;
+ char name[1024];
+
+ avio_rl64(pb); // reserved 16 bytes
+ avio_rl64(pb); // ...
+ count = avio_rl32(pb); // markers count
+ avio_rl16(pb); // reserved 2 bytes
+ name_len = avio_rl16(pb); // name length
+ avio_skip(pb, name_len);
+
+ for (i = 0; i < count; i++) {
+ int64_t pres_time;
+ int name_len;
+
+ if (avio_feof(pb))
+ return AVERROR_INVALIDDATA;
+
+ avio_rl64(pb); // offset, 8 bytes
+ pres_time = avio_rl64(pb); // presentation time
+ pres_time -= asf->hdr.preroll * 10000;
+ avio_rl16(pb); // entry length
+ avio_rl32(pb); // send time
+ avio_rl32(pb); // flags
+ name_len = avio_rl32(pb); // name length
+ if ((ret = avio_get_str16le(pb, name_len * 2, name,
+ sizeof(name))) < name_len)
+ avio_skip(pb, name_len - ret);
+ avpriv_new_chapter(s, i, (AVRational) { 1, 10000000 }, pres_time,
+ AV_NOPTS_VALUE, name);
+ }
+
+ return 0;
+}
+
+static int asf_read_header(AVFormatContext *s)
+{
+ ASFContext *asf = s->priv_data;
+ ff_asf_guid g;
+ AVIOContext *pb = s->pb;
+ int i;
+ int64_t gsize;
+
+ ff_get_guid(pb, &g);
+ if (ff_guidcmp(&g, &ff_asf_header))
+ 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);
+ int ret = 0;
+ ff_get_guid(pb, &g);
+ gsize = avio_rl64(pb);
+ print_guid(&g);
+ if (!ff_guidcmp(&g, &ff_asf_data_header)) {
+ asf->data_object_offset = avio_tell(pb);
+ /* If not streaming, gsize is not unlimited (how?),
+ * and there is enough space in the file.. */
+ if (!(asf->hdr.flags & 0x01) && gsize >= 100)
+ asf->data_object_size = gsize - 24;
+ else
+ asf->data_object_size = (uint64_t)-1;
+ break;
+ }
+ if (gsize < 24)
+ return AVERROR_INVALIDDATA;
+ if (!ff_guidcmp(&g, &ff_asf_file_header)) {
+ ret = asf_read_file_properties(s, gsize);
+ } else if (!ff_guidcmp(&g, &ff_asf_stream_header)) {
+ ret = asf_read_stream_properties(s, gsize);
+ } else if (!ff_guidcmp(&g, &ff_asf_comment_header)) {
+ asf_read_content_desc(s, gsize);
+ } else if (!ff_guidcmp(&g, &ff_asf_language_guid)) {
+ asf_read_language_list(s, gsize);
+ } else if (!ff_guidcmp(&g, &ff_asf_extended_content_header)) {
+ asf_read_ext_content_desc(s, gsize);
+ } else if (!ff_guidcmp(&g, &ff_asf_metadata_header)) {
+ asf_read_metadata(s, gsize);
+ } else if (!ff_guidcmp(&g, &ff_asf_metadata_library_header)) {
+ asf_read_metadata(s, gsize);
+ } else if (!ff_guidcmp(&g, &ff_asf_ext_stream_header)) {
+ asf_read_ext_stream_properties(s, gsize);
+
+ // there could be an optional stream properties object to follow
+ // if so the next iteration will pick it up
+ continue;
+ } else if (!ff_guidcmp(&g, &ff_asf_head1_guid)) {
+ ff_get_guid(pb, &g);
+ avio_skip(pb, 6);
+ continue;
+ } else if (!ff_guidcmp(&g, &ff_asf_marker_header)) {
+ asf_read_marker(s, gsize);
+ } else if (avio_feof(pb)) {
+ return AVERROR_EOF;
+ } else {
+ if (!s->keylen) {
+ if (!ff_guidcmp(&g, &ff_asf_content_encryption)) {
+ unsigned int len;
+ AVPacket pkt;
+ av_log(s, AV_LOG_WARNING,
+ "DRM protected stream detected, decoding will likely fail!\n");
+ len= avio_rl32(pb);
+ av_log(s, AV_LOG_DEBUG, "Secret data:\n");
+
+ if ((ret = av_get_packet(pb, &pkt, len)) < 0)
+ return ret;
+ av_hex_dump_log(s, AV_LOG_DEBUG, pkt.data, pkt.size);
+ av_packet_unref(&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_INFO, "Digital signature detected!\n");
+ }
+ }
+ }
+ if (ret < 0)
+ return ret;
+
+ if (avio_tell(pb) != gpos + gsize)
+ av_log(s, AV_LOG_DEBUG,
+ "gpos mismatch our pos=%"PRIu64", end=%"PRId64"\n",
+ avio_tell(pb) - gpos, gsize);
+ avio_seek(pb, gpos + gsize, SEEK_SET);
+ }
+ ff_get_guid(pb, &g);
+ avio_rl64(pb);
+ avio_r8(pb);
+ avio_r8(pb);
+ if (avio_feof(pb))
+ return AVERROR_EOF;
+ asf->data_offset = avio_tell(pb);
+ asf->packet_size_left = 0;
+
+ for (i = 0; i < 128; i++) {
+ int stream_num = asf->asfid2avid[i];
+ if (stream_num >= 0) {
+ AVStream *st = s->streams[stream_num];
+ if (!st->codecpar->bit_rate)
+ st->codecpar->bit_rate = asf->stream_bitrates[i];
+ if (asf->dar[i].num > 0 && asf->dar[i].den > 0) {
+ av_reduce(&st->sample_aspect_ratio.num,
+ &st->sample_aspect_ratio.den,
+ asf->dar[i].num, asf->dar[i].den, INT_MAX);
+ } else if ((asf->dar[0].num > 0) && (asf->dar[0].den > 0) &&
+ // Use ASF container value if the stream doesn't set AR.
+ (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO))
+ av_reduce(&st->sample_aspect_ratio.num,
+ &st->sample_aspect_ratio.den,
+ asf->dar[0].num, asf->dar[0].den, INT_MAX);
+
+ av_log(s, AV_LOG_TRACE, "i=%d, st->codecpar->codec_type:%d, asf->dar %d:%d sar=%d:%d\n",
+ i, st->codecpar->codec_type, asf->dar[i].num, asf->dar[i].den,
+ st->sample_aspect_ratio.num, st->sample_aspect_ratio.den);
+
+ // copy and convert language codes to the frontend
+ if (asf->streams[i].stream_language_index < 128) {
+ const char *rfc1766 = asf->stream_languages[asf->streams[i].stream_language_index];
+ if (rfc1766 && strlen(rfc1766) > 1) {
+ const char primary_tag[3] = { rfc1766[0], rfc1766[1], '\0' }; // ignore country code if any
+ const char *iso6392 = ff_convert_lang_to(primary_tag,
+ AV_LANG_ISO639_2_BIBL);
+ if (iso6392)
+ av_dict_set(&st->metadata, "language", iso6392, 0);
+ }
+ }
+ }
+ }
+
+ ff_metadata_conv(&s->metadata, NULL, ff_asf_metadata_conv);
+
+ return 0;
+}
+
+#define DO_2BITS(bits, var, defval) \
+ switch (bits & 3) { \
+ case 3: \
+ var = avio_rl32(pb); \
+ rsize += 4; \
+ break; \
+ case 2: \
+ var = avio_rl16(pb); \
+ rsize += 2; \
+ break; \
+ case 1: \
+ var = avio_r8(pb); \
+ rsize++; \
+ break; \
+ default: \
+ var = defval; \
+ break; \
+ }
+
+/**
+ * Load a single ASF packet into the demuxer.
+ * @param s demux context
+ * @param pb context to read data from
+ * @return 0 on success, <0 on error
+ */
+static int asf_get_packet(AVFormatContext *s, AVIOContext *pb)
+{
+ ASFContext *asf = s->priv_data;
+ uint32_t packet_length, padsize;
+ int rsize = 8;
+ int c, d, e, off;
+
+ if (asf->uses_std_ecc > 0) {
+ // if we do not know packet size, allow skipping up to 32 kB
+ off = 32768;
+ if (asf->no_resync_search)
+ off = 3;
+// else if (s->packet_size > 0 && !asf->uses_std_ecc)
+// off = (avio_tell(pb) - s->internal->data_offset) % s->packet_size + 3;
+
+ c = d = e = -1;
+ while (off-- > 0) {
+ c = d;
+ d = e;
+ e = avio_r8(pb);
+ if (c == 0x82 && !d && !e)
+ break;
+ }
+
+ if (c != 0x82) {
+ /* This code allows handling of -EAGAIN at packet boundaries (i.e.
+ * if the packet sync code above triggers -EAGAIN). This does not
+ * imply complete -EAGAIN handling support at random positions in
+ * the stream. */
+ if (pb->error == AVERROR(EAGAIN))
+ return AVERROR(EAGAIN);
+ 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 (!avio_feof(pb))
+ av_log(s, AV_LOG_ERROR, "ff asf bad non zero\n");
+ return AVERROR_INVALIDDATA;
+ }
+ c = avio_r8(pb);
+ d = avio_r8(pb);
+ rsize += 3;
+ } else if(!avio_feof(pb)) {
+ avio_seek(pb, -1, SEEK_CUR); // FIXME
+ }
+ } else {
+ c = avio_r8(pb);
+ if (c & 0x80) {
+ rsize ++;
+ if (!(c & 0x60)) {
+ d = avio_r8(pb);
+ e = avio_r8(pb);
+ avio_seek(pb, (c & 0xF) - 2, SEEK_CUR);
+ rsize += c & 0xF;
+ }
+
+ if (c != 0x82)
+ avpriv_request_sample(s, "Invalid ECC byte");
+
+ if (!asf->uses_std_ecc)
+ asf->uses_std_ecc = (c == 0x82 && !d && !e) ? 1 : -1;
+
+ c = avio_r8(pb);
+ } else
+ asf->uses_std_ecc = -1;
+ d = avio_r8(pb);
+ }
+
+ asf->packet_flags = c;
+ asf->packet_property = d;
+
+ DO_2BITS(asf->packet_flags >> 5, packet_length, s->packet_size);
+ DO_2BITS(asf->packet_flags >> 1, padsize, 0); // sequence ignored
+ DO_2BITS(asf->packet_flags >> 3, padsize, 0); // padding length
+
+ // the following checks prevent overflows and infinite loops
+ if (!packet_length || packet_length >= (1U << 29)) {
+ av_log(s, AV_LOG_ERROR,
+ "invalid packet_length %"PRIu32" at:%"PRId64"\n",
+ packet_length, avio_tell(pb));
+ return AVERROR_INVALIDDATA;
+ }
+ if (padsize >= packet_length) {
+ av_log(s, AV_LOG_ERROR,
+ "invalid padsize %"PRIu32" at:%"PRId64"\n", padsize, avio_tell(pb));
+ return AVERROR_INVALIDDATA;
+ }
+
+ asf->packet_timestamp = avio_rl32(pb);
+ avio_rl16(pb); /* duration */
+ // rsize has at least 11 bytes which have to be present
+
+ if (asf->packet_flags & 0x01) {
+ asf->packet_segsizetype = avio_r8(pb);
+ rsize++;
+ asf->packet_segments = asf->packet_segsizetype & 0x3f;
+ } else {
+ asf->packet_segments = 1;
+ asf->packet_segsizetype = 0x80;
+ }
+ if (rsize > packet_length - padsize) {
+ asf->packet_size_left = 0;
+ 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 AVERROR_INVALIDDATA;
+ }
+ asf->packet_size_left = packet_length - padsize - rsize;
+ if (packet_length < asf->hdr.min_pktsize)
+ padsize += asf->hdr.min_pktsize - packet_length;
+ asf->packet_padsize = padsize;
+ av_log(s, AV_LOG_TRACE, "packet: size=%d padsize=%d left=%d\n",
+ s->packet_size, asf->packet_padsize, asf->packet_size_left);
+ return 0;
+}
+
+/**
+ *
+ * @return <0 if error
+ */
+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);
+ 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);
+ DO_2BITS(asf->packet_property, asf->packet_replic_size, 0);
+ av_log(asf, AV_LOG_TRACE, "key:%d stream:%d seq:%d offset:%d replic_size:%d num:%X packet_property %X\n",
+ asf->packet_key_frame, asf->stream_index, asf->packet_seq,
+ asf->packet_frag_offset, asf->packet_replic_size, num, asf->packet_property);
+ 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) {
+ 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 %d invalid\n", asfst->packet_obj_size);
+ asfst->packet_obj_size = 0;
+ return AVERROR_INVALIDDATA;
+ }
+ asf->packet_frag_timestamp = avio_rl32(pb); // timestamp
+
+ 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
+ asf->packet_time_start = asf->packet_frag_offset;
+ asf->packet_frag_offset = 0;
+ asf->packet_frag_timestamp = asf->packet_timestamp;
+
+ asf->packet_time_delta = avio_r8(pb);
+ rsize++;
+ } else if (asf->packet_replic_size != 0) {
+ av_log(s, AV_LOG_ERROR, "unexpected packet_replic_size of %d\n",
+ asf->packet_replic_size);
+ return 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 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-%d+%d)\n",
+ asf->packet_frag_size, asf->packet_size_left, rsize, asf->packet_padsize);
+ return AVERROR_INVALIDDATA;
+ } else {
+ int diff = asf->packet_frag_size - (asf->packet_size_left - rsize);
+ asf->packet_size_left += diff;
+ asf->packet_padsize -= diff;
+ }
+ }
+ } else {
+ 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 AVERROR_INVALIDDATA;
+ }
+ asf->packet_size_left -= rsize;
+
+ return 0;
+}
+
+/**
+ * Parse data from individual ASF packets (which were previously loaded
+ * with asf_get_packet()).
+ * @param s demux context
+ * @param pb context to read data from
+ * @param pkt pointer to store packet data into
+ * @return 0 if data was stored in pkt, <0 on error or 1 if more ASF
+ * packets need to be loaded (through asf_get_packet())
+ */
+static int asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt)
+{
+ ASFContext *asf = s->priv_data;
+ ASFStream *asf_st = 0;
+ for (;;) {
+ int ret;
+ if (avio_feof(pb))
+ return AVERROR_EOF;
+ if (asf->packet_size_left < FRAME_HEADER_SIZE ||
+ asf->packet_segments < 1 && asf->packet_time_start == 0) {
+ int ret = asf->packet_size_left + asf->packet_padsize;
+
+ if (asf->packet_size_left && asf->packet_size_left < FRAME_HEADER_SIZE)
+ av_log(s, AV_LOG_WARNING, "Skip due to FRAME_HEADER_SIZE\n");
+
+ assert(ret >= 0);
+ /* fail safe */
+ avio_skip(pb, ret);
+
+ asf->packet_pos = avio_tell(pb);
+ if (asf->data_object_size != (uint64_t)-1 &&
+ (asf->packet_pos - asf->data_object_offset >= asf->data_object_size))
+ return AVERROR_EOF; /* Do not exceed the size of the data object */
+ return 1;
+ }
+ if (asf->packet_time_start == 0) {
+ if (asf_read_frame_header(s, pb) < 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 || 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);
+ asf->packet_size_left -= asf->packet_frag_size;
+ if (asf->stream_index < 0)
+ av_log(s, AV_LOG_ERROR, "ff asf skip %d (unknown stream)\n",
+ asf->packet_frag_size);
+ continue;
+ }
+ asf->asf_st = &asf->streams[s->streams[asf->stream_index]->id];
+ if (!asf->packet_frag_offset)
+ asf->asf_st->skip_to_key = 0;
+ }
+ asf_st = asf->asf_st;
+ av_assert0(asf_st);
+
+ if (!asf_st->frag_offset && asf->packet_frag_offset) {
+ av_log(s, AV_LOG_TRACE, "skipping asf data pkt with fragment offset for "
+ "stream:%d, expected:%d but got %d from pkt)\n",
+ asf->stream_index, asf_st->frag_offset,
+ asf->packet_frag_offset);
+ avio_skip(pb, asf->packet_frag_size);
+ asf->packet_size_left -= asf->packet_frag_size;
+ continue;
+ }
+
+ if (asf->packet_replic_size == 1) {
+ // 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_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_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_st->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_st->packet_obj_size);
+ asf_st->frag_offset = 0;
+ av_packet_unref(&asf_st->pkt);
+ }
+ /* new packet */
+ if ((ret = av_new_packet(&asf_st->pkt, asf_st->packet_obj_size)) < 0)
+ return ret;
+ asf_st->seq = asf->packet_seq;
+ 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;
+ pal = av_packet_new_side_data(&asf_st->pkt, AV_PKT_DATA_PALETTE,
+ AVPALETTE_SIZE);
+ if (!pal) {
+ av_log(s, AV_LOG_ERROR, "Cannot append palette to packet\n");
+ } else {
+ memcpy(pal, asf_st->palette, AVPALETTE_SIZE);
+ asf_st->palette_changed = 0;
+ }
+ }
+ av_log(asf, AV_LOG_TRACE, "new packet: stream:%d key:%d packet_key:%d audio:%d size:%d\n",
+ asf->stream_index, asf->packet_key_frame,
+ asf_st->pkt.flags & AV_PKT_FLAG_KEY,
+ s->streams[asf->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO,
+ asf_st->packet_obj_size);
+ if (s->streams[asf->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
+ asf->packet_key_frame = 1;
+ if (asf->packet_key_frame)
+ asf_st->pkt.flags |= AV_PKT_FLAG_KEY;
+ }
+
+ /* read data */
+ av_log(asf, AV_LOG_TRACE, "READ PACKET s:%d os:%d o:%d,%d l:%d DATA:%p\n",
+ s->packet_size, asf_st->pkt.size, asf->packet_frag_offset,
+ asf_st->frag_offset, asf->packet_frag_size, asf_st->pkt.data);
+ asf->packet_size_left -= asf->packet_frag_size;
+ if (asf->packet_size_left < 0)
+ continue;
+
+ if (asf->packet_frag_offset >= asf_st->pkt.size ||
+ asf->packet_frag_size > asf_st->pkt.size - asf->packet_frag_offset) {
+ av_log(s, AV_LOG_ERROR,
+ "packet fragment position invalid %u,%u not in %u\n",
+ asf->packet_frag_offset, asf->packet_frag_size,
+ asf_st->pkt.size);
+ 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) {
+ if (ret < 0 || asf->packet_frag_offset + ret == 0)
+ return ret < 0 ? ret : AVERROR_EOF;
+
+ if (asf_st->ds_span > 1) {
+ // scrambling, we can either drop it completely or fill the remainder
+ // TODO: should we fill the whole packet instead of just the current
+ // fragment?
+ memset(asf_st->pkt.data + asf->packet_frag_offset + ret, 0,
+ asf->packet_frag_size - ret);
+ ret = asf->packet_frag_size;
+ } else {
+ // no scrambling, so we can return partial packets
+ av_shrink_packet(&asf_st->pkt, asf->packet_frag_offset + ret);
+ }
+ }
+ if (s->key && s->keylen == 20)
+ ff_asfcrypt_dec(s->key, asf_st->pkt.data + asf->packet_frag_offset,
+ ret);
+ asf_st->frag_offset += ret;
+ /* test if whole packet is read */
+ if (asf_st->frag_offset == asf_st->pkt.size) {
+ // workaround for macroshit radio DVR-MS files
+ if (s->streams[asf->stream_index]->codecpar->codec_id == AV_CODEC_ID_MPEG2VIDEO &&
+ asf_st->pkt.size > 100) {
+ int i;
+ for (i = 0; i < asf_st->pkt.size && !asf_st->pkt.data[i]; i++)
+ ;
+ if (i == asf_st->pkt.size) {
+ av_log(s, AV_LOG_DEBUG, "discarding ms fart\n");
+ asf_st->frag_offset = 0;
+ av_packet_unref(&asf_st->pkt);
+ continue;
+ }
+ }
+
+ /* return packet */
+ if (asf_st->ds_span > 1) {
+ if (asf_st->pkt.size != asf_st->ds_packet_size * asf_st->ds_span) {
+ av_log(s, AV_LOG_ERROR,
+ "pkt.size != ds_packet_size * ds_span (%d %d %d)\n",
+ asf_st->pkt.size, asf_st->ds_packet_size,
+ asf_st->ds_span);
+ } else {
+ /* packet descrambling */
+ AVBufferRef *buf = av_buffer_alloc(asf_st->pkt.size +
+ AV_INPUT_BUFFER_PADDING_SIZE);
+ if (buf) {
+ uint8_t *newdata = buf->data;
+ int offset = 0;
+ memset(newdata + asf_st->pkt.size, 0,
+ AV_INPUT_BUFFER_PADDING_SIZE);
+ while (offset < asf_st->pkt.size) {
+ int off = offset / asf_st->ds_chunk_size;
+ int row = off / asf_st->ds_span;
+ int col = off % asf_st->ds_span;
+ int idx = row + col * asf_st->ds_packet_size / asf_st->ds_chunk_size;
+ assert(offset + asf_st->ds_chunk_size <= asf_st->pkt.size);
+ assert(idx + 1 <= asf_st->pkt.size / asf_st->ds_chunk_size);
+ memcpy(newdata + offset,
+ asf_st->pkt.data + idx * asf_st->ds_chunk_size,
+ asf_st->ds_chunk_size);
+ offset += asf_st->ds_chunk_size;
+ }
+ av_buffer_unref(&asf_st->pkt.buf);
+ asf_st->pkt.buf = buf;
+ asf_st->pkt.data = buf->data;
+ }
+ }
+ }
+ asf_st->frag_offset = 0;
+ *pkt = asf_st->pkt;
+ asf_st->pkt.buf = 0;
+ asf_st->pkt.size = 0;
+ asf_st->pkt.data = 0;
+ asf_st->pkt.side_data_elems = 0;
+ asf_st->pkt.side_data = NULL;
+ break; // packet completed
+ }
+ }
+ return 0;
+}
+
+static int asf_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ ASFContext *asf = s->priv_data;
+
+ for (;;) {
+ int ret;
+
+ /* parse cached packets, if any */
+ if ((ret = asf_parse_packet(s, s->pb, pkt)) <= 0)
+ return ret;
+ if ((ret = asf_get_packet(s, s->pb)) < 0)
+ assert(asf->packet_size_left < FRAME_HEADER_SIZE ||
+ asf->packet_segments < 1);
+ asf->packet_time_start = 0;
+ }
+}
+
+// Added to support seeking after packets have been read
+// If information is not reset, read_packet fails due to
+// leftover information from previous reads
+static void asf_reset_header(AVFormatContext *s)
+{
+ ASFContext *asf = s->priv_data;
+ ASFStream *asf_st;
+ int i;
+
+ asf->packet_size_left = 0;
+ asf->packet_flags = 0;
+ asf->packet_property = 0;
+ asf->packet_timestamp = 0;
+ asf->packet_segsizetype = 0;
+ asf->packet_segments = 0;
+ asf->packet_seq = 0;
+ asf->packet_replic_size = 0;
+ asf->packet_key_frame = 0;
+ asf->packet_padsize = 0;
+ asf->packet_frag_offset = 0;
+ asf->packet_frag_size = 0;
+ asf->packet_frag_timestamp = 0;
+ asf->packet_multi_size = 0;
+ asf->packet_time_delta = 0;
+ asf->packet_time_start = 0;
+
+ for (i = 0; i < 128; i++) {
+ asf_st = &asf->streams[i];
+ av_packet_unref(&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]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
+ continue;
+
+ asf_st->skip_to_key = 1;
+ }
+}
+
+static int asf_read_close(AVFormatContext *s)
+{
+ asf_reset_header(s);
+
+ return 0;
+}
+
+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;
+ int64_t pos = *ppos;
+ int i;
+ int64_t start_pos[ASF_MAX_STREAMS];
+
+ for (i = 0; i < s->nb_streams; i++)
+ start_pos[i] = pos;
+
+ if (s->packet_size > 0)
+ pos = (pos + s->packet_size - 1 - s->internal->data_offset) /
+ s->packet_size * s->packet_size +
+ s->internal->data_offset;
+ *ppos = pos;
+ if (avio_seek(s->pb, pos, SEEK_SET) < 0)
+ return AV_NOPTS_VALUE;
+
+ ff_read_frame_flush(s);
+ asf_reset_header(s);
+ for (;;) {
+ if (av_read_frame(s, pkt) < 0) {
+ av_log(s, AV_LOG_INFO, "asf_read_pts failed\n");
+ return AV_NOPTS_VALUE;
+ }
+
+ pts = pkt->dts;
+
+ if (pkt->flags & AV_PKT_FLAG_KEY) {
+ i = pkt->stream_index;
+
+ 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;
+ av_assert1(pkt->pos == asf_st->packet_pos);
+
+ av_add_index_entry(s->streams[i], pos, pts, pkt->size,
+ pos - start_pos[i] + 1, AVINDEX_KEYFRAME);
+ start_pos[i] = asf_st->packet_pos + 1;
+
+ if (pkt->stream_index == stream_index) {
+ av_packet_unref(pkt);
+ break;
+ }
+ }
+ av_packet_unref(pkt);
+ }
+
+ *ppos = pos;
+ return pts;
+}
+
+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);
+ int64_t ret;
+
+ if((ret = avio_seek(s->pb, asf->data_object_offset + asf->data_object_size, SEEK_SET)) < 0) {
+ return ret;
+ }
+
+ 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, &ff_asf_simple_index_header)) {
+ int64_t gsize = avio_rl64(s->pb);
+ if (gsize < 24 || avio_feof(s->pb)) {
+ goto end;
+ }
+ avio_skip(s->pb, gsize - 24);
+ if ((ret = ff_get_guid(s->pb, &g)) < 0)
+ goto end;
+ }
+
+ {
+ 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;
+ itime = avio_rl64(s->pb);
+ pct = avio_rl32(s->pb);
+ ict = avio_rl32(s->pb);
+ av_log(s, AV_LOG_DEBUG,
+ "itime:0x%"PRIx64", pct:%d, ict:%d\n", itime, pct, ict);
+
+ for (i = 0; i < ict; i++) {
+ int pktnum = avio_rl32(s->pb);
+ int pktct = avio_rl16(s->pb);
+ int64_t pos = s->internal->data_offset + s->packet_size * (int64_t)pktnum;
+ int64_t index_pts = FFMAX(av_rescale(itime, i, 10000) - asf->hdr.preroll, 0);
+
+ if (avio_feof(s->pb)) {
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+
+ if (pos != last_pos) {
+ av_log(s, AV_LOG_DEBUG, "pktnum:%d, pktct:%d pts: %"PRId64"\n",
+ pktnum, pktct, index_pts);
+ av_add_index_entry(s->streams[stream_index], pos, index_pts,
+ s->packet_size, 0, AVINDEX_KEYFRAME);
+ last_pos = pos;
+ }
+ }
+ asf->index_read = ict > 1;
+ }
+end:
+// if (avio_feof(s->pb)) {
+// ret = 0;
+// }
+ avio_seek(s->pb, current_pos, SEEK_SET);
+ return ret;
+}
+
+static int asf_read_seek(AVFormatContext *s, int stream_index,
+ int64_t pts, int flags)
+{
+ ASFContext *asf = s->priv_data;
+ AVStream *st = s->streams[stream_index];
+ int ret = 0;
+
+ if (s->packet_size <= 0)
+ return -1;
+
+ /* Try using the protocol's read_seek if available */
+ if (s->pb) {
+ int64_t ret = avio_seek_time(s->pb, stream_index, pts, flags);
+ if (ret >= 0)
+ asf_reset_header(s);
+ if (ret != AVERROR(ENOSYS))
+ return ret;
+ }
+
+ /* explicitly handle the case of seeking to 0 */
+ if (!pts) {
+ asf_reset_header(s);
+ avio_seek(s->pb, s->internal->data_offset, SEEK_SET);
+ return 0;
+ }
+
+ if (!asf->index_read) {
+ ret = asf_build_simple_index(s, stream_index);
+ if (ret < 0)
+ asf->index_read = -1;
+ }
+
+ if (asf->index_read > 0 && st->index_entries) {
+ int index = av_index_search_timestamp(st, pts, flags);
+ if (index >= 0) {
+ /* find the position */
+ uint64_t pos = st->index_entries[index].pos;
+
+ /* do the seek */
+ av_log(s, AV_LOG_DEBUG, "SEEKTO: %"PRId64"\n", pos);
+ if(avio_seek(s->pb, pos, SEEK_SET) < 0)
+ return -1;
+ asf_reset_header(s);
+ skip_to_key(s);
+ return 0;
+ }
+ }
+ /* no index or seeking by index failed */
+ if (ff_seek_frame_binary(s, stream_index, pts, flags) < 0)
+ return -1;
+ asf_reset_header(s);
+ skip_to_key(s);
+ return 0;
+}
+
+AVInputFormat ff_asf_demuxer = {
+ .name = "asf",
+ .long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"),
+ .priv_data_size = sizeof(ASFContext),
+ .read_probe = asf_probe,
+ .read_header = asf_read_header,
+ .read_packet = asf_read_packet,
+ .read_close = asf_read_close,
+ .read_seek = asf_read_seek,
+ .read_timestamp = asf_read_pts,
+ .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH,
+ .priv_class = &asf_class,
+};
diff --git a/libavformat/asfdec.c b/libavformat/asfdec_o.c
index 9e1f8f6..5122e33 100644
--- a/libavformat/asfdec.c
+++ b/libavformat/asfdec_o.c
@@ -2,20 +2,20 @@
* Microsoft Advanced Streaming Format demuxer
* Copyright (c) 2014 Alexandra Hájková
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -151,7 +151,7 @@ static int asf_probe(AVProbeData *pd)
{
/* check file header */
if (!ff_guidcmp(pd->buf, &ff_asf_header))
- return AVPROBE_SCORE_MAX;
+ return AVPROBE_SCORE_MAX/2;
else
return 0;
}
@@ -166,7 +166,7 @@ static void swap_guid(ff_asf_guid guid)
static void align_position(AVIOContext *pb, int64_t offset, uint64_t size)
{
- if (avio_tell(pb) != offset + size)
+ if (size < INT64_MAX - offset && avio_tell(pb) != offset + size)
avio_seek(pb, offset + size, SEEK_SET);
}
@@ -455,13 +455,15 @@ fail:
return ret;
}
-static void get_id3_tag(AVFormatContext *s)
+static void get_id3_tag(AVFormatContext *s, int len)
{
ID3v2ExtraMeta *id3v2_extra_meta = NULL;
- ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);
- if (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_parse_chapters(s, &id3v2_extra_meta);
+ }
ff_id3v2_free_extra_meta(&id3v2_extra_meta);
}
@@ -480,7 +482,7 @@ static int process_metadata(AVFormatContext *s, const uint8_t *name, uint16_t na
if (!strcmp(name, "WM/Picture")) // handle cover art
asf_read_picture(s, val_len);
else if (!strcmp(name, "ID3")) // handle ID3 tag
- get_id3_tag(s);
+ get_id3_tag(s, val_len);
else
asf_read_value(s, name, val_len, type, met);
break;
@@ -791,7 +793,7 @@ static int asf_read_stream_properties(AVFormatContext *s, const GUIDParseTable *
switch (type) {
case AVMEDIA_TYPE_AUDIO:
asf_st->type = AVMEDIA_TYPE_AUDIO;
- if ((ret = ff_get_wav_header(s, pb, st->codecpar, ts_data_len)) < 0)
+ if ((ret = ff_get_wav_header(s, pb, st->codecpar, ts_data_len, 0)) < 0)
return ret;
break;
case AVMEDIA_TYPE_VIDEO:
@@ -830,7 +832,7 @@ static void set_language(AVFormatContext *s, const char *rfc1766, AVDictionary *
// language abbr should contain at least 2 chars
if (rfc1766 && strlen(rfc1766) > 1) {
const char primary_tag[3] = { rfc1766[0], rfc1766[1], '\0' }; // ignore country code if any
- const char *iso6392 = av_convert_lang_to(primary_tag,
+ const char *iso6392 = ff_convert_lang_to(primary_tag,
AV_LANG_ISO639_2_BIBL);
if (iso6392)
if (av_dict_set(met, "language", iso6392, 0) < 0)
@@ -1056,8 +1058,8 @@ static const GUIDParseTable gdef[] = {
{ "Mutex Language", { 0xD6, 0xE2, 0x2A, 0x00, 0x25, 0xDA, 0x11, 0xD1, 0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE }, asf_read_unknown, 1 },
{ "Mutex Bitrate", { 0xD6, 0xE2, 0x2A, 0x01, 0x25, 0xDA, 0x11, 0xD1, 0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE }, asf_read_unknown, 1 },
{ "Mutex Unknown", { 0xD6, 0xE2, 0x2A, 0x02, 0x25, 0xDA, 0x11, 0xD1, 0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE }, asf_read_unknown, 1 },
- { "Bandwith Sharing Exclusive", { 0xAF, 0x60, 0x60, 0xAA, 0x51, 0x97, 0x11, 0xD2, 0xB6, 0xAF, 0x00, 0xC0, 0x4F, 0xD9, 0x08, 0xE9 }, asf_read_unknown, 1 },
- { "Bandwith Sharing Partial", { 0xAF, 0x60, 0x60, 0xAB, 0x51, 0x97, 0x11, 0xD2, 0xB6, 0xAF, 0x00, 0xC0, 0x4F, 0xD9, 0x08, 0xE9 }, asf_read_unknown, 1 },
+ { "Bandwidth Sharing Exclusive", { 0xAF, 0x60, 0x60, 0xAA, 0x51, 0x97, 0x11, 0xD2, 0xB6, 0xAF, 0x00, 0xC0, 0x4F, 0xD9, 0x08, 0xE9 }, asf_read_unknown, 1 },
+ { "Bandwidth Sharing Partial", { 0xAF, 0x60, 0x60, 0xAB, 0x51, 0x97, 0x11, 0xD2, 0xB6, 0xAF, 0x00, 0xC0, 0x4F, 0xD9, 0x08, 0xE9 }, asf_read_unknown, 1 },
{ "Payload Extension System Timecode", { 0x39, 0x95, 0x95, 0xEC, 0x86, 0x67, 0x4E, 0x2D, 0x8F, 0xDB, 0x98, 0x81, 0x4C, 0xE7, 0x6C, 0x1E }, asf_read_unknown, 1 },
{ "Payload Extension System File Name", { 0xE1, 0x65, 0xEC, 0x0E, 0x19, 0xED, 0x45, 0xD7, 0xB4, 0xA7, 0x25, 0xCB, 0xD1, 0xE2, 0x8E, 0x9B }, asf_read_unknown, 1 },
{ "Payload Extension System Content Type", { 0xD5, 0x90, 0xDC, 0x20, 0x07, 0xBC, 0x43, 0x6C, 0x9C, 0xF7, 0xF3, 0xBB, 0xFB, 0xF1, 0xA4, 0xDC }, asf_read_unknown, 1 },
@@ -1781,8 +1783,8 @@ failed:
return ret;
}
-AVInputFormat ff_asf_demuxer = {
- .name = "asf",
+AVInputFormat ff_asf_o_demuxer = {
+ .name = "asf_o",
.long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"),
.priv_data_size = sizeof(ASFContext),
.read_probe = asf_probe,
diff --git a/libavformat/asfenc.c b/libavformat/asfenc.c
index a40c02d..3cfe75a 100644
--- a/libavformat/asfenc.c
+++ b/libavformat/asfenc.c
@@ -2,37 +2,38 @@
* 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 "libavutil/parseutils.h"
+#include "libavutil/opt.h"
#include "avformat.h"
+#include "avlanguage.h"
#include "avio_internal.h"
#include "internal.h"
#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 \
@@ -172,26 +173,36 @@
ASF_PAYLOAD_REPLICATED_DATA_LENGTH + \
ASF_PAYLOAD_LENGTH_FIELD_SIZE)
-#define SINGLE_PAYLOAD_DATA_LENGTH \
- (PACKET_SIZE - \
- PACKET_HEADER_MIN_SIZE - \
+#define SINGLE_PAYLOAD_HEADERS \
+ (PACKET_HEADER_MIN_SIZE + \
PAYLOAD_HEADER_SIZE_SINGLE_PAYLOAD)
-#define MULTI_PAYLOAD_CONSTANT \
- (PACKET_SIZE - \
- PACKET_HEADER_MIN_SIZE - \
- 1 - /* Payload Flags */ \
+#define MULTI_PAYLOAD_HEADERS \
+ (PACKET_HEADER_MIN_SIZE + \
+ 1 + /* Payload Flags */ \
2 * PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS)
#define DATA_HEADER_SIZE 50
+#define PACKET_SIZE_MAX 65536
+#define PACKET_SIZE_MIN 100
+
+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;
@@ -203,48 +214,53 @@ typedef struct ASFStream {
int palette_changed;
uint32_t palette[256];
+
+ int payload_ext_ct;
+ ASFPayload payload[8];
} ASFStream;
typedef struct ASFContext {
+ AVClass *av_class;
uint32_t seqno;
int is_streamed;
ASFStream streams[128]; ///< it's max number and it's not that big
+ const char *languages[128];
+ int nb_languages;
+ int64_t creation_time;
/* non-streamed additional info */
uint64_t nb_packets; ///< how many packets are there in the file, invalid if broadcasting
- uint64_t duration; ///< in ms
+ int64_t duration; ///< in 100ns units
/* 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];
+ uint8_t packet_buf[PACKET_SIZE_MAX];
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;
+ int packet_size;
} 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;
@@ -265,7 +281,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;
}
@@ -298,12 +314,12 @@ static void put_chunk(AVFormatContext *s, int type,
asf->seqno++;
}
-/* convert from unix to windows time */
-static int64_t unix_to_file_time(int ti)
+/* convert from av time to windows time */
+static int64_t unix_to_file_time(int64_t ti)
{
int64_t t;
- t = ti * INT64_C(10000000);
+ t = ti * INT64_C(10);
t += INT64_C(116444736000000000);
return t;
}
@@ -313,7 +329,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;
@@ -331,7 +347,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
@@ -373,13 +389,14 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
ASFContext *asf = s->priv_data;
AVIOContext *pb = s->pb;
AVDictionaryEntry *tags[5];
- int header_size, n, extra_size, extra_size2, wav_extra_size, file_time;
- int has_title;
+ int header_size, n, extra_size, extra_size2, wav_extra_size;
+ int has_title, has_aspect_ratio = 0;
int metadata_count;
AVCodecParameters *par;
int64_t header_offset, cur_pos, hpos;
int bit_rate;
- uint64_t play_duration, send_duration;
+ int64_t duration;
+ int audio_language_counts[128] = { 0 };
ff_metadata_conv(&s->metadata, ff_asf_metadata_conv, NULL);
@@ -389,34 +406,58 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
tags[3] = av_dict_get(s->metadata, "comment", NULL, 0);
tags[4] = av_dict_get(s->metadata, "rating", NULL, 0);
- if (asf->duration > UINT64_MAX / 10000 - PREROLL_TIME) {
- av_log(s, AV_LOG_WARNING, "Duration %"PRIu64" too large\n", asf->duration);
- if (s->error_recognition & AV_EF_EXPLODE)
- return AVERROR(ERANGE);
- send_duration = 0;
- play_duration = 0;
- } else {
- send_duration = asf->duration * 10000;
- play_duration = (asf->duration + PREROLL_TIME) * 10000;
+ duration = asf->duration + PREROLL_TIME * 10000;
+ has_title = tags[0] || tags[1] || tags[2] || tags[3] || tags[4];
+
+ if (!file_size) {
+ if (ff_parse_creation_time_metadata(s, &asf->creation_time, 0) != 0)
+ av_dict_set(&s->metadata, "creation_time", NULL, 0);
}
- has_title = tags[0] || tags[1] || tags[2] || tags[3] || tags[4];
metadata_count = av_dict_count(s->metadata);
bit_rate = 0;
for (n = 0; n < s->nb_streams; n++) {
+ AVDictionaryEntry *entry;
par = s->streams[n]->codecpar;
avpriv_set_pts_info(s->streams[n], 32, 1, 1000); /* 32 bit pts in ms */
bit_rate += par->bit_rate;
+ if ( par->codec_type == AVMEDIA_TYPE_VIDEO
+ && par->sample_aspect_ratio.num > 0
+ && par->sample_aspect_ratio.den > 0)
+ has_aspect_ratio++;
+
+ entry = av_dict_get(s->streams[n]->metadata, "language", NULL, 0);
+ if (entry) {
+ const char *iso6391lang = ff_convert_lang_to(entry->value, AV_LANG_ISO639_1);
+ if (iso6391lang) {
+ int i;
+ for (i = 0; i < asf->nb_languages; i++) {
+ if (!strcmp(asf->languages[i], iso6391lang)) {
+ asf->streams[n].stream_language_index = i;
+ break;
+ }
+ }
+ if (i >= asf->nb_languages) {
+ asf->languages[asf->nb_languages] = iso6391lang;
+ asf->streams[n].stream_language_index = asf->nb_languages;
+ asf->nb_languages++;
+ }
+ if (par->codec_type == AVMEDIA_TYPE_AUDIO)
+ audio_language_counts[asf->streams[n].stream_language_index]++;
+ }
+ } else {
+ asf->streams[n].stream_language_index = 128;
+ }
}
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); /* ??? */
@@ -425,25 +466,116 @@ 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));
+ avio_wl64(pb, unix_to_file_time(asf->creation_time));
avio_wl64(pb, asf->nb_packets); /* number of packets */
- avio_wl64(pb, play_duration); /* end time stamp (in 100ns units) */
- avio_wl64(pb, send_duration); /* duration (in 100ns units) */
+ avio_wl64(pb, duration); /* end time stamp (in 100ns units) */
+ avio_wl64(pb, asf->duration); /* duration (in 100ns units) */
avio_wl64(pb, PREROLL_TIME); /* start time stamp */
avio_wl32(pb, (asf->is_streamed || !(pb->seekable & AVIO_SEEKABLE_NORMAL)) ? 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 */
+ /* header_extension */
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);
+ avio_wl32(pb, 0); /* length, to be filled later */
+ if (asf->nb_languages) {
+ int64_t hpos2;
+ int i;
+ int nb_audio_languages = 0;
+
+ hpos2 = put_header(pb, &ff_asf_language_guid);
+ avio_wl16(pb, asf->nb_languages);
+ for (i = 0; i < asf->nb_languages; i++) {
+ avio_w8(pb, 6);
+ avio_put_str16le(pb, asf->languages[i]);
+ }
+ end_header(pb, hpos2);
+
+ for (i = 0; i < asf->nb_languages; i++)
+ if (audio_language_counts[i])
+ nb_audio_languages++;
+
+ if (nb_audio_languages > 1) {
+ hpos2 = put_header(pb, &ff_asf_group_mutual_exclusion_object);
+ ff_put_guid(pb, &ff_asf_mutex_language);
+ avio_wl16(pb, nb_audio_languages);
+ for (i = 0; i < asf->nb_languages; i++) {
+ if (audio_language_counts[i]) {
+ avio_wl16(pb, audio_language_counts[i]);
+ for (n = 0; n < s->nb_streams; n++)
+ if (asf->streams[n].stream_language_index == i && s->streams[n]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
+ avio_wl16(pb, n + 1);
+ }
+ }
+ end_header(pb, hpos2);
+ }
+
+ for (n = 0; n < s->nb_streams; n++) {
+ int64_t es_pos;
+ if (asf->streams[n].stream_language_index > 127)
+ continue;
+ es_pos = put_header(pb, &ff_asf_extended_stream_properties_object);
+ avio_wl64(pb, 0); /* start time */
+ avio_wl64(pb, 0); /* end time */
+ avio_wl32(pb, s->streams[n]->codecpar->bit_rate); /* data bitrate bps */
+ avio_wl32(pb, 5000); /* buffer size ms */
+ avio_wl32(pb, 0); /* initial buffer fullness */
+ avio_wl32(pb, s->streams[n]->codecpar->bit_rate); /* peak data bitrate */
+ avio_wl32(pb, 5000); /* maximum buffer size ms */
+ avio_wl32(pb, 0); /* max initial buffer fullness */
+ avio_wl32(pb, 0); /* max object size */
+ avio_wl32(pb, (!asf->is_streamed && (pb->seekable & AVIO_SEEKABLE_NORMAL)) << 1); /* flags - seekable */
+ avio_wl16(pb, n + 1); /* stream number */
+ avio_wl16(pb, asf->streams[n].stream_language_index); /* language id index */
+ avio_wl64(pb, 0); /* avg time per frame */
+ avio_wl16(pb, 0); /* stream name count */
+ avio_wl16(pb, 0); /* payload extension system count */
+ end_header(pb, es_pos);
+ }
+ }
+ if (has_aspect_ratio) {
+ int64_t hpos2;
+ hpos2 = put_header(pb, &ff_asf_metadata_header);
+ avio_wl16(pb, 2 * has_aspect_ratio);
+ for (n = 0; n < s->nb_streams; n++) {
+ par = s->streams[n]->codecpar;
+ if ( par->codec_type == AVMEDIA_TYPE_VIDEO
+ && par->sample_aspect_ratio.num > 0
+ && par->sample_aspect_ratio.den > 0) {
+ AVRational sar = par->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);
+ }
+ {
+ int64_t pos1;
+ pos1 = avio_tell(pb);
+ avio_seek(pb, hpos + 42, SEEK_SET);
+ avio_wl32(pb, pos1 - hpos - 46);
+ avio_seek(pb, pos1, SEEK_SET);
+ }
end_header(pb, hpos);
/* title and other info */
@@ -480,7 +612,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 */
@@ -490,7 +622,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
par = s->streams[n]->codecpar;
asf->streams[n].num = n + 1;
- asf->streams[n].seq = 0;
+ asf->streams[n].seq = 1;
switch (par->codec_type) {
case AVMEDIA_TYPE_AUDIO:
@@ -508,11 +640,11 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
hpos = put_header(pb, &ff_asf_stream_header);
if (par->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);
@@ -523,7 +655,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
if (par->codec_type == AVMEDIA_TYPE_AUDIO) {
/* WAVEFORMATEX header */
- int wavsize = ff_put_wav_header(s, pb, par);
+ int wavsize = ff_put_wav_header(s, pb, par, FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX);
if (wavsize < 0)
return -1;
@@ -551,7 +683,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
avio_wl16(pb, 40 + par->extradata_size); /* size */
/* BITMAPINFOHEADER header */
- ff_put_bmp_header(pb, par, ff_codec_bmp_tags, 1);
+ ff_put_bmp_header(pb, par, 1, 0);
}
end_header(pb, hpos);
}
@@ -559,7 +691,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;
@@ -633,9 +765,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); /* ??? */
@@ -646,13 +778,19 @@ static int asf_write_header(AVFormatContext *s)
{
ASFContext *asf = s->priv_data;
- s->packet_size = PACKET_SIZE;
+ s->packet_size = asf->packet_size;
+ s->max_interleave_delta = 0;
asf->nb_packets = 0;
- asf->last_indexed_pts = 0;
+ if (s->nb_streams > 127) {
+ av_log(s, AV_LOG_ERROR, "ASF can only handle 127 streams\n");
+ return AVERROR(EINVAL);
+ }
+
asf->index_ptr = av_malloc(sizeof(ASFIndex) * ASF_INDEX_BLOCK);
+ if (!asf->index_ptr)
+ return AVERROR(ENOMEM);
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
@@ -672,6 +810,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;
}
@@ -698,7 +839,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++)
@@ -737,20 +878,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);
+ packet_filled_size = asf->packet_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);
@@ -765,7 +905,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;
@@ -788,7 +928,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
@@ -796,7 +936,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;
@@ -806,13 +946,14 @@ static void put_frame(AVFormatContext *s, ASFStream *stream, AVStream *avst,
while (m_obj_offset < m_obj_size) {
payload_len = m_obj_size - m_obj_offset;
if (asf->packet_timestamp_start == -1) {
- asf->multi_payloads_present = (payload_len < MULTI_PAYLOAD_CONSTANT);
+ const int multi_payload_constant = (asf->packet_size - MULTI_PAYLOAD_HEADERS);
+ asf->multi_payloads_present = (payload_len < multi_payload_constant);
- asf->packet_size_left = PACKET_SIZE;
+ asf->packet_size_left = asf->packet_size;
if (asf->multi_payloads_present) {
- frag_len1 = MULTI_PAYLOAD_CONSTANT - 1;
+ frag_len1 = multi_payload_constant - 1;
} else {
- frag_len1 = SINGLE_PAYLOAD_DATA_LENGTH;
+ frag_len1 = asf->packet_size - SINGLE_PAYLOAD_HEADERS;
}
asf->packet_timestamp_start = timestamp;
} else {
@@ -826,6 +967,11 @@ static void put_frame(AVFormatContext *s, ASFStream *stream, AVStream *avst,
flush_packet(s);
continue;
}
+ if (asf->packet_timestamp_start > INT64_MAX - UINT16_MAX ||
+ timestamp > asf->packet_timestamp_start + UINT16_MAX) {
+ flush_packet(s);
+ continue;
+ }
}
if (frag_len1 > 0) {
if (payload_len > frag_len1)
@@ -854,19 +1000,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;
AVCodecParameters *par;
- 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);
par = s->streams[pkt->stream_index]->codecpar;
@@ -876,58 +1068,43 @@ 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);
-
- if (pts > UINT64_MAX - pkt->duration)
- return AVERROR(ERANGE);
- asf->duration = FFMAX(asf->duration, pts + pkt->duration);
+ av_assert0(pts != AV_NOPTS_VALUE);
+ if ( pts < - PREROLL_TIME
+ || pts > (INT_MAX-3)/10000LL * ASF_INDEXED_INTERVAL - PREROLL_TIME) {
+ av_log(s, AV_LOG_ERROR, "input pts %"PRId64" is invalid\n", pts);
+ return AVERROR(EINVAL);
+ }
+ 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)) {
- if (pts / 1000LL > INT_MAX)
- return AVERROR(ERANGE);
-
- start_sec = pts / 1000;
- if (start_sec != asf->last_indexed_pts / 1000) {
- 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 = pts;
- }
+ 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);
@@ -943,6 +1120,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)
@@ -950,8 +1128,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 & AVIO_SEEKABLE_NORMAL)) {
@@ -963,18 +1144,30 @@ 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;
}
+static const AVOption asf_options[] = {
+ { "packet_size", "Packet size", offsetof(ASFContext, packet_size), AV_OPT_TYPE_INT, {.i64 = 3200}, PACKET_SIZE_MIN, PACKET_SIZE_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { NULL },
+};
+
#if CONFIG_ASF_MUXER
+static const AVClass asf_muxer_class = {
+ .class_name = "ASF muxer",
+ .item_name = av_default_item_name,
+ .option = asf_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVOutputFormat ff_asf_muxer = {
.name = "asf",
.long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"),
.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,
@@ -983,17 +1176,25 @@ AVOutputFormat ff_asf_muxer = {
.codec_tag = (const AVCodecTag * const []) {
codec_asf_bmp_tags, ff_codec_bmp_tags, ff_codec_wav_tags, 0
},
+ .priv_class = &asf_muxer_class,
};
#endif /* CONFIG_ASF_MUXER */
#if CONFIG_ASF_STREAM_MUXER
+static const AVClass asf_stream_muxer_class = {
+ .class_name = "ASF stream muxer",
+ .item_name = av_default_item_name,
+ .option = asf_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVOutputFormat ff_asf_stream_muxer = {
.name = "asf_stream",
.long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"),
.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,
@@ -1002,5 +1203,6 @@ AVOutputFormat ff_asf_stream_muxer = {
.codec_tag = (const AVCodecTag * const []) {
codec_asf_bmp_tags, ff_codec_bmp_tags, ff_codec_wav_tags, 0
},
+ .priv_class = &asf_stream_muxer_class,
};
#endif /* CONFIG_ASF_STREAM_MUXER */
diff --git a/libavformat/assdec.c b/libavformat/assdec.c
index 059cefe..d89c14e 100644
--- a/libavformat/assdec.c
+++ b/libavformat/assdec.c
@@ -1,224 +1,192 @@
/*
* 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);
+
+ while (ff_text_peek_r8(&tr) == '\r' || ff_text_peek_r8(&tr) == '\n')
+ ff_text_r8(&tr);
+
+ ff_text_read(&tr, buf, sizeof(buf));
- if (!memcmp(p->buf, header, strlen(header)) ||
- !memcmp(p->buf + 3, header, strlen(header)))
+ 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_log(NULL, AV_LOG_TRACE, "%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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
- st->codecpar->codec_id = AV_CODEC_ID_SSA;
+ st->codecpar->codec_id = AV_CODEC_ID_ASS;
- header_remaining = INT_MAX;
- dst[0] = &st->codecpar->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));
+ ass->q.keep_duplicates = 1;
- if (!memcmp(line, "[Events]", 8))
- header_remaining = 2;
- else if (line[0] == '[')
- header_remaining = INT_MAX;
+ for (;;) {
+ int64_t pos = get_line(&line, &tr);
+ int64_t ts_start = AV_NOPTS_VALUE;
+ int duration = -1;
+ AVPacket *sub;
- i = header_remaining == 0;
+ if (!line.str[0]) // EOF
+ break;
- 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->codecpar->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 = ff_bprint_to_codecpar_extradata(st->codecpar, &header);
+ if (res < 0)
+ goto end;
-fail:
- read_close(s);
+ ff_subtitles_queue_finalize(s, &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]->codecpar->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 c6f9851..d50f18f 100644
--- a/libavformat/assenc.c
+++ b/libavformat/assenc.c
@@ -2,85 +2,241 @@
* 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"
-typedef struct ASSContext{
- unsigned int extra_index;
-}ASSContext;
+#include "libavutil/opt.h"
+
+typedef struct DialogueLine {
+ int readorder;
+ char *line;
+ struct DialogueLine *prev, *next;
+} DialogueLine;
+
+typedef struct ASSContext {
+ const AVClass *class;
+ int expected_readorder;
+ DialogueLine *dialogue_cache;
+ DialogueLine *last_added_dialogue;
+ int cache_size;
+ int ssa_mode;
+ int ignore_readorder;
+ uint8_t *trailer;
+ size_t trailer_size;
+} ASSContext;
static int write_header(AVFormatContext *s)
{
ASSContext *ass = s->priv_data;
AVCodecParameters *par = s->streams[0]->codecpar;
- uint8_t *last= NULL;
- if(s->nb_streams != 1 || par->codec_id != AV_CODEC_ID_SSA){
+ if (s->nb_streams != 1 || par->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);
}
+ avpriv_set_pts_info(s->streams[0], 64, 1, 100);
+ if (par->extradata_size > 0) {
+ size_t header_size = par->extradata_size;
+ uint8_t *trailer = strstr(par->extradata, "\n[Events]");
- while(ass->extra_index < par->extradata_size){
- uint8_t *p = par->extradata + ass->extra_index;
- uint8_t *end= strchr(p, '\n');
- if(!end) end= par->extradata + par->extradata_size;
- else end++;
+ if (trailer)
+ trailer = strstr(trailer, "Format:");
+ if (trailer)
+ trailer = strstr(trailer, "\n");
- avio_write(s->pb, p, end-p);
- ass->extra_index += end-p;
+ if (trailer++) {
+ header_size = (trailer - par->extradata);
+ ass->trailer_size = par->extradata_size - header_size;
+ if (ass->trailer_size)
+ ass->trailer = trailer;
+ }
- if(last && !memcmp(last, "[Events]", 8))
- break;
- last=p;
+ avio_write(s->pb, par->extradata, header_size);
+ if (par->extradata[header_size - 1] != '\n')
+ avio_write(s->pb, "\r\n", 2);
+ ass->ssa_mode = !strstr(par->extradata, "\n[V4+ Styles]");
+ if (!strstr(par->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 (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);
+}
+
+static void insert_dialogue(ASSContext *ass, DialogueLine *dialogue)
+{
+ DialogueLine *cur, *next = NULL, *prev = NULL;
+
+ /* 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;
+ }
+ }
+
+ /* 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;
+ }
+ }
+
+ 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;
+
+ 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);
+
return 0;
}
static int write_trailer(AVFormatContext *s)
{
ASSContext *ass = s->priv_data;
- AVCodecParameters *par = s->streams[0]->codecpar;
- avio_write(s->pb, par->extradata + ass->extra_index,
- par->extradata_size - ass->extra_index);
+ purge_dialogues(s, 1);
+
+ if (ass->trailer) {
+ avio_write(s->pb, ass->trailer, ass->trailer_size);
+ }
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_BOOL, {.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"),
- .mime_type = "text/x-ssa",
+ .mime_type = "text/x-ass",
.extensions = "ass,ssa",
.priv_data_size = sizeof(ASSContext),
- .subtitle_codec = AV_CODEC_ID_SSA,
+ .subtitle_codec = AV_CODEC_ID_ASS,
.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 0000000..9de74aa
--- /dev/null
+++ b/libavformat/ast.c
@@ -0,0 +1,29 @@
+/*
+ * AST common code
+ * Copyright (c) 2012 James Almer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "internal.h"
+
+const AVCodecTag ff_codec_ast_tags[] = {
+ { AV_CODEC_ID_ADPCM_AFC, 0 },
+ { AV_CODEC_ID_PCM_S16BE_PLANAR, 1 },
+ { AV_CODEC_ID_NONE, 0 },
+};
diff --git a/libavformat/ast.h b/libavformat/ast.h
new file mode 100644
index 0000000..4a399ea
--- /dev/null
+++ b/libavformat/ast.h
@@ -0,0 +1,30 @@
+/*
+ * AST common code
+ * Copyright (c) 2012 James Almer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_AST_H
+#define AVFORMAT_AST_H
+
+#include "avformat.h"
+#include "internal.h"
+
+extern const AVCodecTag ff_codec_ast_tags[];
+
+#endif /* AVFORMAT_AST_H */
diff --git a/libavformat/astdec.c b/libavformat/astdec.c
new file mode 100644
index 0000000..4ba08c2
--- /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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->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->codecpar->channels = avio_rb16(s->pb);
+ if (!st->codecpar->channels)
+ return AVERROR_INVALIDDATA;
+
+ if (st->codecpar->channels == 2)
+ st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
+ else if (st->codecpar->channels == 4)
+ st->codecpar->channel_layout = AV_CH_LAYOUT_4POINT0;
+
+ avio_skip(s->pb, 2);
+ st->codecpar->sample_rate = avio_rb32(s->pb);
+ if (st->codecpar->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->codecpar->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 (!s->streams[0]->codecpar->channels || size > INT_MAX / s->streams[0]->codecpar->channels)
+ return AVERROR_INVALIDDATA;
+
+ size *= s->streams[0]->codecpar->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 %"PRIx32"\n", type);
+ avio_skip(s->pb, size);
+ ret = AVERROR_INVALIDDATA;
+ }
+
+ return ret;
+}
+
+AVInputFormat ff_ast_demuxer = {
+ .name = "ast",
+ .long_name = NULL_IF_CONFIG_SMALL("AST (Audio Stream)"),
+ .read_probe = ast_probe,
+ .read_header = ast_read_header,
+ .read_packet = ast_read_packet,
+ .extensions = "ast",
+ .flags = AVFMT_GENERIC_INDEX,
+ .codec_tag = (const AVCodecTag* const []){ff_codec_ast_tags, 0},
+};
diff --git a/libavformat/astenc.c b/libavformat/astenc.c
new file mode 100644
index 0000000..578e658
--- /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, par->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;
+ AVCodecParameters *par;
+ unsigned int codec_tag;
+
+ if (s->nb_streams == 1) {
+ par = s->streams[0]->codecpar;
+ } else {
+ av_log(s, AV_LOG_ERROR, "only one stream is supported\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (par->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, par->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, par->channels);
+ avio_wb16(pb, 0); /* Loop flag */
+ avio_wb32(pb, par->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;
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ int size = pkt->size / par->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;
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ int64_t file_size = avio_tell(pb);
+ int64_t samples = (file_size - 64 - (32 * s->streams[0]->nb_frames)) / par->block_align; /* PCM_S16BE_PLANAR */
+
+ av_log(s, AV_LOG_DEBUG, "total samples: %"PRId64"\n", samples);
+
+ if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ /* 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/async.c b/libavformat/async.c
new file mode 100644
index 0000000..54dbd23
--- /dev/null
+++ b/libavformat/async.c
@@ -0,0 +1,699 @@
+/*
+ * Input async protocol.
+ * Copyright (c) 2015 Zhang Rui <bbcallen@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
+ *
+ * Based on libavformat/cache.c by Michael Niedermayer
+ */
+
+ /**
+ * @TODO
+ * support timeout
+ * support work with concatdec, hls
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/error.h"
+#include "libavutil/fifo.h"
+#include "libavutil/log.h"
+#include "libavutil/opt.h"
+#include "libavutil/thread.h"
+#include "url.h"
+#include <stdint.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#define BUFFER_CAPACITY (4 * 1024 * 1024)
+#define READ_BACK_CAPACITY (4 * 1024 * 1024)
+#define SHORT_SEEK_THRESHOLD (256 * 1024)
+
+typedef struct RingBuffer
+{
+ AVFifoBuffer *fifo;
+ int read_back_capacity;
+
+ int read_pos;
+} RingBuffer;
+
+typedef struct Context {
+ AVClass *class;
+ URLContext *inner;
+
+ int seek_request;
+ int64_t seek_pos;
+ int seek_whence;
+ int seek_completed;
+ int64_t seek_ret;
+
+ int inner_io_error;
+ int io_error;
+ int io_eof_reached;
+
+ int64_t logical_pos;
+ int64_t logical_size;
+ RingBuffer ring;
+
+ pthread_cond_t cond_wakeup_main;
+ pthread_cond_t cond_wakeup_background;
+ pthread_mutex_t mutex;
+ pthread_t async_buffer_thread;
+
+ int abort_request;
+ AVIOInterruptCB interrupt_callback;
+} Context;
+
+static int ring_init(RingBuffer *ring, unsigned int capacity, int read_back_capacity)
+{
+ memset(ring, 0, sizeof(RingBuffer));
+ ring->fifo = av_fifo_alloc(capacity + read_back_capacity);
+ if (!ring->fifo)
+ return AVERROR(ENOMEM);
+
+ ring->read_back_capacity = read_back_capacity;
+ return 0;
+}
+
+static void ring_destroy(RingBuffer *ring)
+{
+ av_fifo_freep(&ring->fifo);
+}
+
+static void ring_reset(RingBuffer *ring)
+{
+ av_fifo_reset(ring->fifo);
+ ring->read_pos = 0;
+}
+
+static int ring_size(RingBuffer *ring)
+{
+ return av_fifo_size(ring->fifo) - ring->read_pos;
+}
+
+static int ring_space(RingBuffer *ring)
+{
+ return av_fifo_space(ring->fifo);
+}
+
+static int ring_generic_read(RingBuffer *ring, void *dest, int buf_size, void (*func)(void*, void*, int))
+{
+ int ret;
+
+ av_assert2(buf_size <= ring_size(ring));
+ ret = av_fifo_generic_peek_at(ring->fifo, dest, ring->read_pos, buf_size, func);
+ ring->read_pos += buf_size;
+
+ if (ring->read_pos > ring->read_back_capacity) {
+ av_fifo_drain(ring->fifo, ring->read_pos - ring->read_back_capacity);
+ ring->read_pos = ring->read_back_capacity;
+ }
+
+ return ret;
+}
+
+static int ring_generic_write(RingBuffer *ring, void *src, int size, int (*func)(void*, void*, int))
+{
+ av_assert2(size <= ring_space(ring));
+ return av_fifo_generic_write(ring->fifo, src, size, func);
+}
+
+static int ring_size_of_read_back(RingBuffer *ring)
+{
+ return ring->read_pos;
+}
+
+static int ring_drain(RingBuffer *ring, int offset)
+{
+ av_assert2(offset >= -ring_size_of_read_back(ring));
+ av_assert2(offset <= -ring_size(ring));
+ ring->read_pos += offset;
+ return 0;
+}
+
+static int async_check_interrupt(void *arg)
+{
+ URLContext *h = arg;
+ Context *c = h->priv_data;
+
+ if (c->abort_request)
+ return 1;
+
+ if (ff_check_interrupt(&c->interrupt_callback))
+ c->abort_request = 1;
+
+ return c->abort_request;
+}
+
+static int wrapped_url_read(void *src, void *dst, int size)
+{
+ URLContext *h = src;
+ Context *c = h->priv_data;
+ int ret;
+
+ ret = ffurl_read(c->inner, dst, size);
+ c->inner_io_error = ret < 0 ? ret : 0;
+
+ return ret;
+}
+
+static void *async_buffer_task(void *arg)
+{
+ URLContext *h = arg;
+ Context *c = h->priv_data;
+ RingBuffer *ring = &c->ring;
+ int ret = 0;
+ int64_t seek_ret;
+
+ while (1) {
+ int fifo_space, to_copy;
+
+ pthread_mutex_lock(&c->mutex);
+ if (async_check_interrupt(h)) {
+ c->io_eof_reached = 1;
+ c->io_error = AVERROR_EXIT;
+ pthread_cond_signal(&c->cond_wakeup_main);
+ pthread_mutex_unlock(&c->mutex);
+ break;
+ }
+
+ if (c->seek_request) {
+ seek_ret = ffurl_seek(c->inner, c->seek_pos, c->seek_whence);
+ if (seek_ret >= 0) {
+ c->io_eof_reached = 0;
+ c->io_error = 0;
+ ring_reset(ring);
+ }
+
+ c->seek_completed = 1;
+ c->seek_ret = seek_ret;
+ c->seek_request = 0;
+
+
+ pthread_cond_signal(&c->cond_wakeup_main);
+ pthread_mutex_unlock(&c->mutex);
+ continue;
+ }
+
+ fifo_space = ring_space(ring);
+ if (c->io_eof_reached || fifo_space <= 0) {
+ pthread_cond_signal(&c->cond_wakeup_main);
+ pthread_cond_wait(&c->cond_wakeup_background, &c->mutex);
+ pthread_mutex_unlock(&c->mutex);
+ continue;
+ }
+ pthread_mutex_unlock(&c->mutex);
+
+ to_copy = FFMIN(4096, fifo_space);
+ ret = ring_generic_write(ring, (void *)h, to_copy, wrapped_url_read);
+
+ pthread_mutex_lock(&c->mutex);
+ if (ret <= 0) {
+ c->io_eof_reached = 1;
+ if (c->inner_io_error < 0)
+ c->io_error = c->inner_io_error;
+ }
+
+ pthread_cond_signal(&c->cond_wakeup_main);
+ pthread_mutex_unlock(&c->mutex);
+ }
+
+ return NULL;
+}
+
+static int async_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
+{
+ Context *c = h->priv_data;
+ int ret;
+ AVIOInterruptCB interrupt_callback = {.callback = async_check_interrupt, .opaque = h};
+
+ av_strstart(arg, "async:", &arg);
+
+ ret = ring_init(&c->ring, BUFFER_CAPACITY, READ_BACK_CAPACITY);
+ if (ret < 0)
+ goto fifo_fail;
+
+ /* wrap interrupt callback */
+ c->interrupt_callback = h->interrupt_callback;
+ ret = ffurl_open_whitelist(&c->inner, arg, flags, &interrupt_callback, options, h->protocol_whitelist, h->protocol_blacklist, h);
+ if (ret != 0) {
+ av_log(h, AV_LOG_ERROR, "ffurl_open failed : %s, %s\n", av_err2str(ret), arg);
+ goto url_fail;
+ }
+
+ c->logical_size = ffurl_size(c->inner);
+ h->is_streamed = c->inner->is_streamed;
+
+ ret = pthread_mutex_init(&c->mutex, NULL);
+ if (ret != 0) {
+ av_log(h, AV_LOG_ERROR, "pthread_mutex_init failed : %s\n", av_err2str(ret));
+ goto mutex_fail;
+ }
+
+ ret = pthread_cond_init(&c->cond_wakeup_main, NULL);
+ if (ret != 0) {
+ av_log(h, AV_LOG_ERROR, "pthread_cond_init failed : %s\n", av_err2str(ret));
+ goto cond_wakeup_main_fail;
+ }
+
+ ret = pthread_cond_init(&c->cond_wakeup_background, NULL);
+ if (ret != 0) {
+ av_log(h, AV_LOG_ERROR, "pthread_cond_init failed : %s\n", av_err2str(ret));
+ goto cond_wakeup_background_fail;
+ }
+
+ ret = pthread_create(&c->async_buffer_thread, NULL, async_buffer_task, h);
+ if (ret) {
+ av_log(h, AV_LOG_ERROR, "pthread_create failed : %s\n", av_err2str(ret));
+ goto thread_fail;
+ }
+
+ return 0;
+
+thread_fail:
+ pthread_cond_destroy(&c->cond_wakeup_background);
+cond_wakeup_background_fail:
+ pthread_cond_destroy(&c->cond_wakeup_main);
+cond_wakeup_main_fail:
+ pthread_mutex_destroy(&c->mutex);
+mutex_fail:
+ ffurl_close(c->inner);
+url_fail:
+ ring_destroy(&c->ring);
+fifo_fail:
+ return ret;
+}
+
+static int async_close(URLContext *h)
+{
+ Context *c = h->priv_data;
+ int ret;
+
+ pthread_mutex_lock(&c->mutex);
+ c->abort_request = 1;
+ pthread_cond_signal(&c->cond_wakeup_background);
+ pthread_mutex_unlock(&c->mutex);
+
+ ret = pthread_join(c->async_buffer_thread, NULL);
+ if (ret != 0)
+ av_log(h, AV_LOG_ERROR, "pthread_join(): %s\n", av_err2str(ret));
+
+ pthread_cond_destroy(&c->cond_wakeup_background);
+ pthread_cond_destroy(&c->cond_wakeup_main);
+ pthread_mutex_destroy(&c->mutex);
+ ffurl_close(c->inner);
+ ring_destroy(&c->ring);
+
+ return 0;
+}
+
+static int async_read_internal(URLContext *h, void *dest, int size, int read_complete,
+ void (*func)(void*, void*, int))
+{
+ Context *c = h->priv_data;
+ RingBuffer *ring = &c->ring;
+ int to_read = size;
+ int ret = 0;
+
+ pthread_mutex_lock(&c->mutex);
+
+ while (to_read > 0) {
+ int fifo_size, to_copy;
+ if (async_check_interrupt(h)) {
+ ret = AVERROR_EXIT;
+ break;
+ }
+ fifo_size = ring_size(ring);
+ to_copy = FFMIN(to_read, fifo_size);
+ if (to_copy > 0) {
+ ring_generic_read(ring, dest, to_copy, func);
+ if (!func)
+ dest = (uint8_t *)dest + to_copy;
+ c->logical_pos += to_copy;
+ to_read -= to_copy;
+ ret = size - to_read;
+
+ if (to_read <= 0 || !read_complete)
+ break;
+ } else if (c->io_eof_reached) {
+ if (ret <= 0) {
+ if (c->io_error)
+ ret = c->io_error;
+ else
+ ret = AVERROR_EOF;
+ }
+ break;
+ }
+ pthread_cond_signal(&c->cond_wakeup_background);
+ pthread_cond_wait(&c->cond_wakeup_main, &c->mutex);
+ }
+
+ pthread_cond_signal(&c->cond_wakeup_background);
+ pthread_mutex_unlock(&c->mutex);
+
+ return ret;
+}
+
+static int async_read(URLContext *h, unsigned char *buf, int size)
+{
+ return async_read_internal(h, buf, size, 0, NULL);
+}
+
+static void fifo_do_not_copy_func(void* dest, void* src, int size) {
+ // do not copy
+}
+
+static int64_t async_seek(URLContext *h, int64_t pos, int whence)
+{
+ Context *c = h->priv_data;
+ RingBuffer *ring = &c->ring;
+ int64_t ret;
+ int64_t new_logical_pos;
+ int fifo_size;
+ int fifo_size_of_read_back;
+
+ if (whence == AVSEEK_SIZE) {
+ av_log(h, AV_LOG_TRACE, "async_seek: AVSEEK_SIZE: %"PRId64"\n", (int64_t)c->logical_size);
+ return c->logical_size;
+ } else if (whence == SEEK_CUR) {
+ av_log(h, AV_LOG_TRACE, "async_seek: %"PRId64"\n", pos);
+ new_logical_pos = pos + c->logical_pos;
+ } else if (whence == SEEK_SET){
+ av_log(h, AV_LOG_TRACE, "async_seek: %"PRId64"\n", pos);
+ new_logical_pos = pos;
+ } else {
+ return AVERROR(EINVAL);
+ }
+ if (new_logical_pos < 0)
+ return AVERROR(EINVAL);
+
+ fifo_size = ring_size(ring);
+ fifo_size_of_read_back = ring_size_of_read_back(ring);
+ if (new_logical_pos == c->logical_pos) {
+ /* current position */
+ return c->logical_pos;
+ } else if ((new_logical_pos >= (c->logical_pos - fifo_size_of_read_back)) &&
+ (new_logical_pos < (c->logical_pos + fifo_size + SHORT_SEEK_THRESHOLD))) {
+ int pos_delta = (int)(new_logical_pos - c->logical_pos);
+ /* fast seek */
+ av_log(h, AV_LOG_TRACE, "async_seek: fask_seek %"PRId64" from %d dist:%d/%d\n",
+ new_logical_pos, (int)c->logical_pos,
+ (int)(new_logical_pos - c->logical_pos), fifo_size);
+
+ if (pos_delta > 0) {
+ // fast seek forwards
+ async_read_internal(h, NULL, pos_delta, 1, fifo_do_not_copy_func);
+ } else {
+ // fast seek backwards
+ ring_drain(ring, pos_delta);
+ c->logical_pos = new_logical_pos;
+ }
+
+ return c->logical_pos;
+ } else if (c->logical_size <= 0) {
+ /* can not seek */
+ return AVERROR(EINVAL);
+ } else if (new_logical_pos > c->logical_size) {
+ /* beyond end */
+ return AVERROR(EINVAL);
+ }
+
+ pthread_mutex_lock(&c->mutex);
+
+ c->seek_request = 1;
+ c->seek_pos = new_logical_pos;
+ c->seek_whence = SEEK_SET;
+ c->seek_completed = 0;
+ c->seek_ret = 0;
+
+ while (1) {
+ if (async_check_interrupt(h)) {
+ ret = AVERROR_EXIT;
+ break;
+ }
+ if (c->seek_completed) {
+ if (c->seek_ret >= 0)
+ c->logical_pos = c->seek_ret;
+ ret = c->seek_ret;
+ break;
+ }
+ pthread_cond_signal(&c->cond_wakeup_background);
+ pthread_cond_wait(&c->cond_wakeup_main, &c->mutex);
+ }
+
+ pthread_mutex_unlock(&c->mutex);
+
+ return ret;
+}
+
+#define OFFSET(x) offsetof(Context, x)
+#define D AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption options[] = {
+ {NULL},
+};
+
+#undef D
+#undef OFFSET
+
+static const AVClass async_context_class = {
+ .class_name = "Async",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const URLProtocol ff_async_protocol = {
+ .name = "async",
+ .url_open2 = async_open,
+ .url_read = async_read,
+ .url_seek = async_seek,
+ .url_close = async_close,
+ .priv_data_size = sizeof(Context),
+ .priv_data_class = &async_context_class,
+};
+
+#if 0
+
+#define TEST_SEEK_POS (1536)
+#define TEST_STREAM_SIZE (2048)
+
+typedef struct TestContext {
+ AVClass *class;
+ int64_t logical_pos;
+ int64_t logical_size;
+
+ /* options */
+ int opt_read_error;
+} TestContext;
+
+static int async_test_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
+{
+ TestContext *c = h->priv_data;
+ c->logical_pos = 0;
+ c->logical_size = TEST_STREAM_SIZE;
+ return 0;
+}
+
+static int async_test_close(URLContext *h)
+{
+ return 0;
+}
+
+static int async_test_read(URLContext *h, unsigned char *buf, int size)
+{
+ TestContext *c = h->priv_data;
+ int i;
+ int read_len = 0;
+
+ if (c->opt_read_error)
+ return c->opt_read_error;
+
+ if (c->logical_pos >= c->logical_size)
+ return AVERROR_EOF;
+
+ for (i = 0; i < size; ++i) {
+ buf[i] = c->logical_pos & 0xFF;
+
+ c->logical_pos++;
+ read_len++;
+
+ if (c->logical_pos >= c->logical_size)
+ break;
+ }
+
+ return read_len;
+}
+
+static int64_t async_test_seek(URLContext *h, int64_t pos, int whence)
+{
+ TestContext *c = h->priv_data;
+ int64_t new_logical_pos;
+
+ if (whence == AVSEEK_SIZE) {
+ return c->logical_size;
+ } else if (whence == SEEK_CUR) {
+ new_logical_pos = pos + c->logical_pos;
+ } else if (whence == SEEK_SET){
+ new_logical_pos = pos;
+ } else {
+ return AVERROR(EINVAL);
+ }
+ if (new_logical_pos < 0)
+ return AVERROR(EINVAL);
+
+ c->logical_pos = new_logical_pos;
+ return new_logical_pos;
+}
+
+#define OFFSET(x) offsetof(TestContext, x)
+#define D AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption async_test_options[] = {
+ { "async-test-read-error", "cause read fail",
+ OFFSET(opt_read_error), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, .flags = D },
+ {NULL},
+};
+
+#undef D
+#undef OFFSET
+
+static const AVClass async_test_context_class = {
+ .class_name = "Async-Test",
+ .item_name = av_default_item_name,
+ .option = async_test_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const URLProtocol ff_async_test_protocol = {
+ .name = "async-test",
+ .url_open2 = async_test_open,
+ .url_read = async_test_read,
+ .url_seek = async_test_seek,
+ .url_close = async_test_close,
+ .priv_data_size = sizeof(TestContext),
+ .priv_data_class = &async_test_context_class,
+};
+
+int main(void)
+{
+ URLContext *h = NULL;
+ int i;
+ int ret;
+ int64_t size;
+ int64_t pos;
+ int64_t read_len;
+ unsigned char buf[4096];
+ AVDictionary *opts = NULL;
+
+ ffurl_register_protocol(&ff_async_protocol);
+ ffurl_register_protocol(&ff_async_test_protocol);
+
+ /*
+ * test normal read
+ */
+ ret = ffurl_open(&h, "async:async-test:", AVIO_FLAG_READ, NULL, NULL);
+ printf("open: %d\n", ret);
+
+ size = ffurl_size(h);
+ printf("size: %"PRId64"\n", size);
+
+ pos = ffurl_seek(h, 0, SEEK_CUR);
+ read_len = 0;
+ while (1) {
+ ret = ffurl_read(h, buf, sizeof(buf));
+ if (ret == AVERROR_EOF) {
+ printf("read-error: AVERROR_EOF at %"PRId64"\n", ffurl_seek(h, 0, SEEK_CUR));
+ break;
+ }
+ else if (ret == 0)
+ break;
+ else if (ret < 0) {
+ printf("read-error: %d at %"PRId64"\n", ret, ffurl_seek(h, 0, SEEK_CUR));
+ goto fail;
+ } else {
+ for (i = 0; i < ret; ++i) {
+ if (buf[i] != (pos & 0xFF)) {
+ printf("read-mismatch: actual %d, expecting %d, at %"PRId64"\n",
+ (int)buf[i], (int)(pos & 0xFF), pos);
+ break;
+ }
+ pos++;
+ }
+ }
+
+ read_len += ret;
+ }
+ printf("read: %"PRId64"\n", read_len);
+
+ /*
+ * test normal seek
+ */
+ ret = ffurl_read(h, buf, 1);
+ printf("read: %d\n", ret);
+
+ pos = ffurl_seek(h, TEST_SEEK_POS, SEEK_SET);
+ printf("seek: %"PRId64"\n", pos);
+
+ read_len = 0;
+ while (1) {
+ ret = ffurl_read(h, buf, sizeof(buf));
+ if (ret == AVERROR_EOF)
+ break;
+ else if (ret == 0)
+ break;
+ else if (ret < 0) {
+ printf("read-error: %d at %"PRId64"\n", ret, ffurl_seek(h, 0, SEEK_CUR));
+ goto fail;
+ } else {
+ for (i = 0; i < ret; ++i) {
+ if (buf[i] != (pos & 0xFF)) {
+ printf("read-mismatch: actual %d, expecting %d, at %"PRId64"\n",
+ (int)buf[i], (int)(pos & 0xFF), pos);
+ break;
+ }
+ pos++;
+ }
+ }
+
+ read_len += ret;
+ }
+ printf("read: %"PRId64"\n", read_len);
+
+ ret = ffurl_read(h, buf, 1);
+ printf("read: %d\n", ret);
+
+ /*
+ * test read error
+ */
+ ffurl_close(h);
+ av_dict_set_int(&opts, "async-test-read-error", -10000, 0);
+ ret = ffurl_open(&h, "async:async-test:", AVIO_FLAG_READ, NULL, &opts);
+ printf("open: %d\n", ret);
+
+ ret = ffurl_read(h, buf, 1);
+ printf("read: %d\n", ret);
+
+fail:
+ av_dict_free(&opts);
+ ffurl_close(h);
+ return 0;
+}
+
+#endif
diff --git a/libavformat/au.c b/libavformat/au.c
index 20c9d41..520824f 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_DEFAULT_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 },
};
@@ -55,11 +66,77 @@ static int au_probe(AVProbeData *p)
return 0;
}
+static int au_read_annotation(AVFormatContext *s, int size)
+{
+ static const char * keys[] = {
+ "title",
+ "artist",
+ "album",
+ "track",
+ "genre",
+ NULL };
+ AVIOContext *pb = s->pb;
+ enum { PARSE_KEY, PARSE_VALUE, PARSE_FINISHED } state = PARSE_KEY;
+ char c;
+ AVBPrint bprint;
+ char * key = NULL;
+ char * value = NULL;
+ int i;
+
+ av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED);
+
+ while (size-- > 0) {
+ c = avio_r8(pb);
+ switch(state) {
+ case PARSE_KEY:
+ if (c == '\0') {
+ state = PARSE_FINISHED;
+ } else if (c == '=') {
+ av_bprint_finalize(&bprint, &key);
+ av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED);
+ state = PARSE_VALUE;
+ } else {
+ av_bprint_chars(&bprint, c, 1);
+ }
+ break;
+ case PARSE_VALUE:
+ if (c == '\0' || c == '\n') {
+ if (av_bprint_finalize(&bprint, &value) != 0) {
+ av_log(s, AV_LOG_ERROR, "Memory error while parsing AU metadata.\n");
+ } else {
+ av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED);
+ for (i = 0; keys[i] != NULL && key != NULL; i++) {
+ if (av_strcasecmp(keys[i], key) == 0) {
+ av_dict_set(&(s->metadata), keys[i], value, AV_DICT_DONT_STRDUP_VAL);
+ av_freep(&key);
+ value = NULL;
+ }
+ }
+ }
+ av_freep(&key);
+ av_freep(&value);
+ state = (c == '\0') ? PARSE_FINISHED : PARSE_KEY;
+ } else {
+ av_bprint_chars(&bprint, c, 1);
+ }
+ break;
+ case PARSE_FINISHED:
+ break;
+ default:
+ /* should never happen */
+ av_assert0(0);
+ }
+ }
+ av_bprint_finalize(&bprint, NULL);
+ av_freep(&key);
+ return 0;
+}
+
#define BLOCK_SIZE 1024
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,15 +148,20 @@ 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);
channels = avio_rb32(pb);
if (size > 24) {
- /* skip unused data */
- avio_skip(pb, size - 24);
+ /* parse annotation field to get metadata */
+ au_read_annotation(s, size - 24);
}
codec = ff_codec_get_id(codec_au_tags, id);
@@ -90,7 +172,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 +203,11 @@ static int au_read_header(AVFormatContext *s)
st->codecpar->codec_id = codec;
st->codecpar->channels = channels;
st->codecpar->sample_rate = rate;
+ st->codecpar->bits_per_coded_sample = bps;
st->codecpar->bit_rate = channels * rate * bps;
- st->codecpar->block_align = channels * bps >> 3;
+ st->codecpar->block_align = FFMAX(bps * st->codecpar->channels / 8, 1);
+ if (data_size != AU_UNKNOWN_SIZE)
+ st->duration = (((int64_t)data_size)<<3) / (st->codecpar->channels * (int64_t)bps);
st->start_time = 0;
avpriv_set_pts_info(st, 64, 1, rate);
@@ -122,27 +215,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]->codecpar->block_align);
- if (ret < 0)
- return ret;
-
- pkt->stream_index = 0;
- pkt->duration = ret / s->streams[0]->codecpar->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 },
};
@@ -151,37 +229,87 @@ AVInputFormat ff_au_demuxer = {
#if CONFIG_AU_MUXER
-#include "rawenc.h"
+typedef struct AUContext {
+ uint32_t header_size;
+} AUContext;
-/* if we don't know the size in advance */
-#define AU_UNKNOWN_SIZE ((uint32_t)(~0))
+#include "rawenc.h"
-/* AUDIO_FILE header */
-static int put_au_header(AVIOContext *pb, AVCodecParameters *par)
+static int au_get_annotations(AVFormatContext *s, char **buffer)
{
- if (!par->codec_tag)
- return AVERROR(EINVAL);
-
- ffio_wfourcc(pb, ".snd"); /* magic number */
- avio_wb32(pb, 24); /* header size */
- avio_wb32(pb, AU_UNKNOWN_SIZE); /* data size */
- avio_wb32(pb, par->codec_tag); /* codec ID */
- avio_wb32(pb, par->sample_rate);
- avio_wb32(pb, par->channels);
-
- return 0;
+ static const char * keys[] = {
+ "Title",
+ "Artist",
+ "Album",
+ "Track",
+ "Genre",
+ NULL };
+ int i;
+ int cnt = 0;
+ AVDictionary *m = s->metadata;
+ AVDictionaryEntry *t = NULL;
+ AVBPrint bprint;
+
+ av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED);
+
+ for (i = 0; keys[i] != NULL; i++) {
+ t = av_dict_get(m, keys[i], NULL, 0);
+ if (t != NULL) {
+ if (cnt++)
+ av_bprint_chars(&bprint, '\n', 1);
+ av_bprint_append_data(&bprint, keys[i], strlen(keys[i]));
+ av_bprint_chars(&bprint, '=', 1);
+ av_bprint_append_data(&bprint, t->value, strlen(t->value));
+ }
+ }
+ /* pad with 0's */
+ av_bprint_append_data(&bprint, "\0\0\0\0\0\0\0\0", 8);
+ return av_bprint_finalize(&bprint, buffer);
}
static int au_write_header(AVFormatContext *s)
{
- AVIOContext *pb = s->pb;
int ret;
+ AUContext *au = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ char *annotations = NULL;
- s->priv_data = NULL;
+ au->header_size = AU_DEFAULT_HEADER_SIZE;
- if ((ret = put_au_header(pb, s->streams[0]->codecpar)) < 0)
- return ret;
+ if (s->nb_streams != 1) {
+ av_log(s, AV_LOG_ERROR, "only one stream is supported\n");
+ return AVERROR(EINVAL);
+ }
+
+ par->codec_tag = ff_codec_get_tag(codec_au_tags, par->codec_id);
+ if (!par->codec_tag) {
+ av_log(s, AV_LOG_ERROR, "unsupported codec\n");
+ return AVERROR(EINVAL);
+ }
+ if (av_dict_count(s->metadata) > 0) {
+ ret = au_get_annotations(s, &annotations);
+ if (ret < 0)
+ return ret;
+ if (annotations != NULL) {
+ au->header_size = (24 + strlen(annotations) + 8) & ~7;
+ if (au->header_size < AU_DEFAULT_HEADER_SIZE)
+ au->header_size = AU_DEFAULT_HEADER_SIZE;
+ }
+ }
+ ffio_wfourcc(pb, ".snd"); /* magic number */
+ avio_wb32(pb, au->header_size); /* header size */
+ avio_wb32(pb, AU_UNKNOWN_SIZE); /* data size */
+ avio_wb32(pb, par->codec_tag); /* codec ID */
+ avio_wb32(pb, par->sample_rate);
+ avio_wb32(pb, par->channels);
+ if (annotations != NULL) {
+ avio_write(pb, annotations, au->header_size - 24);
+ av_freep(&annotations);
+ } else {
+ avio_wb64(pb, 0); /* annotation field */
+ }
avio_flush(pb);
return 0;
@@ -190,13 +318,13 @@ static int au_write_header(AVFormatContext *s)
static int au_write_trailer(AVFormatContext *s)
{
AVIOContext *pb = s->pb;
- int64_t file_size;
+ AUContext *au = s->priv_data;
+ int64_t file_size = avio_tell(pb);
- if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && 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);
}
@@ -209,6 +337,7 @@ AVOutputFormat ff_au_muxer = {
.long_name = NULL_IF_CONFIG_SMALL("Sun AU"),
.mime_type = "audio/basic",
.extensions = "au",
+ .priv_data_size = sizeof(AUContext),
.audio_codec = AV_CODEC_ID_PCM_S16BE,
.video_codec = AV_CODEC_ID_NONE,
.write_header = au_write_header,
diff --git a/libavformat/audiointerleave.c b/libavformat/audiointerleave.c
index aa379f6..6d4954b 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->codecpar->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->codecpar->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->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
AVPacket new_pkt = { 0 };
- while (interleave_new_audio_packet(s, &new_pkt, i, flush))
+ while ((ret = interleave_new_audio_packet(s, &new_pkt, i, flush)) > 0) {
if ((ret = ff_interleave_add_packet(s, &new_pkt, compare_ts)) < 0)
return ret;
+ }
+ if (ret < 0)
+ return ret;
}
}
diff --git a/libavformat/audiointerleave.h b/libavformat/audiointerleave.h
index 9c7b548..4d77832 100644
--- a/libavformat/audiointerleave.h
+++ b/libavformat/audiointerleave.h
@@ -3,20 +3,20 @@
*
* Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/avc.c b/libavformat/avc.c
index 5bfbad8..094a958 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 + AV_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 2442e5f..c5e80ff 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 547b88b..b0de66a 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
*/
@@ -28,8 +28,8 @@
*/
/**
- * @defgroup libavf I/O and Muxing/Demuxing Library
- * @{
+ * @defgroup libavf libavformat
+ * I/O and Muxing/Demuxing Library
*
* Libavformat (lavf) is a library for dealing with various media container
* formats. Its main two purposes are demuxing - i.e. splitting a media file
@@ -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).
@@ -67,7 +67,7 @@
* with an AVFMT_NOFILE format).
*
* @section lavf_options Passing options to (de)muxers
- * Lavf allows to configure muxers and demuxers using the @ref avoptions
+ * It is possible to configure lavf muxers and demuxers using the @ref avoptions
* mechanism. Generic (format-independent) libavformat options are provided by
* AVFormatContext, they can be examined from a user program by calling
* av_opt_next() / av_opt_find() on an allocated AVFormatContext (or its AVClass
@@ -78,6 +78,20 @@
* if its AVClass is non-NULL, and the protocols layer. See the discussion on
* nesting in @ref avoptions documentation to learn how to access those.
*
+ * @section urls
+ * URL strings in libavformat are made of a scheme/protocol, a ':', and a
+ * scheme specific string. URLs without a scheme and ':' used for local files
+ * are supported but deprecated. "file:" should be used for local files.
+ *
+ * It is important that the scheme string is not taken from untrusted
+ * sources without checks.
+ *
+ * Note that some schemes/protocols are quite powerful, allowing access to
+ * both local and remote files, parts of them, concatenations of them, local
+ * audio and video devices and so on.
+ *
+ * @{
+ *
* @defgroup lavf_decoding Demuxing
* @{
* Demuxers read a media file and split it into chunks of data (@em packets). A
@@ -88,10 +102,10 @@
* cleanup.
*
* @section lavf_decoding_open Opening a media file
- * The minimum information required to open a file is its URL or filename, which
+ * The minimum information required to open a file is its URL, which
* is passed to avformat_open_input(), as in the following code:
* @code
- * const char *url = "in.mp3";
+ * const char *url = "file:in.mp3";
* AVFormatContext *s = NULL;
* int ret = avformat_open_input(&s, url, NULL, NULL);
* if (ret < 0)
@@ -233,6 +247,53 @@
*
* @defgroup lavf_io I/O Read/Write
* @{
+ * @section lavf_io_dirlist Directory listing
+ * The directory listing API makes it possible to list files on remote servers.
+ *
+ * Some of possible use cases:
+ * - an "open file" dialog to choose files from a remote location,
+ * - a recursive media finder providing a player with an ability to play all
+ * files from a given directory.
+ *
+ * @subsection lavf_io_dirlist_open Opening a directory
+ * At first, a directory needs to be opened by calling avio_open_dir()
+ * supplied with a URL and, optionally, ::AVDictionary containing
+ * protocol-specific parameters. The function returns zero or positive
+ * integer and allocates AVIODirContext on success.
+ *
+ * @code
+ * AVIODirContext *ctx = NULL;
+ * if (avio_open_dir(&ctx, "smb://example.com/some_dir", NULL) < 0) {
+ * fprintf(stderr, "Cannot open directory.\n");
+ * abort();
+ * }
+ * @endcode
+ *
+ * This code tries to open a sample directory using smb protocol without
+ * any additional parameters.
+ *
+ * @subsection lavf_io_dirlist_read Reading entries
+ * Each directory's entry (i.e. file, another directory, anything else
+ * within ::AVIODirEntryType) is represented by AVIODirEntry.
+ * Reading consecutive entries from an opened AVIODirContext is done by
+ * repeatedly calling avio_read_dir() on it. Each call returns zero or
+ * positive integer if successful. Reading can be stopped right after the
+ * NULL entry has been read -- it means there are no entries left to be
+ * read. The following code reads all entries from a directory associated
+ * with ctx and prints their names to standard output.
+ * @code
+ * AVIODirEntry *entry = NULL;
+ * for (;;) {
+ * if (avio_read_dir(ctx, &entry) < 0) {
+ * fprintf(stderr, "Cannot list directory.\n");
+ * abort();
+ * }
+ * if (!entry)
+ * break;
+ * printf("%s\n", entry->name);
+ * avio_free_directory_entry(&entry);
+ * }
+ * @endcode
* @}
*
* @defgroup lavf_codec Demuxers
@@ -264,6 +325,8 @@
struct AVFormatContext;
+struct AVDeviceInfoList;
+struct AVDeviceCapabilitiesQuery;
/**
* @defgroup metadata_api Public Metadata API
@@ -275,7 +338,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.
*
@@ -402,6 +465,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
@@ -434,7 +500,12 @@ typedef struct AVProbeData {
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
@@ -504,8 +575,69 @@ 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 */
+ /**
+ * Initialize format. May allocate data here, and set any AVFormatContext or
+ * AVStream parameters that need to be set before packets are sent.
+ * This method must not write output.
+ *
+ * Return 0 if streams were fully configured, 1 if not, negative AVERROR on failure
+ *
+ * Any allocations made here must be freed in deinit().
+ */
+ int (*init)(struct AVFormatContext *);
+ /**
+ * Deinitialize format. If present, this is called whenever the muxer is being
+ * destroyed, regardless of whether or not the header has been written.
+ *
+ * If a trailer is being written, this is called after write_trailer().
+ *
+ * This is called if init() fails as well.
+ */
+ void (*deinit)(struct AVFormatContext *);
+ /**
+ * Set up any necessary bitstream filtering and extract any extra data needed
+ * for the global header.
+ * Return 0 if more packets from this stream must be checked; 1 if not.
+ */
+ int (*check_bitstream)(struct AVFormatContext *, const AVPacket *pkt);
} AVOutputFormat;
/**
* @}
@@ -532,7 +664,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;
@@ -641,6 +773,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;
/**
* @}
@@ -652,12 +802,23 @@ 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
+#define AVINDEX_DISCARD_FRAME 0x0002 /**
+ * Flag is used to indicate which frame should be discarded after decoding.
+ */
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).
int min_distance; /**< Minimum distance between this and the previous keyframe, used to avoid unneeded searching. */
@@ -681,15 +842,35 @@ typedef struct AVIndexEntry {
#define AV_DISPOSITION_CLEAN_EFFECTS 0x0200 /**< stream without voice */
/**
* The stream is stored in the file as an attached picture/"cover art" (e.g.
- * APIC frame in ID3v2). The single packet associated with it will be returned
- * among the first few packets read from the file unless seeking takes place.
- * It can also be accessed at any time in AVStream.attached_pic.
+ * APIC frame in ID3v2). The first (usually only) packet associated with it
+ * will be returned among the first few packets read from the file unless
+ * seeking takes place. It can also be accessed at any time in
+ * AVStream.attached_pic.
*/
#define AV_DISPOSITION_ATTACHED_PIC 0x0400
+/**
+ * The stream is sparse, and contains thumbnail images, often corresponding
+ * to chapter markers. Only ever used with AV_DISPOSITION_ATTACHED_PIC.
+ */
+#define AV_DISPOSITION_TIMED_THUMBNAILS 0x0800
typedef struct AVStreamInternal AVStreamInternal;
/**
+ * 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
@@ -736,10 +917,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;
@@ -747,6 +930,9 @@ typedef struct AVStream {
* Decoding: duration of the stream, in stream time base.
* If a source file does not specify a duration, but does specify
* a bitrate, this value will be estimated from bitrate and file size.
+ *
+ * Encoding: May be set by the caller before avformat_write_header() to
+ * provide a hint to the muxer about the estimated duration.
*/
int64_t duration;
@@ -798,6 +984,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;
/**
@@ -813,33 +1001,38 @@ typedef struct AVStream {
int event_flags;
#define AVSTREAM_EVENT_FLAG_METADATA_UPDATED 0x0001 ///< The call resulted in updated metadata.
- /*
- * Codec parameters associated with this stream. Allocated and freed by
- * libavformat in avformat_new_stream() and avformat_free_context()
- * respectively.
- *
- * - demuxing: filled by libavformat on stream creation or in
- * avformat_find_stream_info()
- * - muxing: filled by the caller before avformat_write_header()
- */
- AVCodecParameters *codecpar;
-
/*****************************************************************
* 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.
+ * Internal note: be aware that physically removing these fields
+ * will break ABI. Replace removed fields with dummy fields, and
+ * add new fields to AVStreamInternal.
*****************************************************************
*/
/**
- * Stream information used internally by av_find_stream_info()
+ * Stream information used internally by avformat_find_stream_info()
*/
-#define MAX_STD_TIMEBASES (60*12+5)
+#define MAX_STD_TIMEBASES (30*12+30+3+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.
*/
@@ -853,6 +1046,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;
@@ -861,11 +1061,10 @@ typedef struct AVStream {
/**
* Number of packets to buffer for codec probing
*/
-#define MAX_PROBE_PACKETS 2500
int probe_packets;
/**
- * Number of frames that have been demuxed during av_find_stream_info()
+ * Number of frames that have been demuxed during avformat_find_stream_info()
*/
int codec_info_nb_frames;
@@ -887,12 +1086,176 @@ typedef struct AVStream {
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 number of samples that should be skipped from the start of
+ * the stream (the samples are removed from packets with pts==0, which also
+ * assumes negative timestamps do not happen).
+ * Intended for use with formats such as mp3 with ad-hoc gapless audio
+ * support.
+ */
+ int64_t start_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;
+
+ /*****************************************************************
+ * All fields above this line are not part of the public API.
+ * Fields below are part of the public API and ABI again.
+ *****************************************************************
+ */
+
+ /**
+ * 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;
+
+ struct FFFrac *priv_pts;
+
+ /**
* An opaque field for libavformat internal usage.
* Must not be accessed in any way by callers.
*/
AVStreamInternal *internal;
+
+ /*
+ * Codec parameters associated with this stream. Allocated and freed by
+ * libavformat in avformat_new_stream() and avformat_free_context()
+ * respectively.
+ *
+ * - demuxing: filled by libavformat on stream creation or in
+ * avformat_find_stream_info()
+ * - muxing: filled by the caller before avformat_write_header()
+ */
+ AVCodecParameters *codecpar;
} 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
/**
@@ -908,6 +1271,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
@@ -920,6 +1300,26 @@ 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);
+
+typedef int (*AVOpenCallback)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags,
+ const AVIOInterruptCB *int_cb, AVDictionary **options);
+
+/**
+ * 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;
/**
@@ -929,6 +1329,12 @@ typedef struct AVFormatInternal AVFormatInternal;
* version bump.
* sizeof(AVFormatContext) must not be used outside libav*, use
* avformat_alloc_context() to create an AVFormatContext.
+ *
+ * Fields can be accessed through AVOptions (av_opt*),
+ * the name string used matches the associated command line parameter name and
+ * can be found in libavformat/options_table.h.
+ * The AVOption/command line parameter names differ in some cases from the C
+ * structure field names for historic reasons or brevity.
*/
typedef struct AVFormatContext {
/**
@@ -1030,9 +1436,9 @@ 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;
+ int64_t bit_rate;
unsigned int packet_size;
int max_delay;
@@ -1059,20 +1465,30 @@ 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)
+#if FF_API_LAVF_KEEPSIDE_FLAG
+#define AVFMT_FLAG_KEEP_SIDE_DATA 0x40000 ///< Don't merge side data but keep it separate. Deprecated, will be the default.
+#endif
+#define AVFMT_FLAG_FAST_SEEK 0x80000 ///< Enable fast, but inaccurate seeks for some formats
+#define AVFMT_FLAG_SHORTEST 0x100000 ///< Stop muxing when the shortest stream stops.
+#define AVFMT_FLAG_AUTO_BSF 0x200000 ///< Wait for packet data before writing a header, and add bitstream filters as requested by the muxer
/**
* Maximum size of the data read from input for determining
* the input container format.
* Demuxing only, set by the caller before avformat_open_input().
*/
- unsigned int probesize;
+ int64_t 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().
+ * Can be set to 0 to let avformat choose using a heuristic.
*/
- int max_analyze_duration;
+ int64_t max_analyze_duration;
const uint8_t *key;
int keylen;
@@ -1144,7 +1560,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;
@@ -1220,7 +1641,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
*/
@@ -1230,17 +1651,232 @@ 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
+ * - 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
+ * - 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
+ * - 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
+ */
+ int use_wallclock_as_timestamps;
+
+ /**
+ * avio flags, used to force AVIO_FLAG_DIRECT.
+ * - encoding: unused
+ * - decoding: Set by user
+ */
+ 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
+ */
+ enum AVDurationEstimationMethod duration_estimation_method;
+
+ /**
+ * Skip initial bytes when opening stream
+ * - encoding: unused
+ * - decoding: Set by user
+ */
+ int64_t skip_initial_bytes;
+
+ /**
+ * Correct single timestamp overflows
+ * - encoding: unused
+ * - decoding: Set by user
+ */
+ unsigned int correct_ts_overflow;
+
+ /**
+ * Force seeking to any (also non key) frames.
+ * - encoding: unused
+ * - decoding: Set by user
+ */
+ int seek2any;
+
+ /**
+ * Flush the I/O context after each packet.
+ * - encoding: Set by user
+ * - 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
+ */
+ int probe_score;
+
+ /**
+ * number of bytes to read maximally to identify format.
+ * - encoding: unused
+ * - decoding: set by user
+ */
+ int format_probesize;
+
+ /**
+ * ',' separated list of allowed decoders.
+ * If NULL then all are allowed
+ * - encoding: unused
+ * - decoding: set by user
+ */
+ char *codec_whitelist;
+
+ /**
+ * ',' separated list of allowed demuxers.
+ * If NULL then all are allowed
+ * - encoding: unused
+ * - decoding: set by user
+ */
+ char *format_whitelist;
+
+ /**
* An opaque field for libavformat internal usage.
* Must not be accessed in any way by callers.
*/
AVFormatInternal *internal;
/**
- * Arbitrary user data set by the caller.
+ * 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
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ 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.
*/
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
+ */
+ int64_t output_ts_offset;
+
+ /**
+ * dump format separator.
+ * can be ", " or "\n " or anything else
+ * - 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;
+
+#if FF_API_OLD_OPEN_CALLBACKS
+ /**
+ * Called to open further IO contexts when needed for demuxing.
+ *
+ * This can be set by the user application to perform security checks on
+ * the URLs before opening them.
+ * The function should behave like avio_open2(), AVFormatContext is provided
+ * as contextual information and to reach AVFormatContext.opaque.
+ *
+ * If NULL then some simple checks are used together with avio_open2().
+ *
+ * Must not be accessed directly from outside avformat.
+ * @See av_format_set_open_cb()
+ *
+ * Demuxing: Set by user.
+ *
+ * @deprecated Use io_open and io_close.
+ */
+ attribute_deprecated
+ int (*open_cb)(struct AVFormatContext *s, AVIOContext **p, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options);
+#endif
+
+ /**
+ * ',' separated list of allowed protocols.
+ * - encoding: unused
+ * - decoding: set by user
+ */
+ char *protocol_whitelist;
+
+ /*
* A callback for opening new IO streams.
*
* Whenever a muxer or a demuxer needs to open an IO stream (typically from
@@ -1269,24 +1905,57 @@ typedef struct AVFormatContext {
void (*io_close)(struct AVFormatContext *s, AVIOContext *pb);
/**
- * A comma-separated list of protocol names that will not be used internally
- * by libavformat. If this field is a non-empty string, then protocols
- * listed here will be forbidden.
- *
- * This field should be set using AVOptions.
+ * ',' separated list of disallowed protocols.
+ * - encoding: unused
+ * - decoding: set by user
*/
char *protocol_blacklist;
/**
- * A comma-separated list of protocol names that can be used internally by
- * libavformat. If this field is a non-empty string, all protocols not
- * listed here will be forbidden.
- *
- * This field should be set using AVOptions.
+ * The maximum number of streams.
+ * - encoding: unused
+ * - decoding: set by user
*/
- char *protocol_whitelist;
+ int max_streams;
} AVFormatContext;
+/**
+ * Accessors for some AVFormatContext fields. These used to be provided for ABI
+ * compatibility, and do not need to be used anymore.
+ */
+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);
+#if FF_API_OLD_OPEN_CALLBACKS
+attribute_deprecated AVOpenCallback av_format_get_open_cb(const AVFormatContext *s);
+attribute_deprecated void av_format_set_open_cb(AVFormatContext *s, AVOpenCallback callback);
+#endif
+
+/**
+ * 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;
@@ -1324,7 +1993,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);
@@ -1390,6 +2058,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
@@ -1432,8 +2103,13 @@ uint8_t *av_stream_new_side_data(AVStream *stream,
* @param size pointer for side information size to store (optional)
* @return pointer to data if present or NULL otherwise
*/
+#if FF_API_NOCONST_GET_SIDE_DATA
uint8_t *av_stream_get_side_data(AVStream *stream,
enum AVPacketSideDataType type, int *size);
+#else
+uint8_t *av_stream_get_side_data(const AVStream *stream,
+ enum AVPacketSideDataType type, int *size);
+#endif
AVProgram *av_new_program(AVFormatContext *s, int id);
@@ -1443,6 +2119,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
* @{
*/
@@ -1476,6 +2171,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
@@ -1483,15 +2187,23 @@ AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score
*
* @param pb the bytestream to probe
* @param fmt the input format is put here
- * @param filename the filename of the stream
+ * @param url the url of the stream
* @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 *url, 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,
+ const char *url, void *logctx,
unsigned int offset, unsigned int max_probe_size);
/**
@@ -1502,7 +2214,7 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt,
* May be a pointer to NULL, in which case an AVFormatContext is allocated by this
* function and written into ps.
* Note that a user-supplied AVFormatContext will be freed on failure.
- * @param filename Name of the stream to open.
+ * @param url URL of the stream to open.
* @param fmt If non-NULL, this parameter forces a specific input format.
* Otherwise the format is autodetected.
* @param options A dictionary filled with AVFormatContext and demuxer-private options.
@@ -1513,7 +2225,10 @@ int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt,
*
* @note If you want to use custom IO, preallocate the format context and set its pb field.
*/
-int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options);
+int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
+
+attribute_deprecated
+int av_demuxer_open(AVFormatContext *ic);
/**
* Read packets of a media file to get stream information. This
@@ -1539,6 +2254,20 @@ 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);
+
+void av_program_add_stream_index(AVFormatContext *ac, int progid, unsigned int idx);
+
+/**
* 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.
@@ -1625,6 +2354,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
@@ -1641,6 +2371,24 @@ int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,
int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags);
/**
+ * Discard all internally buffered data. This can be useful when dealing with
+ * discontinuities in the byte stream. Generally works only with formats that
+ * can resync. This includes headerless formats like MPEG-TS/TS but should also
+ * work with NUT, Ogg and in a limited way AVI for example.
+ *
+ * The set of streams, the detected duration, stream parameters and codecs do
+ * not change when calling this function. If you want a complete reset, it's
+ * better to open a new AVFormatContext.
+ *
+ * This does not flush the AVIOContext (s->pb). If necessary, call
+ * avio_flush(s->pb) before calling this function.
+ *
+ * @param s media file handle
+ * @return >=0 on success, error code otherwise
+ */
+int avformat_flush(AVFormatContext *s);
+
+/**
* Start playing a network-based stream (e.g. RTSP stream) at the
* current position.
*/
@@ -1671,6 +2419,10 @@ void avformat_close_input(AVFormatContext **s);
* @addtogroup lavf_encoding
* @{
*/
+
+#define AVSTREAM_INIT_IN_WRITE_HEADER 0 ///< stream parameters initialized in avformat_write_header
+#define AVSTREAM_INIT_IN_INIT_OUTPUT 1 ///< stream parameters initialized in avformat_init_output
+
/**
* Allocate the stream private data and write the stream header to
* an output media file.
@@ -1682,13 +2434,38 @@ void avformat_close_input(AVFormatContext **s);
* On return this parameter will be destroyed and replaced with a dict containing
* options that were not found. May be NULL.
*
- * @return 0 on success, negative AVERROR on failure.
+ * @return AVSTREAM_INIT_IN_WRITE_HEADER on success if the codec had not already been fully initialized in avformat_init,
+ * AVSTREAM_INIT_IN_INIT_OUTPUT on success if the codec had already been fully initialized in avformat_init,
+ * negative AVERROR on failure.
*
- * @see av_opt_find, av_dict_set, avio_open, av_oformat_next.
+ * @see av_opt_find, av_dict_set, avio_open, av_oformat_next, avformat_init_output.
*/
+av_warn_unused_result
int avformat_write_header(AVFormatContext *s, AVDictionary **options);
/**
+ * Allocate the stream private data and initialize the codec, but do not write the header.
+ * May optionally be used before avformat_write_header to initialize stream parameters
+ * before actually writing the header.
+ * If using this function, do not pass the same options to avformat_write_header.
+ *
+ * @param s Media file handle, must be allocated with avformat_alloc_context().
+ * Its oformat field must be set to the desired output format;
+ * Its pb field must be set to an already opened AVIOContext.
+ * @param options An AVDictionary filled with AVFormatContext and muxer-private options.
+ * On return this parameter will be destroyed and replaced with a dict containing
+ * options that were not found. May be NULL.
+ *
+ * @return AVSTREAM_INIT_IN_WRITE_HEADER on success if the codec requires avformat_write_header to fully initialize,
+ * AVSTREAM_INIT_IN_INIT_OUTPUT on success if the codec has been fully initialized,
+ * negative AVERROR on failure.
+ *
+ * @see av_opt_find, av_dict_set, avio_open, av_oformat_next, avformat_write_header.
+ */
+av_warn_unused_result
+int avformat_init_output(AVFormatContext *s, AVDictionary **options);
+
+/**
* Write a packet to an output media file.
*
* This function passes the packet directly to the muxer, without any buffering
@@ -1773,6 +2550,44 @@ int av_write_frame(AVFormatContext *s, AVPacket *pkt);
int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);
/**
+ * Write an 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 an uncoded frame to an output media file.
+ *
+ * If the muxer supports it, this function makes it possible 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.
*
@@ -1807,6 +2622,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);
+
+
+/**
* @}
*/
@@ -1853,7 +2687,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);
/**
@@ -1867,8 +2701,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.
@@ -1890,6 +2724,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);
/**
@@ -1948,15 +2794,18 @@ 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 output
+ * @param is_output Select whether the specified context is an input(0) or output(1)
*/
void av_dump_format(AVFormatContext *ic,
int index,
const char *url,
int is_output);
+
+#define AV_FRAME_FILENAME_FLAGS_MULTIPLE 1 ///< Allow multiple %d
+
/**
* Return in 'buf' the path with '%d' replaced by a number.
*
@@ -1967,8 +2816,12 @@ void av_dump_format(AVFormatContext *ic,
* @param buf_size destination buffer size
* @param path numbered sequence string
* @param number frame number
+ * @param flags AV_FRAME_FILENAME_FLAGS_*
* @return 0 if OK, -1 on format error
*/
+int av_get_frame_filename2(char *buf, int buf_size,
+ const char *path, int number, int flags);
+
int av_get_frame_filename(char *buf, int buf_size,
const char *path, int number);
@@ -2042,10 +2895,113 @@ 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);
+
+#if FF_API_OLD_BSF
+/**
+ * Apply a list of bitstream filters to a packet.
+ *
+ * @param codec AVCodecContext, usually from an AVStream
+ * @param pkt the packet to apply filters to. If, on success, the returned
+ * packet has size == 0 and side_data_elems == 0, it indicates that
+ * the packet should be dropped
+ * @param bsfc a NULL-terminated list of filters to apply
+ * @return >=0 on success;
+ * AVERROR code on failure
+ */
+attribute_deprecated
+int av_apply_bitstream_filters(AVCodecContext *codec, AVPacket *pkt,
+ AVBitStreamFilterContext *bsfc);
+#endif
+
+enum AVTimebaseSource {
+ AVFMT_TBCF_AUTO = -1,
+ AVFMT_TBCF_DECODER,
+ AVFMT_TBCF_DEMUXER,
+#if FF_API_R_FRAME_RATE
+ AVFMT_TBCF_R_FRAMERATE,
+#endif
+};
+
+/**
+ * Transfer internal timing information from one stream to another.
+ *
+ * This function is useful when doing stream copy.
+ *
+ * @param ofmt target output format for ost
+ * @param ost output stream which needs timings copy and adjustments
+ * @param ist reference input stream to copy timings from
+ * @param copy_tb define from where the stream codec timebase needs to be imported
+ */
+int avformat_transfer_internal_stream_timing_info(const AVOutputFormat *ofmt,
+ AVStream *ost, const AVStream *ist,
+ enum AVTimebaseSource copy_tb);
+
+/**
+ * Get the internal codec timebase from a stream.
+ *
+ * @param st input stream to extract the timebase from
+ */
+AVRational av_stream_get_codec_timebase(const AVStream *st);
+
+/**
* @}
*/
diff --git a/libavformat/avformatres.rc b/libavformat/avformatres.rc
new file mode 100644
index 0000000..ffe61e0
--- /dev/null
+++ b/libavformat/avformatres.rc
@@ -0,0 +1,55 @@
+/*
+ * Windows resource file for libavformat
+ *
+ * Copyright (C) 2012 James Almer
+ * Copyright (C) 2013 Tiancheng "Timothy" Gu
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <windows.h>
+#include "libavformat/version.h"
+#include "libavutil/ffversion.h"
+#include "config.h"
+
+1 VERSIONINFO
+FILEVERSION LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO, 0
+PRODUCTVERSION LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO, 0
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_DLL
+{
+ BLOCK "StringFileInfo"
+ {
+ BLOCK "040904B0"
+ {
+ VALUE "CompanyName", "FFmpeg Project"
+ VALUE "FileDescription", "FFmpeg container format library"
+ VALUE "FileVersion", AV_STRINGIFY(LIBAVFORMAT_VERSION)
+ VALUE "InternalName", "libavformat"
+ VALUE "LegalCopyright", "Copyright (C) 2000-" AV_STRINGIFY(CONFIG_THIS_YEAR) " FFmpeg Project"
+ VALUE "OriginalFilename", "avformat" BUILDSUF "-" AV_STRINGIFY(LIBAVFORMAT_VERSION_MAJOR) SLIBSUF
+ VALUE "ProductName", "FFmpeg"
+ VALUE "ProductVersion", FFMPEG_VERSION
+ }
+ }
+
+ BLOCK "VarFileInfo"
+ {
+ VALUE "Translation", 0x0409, 0x04B0
+ }
+}
diff --git a/libavformat/avi.h b/libavformat/avi.h
index e05db9c..b1711f0 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
*/
@@ -29,10 +29,13 @@
#define AVIF_COPYRIGHTED 0x00020000
#define AVI_MAX_RIFF_SIZE 0x40000000LL
-#define AVI_MASTER_INDEX_SIZE 256
#define AVI_MAX_STREAM_COUNT 100
+/* stream header flags */
+#define AVISF_VIDEO_PALCHANGES 0x00010000
+
/* index flags */
-#define AVIIF_INDEX 0x10
+#define AVIIF_INDEX 0x00000010
+#define AVIIF_NO_TIME 0x00000100
#endif /* AVFORMAT_AVI_H */
diff --git a/libavformat/avidec.c b/libavformat/avidec.c
index 870066e..b8a31dc 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"
@@ -33,9 +35,9 @@
#include "internal.h"
#include "isom.h"
#include "riff.h"
-
-#undef NDEBUG
-#include <assert.h>
+#include "libavcodec/bytestream.h"
+#include "libavcodec/exif.h"
+#include "libavcodec/internal.h"
typedef struct AVIStream {
int64_t frame_offset; /* current frame (video) or byte (audio) counter
@@ -60,12 +62,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;
@@ -74,9 +80,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_BOOL, {.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,13 +117,9 @@ static const AVMetadataConv avi_metadata_conv[] = {
static int avi_load_index(AVFormatContext *s);
static int guess_ni_flag(AVFormatContext *s);
-#define print_tag(str, tag, size) \
- av_log(NULL, AV_LOG_TRACE, "%s: tag=%c%c%c%c size=0x%x\n", \
- str, tag & 0xff, \
- (tag >> 8) & 0xff, \
- (tag >> 16) & 0xff, \
- (tag >> 24) & 0xff, \
- size)
+#define print_tag(str, tag, size) \
+ av_log(NULL, AV_LOG_TRACE, "pos:%"PRIX64" %s: tag=%s size=0x%x\n", \
+ avio_tell(pb), str, av_fourcc2str(tag), size) \
static inline int get_duration(AVIStream *ast, int len)
{
@@ -115,7 +134,7 @@ static inline int get_duration(AVIStream *ast, int len)
static int get_riff(AVFormatContext *s, AVIOContext *pb)
{
AVIContext *avi = s->priv_data;
- char header[8];
+ char header[8] = {0};
int i;
/* check RIFF header */
@@ -137,11 +156,11 @@ static int get_riff(AVFormatContext *s, AVIOContext *pb)
return 0;
}
-static int read_braindead_odml_indx(AVFormatContext *s, int frame_num)
+static int read_odml_index(AVFormatContext *s, int frame_num)
{
AVIContext *avi = s->priv_data;
AVIOContext *pb = s->pb;
- int longs_pre_entry = avio_rl16(pb);
+ int longs_per_entry = avio_rl16(pb);
int index_sub_type = avio_r8(pb);
int index_type = avio_r8(pb);
int entries_in_use = avio_rl32(pb);
@@ -156,13 +175,14 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num)
int64_t filesize = avi->fsize;
av_log(s, AV_LOG_TRACE,
- "longs_pre_entry:%d index_type:%d entries_in_use:%d "
- "chunk_id:%X base:%16"PRIX64"\n",
- longs_pre_entry,
+ "longs_per_entry:%d index_type:%d entries_in_use:%d "
+ "chunk_id:%X base:%16"PRIX64" frame_num:%d\n",
+ longs_per_entry,
index_type,
entries_in_use,
chunk_id,
- base);
+ base,
+ frame_num);
if (stream_id >= s->nb_streams || stream_id < 0)
return AVERROR_INVALIDDATA;
@@ -174,7 +194,7 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num)
avio_rl32(pb);
- if (index_type && longs_pre_entry != 2)
+ if (index_type && longs_per_entry != 2)
return AVERROR_INVALIDDATA;
if (index_type > 1)
return AVERROR_INVALIDDATA;
@@ -198,12 +218,12 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num)
av_log(s, AV_LOG_TRACE, "pos:%"PRId64", len:%X\n", pos, len);
- if (pb->eof_reached)
+ 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);
@@ -216,7 +236,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);
@@ -226,16 +246,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);
+ read_odml_index(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;
}
@@ -281,7 +306,8 @@ static int avi_read_tag(AVFormatContext *s, AVStream *st, uint32_t tag,
value = av_malloc(size + 1);
if (!value)
return AVERROR(ENOMEM);
- avio_read(pb, value, size);
+ if (avio_read(pb, value, size) != size)
+ return AVERROR_INVALIDDATA;
value[size] = 0;
AV_WL32(key, tag);
@@ -314,18 +340,19 @@ static void avi_metadata_creation_time(AVDictionary **metadata, char *date)
static void avi_read_nikon(AVFormatContext *s, uint64_t end)
{
- while (avio_tell(s->pb) < end) {
+ while (avio_tell(s->pb) < end && !avio_feof(s->pb)) {
uint32_t tag = avio_rl32(s->pb);
uint32_t size = avio_rl32(s->pb);
switch (tag) {
case MKTAG('n', 'c', 't', 'g'): /* Nikon Tags */
{
uint64_t tag_end = avio_tell(s->pb) + size;
- while (avio_tell(s->pb) < tag_end) {
+ while (avio_tell(s->pb) < tag_end && !avio_feof(s->pb)) {
uint16_t tag = avio_rl16(s->pb);
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) {
@@ -354,6 +381,88 @@ static void avi_read_nikon(AVFormatContext *s, uint64_t end)
}
}
+static int avi_extract_stream_metadata(AVFormatContext *s, AVStream *st)
+{
+ GetByteContext gb;
+ uint8_t *data = st->codecpar->extradata;
+ int data_size = st->codecpar->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(s, &gb, 1, 0, &st->metadata);
+ break;
+ case MKTAG('C', 'A', 'S', 'I'):
+ avpriv_request_sample(s, "RIFF stream data tag type CASI (%u)", tag);
+ break;
+ case MKTAG('Z', 'o', 'r', 'a'):
+ avpriv_request_sample(s, "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;
+ int64_t bitrate;
+
+ for (j = 0; j < st->nb_index_entries; j++)
+ len += st->index_entries[j].size;
+
+ if (st->nb_index_entries < 2 || st->codecpar->bit_rate > 0)
+ continue;
+ duration = st->index_entries[j-1].timestamp - st->index_entries[0].timestamp;
+ bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num);
+ if (bitrate <= INT_MAX && bitrate > 0) {
+ st->codecpar->bit_rate = bitrate;
+ }
+ }
+ return 1;
+}
+
static int avi_read_header(AVFormatContext *s)
{
AVIContext *avi = s->priv_data;
@@ -369,6 +478,7 @@ static int avi_read_header(AVFormatContext *s)
uint64_t list_end = 0;
int64_t pos;
int ret;
+ AVDictionaryEntry *dict_entry;
avi->stream_index = -1;
@@ -376,7 +486,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;
@@ -385,7 +497,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);
@@ -433,7 +545,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;
@@ -489,7 +601,17 @@ static int avi_read_header(AVFormatContext *s)
ast = s->streams[0]->priv_data;
av_freep(&s->streams[0]->codecpar->extradata);
av_freep(&s->streams[0]->codecpar);
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ av_freep(&s->streams[0]->codec);
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+ if (s->streams[0]->info)
+ av_freep(&s->streams[0]->info->duration_error);
av_freep(&s->streams[0]->info);
+ if (s->streams[0]->internal)
+ av_freep(&s->streams[0]->internal->avctx);
+ av_freep(&s->streams[0]->internal);
av_freep(&s->streams[0]);
s->nb_streams = 0;
if (CONFIG_DV_DEMUXER) {
@@ -517,7 +639,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 */
@@ -548,7 +670,11 @@ static int avi_read_header(AVFormatContext *s)
st->start_time = 0;
avio_rl32(pb); /* buffer size */
avio_rl32(pb); /* quality */
- ast->sample_size = avio_rl32(pb); /* sample size */
+ 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);
ast->cum_len *= FFMAX(1, ast->sample_size);
av_log(s, AV_LOG_TRACE, "%"PRIu32" %"PRIu32" %d\n",
ast->rate, ast->scale, ast->sample_size);
@@ -558,6 +684,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;
@@ -569,8 +696,7 @@ static int avi_read_header(AVFormatContext *s)
codec_type = AVMEDIA_TYPE_DATA;
break;
default:
- av_log(s, AV_LOG_ERROR, "unknown stream type %X\n", tag1);
- goto fail;
+ av_log(s, AV_LOG_INFO, "unknown stream type %X\n", tag1);
}
if (ast->sample_size < 0) {
@@ -589,20 +715,33 @@ static int avi_read_header(AVFormatContext *s)
ast->sample_size = 0;
}
- 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 && (codec_type == AVMEDIA_TYPE_AUDIO ||
+ codec_type == AVMEDIA_TYPE_VIDEO))
+ 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->codecpar->codec_type != AVMEDIA_TYPE_UNKNOWN) {
+ avio_skip(pb, size);
+ break;
+ }
switch (codec_type) {
case AVMEDIA_TYPE_VIDEO:
if (amv_file_format) {
@@ -613,7 +752,7 @@ static int avi_read_header(AVFormatContext *s)
avio_skip(pb, size);
break;
}
- tag1 = ff_get_bmp_header(pb, st, NULL);
+ tag1 = ff_get_bmp_header(pb, st, &esize);
if (tag1 == MKTAG('D', 'X', 'S', 'B') ||
tag1 == MKTAG('D', 'X', 'S', 'A')) {
@@ -623,17 +762,17 @@ static int avi_read_header(AVFormatContext *s)
break;
}
- if (size > 10 * 4 && size < (1 << 30)) {
- st->codecpar->extradata_size = size - 10 * 4;
- st->codecpar->extradata = av_malloc(st->codecpar->extradata_size +
- AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata) {
- st->codecpar->extradata_size = 0;
- return AVERROR(ENOMEM);
+ if (size > 10 * 4 && size < (1 << 30) && size < avi->fsize) {
+ if (esize == size-1 && (esize&1)) {
+ st->codecpar->extradata_size = esize - 10 * 4;
+ } else
+ st->codecpar->extradata_size = size - 10 * 4;
+ if (st->codecpar->extradata) {
+ av_log(s, AV_LOG_WARNING, "New extradata in strf chunk, freeing previous one.\n");
+ av_freep(&st->codecpar->extradata);
}
- avio_read(pb,
- st->codecpar->extradata,
- st->codecpar->extradata_size);
+ if (ff_get_extradata(s, st->codecpar, pb, st->codecpar->extradata_size) < 0)
+ return AVERROR(ENOMEM);
}
// FIXME: check if the encoder really did this correctly
@@ -643,7 +782,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->codecpar->extradata_size &&
(st->codecpar->bits_per_coded_sample <= 8)) {
int pal_size = (1 << st->codecpar->bits_per_coded_sample) << 2;
@@ -652,8 +791,12 @@ static int avi_read_header(AVFormatContext *s)
pal_size = FFMIN(pal_size, st->codecpar->extradata_size);
pal_src = st->codecpar->extradata +
st->codecpar->extradata_size - pal_size;
+ /* Exclude the "BottomUp" field from the palette */
+ if (pal_src - st->codecpar->extradata >= 9 &&
+ !memcmp(st->codecpar->extradata + st->codecpar->extradata_size - 9, "BottomUp", 9))
+ pal_src -= 9;
for (i = 0; i < pal_size / 4; i++)
- ast->pal[i] = (0xFFu << 24) | AV_RL32(pal_src + 4 * i);
+ ast->pal[i] = 0xFFU<<24 | AV_RL32(pal_src + 4 * i);
ast->has_pal = 1;
}
@@ -665,14 +808,12 @@ static int avi_read_header(AVFormatContext *s)
tag1);
/* If codec is not found yet, try with the mov tags. */
if (!st->codecpar->codec_id) {
- char tag_buf[32];
- av_get_codec_tag_string(tag_buf, sizeof(tag_buf), tag1);
st->codecpar->codec_id =
ff_codec_get_id(ff_codec_movvideo_tags, tag1);
if (st->codecpar->codec_id)
av_log(s, AV_LOG_WARNING,
"mov tag found in avi (fourcc %s)\n",
- tag_buf);
+ av_fourcc2str(tag1));
}
/* This is needed to get the pict type which is necessary
* for generating correct pts. */
@@ -682,11 +823,10 @@ static int avi_read_header(AVFormatContext *s)
ast->handler == MKTAG('X', 'V', 'I', 'D'))
st->codecpar->codec_tag = MKTAG('X', 'V', 'I', 'D');
- // Support "Resolution 1:1" for Avid AVI Codec
- if (tag1 == MKTAG('A', 'V', 'R', 'n') &&
- st->codecpar->extradata_size >= 31 &&
- !memcmp(&st->codecpar->extradata[28], "1:1", 3))
- st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
+ if (st->codecpar->codec_tag == MKTAG('V', 'S', 'S', 'H'))
+ st->need_parsing = AVSTREAM_PARSE_FULL;
+ if (st->codecpar->codec_id == AV_CODEC_ID_RV40)
+ st->need_parsing = AVSTREAM_PARSE_NONE;
if (st->codecpar->codec_tag == 0 && st->codecpar->height > 0 &&
st->codecpar->extradata_size < 1U << 30) {
@@ -705,7 +845,7 @@ static int avi_read_header(AVFormatContext *s)
// avio_skip(pb, size - 5 * 4);
break;
case AVMEDIA_TYPE_AUDIO:
- ret = ff_get_wav_header(s, pb, st->codecpar, size);
+ ret = ff_get_wav_header(s, pb, st->codecpar, size, 0);
if (ret < 0)
return ret;
ast->dshow_block_align = st->codecpar->block_align;
@@ -731,20 +871,37 @@ static int avi_read_header(AVFormatContext *s)
if (st->codecpar->codec_id == AV_CODEC_ID_AAC &&
st->codecpar->extradata_size)
st->need_parsing = AVSTREAM_PARSE_NONE;
+ // The flac parser does not work with AVSTREAM_PARSE_TIMESTAMPS
+ if (st->codecpar->codec_id == AV_CODEC_ID_FLAC)
+ st->need_parsing = AVSTREAM_PARSE_NONE;
/* AVI files with Xan DPCM audio (wrongly) declare PCM
* audio in the header but have Axan as stream_code_tag. */
if (ast->handler == AV_RL32("Axan")) {
st->codecpar->codec_id = AV_CODEC_ID_XAN_DPCM;
st->codecpar->codec_tag = 0;
+ ast->dshow_block_align = 0;
}
if (amv_file_format) {
st->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_AMV;
ast->dshow_block_align = 0;
}
+ if ((st->codecpar->codec_id == AV_CODEC_ID_AAC ||
+ st->codecpar->codec_id == AV_CODEC_ID_FLAC ||
+ st->codecpar->codec_id == AV_CODEC_ID_MP2 ) && 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->codecpar->codec_id == AV_CODEC_ID_AAC && ast->dshow_block_align == 1024 && ast->sample_size == 1024 ||
+ st->codecpar->codec_id == AV_CODEC_ID_AAC && ast->dshow_block_align == 4096 && ast->sample_size == 4096 ||
+ st->codecpar->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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
- st->codecpar->codec_id = AV_CODEC_ID_PROBE;
+ st->request_probe= 1;
+ avio_skip(pb, size);
break;
default:
st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
@@ -755,11 +912,40 @@ 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]->codecpar->extradata_size
+ || s->streams[stream_index]->codecpar->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 (st->codecpar->extradata) {
+ av_log(s, AV_LOG_WARNING, "New extradata in strd chunk, freeing previous one.\n");
+ av_freep(&st->codecpar->extradata);
+ }
+ if (ff_get_extradata(s, st->codecpar, pb, size) < 0)
+ return AVERROR(ENOMEM);
+ }
+
+ if (st->codecpar->extradata_size & 1) //FIXME check if the encoder really did this correctly
+ avio_r8(pb);
+
+ ret = avi_extract_stream_metadata(s, 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'):
pos = avio_tell(pb);
- if ((pb->seekable & AVIO_SEEKABLE_NORMAL) &&
- !(s->flags & AVFMT_FLAG_IGNIDX) &&
- read_braindead_odml_indx(s, 0) < 0 &&
+ if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && !(s->flags & AVFMT_FLAG_IGNIDX) &&
+ avi->use_odml &&
+ read_odml_index(s, 0) < 0 &&
(s->error_recognition & AV_EF_EXPLODE))
goto fail;
avio_seek(pb, pos + size, SEEK_SET);
@@ -803,13 +989,17 @@ static int avi_read_header(AVFormatContext *s)
if (size > 1000000) {
av_log(s, AV_LOG_ERROR,
"Something went wrong during header parsing, "
- "I will ignore it and try to continue anyway.\n");
+ "tag %s has size %u, "
+ "I will ignore it and try to continue anyway.\n",
+ av_fourcc2str(tag), size);
if (s->error_recognition & AV_EF_EXPLODE)
goto fail;
avi->movi_list = avio_tell(pb) - 4;
avi->movi_end = avi->fsize;
goto end_of_header;
}
+ /* Do not fail for very large idx1 tags */
+ case MKTAG('i', 'd', 'x', '1'):
/* skip tag */
size += (size & 1);
avio_skip(pb, size);
@@ -827,17 +1017,32 @@ fail:
if (!avi->index_loaded && (pb->seekable & AVIO_SEEKABLE_NORMAL))
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->codecpar->codec_id == AV_CODEC_ID_MPEG1VIDEO
+ || st->codecpar->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");
@@ -855,15 +1060,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);
@@ -881,16 +1088,31 @@ 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 (strcmp(sub_demuxer->name, "srt") && strcmp(sub_demuxer->name, "ass"))
goto error;
if (!(ast->sub_ctx = avformat_alloc_context()))
goto error;
ast->sub_ctx->pb = pb;
+
+ if (ff_copy_whiteblacklists(ast->sub_ctx, s) < 0)
+ goto error;
+
if (!avformat_open_input(&ast->sub_ctx, "", sub_demuxer, NULL)) {
+ if (ast->sub_ctx->nb_streams != 1)
+ goto error;
ff_read_packet(ast->sub_ctx, &ast->sub_pkt);
avcodec_parameters_copy(st->codecpar, ast->sub_ctx->streams[0]->codecpar);
time_base = ast->sub_ctx->streams[0]->time_base;
@@ -901,6 +1123,7 @@ static int read_gab2_sub(AVStream *st, AVPacket *pkt)
return 1;
error:
+ av_freep(&ast->sub_ctx);
avio_context_free(&pb);
}
return 0;
@@ -940,7 +1163,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') {
@@ -950,6 +1173,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;
@@ -961,7 +1188,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++)
@@ -971,16 +1198,17 @@ start_sync:
size = d[4] + (d[5] << 8) + (d[6] << 16) + (d[7] << 24);
n = get_stream_idx(d + 2);
- av_log(s, AV_LOG_TRACE, "%X %X %X %X %X %X %X %X %"PRId64" %u %d\n",
+ ff_tlog(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##
if ((d[0] == 'i' && d[1] == 'x' && n < s->nb_streams) ||
// parse JUNK
(d[0] == 'J' && d[1] == 'U' && d[2] == 'N' && d[3] == 'K') ||
- (d[0] == 'i' && d[1] == 'd' && d[2] == 'x' && d[3] == '1')) {
+ (d[0] == 'i' && d[1] == 'd' && d[2] == 'x' && d[3] == '1') ||
+ (d[0] == 'i' && d[1] == 'n' && d[2] == 'd' && d[3] == 'x')) {
avio_skip(pb, size);
goto start_sync;
}
@@ -1013,16 +1241,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->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
- st1->codecpar->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 ->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
+ && st1->codecpar->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;
@@ -1031,19 +1265,6 @@ start_sync:
}
}
- if (!avi->dv_demux &&
- ((st->discard >= AVDISCARD_DEFAULT && size == 0) /* ||
- // FIXME: needs a little reordering
- (st->discard >= AVDISCARD_NONKEY &&
- !(pkt->flags & AV_PKT_FLAG_KEY)) */
- || st->discard >= AVDISCARD_ALL)) {
- if (!exit_early) {
- ast->frame_offset += get_duration(ast, size);
- }
- avio_skip(pb, size);
- goto start_sync;
- }
-
if (d[2] == 'p' && d[3] == 'c' && size <= 4 * 256 + 4) {
int k = avio_r8(pb);
int last = (k + avio_r8(pb) - 1) & 0xFF;
@@ -1052,7 +1273,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;
@@ -1070,11 +1291,23 @@ start_sync:
ast->prefix_count = 0;
}
+ if (!avi->dv_demux &&
+ ((st->discard >= AVDISCARD_DEFAULT && size == 0) /* ||
+ // FIXME: needs a little reordering
+ (st->discard >= AVDISCARD_NONKEY &&
+ !(pkt->flags & AV_PKT_FLAG_KEY)) */
+ || st->discard >= AVDISCARD_ALL)) {
+
+ ast->frame_offset += get_duration(ast, size);
+ avio_skip(pb, size);
+ goto start_sync;
+ }
+
avi->stream_index = n;
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) {
@@ -1087,6 +1320,8 @@ start_sync:
}
}
+ if (pb->error)
+ return pb->error;
return AVERROR_EOF;
}
@@ -1128,10 +1363,7 @@ static int ni_prepare_read(AVFormatContext *s)
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,
@@ -1146,15 +1378,18 @@ static int ni_prepare_read(AVFormatContext *s)
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;
return 0;
}
@@ -1203,8 +1438,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,
@@ -1221,13 +1457,13 @@ resync:
if (CONFIG_DV_DEMUXER && avi->dv_demux) {
AVBufferRef *avbuf = pkt->buf;
size = avpriv_dv_produce_packet(avi->dv_demux, pkt,
- pkt->data, pkt->size);
+ pkt->data, pkt->size, pkt->pos);
pkt->buf = avbuf;
pkt->flags |= AV_PKT_FLAG_KEY;
if (size < 0)
av_packet_unref(pkt);
} else if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE &&
- !st->codecpar->codec_tag && read_gab2_sub(st, pkt)) {
+ !st->codecpar->codec_tag && read_gab2_sub(s, st, pkt)) {
ast->frame_offset++;
avi->stream_index = -1;
ast->remaining = 0;
@@ -1240,17 +1476,33 @@ resync:
pkt->dts /= ast->sample_size;
pkt->stream_index = avi->stream_index;
- if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (st->codecpar->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;
+ uint32_t state=-1;
+ if (st->codecpar->codec_id == AV_CODEC_ID_MPEG4) {
+ const uint8_t *ptr = pkt->data, *end = ptr + FFMIN(size, 256);
+ while (ptr < end) {
+ ptr = avpriv_find_start_code(ptr, end, &state);
+ if (state == 0x1B6 && ptr < end) {
+ key = !(*ptr & 0xC0);
+ break;
+ }
+ }
+ }
+ if (!key)
+ e->flags &= ~AVINDEX_KEYFRAME;
+ }
if (e->flags & AVINDEX_KEYFRAME)
pkt->flags |= AV_PKT_FLAG_KEY;
+ }
} else {
pkt->flags |= AV_PKT_FLAG_KEY;
}
@@ -1262,6 +1514,22 @@ resync:
ast->packet_size = 0;
}
+ if (!avi->non_interleaved && pkt->pos >= 0 && ast->seek_pos > pkt->pos) {
+ av_packet_unref(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;
}
@@ -1279,9 +1547,12 @@ static int avi_read_idx1(AVFormatContext *s, int size)
int nb_index_entries, i;
AVStream *st;
AVIStream *ast;
- unsigned int index, tag, flags, pos, len, first_packet = 1;
- unsigned last_pos = -1;
+ int64_t pos;
+ unsigned int index, tag, flags, len, first_packet = 1;
+ int64_t 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)
@@ -1294,13 +1565,21 @@ 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]->codecpar->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);
len = avio_rl32(pb);
- av_log(s, AV_LOG_TRACE, "%d: tag=0x%x flags=0x%x pos=0x%x len=%d/",
+ av_log(s, AV_LOG_TRACE, "%d: tag=0x%x flags=0x%x pos=0x%"PRIx64" len=%d/",
i, tag, flags, pos, len);
index = ((tag & 0xff) - '0') * 10;
@@ -1310,24 +1589,39 @@ static int avi_read_idx1(AVFormatContext *s, int size)
st = s->streams[index];
ast = st->priv_data;
- if (first_packet && first_packet_pos && len) {
- data_offset = first_packet_pos - pos;
+ /* Skip 'xxpc' palette change entries in the index until a logic
+ * to process these is properly implemented. */
+ if ((tag >> 16 & 0xff) == 'p' && (tag >> 24 & 0xff) == 'c')
+ continue;
+
+ if (first_packet && first_packet_pos) {
+ if (avi->movi_list + 4 != pos || pos + 500 > first_packet_pos)
+ data_offset = first_packet_pos - pos;
first_packet = 0;
}
pos += data_offset;
av_log(s, AV_LOG_TRACE, "%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;
}
@@ -1341,7 +1635,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;
@@ -1407,9 +1700,15 @@ static int guess_ni_flag(AVFormatContext *s)
if (n >= 2) {
int64_t pos = st->index_entries[0].pos;
- avio_seek(s->pb, pos + 4, SEEK_SET);
+ unsigned tag[2];
+ avio_seek(s->pb, pos, SEEK_SET);
+ tag[0] = avio_r8(s->pb);
+ tag[1] = avio_r8(s->pb);
+ avio_rl16(s->pb);
size = avio_rl32(s->pb);
- if (pos + size > st->index_entries[1].pos)
+ if (get_stream_idx(tag) == i && pos + size > st->index_entries[1].pos)
+ last_start = INT64_MAX;
+ if (get_stream_idx(tag) == i && size == st->index_entries[0].size + 8)
last_start = INT64_MAX;
}
@@ -1432,25 +1731,32 @@ 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_log(s, AV_LOG_TRACE, "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);
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
}
@@ -1475,7 +1781,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
@@ -1487,16 +1793,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;
@@ -1510,15 +1823,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;
@@ -1534,35 +1850,45 @@ static int avi_read_seek(AVFormatContext *s, int stream_index,
if (st2->nb_index_entries <= 0)
continue;
-// assert(st2->codecpar->block_align);
- assert((int64_t)st2->time_base.num * ast2->rate ==
- (int64_t)st2->time_base.den * ast2->scale);
+// av_assert1(st2->codecpar->block_align);
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->codecpar->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_log(s, AV_LOG_TRACE, "%"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->codecpar->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;
}
@@ -1576,15 +1902,15 @@ static int avi_read_close(AVFormatContext *s)
AVIStream *ast = st->priv_data;
if (ast) {
if (ast->sub_ctx) {
- avio_context_free(&ast->sub_ctx->pb);
+ av_freep(&ast->sub_ctx->pb);
avformat_close_input(&ast->sub_ctx);
}
- av_free(ast->sub_buffer);
+ av_freep(&ast->sub_buffer);
av_packet_unref(&ast->sub_pkt);
}
}
- av_free(avi->dv_demux);
+ av_freep(&avi->dv_demux);
return 0;
}
@@ -1595,8 +1921,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;
@@ -1612,4 +1938,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 84a316f..483f5b5 100644
--- a/libavformat/avienc.c
+++ b/libavformat/avienc.c
@@ -2,29 +2,42 @@
* AVI muxer
* Copyright (c) 2000 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+
+#include <math.h>
+
#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/avutil.h"
+#include "libavutil/internal.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
+#include "libavutil/avassert.h"
+#include "libavutil/timestamp.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "libavcodec/raw.h"
/*
* TODO:
@@ -32,40 +45,116 @@
*/
typedef struct AVIIentry {
- unsigned int flags, pos, len;
+ char tag[4];
+ unsigned int flags;
+ unsigned int pos;
+ unsigned int len;
} AVIIentry;
#define AVI_INDEX_CLUSTER_SIZE 16384
+#define AVI_MASTER_INDEX_PREFIX_SIZE (8+2+1+1+4+8+4+4)
+#define AVI_MASTER_INDEX_ENTRY_SIZE 16 /* bytes per entry */
+#define AVI_MASTER_INDEX_SIZE_DEFAULT 256 /* number of entries */
typedef struct AVIIndex {
int64_t indx_start;
+ int64_t audio_strm_offset;
int entry;
int ents_allocated;
+ int master_odml_riff_id_base;
AVIIentry** cluster;
} AVIIndex;
typedef struct AVIContext {
+ const AVClass *class;
int64_t riff_start, movi_list, odml_list;
int64_t frames_hdr_all;
int riff_id;
+ int reserve_index_space;
+ int master_index_max_size;
+ int write_channel_mask;
} 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;
+ int sample_requested;
+
+ int64_t last_dts;
AVIIndex indexes;
+
+ int64_t strh_flags_offset;
+
+ uint32_t palette[AVPALETTE_COUNT];
+ uint32_t old_palette[AVPALETTE_COUNT];
+ int64_t pal_offset;
} AVIStream;
-static inline AVIIentry *avi_get_ientry(AVIIndex *idx, int ent_id)
+static int avi_write_packet_internal(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;
return &idx->cluster[cl][id];
}
+static int avi_add_ientry(AVFormatContext *s, int stream_index, char *tag,
+ unsigned int flags, unsigned int size)
+{
+ AVIContext *avi = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVIStream *avist = s->streams[stream_index]->priv_data;
+ 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) {
+ idx->cluster = av_realloc_f(idx->cluster, sizeof(void*), cl+1);
+ if (!idx->cluster) {
+ idx->ents_allocated = 0;
+ idx->entry = 0;
+ return AVERROR(ENOMEM);
+ }
+ idx->cluster[cl] =
+ av_malloc(AVI_INDEX_CLUSTER_SIZE * sizeof(AVIIentry));
+ if (!idx->cluster[cl])
+ return AVERROR(ENOMEM);
+ idx->ents_allocated += AVI_INDEX_CLUSTER_SIZE;
+ }
+
+ if (tag)
+ memcpy(idx->cluster[cl][id].tag, tag, 4);
+ else
+ memset(idx->cluster[cl][id].tag, 0, 4);
+ 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++;
+
+ return 0;
+}
+
+static av_cold int avi_init(struct AVFormatContext *s)
+{
+ AVIContext *avi = s->priv_data;
+
+ if (avi->reserve_index_space > 0) {
+ avi->master_index_max_size = (avi->reserve_index_space - AVI_MASTER_INDEX_PREFIX_SIZE) / AVI_MASTER_INDEX_ENTRY_SIZE;
+ avi->master_index_max_size = FFMAX(avi->master_index_max_size, 16);
+ } else
+ avi->master_index_max_size = AVI_MASTER_INDEX_SIZE_DEFAULT;
+ av_log(s, AV_LOG_DEBUG, "reserve_index_space:%d master_index_max_size:%d\n",
+ avi->reserve_index_space, avi->master_index_max_size);
+
+ return 1; /* stream initialization continues in avi_write_header */
+}
+
static int64_t avi_start_new_riff(AVFormatContext *s, AVIOContext *pb,
const char *riff_tag, const char *list_tag)
{
@@ -76,6 +165,7 @@ static int64_t avi_start_new_riff(AVFormatContext *s, AVIOContext *pb,
avi->riff_id++;
for (i = 0; i < s->nb_streams; i++) {
AVIStream *avist = s->streams[i]->priv_data;
+ avist->indexes.audio_strm_offset = avist->audio_strm_length;
avist->indexes.entry = 0;
}
@@ -117,7 +207,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);
par = s->streams[n]->codecpar;
avio_seek(pb, avist->frames_hdr_strm, SEEK_SET);
ff_parse_specific_params(s->streams[n], &au_byterate, &au_ssize, &au_scale);
@@ -129,7 +219,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);
}
@@ -138,20 +228,50 @@ static int avi_write_counters(AVFormatContext *s, int riff_id)
return 0;
}
+static void write_odml_master(AVFormatContext *s, int stream_index)
+{
+ AVIOContext *pb = s->pb;
+ AVIContext *avi = s->priv_data;
+ AVStream *st = s->streams[stream_index];
+ AVCodecParameters *par = st->codecpar;
+ AVIStream *avist = st->priv_data;
+ unsigned char tag[5];
+ int j;
+
+ /* Starting to lay out AVI OpenDML master index.
+ * We want to make it JUNK entry for now, since we'd
+ * like to get away without making AVI an OpenDML one
+ * for compatibility reasons. */
+ avist->indexes.indx_start = ff_start_tag(pb, "JUNK");
+ avio_wl16(pb, 4); /* wLongsPerEntry */
+ avio_w8(pb, 0); /* bIndexSubType (0 == frame index) */
+ avio_w8(pb, 0); /* bIndexType (0 == AVI_INDEX_OF_INDEXES) */
+ avio_wl32(pb, 0); /* nEntriesInUse (will fill out later on) */
+ ffio_wfourcc(pb, avi_stream2fourcc(tag, stream_index, par->codec_type));
+ /* dwChunkId */
+ avio_wl64(pb, 0); /* dwReserved[3] */
+ avio_wl32(pb, 0); /* Must be 0. */
+ for (j = 0; j < avi->master_index_max_size * 2; j++)
+ avio_wl64(pb, 0);
+ ff_end_tag(pb, avist->indexes.indx_start);
+}
+
static int avi_write_header(AVFormatContext *s)
{
AVIContext *avi = s->priv_data;
AVIOContext *pb = s->pb;
int bitrate, n, i, nb_frames, au_byterate, au_ssize, au_scale;
+ int64_t max_stream_duration = 0;
AVCodecParameters *video_par;
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++) {
@@ -172,13 +292,34 @@ static int avi_write_header(AVFormatContext *s)
video_par = NULL;
for (n = 0; n < s->nb_streams; n++) {
AVCodecParameters *par = s->streams[n]->codecpar;
- bitrate += par->bit_rate;
+ AVStream *st = s->streams[n];
+ bitrate = FFMIN(bitrate + par->bit_rate, INT32_MAX);
+ if (st->duration > 0) {
+ int64_t stream_duration = av_rescale_q(st->duration, st->time_base, AV_TIME_BASE_Q);
+ max_stream_duration = FFMAX(stream_duration, max_stream_duration);
+ }
if (par->codec_type == AVMEDIA_TYPE_VIDEO) {
video_par = par;
- video_st = s->streams[n];
+ video_st = st;
}
}
+ /* guess master index size based on bitrate and duration */
+ if (!avi->reserve_index_space) {
+ double duration_est, filesize_est;
+ if (s->duration > 0)
+ duration_est = (double)s->duration / AV_TIME_BASE;
+ else if (max_stream_duration > 0)
+ duration_est = (double)max_stream_duration / AV_TIME_BASE;
+ else
+ duration_est = 10 * 60 * 60; /* default to 10 hours */
+ filesize_est = duration_est * (bitrate / 8) * 1.10; /* add 10% safety margin for muxer+bitrate */
+ avi->master_index_max_size = FFMAX((int)ceil(filesize_est / AVI_MAX_RIFF_SIZE) + 1,
+ avi->master_index_max_size);
+ av_log(s, AV_LOG_DEBUG, "duration_est:%0.3f, filesize_est:%0.1fGiB, master_index_max_size:%d\n",
+ duration_est, filesize_est / (1024*1024*1024), avi->master_index_max_size);
+ }
+
nb_frames = 0;
// TODO: should be avg_frame_rate
@@ -246,6 +387,7 @@ static int avi_write_header(AVFormatContext *s)
avio_wl32(pb, par->codec_tag);
else
avio_wl32(pb, 1);
+ avist->strh_flags_offset = avio_tell(pb);
avio_wl32(pb, 0); /* flags */
avio_wl16(pb, 0); /* priority */
avio_wl16(pb, 0); /* language */
@@ -253,9 +395,18 @@ static int avi_write_header(AVFormatContext *s)
ff_parse_specific_params(st, &au_byterate, &au_ssize, &au_scale);
+ if ( par->codec_type == AVMEDIA_TYPE_VIDEO
+ && par->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 (par->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 */
@@ -266,7 +417,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 (par->codec_type == AVMEDIA_TYPE_VIDEO)
avio_wl32(pb, 1024 * 1024);
else if (par->codec_type == AVMEDIA_TYPE_AUDIO)
@@ -281,6 +432,9 @@ static int avi_write_header(AVFormatContext *s)
ff_end_tag(pb, strh);
if (par->codec_type != AVMEDIA_TYPE_DATA) {
+ int ret, flags;
+ enum AVPixelFormat pix_fmt;
+
strf = ff_start_tag(pb, "strf");
switch (par->codec_type) {
case AVMEDIA_TYPE_SUBTITLE:
@@ -289,43 +443,55 @@ static int avi_write_header(AVFormatContext *s)
if (par->codec_id != AV_CODEC_ID_XSUB)
break;
case AVMEDIA_TYPE_VIDEO:
- ff_put_bmp_header(pb, par, ff_codec_bmp_tags, 0);
+ /* WMP expects RGB 5:5:5 rawvideo in avi to have bpp set to 16. */
+ if ( !par->codec_tag
+ && par->codec_id == AV_CODEC_ID_RAWVIDEO
+ && par->format == AV_PIX_FMT_RGB555LE
+ && par->bits_per_coded_sample == 15)
+ par->bits_per_coded_sample = 16;
+ avist->pal_offset = avio_tell(pb) + 40;
+ ff_put_bmp_header(pb, par, 0, 0);
+ pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_avi,
+ par->bits_per_coded_sample);
+ if ( !par->codec_tag
+ && par->codec_id == AV_CODEC_ID_RAWVIDEO
+ && par->format != pix_fmt
+ && par->format != 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(par->format));
break;
case AVMEDIA_TYPE_AUDIO:
- if (ff_put_wav_header(s, pb, par) < 0)
- return -1;
+ flags = (avi->write_channel_mask == 0) ? FF_PUT_WAV_HEADER_SKIP_CHANNELMASK : 0;
+ if ((ret = ff_put_wav_header(s, pb, par, flags)) < 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(par->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 (par->codec_id == AV_CODEC_ID_XSUB
+ && (t = av_dict_get(s->streams[i]->metadata, "language", NULL, 0))) {
+ const char* langstr = ff_convert_lang_to(t->value, AV_LANG_ISO639_1);
+ t = NULL;
+ if (langstr) {
+ char* str = av_asprintf("Subtitle - %s-xx;02", langstr);
+ if (!str)
+ return AVERROR(ENOMEM);
+ ff_riff_write_info_tag(s->pb, "strn", str);
+ av_free(str);
+ }
+ }
}
if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
- unsigned char tag[5];
- int j;
-
- /* Starting to lay out AVI OpenDML master index.
- * We want to make it JUNK entry for now, since we'd
- * like to get away without making AVI an OpenDML one
- * for compatibility reasons. */
- avist->indexes.entry = avist->indexes.ents_allocated = 0;
- avist->indexes.indx_start = ff_start_tag(pb, "JUNK");
- avio_wl16(pb, 4); /* wLongsPerEntry */
- avio_w8(pb, 0); /* bIndexSubType (0 == frame index) */
- avio_w8(pb, 0); /* bIndexType (0 == AVI_INDEX_OF_INDEXES) */
- avio_wl32(pb, 0); /* nEntriesInUse (will fill out later on) */
- ffio_wfourcc(pb, avi_stream2fourcc(tag, i, par->codec_type));
- /* dwChunkId */
- avio_wl64(pb, 0); /* dwReserved[3] */
- // avio_wl32(pb, 0); /* Must be 0. */
- for (j = 0; j < AVI_MASTER_INDEX_SIZE * 2; j++)
- avio_wl64(pb, 0);
- ff_end_tag(pb, avist->indexes.indx_start);
+ write_odml_master(s, i);
}
if (par->codec_type == AVMEDIA_TYPE_VIDEO &&
@@ -341,7 +507,7 @@ static int avi_write_header(AVFormatContext *s)
avio_wl32(pb, 0); // video format = unknown
avio_wl32(pb, 0); // video standard = unknown
// TODO: should be avg_frame_rate
- avio_wl32(pb, lrintf(1.0 / av_q2d(st->time_base)));
+ avio_wl32(pb, (2LL*st->time_base.den + st->time_base.num - 1) / (2LL * st->time_base.num));
avio_wl32(pb, par->width);
avio_wl32(pb, par->height);
avio_wl16(pb, den);
@@ -380,11 +546,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");
@@ -394,6 +567,39 @@ static int avi_write_header(AVFormatContext *s)
return 0;
}
+static void update_odml_entry(AVFormatContext *s, int stream_index, int64_t ix, int size)
+{
+ AVIOContext *pb = s->pb;
+ AVIContext *avi = s->priv_data;
+ AVIStream *avist = s->streams[stream_index]->priv_data;
+ int64_t pos;
+ int au_byterate, au_ssize, au_scale;
+
+ avio_flush(pb);
+ pos = avio_tell(pb);
+
+ /* Updating one entry in the AVI OpenDML master index */
+ avio_seek(pb, avist->indexes.indx_start - 8, SEEK_SET);
+ ffio_wfourcc(pb, "indx"); /* enabling this entry */
+ avio_skip(pb, 8);
+ avio_wl32(pb, avi->riff_id - avist->indexes.master_odml_riff_id_base); /* nEntriesInUse */
+ avio_skip(pb, 16 * (avi->riff_id - avist->indexes.master_odml_riff_id_base));
+ avio_wl64(pb, ix); /* qwOffset */
+ avio_wl32(pb, size); /* dwSize */
+ ff_parse_specific_params(s->streams[stream_index], &au_byterate, &au_ssize, &au_scale);
+ if (s->streams[stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && au_ssize > 0) {
+ uint32_t audio_segm_size = (avist->audio_strm_length - avist->indexes.audio_strm_offset);
+ if ((audio_segm_size % au_ssize > 0) && !avist->sample_requested) {
+ avpriv_request_sample(s, "OpenDML index duration for audio packets with partial frames");
+ avist->sample_requested = 1;
+ }
+ avio_wl32(pb, audio_segm_size / au_ssize); /* dwDuration (sample count) */
+ } else
+ avio_wl32(pb, avist->indexes.entry); /* dwDuration (packet count) */
+
+ avio_seek(pb, pos, SEEK_SET);
+}
+
static int avi_write_ix(AVFormatContext *s)
{
AVIOContext *pb = s->pb;
@@ -402,14 +608,26 @@ static int avi_write_ix(AVFormatContext *s)
char ix_tag[] = "ix00";
int i, j;
- assert(pb->seekable & AVIO_SEEKABLE_NORMAL);
+ av_assert0(pb->seekable & AVIO_SEEKABLE_NORMAL);
- if (avi->riff_id > AVI_MASTER_INDEX_SIZE)
- return -1;
+ for (i = 0; i < s->nb_streams; i++) {
+ AVIStream *avist = s->streams[i]->priv_data;
+ if (avi->riff_id - avist->indexes.master_odml_riff_id_base == avi->master_index_max_size) {
+ int64_t pos;
+ int size = AVI_MASTER_INDEX_PREFIX_SIZE + AVI_MASTER_INDEX_ENTRY_SIZE * avi->master_index_max_size;
+
+ pos = avio_tell(pb);
+ update_odml_entry(s, i, pos, size);
+ write_odml_master(s, i);
+ av_assert1(avio_tell(pb) - pos == size);
+ avist->indexes.master_odml_riff_id_base = avi->riff_id - 1;
+ }
+ av_assert0(avi->riff_id - avist->indexes.master_odml_riff_id_base < avi->master_index_max_size);
+ }
for (i = 0; i < s->nb_streams; i++) {
AVIStream *avist = s->streams[i]->priv_data;
- int64_t ix, pos;
+ int64_t ix;
avi_stream2fourcc(tag, i, s->streams[i]->codecpar->codec_type);
ix_tag[3] = '0' + i;
@@ -434,20 +652,8 @@ static int avi_write_ix(AVFormatContext *s)
avio_wl32(pb, ((uint32_t) ie->len & ~0x80000000) |
(ie->flags & 0x10 ? 0 : 0x80000000));
}
- avio_flush(pb);
- pos = avio_tell(pb);
-
- /* Updating one entry in the AVI OpenDML master index */
- avio_seek(pb, avist->indexes.indx_start - 8, SEEK_SET);
- ffio_wfourcc(pb, "indx"); /* enabling this entry */
- avio_skip(pb, 8);
- avio_wl32(pb, avi->riff_id); /* nEntriesInUse */
- avio_skip(pb, 16 * avi->riff_id);
- avio_wl64(pb, ix); /* qwOffset */
- avio_wl32(pb, pos - ix); /* dwSize */
- avio_wl32(pb, avist->indexes.entry); /* dwDuration */
-
- avio_seek(pb, pos, SEEK_SET);
+
+ update_odml_entry(s, i, ix, avio_tell(pb) - ix);
}
return 0;
}
@@ -487,9 +693,13 @@ static int avi_write_idx1(AVFormatContext *s)
}
if (!empty) {
avist = s->streams[stream_id]->priv_data;
- avi_stream2fourcc(tag, stream_id,
+ if (*ie->tag)
+ ffio_wfourcc(pb, ie->tag);
+ else {
+ avi_stream2fourcc(tag, stream_id,
s->streams[stream_id]->codecpar->codec_type);
- ffio_wfourcc(pb, tag);
+ ffio_wfourcc(pb, tag);
+ }
avio_wl32(pb, ie->flags);
avio_wl32(pb, ie->pos);
avio_wl32(pb, ie->len);
@@ -503,8 +713,129 @@ static int avi_write_idx1(AVFormatContext *s)
return 0;
}
+static int write_skip_frames(AVFormatContext *s, int stream_index, int64_t dts)
+{
+ AVIStream *avist = s->streams[stream_index]->priv_data;
+ AVCodecParameters *par = s->streams[stream_index]->codecpar;
+
+ ff_dlog(s, "dts:%s packet_count:%d stream_index:%d\n", av_ts2str(dts), avist->packet_count, stream_index);
+ while (par->block_align == 0 && dts != AV_NOPTS_VALUE &&
+ dts > avist->packet_count && par->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_internal(s, &empty_packet);
+ ff_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)
{
+ const int stream_index = pkt->stream_index;
+ AVCodecParameters *par = s->streams[stream_index]->codecpar;
+ int ret;
+
+ if (par->codec_id == AV_CODEC_ID_H264 && par->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->size)
+ return avi_write_packet_internal(s, pkt); /* Passthrough */
+
+ if (par->codec_type == AVMEDIA_TYPE_VIDEO) {
+ AVIStream *avist = s->streams[stream_index]->priv_data;
+ AVIOContext *pb = s->pb;
+ AVPacket *opkt = pkt;
+ int reshuffle_ret;
+ if (par->codec_id == AV_CODEC_ID_RAWVIDEO && par->codec_tag == 0) {
+ int64_t bpc = par->bits_per_coded_sample != 15 ? par->bits_per_coded_sample : 16;
+ int expected_stride = ((par->width * bpc + 31) >> 5)*4;
+ reshuffle_ret = ff_reshuffle_raw_rgb(s, &pkt, par, expected_stride);
+ if (reshuffle_ret < 0)
+ return reshuffle_ret;
+ } else
+ reshuffle_ret = 0;
+ if (par->format == AV_PIX_FMT_PAL8) {
+ ret = ff_get_packet_palette(s, opkt, reshuffle_ret, avist->palette);
+ if (ret < 0)
+ goto fail;
+ if (ret) {
+ int pal_size = 1 << par->bits_per_coded_sample;
+ int pc_tag, i;
+
+ av_assert0(par->bits_per_coded_sample >= 0 && par->bits_per_coded_sample <= 8);
+
+ if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && avist->pal_offset) {
+ int64_t cur_offset = avio_tell(pb);
+ avio_seek(pb, avist->pal_offset, SEEK_SET);
+ for (i = 0; i < pal_size; i++) {
+ uint32_t v = avist->palette[i];
+ avio_wl32(pb, v & 0xffffff);
+ }
+ avio_seek(pb, cur_offset, SEEK_SET);
+ memcpy(avist->old_palette, avist->palette, pal_size * 4);
+ avist->pal_offset = 0;
+ }
+ if (memcmp(avist->palette, avist->old_palette, pal_size * 4)) {
+ unsigned char tag[5];
+ avi_stream2fourcc(tag, stream_index, par->codec_type);
+ tag[2] = 'p'; tag[3] = 'c';
+ if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ if (avist->strh_flags_offset) {
+ int64_t cur_offset = avio_tell(pb);
+ avio_seek(pb, avist->strh_flags_offset, SEEK_SET);
+ avio_wl32(pb, AVISF_VIDEO_PALCHANGES);
+ avio_seek(pb, cur_offset, SEEK_SET);
+ avist->strh_flags_offset = 0;
+ }
+ ret = avi_add_ientry(s, stream_index, tag, AVIIF_NO_TIME,
+ pal_size * 4 + 4);
+ if (ret < 0)
+ goto fail;
+ }
+ pc_tag = ff_start_tag(pb, tag);
+ avio_w8(pb, 0);
+ avio_w8(pb, pal_size & 0xFF);
+ avio_wl16(pb, 0); // reserved
+ for (i = 0; i < pal_size; i++) {
+ uint32_t v = avist->palette[i];
+ avio_wb32(pb, v<<8);
+ }
+ ff_end_tag(pb, pc_tag);
+ memcpy(avist->old_palette, avist->palette, pal_size * 4);
+ }
+ }
+ }
+ if (reshuffle_ret) {
+ ret = avi_write_packet_internal(s, pkt);
+
+fail:
+ if (reshuffle_ret)
+ av_packet_free(&pkt);
+ return ret;
+ }
+ }
+
+ return avi_write_packet_internal(s, pkt);
+}
+
+static int avi_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
+{
unsigned char tag[5];
unsigned int flags = 0;
const int stream_index = pkt->stream_index;
@@ -514,16 +845,9 @@ static int avi_write_packet(AVFormatContext *s, AVPacket *pkt)
AVIStream *avist = s->streams[stream_index]->priv_data;
AVCodecParameters *par = s->streams[stream_index]->codecpar;
- while (par->block_align == 0 && pkt->dts != AV_NOPTS_VALUE &&
- pkt->dts > avist->packet_count) {
- AVPacket empty_packet;
+ if (pkt->dts != AV_NOPTS_VALUE)
+ avist->last_dts = pkt->dts + pkt->duration;
- 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);
- }
avist->packet_count++;
// Make sure to put an OpenDML chunk when the file size exceeds the limits
@@ -546,28 +870,10 @@ static int avi_write_packet(AVFormatContext *s, AVPacket *pkt)
avist->audio_strm_length += size;
if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) {
- 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->ents_allocated = 0;
- idx->entry = 0;
- return err;
- }
- idx->cluster[cl] =
- av_malloc(AVI_INDEX_CLUSTER_SIZE * sizeof(AVIIentry));
- if (!idx->cluster[cl])
- return -1;
- 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;
- idx->entry++;
+ int ret;
+ ret = avi_add_ientry(s, stream_index, NULL, flags, size);
+ if (ret < 0)
+ return ret;
}
avio_write(pb, tag, 4);
@@ -587,6 +893,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 & AVIO_SEEKABLE_NORMAL) {
if (avi->riff_id == 1) {
ff_end_tag(pb, avi->movi_list);
@@ -622,17 +933,44 @@ static int avi_write_trailer(AVFormatContext *s)
}
}
+ if (avi->riff_id >= avi->master_index_max_size) {
+ int index_space = AVI_MASTER_INDEX_PREFIX_SIZE +
+ AVI_MASTER_INDEX_ENTRY_SIZE * avi->riff_id;
+ av_log(s, AV_LOG_WARNING, "Output file not strictly OpenDML compliant, "
+ "consider re-muxing with 'reserve_index_space' option value >= %d\n",
+ index_space);
+ }
+
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_SEEKABLE_NORMAL) {
+ avio_seek(pb, avist->frames_hdr_strm + 4, SEEK_SET);
+ avio_wl32(pb, avist->max_size);
+ }
}
return res;
}
+#define OFFSET(x) offsetof(AVIContext, x)
+#define ENC AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ { "reserve_index_space", "reserve space (in bytes) at the beginning of the file for each stream index", OFFSET(reserve_index_space), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, ENC },
+ { "write_channel_mask", "write channel mask into wave format header", OFFSET(write_channel_mask), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, ENC },
+ { NULL },
+};
+
+static const AVClass avi_muxer_class = {
+ .class_name = "AVI muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVOutputFormat ff_avi_muxer = {
.name = "avi",
.long_name = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"),
@@ -641,10 +979,12 @@ AVOutputFormat ff_avi_muxer = {
.priv_data_size = sizeof(AVIContext),
.audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_AC3,
.video_codec = AV_CODEC_ID_MPEG4,
+ .init = avi_init,
.write_header = avi_write_header,
.write_packet = avi_write_packet,
.write_trailer = avi_write_trailer,
.codec_tag = (const AVCodecTag * const []) {
ff_codec_bmp_tags, ff_codec_wav_tags, 0
},
+ .priv_class = &avi_muxer_class,
};
diff --git a/libavformat/avio.c b/libavformat/avio.c
index 1692f1b..4718753 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
*/
@@ -23,6 +23,7 @@
#include "libavutil/dict.h"
#include "libavutil/opt.h"
#include "libavutil/time.h"
+#include "libavutil/avassert.h"
#include "os_support.h"
#include "avformat.h"
#if CONFIG_NETWORK
@@ -49,10 +50,16 @@ static void *urlcontext_child_next(void *obj, void *prev)
return NULL;
}
+#define OFFSET(x) offsetof(URLContext,x)
+#define E AV_OPT_FLAG_ENCODING_PARAM
+#define D AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[] = {
- { "rw_timeout", "Timeout for IO operations (in microseconds)", offsetof(URLContext, rw_timeout), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_DECODING_PARAM },
+ {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D },
+ {"protocol_blacklist", "List of protocols that are not allowed to be used", OFFSET(protocol_blacklist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D },
+ {"rw_timeout", "Timeout for IO operations (in microseconds)", offsetof(URLContext, rw_timeout), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_DECODING_PARAM },
{ NULL }
};
+
const AVClass ffurl_context_class = {
.class_name = "URLContext",
.item_name = urlcontext_to_name,
@@ -65,8 +72,7 @@ const AVClass ffurl_context_class = {
static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up,
const char *filename, int flags,
- const AVIOInterruptCB *int_cb,
- const URLProtocol **protocols)
+ const AVIOInterruptCB *int_cb)
{
URLContext *uc;
int err;
@@ -75,6 +81,16 @@ static int url_alloc_for_protocol(URLContext **puc, const 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);
@@ -87,7 +103,6 @@ static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up,
uc->flags = flags;
uc->is_streamed = 0; /* default = not streamed */
uc->max_packet_size = 0; /* default: stream file */
- uc->protocols = protocols;
if (up->priv_data_size) {
uc->priv_data = av_mallocz(up->priv_data_size);
if (!uc->priv_data) {
@@ -95,8 +110,40 @@ static int url_alloc_for_protocol(URLContext **puc, const 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++;
+
+ if (strcmp(up->name, "subfile"))
+ ret = AVERROR(EINVAL);
+
+ while(ret >= 0 && (key= strchr(p, sep)) && p<key && (val = strchr(key+1, sep))){
+ *val= *key= 0;
+ if (strcmp(p, "start") && strcmp(p, "end")) {
+ ret = AVERROR_OPTION_NOT_FOUND;
+ } else
+ 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)
@@ -118,12 +165,53 @@ fail:
int ffurl_connect(URLContext *uc, AVDictionary **options)
{
- int err =
+ int err;
+ AVDictionary *tmp_opts = NULL;
+ AVDictionaryEntry *e;
+
+ if (!options)
+ options = &tmp_opts;
+
+ // Check that URLContext was initialized correctly and lists are matching if set
+ av_assert0(!(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
+ (uc->protocol_whitelist && !strcmp(uc->protocol_whitelist, e->value)));
+ av_assert0(!(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||
+ (uc->protocol_blacklist && !strcmp(uc->protocol_blacklist, e->value)));
+
+ if (uc->protocol_whitelist && av_match_list(uc->prot->name, uc->protocol_whitelist, ',') <= 0) {
+ av_log(uc, AV_LOG_ERROR, "Protocol '%s' not on whitelist '%s'!\n", uc->prot->name, uc->protocol_whitelist);
+ return AVERROR(EINVAL);
+ }
+
+ if (uc->protocol_blacklist && av_match_list(uc->prot->name, uc->protocol_blacklist, ',') > 0) {
+ av_log(uc, AV_LOG_ERROR, "Protocol '%s' on blacklist '%s'!\n", uc->prot->name, uc->protocol_blacklist);
+ return AVERROR(EINVAL);
+ }
+
+ if (!uc->protocol_whitelist && uc->prot->default_whitelist) {
+ av_log(uc, AV_LOG_DEBUG, "Setting default whitelist '%s'\n", uc->prot->default_whitelist);
+ uc->protocol_whitelist = av_strdup(uc->prot->default_whitelist);
+ if (!uc->protocol_whitelist) {
+ return AVERROR(ENOMEM);
+ }
+ } else if (!uc->protocol_whitelist)
+ av_log(uc, AV_LOG_DEBUG, "No default whitelist set\n"); // This should be an error once all declare a default whitelist
+
+ if ((err = av_dict_set(options, "protocol_whitelist", uc->protocol_whitelist, 0)) < 0)
+ return err;
+ if ((err = av_dict_set(options, "protocol_blacklist", uc->protocol_blacklist, 0)) < 0)
+ return err;
+
+ err =
uc->prot->url_open2 ? uc->prot->url_open2(uc,
uc->filename,
uc->flags,
options) :
uc->prot->url_open(uc, uc->filename, uc->flags);
+
+ av_dict_set(options, "protocol_whitelist", NULL, 0);
+ av_dict_set(options, "protocol_blacklist", NULL, 0);
+
if (err)
return err;
uc->is_connected = 1;
@@ -135,20 +223,41 @@ int ffurl_connect(URLContext *uc, AVDictionary **options)
return 0;
}
+int ffurl_accept(URLContext *s, URLContext **c)
+{
+ av_assert0(!*c);
+ if (s->prot->url_accept)
+ return s->prot->url_accept(s, c);
+ return AVERROR(EBADF);
+}
+
+int ffurl_handshake(URLContext *c)
+{
+ int ret;
+ if (c->prot->url_handshake) {
+ ret = c->prot->url_handshake(c);
+ if (ret)
+ return ret;
+ }
+ c->is_connected = 1;
+ return 0;
+}
+
#define URL_SCHEME_CHARS \
"abcdefghijklmnopqrstuvwxyz" \
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
"0123456789+-."
-int ffurl_alloc(URLContext **puc, const char *filename, int flags,
- const AVIOInterruptCB *int_cb,
- const URLProtocol **protocols)
+static const struct URLProtocol *url_find_protocol(const char *filename)
{
+ const URLProtocol **protocols;
char proto_str[128], proto_nested[128], *ptr;
size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
int i;
- if (filename[proto_len] != ':' || is_dos_path(filename))
+ if (filename[proto_len] != ':' &&
+ (strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) ||
+ is_dos_path(filename))
strcpy(proto_str, "file");
else
av_strlcpy(proto_str, filename,
@@ -158,27 +267,52 @@ int ffurl_alloc(URLContext **puc, const char *filename, int flags,
if ((ptr = strchr(proto_nested, '+')))
*ptr = '\0';
+ protocols = ffurl_get_protocols(NULL, NULL);
+ if (!protocols)
+ return NULL;
for (i = 0; protocols[i]; i++) {
- const URLProtocol *up = protocols[i];
- if (!strcmp(proto_str, up->name))
- return url_alloc_for_protocol(puc, up, filename, flags, int_cb,
- protocols);
+ const URLProtocol *up = protocols[i];
+ if (!strcmp(proto_str, up->name)) {
+ av_freep(&protocols);
+ return up;
+ }
if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
- !strcmp(proto_nested, up->name))
- return url_alloc_for_protocol(puc, up, filename, flags, int_cb,
- protocols);
+ !strcmp(proto_nested, up->name)) {
+ av_freep(&protocols);
+ return up;
+ }
}
+ av_freep(&protocols);
+
+ return NULL;
+}
+
+int ffurl_alloc(URLContext **puc, const char *filename, int flags,
+ const AVIOInterruptCB *int_cb)
+{
+ const URLProtocol *p = NULL;
+
+ 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 FFmpeg with "
+ "openssl, gnutls "
+ "or securetransport enabled.\n");
return AVERROR_PROTOCOL_NOT_FOUND;
}
-int ffurl_open(URLContext **puc, const char *filename, int flags,
- const AVIOInterruptCB *int_cb, AVDictionary **options,
- const URLProtocol **protocols,
- URLContext *parent)
+int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
+ const AVIOInterruptCB *int_cb, AVDictionary **options,
+ const char *whitelist, const char* blacklist,
+ URLContext *parent)
{
- int ret = ffurl_alloc(puc, filename, flags, int_cb, protocols);
- if (ret)
+ AVDictionary *tmp_opts = NULL;
+ AVDictionaryEntry *e;
+ int ret = ffurl_alloc(puc, filename, flags, int_cb);
+ if (ret < 0)
return ret;
if (parent)
av_opt_copy(*puc, parent);
@@ -188,7 +322,28 @@ int ffurl_open(URLContext **puc, const char *filename, int flags,
if (options && (*puc)->prot->priv_data_class &&
(ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
goto fail;
+
+ if (!options)
+ options = &tmp_opts;
+
+ av_assert0(!whitelist ||
+ !(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
+ !strcmp(whitelist, e->value));
+ av_assert0(!blacklist ||
+ !(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||
+ !strcmp(blacklist, e->value));
+
+ if ((ret = av_dict_set(options, "protocol_whitelist", whitelist, 0)) < 0)
+ goto fail;
+
+ if ((ret = av_dict_set(options, "protocol_blacklist", blacklist, 0)) < 0)
+ goto fail;
+
+ if ((ret = av_opt_set_dict(*puc, options)) < 0)
+ goto fail;
+
ret = ffurl_connect(*puc, options);
+
if (!ret)
return 0;
fail:
@@ -197,6 +352,13 @@ fail:
return ret;
}
+int ffurl_open(URLContext **puc, const char *filename, int flags,
+ const AVIOInterruptCB *int_cb, AVDictionary **options)
+{
+ return ffurl_open_whitelist(puc, filename, flags,
+ int_cb, options, NULL, NULL, NULL);
+}
+
static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,
int size, int size_min,
int (*transfer_func)(URLContext *h,
@@ -209,6 +371,8 @@ static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,
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;
@@ -227,15 +391,15 @@ static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,
}
av_usleep(1000);
}
- } else if (ret < 1)
- return (ret < 0 && ret != AVERROR_EOF) ? ret : len;
+ } else if (ret == AVERROR_EOF)
+ return (len > 0) ? len : AVERROR_EOF;
+ else if (ret < 0)
+ return ret;
if (ret) {
fast_retries = FFMAX(fast_retries, 2);
wait_since = 0;
}
len += ret;
- if (ff_check_interrupt(&h->interrupt_callback))
- return AVERROR_EXIT;
}
return len;
}
@@ -262,7 +426,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,
+ return retry_transfer_wrapper(h, (unsigned char *)buf, size, size,
(int (*)(struct URLContext *, uint8_t *, int))
h->prot->url_write);
}
@@ -277,8 +441,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 */
@@ -292,27 +457,32 @@ 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_opt_free(h);
+ av_freep(hh);
return ret;
}
-int avio_check(const char *url, int flags)
+int ffurl_close(URLContext *h)
{
- const URLProtocol **protocols;
- URLContext *h;
- int ret;
+ return ffurl_closep(&h);
+}
- protocols = ffurl_get_protocols(NULL, NULL);
- if (!protocols)
- return AVERROR(ENOMEM);
- ret = ffurl_alloc(&h, url, flags, NULL, protocols);
- if (ret) {
- av_freep(&protocols);
+const char *avio_find_protocol_name(const char *url)
+{
+ const 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 < 0)
return ret;
- }
if (h->prot->url_check) {
ret = h->prot->url_check(h, flags);
@@ -323,10 +493,121 @@ int avio_check(const char *url, int flags)
}
ffurl_close(h);
- av_freep(&protocols);
return ret;
}
+int avpriv_io_move(const char *url_src, const char *url_dst)
+{
+ URLContext *h_src, *h_dst;
+ int ret = ffurl_alloc(&h_src, url_src, AVIO_FLAG_READ_WRITE, NULL);
+ if (ret < 0)
+ return ret;
+ ret = ffurl_alloc(&h_dst, url_dst, AVIO_FLAG_WRITE, NULL);
+ if (ret < 0) {
+ ffurl_close(h_src);
+ return ret;
+ }
+
+ if (h_src->prot == h_dst->prot && h_src->prot->url_move)
+ ret = h_src->prot->url_move(h_src, h_dst);
+ else
+ ret = AVERROR(ENOSYS);
+
+ ffurl_close(h_src);
+ ffurl_close(h_dst);
+ return ret;
+}
+
+int avpriv_io_delete(const char *url)
+{
+ URLContext *h;
+ int ret = ffurl_alloc(&h, url, AVIO_FLAG_WRITE, NULL);
+ if (ret < 0)
+ return ret;
+
+ if (h->prot->url_delete)
+ ret = h->prot->url_delete(h);
+ else
+ ret = AVERROR(ENOSYS);
+
+ ffurl_close(h);
+ return ret;
+}
+
+int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options)
+{
+ URLContext *h = NULL;
+ AVIODirContext *ctx = NULL;
+ int ret;
+ av_assert0(s);
+
+ ctx = av_mallocz(sizeof(*ctx));
+ if (!ctx) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ if ((ret = ffurl_alloc(&h, url, AVIO_FLAG_READ, NULL)) < 0)
+ goto fail;
+
+ if (h->prot->url_open_dir && h->prot->url_read_dir && h->prot->url_close_dir) {
+ if (options && h->prot->priv_data_class &&
+ (ret = av_opt_set_dict(h->priv_data, options)) < 0)
+ goto fail;
+ ret = h->prot->url_open_dir(h);
+ } else
+ ret = AVERROR(ENOSYS);
+ if (ret < 0)
+ goto fail;
+
+ h->is_connected = 1;
+ ctx->url_context = h;
+ *s = ctx;
+ return 0;
+
+ fail:
+ av_free(ctx);
+ *s = NULL;
+ ffurl_close(h);
+ return ret;
+}
+
+int avio_read_dir(AVIODirContext *s, AVIODirEntry **next)
+{
+ URLContext *h;
+ int ret;
+
+ if (!s || !s->url_context)
+ return AVERROR(EINVAL);
+ h = s->url_context;
+ if ((ret = h->prot->url_read_dir(h, next)) < 0)
+ avio_free_directory_entry(next);
+ return ret;
+}
+
+int avio_close_dir(AVIODirContext **s)
+{
+ URLContext *h;
+
+ av_assert0(s);
+ if (!(*s) || !(*s)->url_context)
+ return AVERROR(EINVAL);
+ h = (*s)->url_context;
+ h->prot->url_close_dir(h);
+ ffurl_close(h);
+ av_freep(s);
+ *s = NULL;
+ return 0;
+}
+
+void avio_free_directory_entry(AVIODirEntry **entry)
+{
+ if (!entry || !*entry)
+ return;
+ av_free((*entry)->name);
+ av_freep(entry);
+}
+
int64_t ffurl_size(URLContext *h)
{
int64_t pos, size;
@@ -364,6 +645,13 @@ int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles)
return h->prot->url_get_multi_file_handle(h, handles, numhandles);
}
+int ffurl_get_short_seek(URLContext *h)
+{
+ if (!h->prot->url_get_short_seek)
+ return AVERROR(ENOSYS);
+ return h->prot->url_get_short_seek(h);
+}
+
int ffurl_shutdown(URLContext *h, int flags)
{
if (!h->prot->url_shutdown)
diff --git a/libavformat/avio.h b/libavformat/avio.h
index e65135e..f9c5972 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
@@ -61,6 +61,50 @@ typedef struct AVIOInterruptCB {
} AVIOInterruptCB;
/**
+ * Directory entry types.
+ */
+enum AVIODirEntryType {
+ AVIO_ENTRY_UNKNOWN,
+ AVIO_ENTRY_BLOCK_DEVICE,
+ AVIO_ENTRY_CHARACTER_DEVICE,
+ AVIO_ENTRY_DIRECTORY,
+ AVIO_ENTRY_NAMED_PIPE,
+ AVIO_ENTRY_SYMBOLIC_LINK,
+ AVIO_ENTRY_SOCKET,
+ AVIO_ENTRY_FILE,
+ AVIO_ENTRY_SERVER,
+ AVIO_ENTRY_SHARE,
+ AVIO_ENTRY_WORKGROUP,
+};
+
+/**
+ * Describes single entry of the directory.
+ *
+ * Only name and type fields are guaranteed be set.
+ * Rest of fields are protocol or/and platform dependent and might be unknown.
+ */
+typedef struct AVIODirEntry {
+ char *name; /**< Filename */
+ int type; /**< Type of the entry */
+ int utf8; /**< Set to 1 when name is encoded with UTF-8, 0 otherwise.
+ Name can be encoded with UTF-8 even though 0 is set. */
+ int64_t size; /**< File size in bytes, -1 if unknown. */
+ int64_t modification_timestamp; /**< Time of last modification in microseconds since unix
+ epoch, -1 if unknown. */
+ int64_t access_timestamp; /**< Time of last access in microseconds since unix epoch,
+ -1 if unknown. */
+ int64_t status_change_timestamp; /**< Time of last status change in microseconds since unix
+ epoch, -1 if unknown. */
+ int64_t user_id; /**< User ID of owner, -1 if unknown. */
+ int64_t group_id; /**< Group ID of owner, -1 if unknown. */
+ int64_t filemode; /**< Unix file mode, -1 if unknown. */
+} AVIODirEntry;
+
+typedef struct AVIODirContext {
+ struct URLContext *url_context;
+} AVIODirContext;
+
+/**
* Different data types that can be returned via the AVIO
* write_data_type callback.
*/
@@ -93,7 +137,13 @@ enum AVIODataMarkerType {
* Trailer data, which doesn't contain actual content, but only for
* finalizing the output file.
*/
- AVIO_DATA_MARKER_TRAILER
+ AVIO_DATA_MARKER_TRAILER,
+ /**
+ * A point in the output bytestream where the underlying AVIOContext might
+ * flush the buffer depending on latency or buffering requirements. Typically
+ * means the end of a packet.
+ */
+ AVIO_DATA_MARKER_FLUSH_POINT,
};
/**
@@ -122,6 +172,57 @@ typedef struct AVIOContext {
* to any av_opt_* functions in that case.
*/
const AVClass *av_class;
+
+ /*
+ * The following shows the relationship between buffer, buf_ptr,
+ * buf_ptr_max, buf_end, buf_size, and pos, when reading and when writing
+ * (since AVIOContext is used for both):
+ *
+ **********************************************************************************
+ * READING
+ **********************************************************************************
+ *
+ * | buffer_size |
+ * |---------------------------------------|
+ * | |
+ *
+ * buffer buf_ptr buf_end
+ * +---------------+-----------------------+
+ * |/ / / / / / / /|/ / / / / / /| |
+ * read buffer: |/ / consumed / | to be read /| |
+ * |/ / / / / / / /|/ / / / / / /| |
+ * +---------------+-----------------------+
+ *
+ * pos
+ * +-------------------------------------------+-----------------+
+ * input file: | | |
+ * +-------------------------------------------+-----------------+
+ *
+ *
+ **********************************************************************************
+ * WRITING
+ **********************************************************************************
+ *
+ * | buffer_size |
+ * |--------------------------------------|
+ * | |
+ *
+ * buf_ptr_max
+ * buffer (buf_ptr) buf_end
+ * +-----------------------+--------------+
+ * |/ / / / / / / / / / / /| |
+ * write buffer: | / / to be flushed / / | |
+ * |/ / / / / / / / / / / /| |
+ * +-----------------------+--------------+
+ * buf_ptr can be in this
+ * due to a backward seek
+ *
+ * pos
+ * +-------------+----------------------------------------------+
+ * output file: | | |
+ * +-------------+----------------------------------------------+
+ *
+ */
unsigned char *buffer; /**< Start of the buffer. */
int buffer_size; /**< Maximum buffer size */
unsigned char *buf_ptr; /**< Current position in the buffer */
@@ -135,7 +236,7 @@ typedef struct AVIOContext {
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);
int64_t (*seek)(void *opaque, int64_t offset, int whence);
int64_t pos; /**< position in the file of the current buffer */
- int must_flush; /**< true if the next seek should flush */
+ int must_flush; /**< unused */
int eof_reached; /**< true if eof reached */
int write_flag; /**< true if open for writing */
int max_packet_size;
@@ -160,6 +261,60 @@ typedef struct AVIOContext {
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;
+
+ /**
+ * Threshold to favor readahead over seek.
+ * This is current internal only, do not use from outside.
+ */
+ int short_seek_threshold;
+
+ /**
+ * ',' separated list of allowed protocols.
+ */
+ const char *protocol_whitelist;
+
+ /**
+ * ',' separated list of disallowed protocols.
+ */
+ const char *protocol_blacklist;
+
+ /**
* A callback that is used instead of write_packet.
*/
int (*write_data_type)(void *opaque, uint8_t *buf, int buf_size,
@@ -176,10 +331,37 @@ typedef struct AVIOContext {
*/
enum AVIODataMarkerType current_type;
int64_t last_time;
+
+ /**
+ * A callback that is used instead of short_seek_threshold.
+ * This is current internal only, do not use from outside.
+ */
+ int (*short_seek_get)(void *opaque);
+
int64_t written;
+
+ /**
+ * Maximum reached position before a backward seek in the write buffer,
+ * used keeping track of already written data for a later flush.
+ */
+ unsigned char *buf_ptr_max;
+
+ /**
+ * Try to buffer at least this amount of data before flushing it
+ */
+ int min_packet_size;
} AVIOContext;
/**
+ * 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
@@ -194,11 +376,76 @@ typedef struct AVIOContext {
int avio_check(const char *url, int flags);
/**
+ * Move or rename a resource.
+ *
+ * @note url_src and url_dst should share the same protocol and authority.
+ *
+ * @param url_src url to resource to be moved
+ * @param url_dst new url to resource if the operation succeeded
+ * @return >=0 on success or negative on error.
+ */
+int avpriv_io_move(const char *url_src, const char *url_dst);
+
+/**
+ * Delete a resource.
+ *
+ * @param url resource to be deleted.
+ * @return >=0 on success or negative on error.
+ */
+int avpriv_io_delete(const char *url);
+
+/**
+ * Open directory for reading.
+ *
+ * @param s directory read context. Pointer to a NULL pointer must be passed.
+ * @param url directory to be listed.
+ * @param options A dictionary filled with protocol-private options. On return
+ * this parameter will be destroyed and replaced with a dictionary
+ * containing options that were not found. May be NULL.
+ * @return >=0 on success or negative on error.
+ */
+int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options);
+
+/**
+ * Get next directory entry.
+ *
+ * Returned entry must be freed with avio_free_directory_entry(). In particular
+ * it may outlive AVIODirContext.
+ *
+ * @param s directory read context.
+ * @param[out] next next entry or NULL when no more entries.
+ * @return >=0 on success or negative on error. End of list is not considered an
+ * error.
+ */
+int avio_read_dir(AVIODirContext *s, AVIODirEntry **next);
+
+/**
+ * Close directory.
+ *
+ * @note Entries created using avio_read_dir() are not deleted and must be
+ * freeded with avio_free_directory_entry().
+ *
+ * @param s directory read context.
+ * @return >=0 on success or negative on error.
+ */
+int avio_close_dir(AVIODirContext **s);
+
+/**
+ * Free entry allocated by avio_read_dir().
+ *
+ * @param entry entry to be freed.
+ */
+void avio_free_directory_entry(AVIODirEntry **entry);
+
+/**
* Allocate and initialize an AVIOContext for buffered I/O. It must be later
- * freed with av_free().
+ * freed with avio_context_free().
*
* @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.
@@ -206,6 +453,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.
@@ -299,10 +547,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.
@@ -319,12 +564,34 @@ static av_always_inline int64_t avio_tell(AVIOContext *s)
*/
int64_t avio_size(AVIOContext *s);
-/** @warning currently size is limited */
+/**
+ * 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 Writes up to 4 KiB per call */
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
@@ -332,6 +599,15 @@ void avio_flush(AVIOContext *s);
int avio_read(AVIOContext *s, unsigned char *buf, int size);
/**
+ * Read size bytes from AVIOContext into buf. Unlike avio_read(), this is allowed
+ * to read fewer bytes than requested. The missing bytes can be read in the next
+ * call. This always tries to read at least 1 byte.
+ * Useful to reduce latency in certain cases.
+ * @return number of bytes read or AVERROR
+ */
+int avio_read_partial(AVIOContext *s, unsigned char *buf, int size);
+
+/**
* @name Functions for reading from AVIOContext
* @{
*
@@ -403,6 +679,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
@@ -413,7 +697,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);
@@ -433,7 +717,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,
@@ -474,6 +758,18 @@ int avio_closep(AVIOContext **s);
int avio_open_dyn_buf(AVIOContext **s);
/**
+ * Return the written size and a pointer to the buffer.
+ * The AVIOContext stream is left intact.
+ * The buffer must NOT be freed.
+ * No padding is added to the buffer.
+ *
+ * @param s IO context
+ * @param pbuffer pointer to a byte buffer
+ * @return the length of the byte buffer
+ */
+int avio_get_dyn_buf(AVIOContext *s, uint8_t **pbuffer);
+
+/**
* Return the written size and a pointer to the buffer. The buffer
* must be freed with av_free().
* Padding of AV_INPUT_BUFFER_PADDING_SIZE is added to the buffer.
@@ -515,17 +811,57 @@ 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);
+
+/**
+ * Accept and allocate a client context on a server context.
+ * @param s the server context
+ * @param c the client context, must be unallocated
+ * @return >= 0 on success or a negative value corresponding
+ * to an AVERROR on failure
+ */
+int avio_accept(AVIOContext *s, AVIOContext **c);
+
+/**
+ * Perform one step of the protocol handshake to accept a new client.
+ * This function must be called on a client returned by avio_accept() before
+ * using it as a read/write context.
+ * It is separate from avio_accept() because it may block.
+ * A step of the handshake is defined by places where the application may
+ * decide to change the proceedings.
+ * For example, on a protocol with a request header and a reply header, each
+ * one can constitute a step because the application may use the parameters
+ * from the request to change parameters in the reply; or each individual
+ * chunk of the request can constitute a step.
+ * If the handshake is already finished, avio_handshake() does nothing and
+ * returns 0 immediately.
+ *
+ * @param c the client context to perform the handshake on
+ * @return 0 on a complete and successful handshake
+ * > 0 if the handshake progressed, but is not complete
+ * < 0 for an AVERROR code
+ */
+int avio_handshake(AVIOContext *c);
#endif /* AVFORMAT_AVIO_H */
diff --git a/libavformat/avio_internal.h b/libavformat/avio_internal.h
index 8616499..c01835d 100644
--- a/libavformat/avio_internal.h
+++ b/libavformat/avio_internal.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
*/
@@ -53,14 +53,6 @@ int ffio_init_context(AVIOContext *s,
*/
int ffio_read_indirect(AVIOContext *s, unsigned char *buf, int size, const unsigned char **data);
-/**
- * Read size bytes from AVIOContext into buf.
- * This reads at most 1 packet. If that is not enough fewer bytes will be
- * returned.
- * @return number of bytes read or AVERROR
- */
-int ffio_read_partial(AVIOContext *s, unsigned char *buf, int size);
-
void ffio_fill(AVIOContext *s, int b, int count);
static av_always_inline void ffio_wfourcc(AVIOContext *pb, const uint8_t *s)
@@ -77,10 +69,10 @@ 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);
@@ -94,12 +86,25 @@ int ffio_read_size(AVIOContext *s, unsigned char *buf, int size);
/** @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);
unsigned long ffio_get_checksum(AVIOContext *s);
unsigned long ff_crc04C11DB7_update(unsigned long checksum, const uint8_t *buf,
unsigned int len);
+unsigned long ff_crcEDB88320_update(unsigned long checksum, const uint8_t *buf,
+ unsigned int len);
unsigned long ff_crcA001_update(unsigned long checksum, const uint8_t *buf,
unsigned int len);
@@ -122,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);
@@ -137,6 +142,10 @@ int ffio_fdopen(AVIOContext **s, URLContext *h);
*/
int ffio_open_null_buf(AVIOContext **s);
+int ffio_open_whitelist(AVIOContext **s, const char *url, int flags,
+ const AVIOInterruptCB *int_cb, AVDictionary **options,
+ const char *whitelist, const char *blacklist);
+
/**
* Close a null buffer.
*
diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c
index 31476d3..0d4eb05 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"
@@ -41,56 +43,26 @@
#define SHORT_SEEK_THRESHOLD 4096
typedef struct AVIOInternal {
- const AVClass *class;
-
- char *protocol_whitelist;
- char *protocol_blacklist;
-
URLContext *h;
- const URLProtocol **protocols;
} AVIOInternal;
-static void *io_priv_child_next(void *obj, void *prev)
-{
- AVIOInternal *internal = obj;
- return prev ? NULL : internal->h;
-}
-
-static const AVClass *io_priv_child_class_next(const AVClass *prev)
-{
- return prev ? NULL : &ffurl_context_class;
-}
-
-#define OFFSET(x) offsetof(AVIOInternal, x)
-static const AVOption io_priv_options[] = {
- { "protocol_whitelist", "A comma-separated list of allowed protocols",
- OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING },
- { "protocol_blacklist", "A comma-separated list of forbidden protocols",
- OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING },
- { NULL },
-};
-
-static const AVClass io_priv_class = {
- .class_name = "AVIOContext",
- .item_name = av_default_item_name,
- .version = LIBAVUTIL_VERSION_INT,
- .option = io_priv_options,
- .child_next = io_priv_child_next,
- .child_class_next = io_priv_child_class_next,
-};
-
static void *ff_avio_child_next(void *obj, void *prev)
{
AVIOContext *s = obj;
- return prev ? NULL : s->opaque;
+ AVIOInternal *internal = s->opaque;
+ return prev ? NULL : internal->h;
}
static const AVClass *ff_avio_child_class_next(const AVClass *prev)
{
- return prev ? NULL : &io_priv_class;
+ return prev ? NULL : &ffurl_context_class;
}
+#define OFFSET(x) offsetof(AVIOContext,x)
+#define E AV_OPT_FLAG_ENCODING_PARAM
+#define D AV_OPT_FLAG_DECODING_PARAM
static const AVOption ff_avio_options[] = {
+ {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D },
{ NULL },
};
@@ -116,9 +88,12 @@ 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->buf_ptr_max = buffer;
s->opaque = opaque;
+ s->direct = 0;
url_resetbuf(s, write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ);
@@ -126,12 +101,13 @@ int ffio_init_context(AVIOContext *s,
s->read_packet = read_packet;
s->seek = seek;
s->pos = 0;
- s->must_flush = 0;
s->eof_reached = 0;
s->error = 0;
s->seekable = seek ? AVIO_SEEKABLE_NORMAL : 0;
+ s->min_packet_size = 0;
s->max_packet_size = 0;
s->update_checksum = NULL;
+ s->short_seek_threshold = SHORT_SEEK_THRESHOLD;
if (!read_packet && !write_flag) {
s->pos = buffer_size;
@@ -144,6 +120,8 @@ int ffio_init_context(AVIOContext *s,
s->ignore_boundary_point = 0;
s->current_type = AVIO_DATA_MARKER_UNKNOWN;
s->last_time = AV_NOPTS_VALUE;
+ s->short_seek_get = NULL;
+ s->written = 0;
return 0;
}
@@ -170,44 +148,52 @@ void avio_context_free(AVIOContext **ps)
av_freep(ps);
}
-static void flush_buffer(AVIOContext *s)
+static void writeout(AVIOContext *s, const uint8_t *data, int len)
{
- if (s->buf_ptr > s->buffer) {
- int size = s->buf_ptr - s->buffer;
- if (!s->error) {
- int ret = 0;
- if (s->write_data_type)
- ret = s->write_data_type(s->opaque, s->buffer,
- size,
- s->current_type,
- s->last_time);
- else if (s->write_packet)
- ret = s->write_packet(s->opaque, s->buffer,
- size);
- if (ret < 0) {
- s->error = ret;
- } else {
- if (s->pos + size > s->written)
- s->written = s->pos + size;
- }
- }
- if (s->current_type == AVIO_DATA_MARKER_SYNC_POINT ||
- s->current_type == AVIO_DATA_MARKER_BOUNDARY_POINT) {
- s->current_type = AVIO_DATA_MARKER_UNKNOWN;
+ if (!s->error) {
+ int ret = 0;
+ if (s->write_data_type)
+ ret = s->write_data_type(s->opaque, (uint8_t *)data,
+ len,
+ s->current_type,
+ s->last_time);
+ else if (s->write_packet)
+ ret = s->write_packet(s->opaque, (uint8_t *)data, len);
+ if (ret < 0) {
+ s->error = ret;
+ } else {
+ if (s->pos + len > s->written)
+ s->written = s->pos + len;
}
- s->last_time = AV_NOPTS_VALUE;
+ }
+ if (s->current_type == AVIO_DATA_MARKER_SYNC_POINT ||
+ s->current_type == AVIO_DATA_MARKER_BOUNDARY_POINT) {
+ s->current_type = AVIO_DATA_MARKER_UNKNOWN;
+ }
+ s->last_time = AV_NOPTS_VALUE;
+ s->writeout_count ++;
+ s->pos += len;
+}
+
+static void flush_buffer(AVIOContext *s)
+{
+ s->buf_ptr_max = FFMAX(s->buf_ptr, s->buf_ptr_max);
+ if (s->write_flag && s->buf_ptr_max > s->buffer) {
+ writeout(s, s->buffer, s->buf_ptr_max - s->buffer);
if (s->update_checksum) {
s->checksum = s->update_checksum(s->checksum, s->checksum_ptr,
- s->buf_ptr - s->checksum_ptr);
+ s->buf_ptr_max - s->checksum_ptr);
s->checksum_ptr = s->buffer;
}
- s->pos += size;
}
- s->buf_ptr = s->buffer;
+ s->buf_ptr = s->buf_ptr_max = 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);
@@ -229,6 +215,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);
@@ -244,8 +235,10 @@ void avio_write(AVIOContext *s, const unsigned char *buf, int size)
void avio_flush(AVIOContext *s)
{
+ int seekback = s->write_flag ? FFMIN(0, s->buf_ptr - s->buf_ptr_max) : 0;
flush_buffer(s);
- s->must_flush = 0;
+ if (seekback)
+ avio_seek(s, seekback, SEEK_CUR);
}
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
@@ -253,12 +246,16 @@ 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;
+ int short_seek;
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 is the absolute position that the beginning of s->buffer corresponds to in the file
+ pos = s->pos - (s->write_flag ? 0 : buffer_size);
if (whence != SEEK_CUR && whence != SEEK_SET)
return AVERROR(EINVAL);
@@ -267,42 +264,73 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
offset1 = pos + (s->buf_ptr - s->buffer);
if (offset == 0)
return offset1;
+ if (offset > INT64_MAX - offset1)
+ return AVERROR(EINVAL);
offset += offset1;
}
- offset1 = offset - pos;
- if (!s->must_flush &&
- offset1 >= 0 && offset1 < (s->buf_end - s->buffer)) {
+ if (offset < 0)
+ return AVERROR(EINVAL);
+
+ if (s->short_seek_get) {
+ short_seek = s->short_seek_get(s->opaque);
+ /* fallback to default short seek */
+ if (short_seek <= 0)
+ short_seek = s->short_seek_threshold;
+ } else
+ short_seek = s->short_seek_threshold;
+
+ offset1 = offset - pos; // "offset1" is the relative offset from the beginning of s->buffer
+ s->buf_ptr_max = FFMAX(s->buf_ptr_max, s->buf_ptr);
+ if ((!s->direct || !s->seek) &&
+ offset1 >= 0 && offset1 <= (s->write_flag ? s->buf_ptr_max - s->buffer : buffer_size)) {
/* can do the seek inside the buffer */
s->buf_ptr = s->buffer + offset1;
} else if ((!(s->seekable & AVIO_SEEKABLE_NORMAL) ||
- offset1 <= s->buf_end + SHORT_SEEK_THRESHOLD - s->buffer) &&
+ offset1 <= buffer_size + short_seek) &&
!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 {
+ s->buf_ptr = s->buf_end - (s->pos - offset);
+ } 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;
}
if (!s->seek)
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;
+ s->buf_ptr = s->buf_ptr_max = s->buffer;
s->pos = offset;
}
s->eof_reached = 0;
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;
@@ -325,20 +353,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)
@@ -352,26 +398,44 @@ int avio_put_str(AVIOContext *s, const char *str)
return len;
}
-#define PUT_STR16(type, write) \
- int avio_put_str16 ## type(AVIOContext * s, const char *str) \
- { \
- const uint8_t *q = str; \
- int ret = 0; \
- \
- while (*q) { \
- uint32_t ch; \
- uint16_t tmp; \
- \
- GET_UTF8(ch, *q++, break; ) \
- PUT_UTF16(ch, tmp, write(s, tmp); ret += 2; ) \
- } \
- write(s, 0); \
- ret += 2; \
- return ret; \
+static inline int put_str16(AVIOContext *s, const char *str, const int be)
+{
+ const uint8_t *q = str;
+ int ret = 0;
+ int err = 0;
+
+ while (*q) {
+ uint32_t ch;
+ uint16_t tmp;
+
+ GET_UTF8(ch, *q++, goto invalid;)
+ PUT_UTF16(ch, tmp, be ? avio_wb16(s, tmp) : avio_wl16(s, tmp);
+ ret += 2;)
+ continue;
+invalid:
+ av_log(s, AV_LOG_ERROR, "Invalid UTF8 sequence in avio_put_str16%s\n", be ? "be" : "le");
+ err = AVERROR(EINVAL);
+ if (!*(q-1))
+ break;
}
+ if (be)
+ avio_wb16(s, 0);
+ else
+ avio_wl16(s, 0);
+ if (err)
+ return err;
+ ret += 2;
+ return ret;
+}
+
+#define PUT_STR16(type, big_endian) \
+int avio_put_str16 ## type(AVIOContext *s, const char *str) \
+{ \
+return put_str16(s, str, big_endian); \
+}
-PUT_STR16(le, avio_wl16)
-PUT_STR16(be, avio_wb16)
+PUT_STR16(le, 0)
+PUT_STR16(be, 1)
#undef PUT_STR16
@@ -390,7 +454,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);
}
@@ -409,30 +473,35 @@ 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);
}
void avio_write_marker(AVIOContext *s, int64_t time, enum AVIODataMarkerType type)
{
+ if (type == AVIO_DATA_MARKER_FLUSH_POINT) {
+ if (s->buf_ptr - s->buffer >= s->min_packet_size)
+ avio_flush(s);
+ return;
+ }
if (!s->write_data_type)
return;
// If ignoring boundary points, just treat it as unknown
@@ -466,12 +535,11 @@ void avio_write_marker(AVIOContext *s, int64_t time, enum AVIODataMarkerType typ
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)
@@ -489,27 +557,34 @@ 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 && s->buf_ptr != dst) {
+ int ret = ffio_set_buf_size(s, s->orig_buffer_size);
+ if (ret < 0)
+ av_log(s, AV_LOG_WARNING, "Failed to decrease buffer size\n");
- 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)
len = s->read_packet(s->opaque, dst, len);
else
- len = 0;
- if (len <= 0) {
+ len = AVERROR_EOF;
+ if (len == AVERROR_EOF) {
/* do not modify buffer if EOF reached so that a seek back can
be done without rereading data */
s->eof_reached = 1;
- if (len < 0)
- s->error = len;
+ } else if (len < 0) {
+ s->eof_reached = 1;
+ s->error= len;
} else {
s->pos += len;
s->buf_ptr = dst;
s->buf_end = dst + len;
+ s->bytes_read += len;
}
}
@@ -519,6 +594,12 @@ unsigned long ff_crc04C11DB7_update(unsigned long checksum, const uint8_t *buf,
return av_crc(av_crc_get_table(AV_CRC_32_IEEE), checksum, buf, len);
}
+unsigned long ff_crcEDB88320_update(unsigned long checksum, const uint8_t *buf,
+ unsigned int len)
+{
+ return av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE), checksum, buf, len);
+}
+
unsigned long ff_crcA001_update(unsigned long checksum, const uint8_t *buf,
unsigned int len)
{
@@ -560,24 +641,29 @@ int avio_read(AVIOContext *s, unsigned char *buf, int size)
size1 = size;
while (size > 0) {
- len = s->buf_end - s->buf_ptr;
- if (len > size)
- len = size;
+ len = FFMIN(s->buf_end - s->buf_ptr, 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) {
+ // bypass the buffer and read data directly into buf
if(s->read_packet)
len = s->read_packet(s->opaque, buf, size);
- if (len <= 0) {
+ else
+ len = AVERROR_EOF;
+ if (len == AVERROR_EOF) {
/* do not modify buffer if EOF reached so that a seek back can
be done without rereading data */
s->eof_reached = 1;
- if(len<0)
- s->error= len;
+ break;
+ } else if (len < 0) {
+ s->eof_reached = 1;
+ s->error= len;
break;
} else {
s->pos += len;
+ s->bytes_read += len;
size -= len;
buf += len;
+ // reset the buffer
s->buf_ptr = s->buffer;
s->buf_end = s->buffer/* + len*/;
}
@@ -595,8 +681,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;
}
@@ -621,7 +707,7 @@ int ffio_read_indirect(AVIOContext *s, unsigned char *buf, int size, const unsig
}
}
-int ffio_read_partial(AVIOContext *s, unsigned char *buf, int size)
+int avio_read_partial(AVIOContext *s, unsigned char *buf, int size)
{
int len;
@@ -652,8 +738,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;
}
@@ -722,7 +808,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;
@@ -807,6 +895,12 @@ static int64_t io_seek(void *opaque, int64_t offset, int whence)
return ffurl_seek(internal->h, offset, whence);
}
+static int io_short_seek(void *opaque)
+{
+ AVIOInternal *internal = opaque;
+ return ffurl_get_short_seek(internal->h);
+}
+
static int io_read_pause(void *opaque, int pause)
{
AVIOInternal *internal = opaque;
@@ -843,18 +937,28 @@ int ffio_fdopen(AVIOContext **s, URLContext *h)
if (!internal)
goto fail;
- internal->class = &io_priv_class;
internal->h = h;
- av_opt_set_defaults(internal);
-
*s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE,
internal, io_read_packet, io_write_packet, io_seek);
if (!*s)
goto fail;
+ (*s)->protocol_whitelist = av_strdup(h->protocol_whitelist);
+ if (!(*s)->protocol_whitelist && h->protocol_whitelist) {
+ avio_closep(s);
+ goto fail;
+ }
+ (*s)->protocol_blacklist = av_strdup(h->protocol_blacklist);
+ if (!(*s)->protocol_blacklist && h->protocol_blacklist) {
+ avio_closep(s);
+ goto fail;
+ }
+ (*s)->direct = h->flags & AVIO_FLAG_DIRECT;
+
(*s)->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL;
(*s)->max_packet_size = max_packet_size;
+ (*s)->min_packet_size = h->min_packet_size;
if(h->prot) {
(*s)->read_pause = io_read_pause;
(*s)->read_seek = io_read_seek;
@@ -862,16 +966,44 @@ int ffio_fdopen(AVIOContext **s, URLContext *h)
if (h->prot->url_read_seek)
(*s)->seekable |= AVIO_SEEKABLE_TIME;
}
+ (*s)->short_seek_get = io_short_seek;
(*s)->av_class = &ff_avio_class;
return 0;
fail:
- if (internal)
- av_opt_free(internal);
av_freep(&internal);
av_freep(&buffer);
return AVERROR(ENOMEM);
}
+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;
+ ptrdiff_t checksum_ptr_offset = s->checksum_ptr ? s->checksum_ptr - s->buffer : -1;
+
+ buf_size += s->buf_ptr - s->buffer + max_buffer_size;
+
+ if (buf_size < filled || s->seekable || !s->read_packet)
+ 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;
+ if (checksum_ptr_offset >= 0)
+ s->checksum_ptr = s->buffer + checksum_ptr_offset;
+ return 0;
+}
+
int ffio_set_buf_size(AVIOContext *s, int buf_size)
{
uint8_t *buffer;
@@ -881,15 +1013,16 @@ 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;
+ s->buf_ptr = s->buf_ptr_max = buffer;
url_resetbuf(s, s->write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ);
return 0;
}
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;
@@ -901,27 +1034,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) {
@@ -935,7 +1073,6 @@ int ffio_rewind_with_probe_data(AVIOContext *s, unsigned char *buf, int buf_size
s->pos = buf_size;
s->buf_end = s->buf_ptr + buf_size;
s->eof_reached = 0;
- s->must_flush = 0;
return 0;
}
@@ -945,54 +1082,35 @@ int avio_open(AVIOContext **s, const char *filename, int flags)
return avio_open2(s, filename, flags, NULL, NULL);
}
-int avio_open2(AVIOContext **s, const char *filename, int flags,
- const AVIOInterruptCB *int_cb, AVDictionary **options)
+int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,
+ const AVIOInterruptCB *int_cb, AVDictionary **options,
+ const char *whitelist, const char *blacklist
+ )
{
- AVIOInternal *internal;
- const URLProtocol **protocols;
- char *proto_whitelist = NULL, *proto_blacklist = NULL;
- AVDictionaryEntry *e;
URLContext *h;
int err;
- if (options) {
- e = av_dict_get(*options, "protocol_whitelist", NULL, 0);
- if (e)
- proto_whitelist = e->value;
- e = av_dict_get(*options, "protocol_blacklist", NULL, 0);
- if (e)
- proto_blacklist = e->value;
- }
-
- protocols = ffurl_get_protocols(proto_whitelist, proto_blacklist);
- if (!protocols)
- return AVERROR(ENOMEM);
-
- err = ffurl_open(&h, filename, flags, int_cb, options, protocols, NULL);
- if (err < 0) {
- av_freep(&protocols);
+ err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);
+ if (err < 0)
return err;
- }
-
err = ffio_fdopen(s, h);
if (err < 0) {
ffurl_close(h);
- av_freep(&protocols);
return err;
}
+ return 0;
+}
- internal = (*s)->opaque;
- internal->protocols = protocols;
-
- if (options) {
- err = av_opt_set_dict(internal, options);
- if (err < 0) {
- avio_closep(s);
- return err;
- }
- }
+int avio_open2(AVIOContext **s, const char *filename, int flags,
+ const AVIOInterruptCB *int_cb, AVDictionary **options)
+{
+ return ffio_open_whitelist(s, filename, flags, int_cb, options, NULL, NULL);
+}
- return 0;
+int ffio_open2_wrapper(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags,
+ const AVIOInterruptCB *int_cb, AVDictionary **options)
+{
+ return ffio_open_whitelist(pb, url, flags, int_cb, options, s->protocol_whitelist, s->protocol_blacklist);
}
int avio_close(AVIOContext *s)
@@ -1007,11 +1125,13 @@ int avio_close(AVIOContext *s)
internal = s->opaque;
h = internal->h;
- av_opt_free(internal);
-
- av_freep(&internal->protocols);
av_freep(&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_opt_free(s);
avio_context_free(&s);
@@ -1028,7 +1148,7 @@ int avio_closep(AVIOContext **s)
int avio_printf(AVIOContext *s, const char *fmt, ...)
{
va_list ap;
- char buf[4096];
+ char buf[4096]; /* update doc entry in avio.h if changed */
int ret;
va_start(ap, fmt);
@@ -1064,6 +1184,43 @@ 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;
+}
+
+int avio_accept(AVIOContext *s, AVIOContext **c)
+{
+ int ret;
+ AVIOInternal *internal = s->opaque;
+ URLContext *sc = internal->h;
+ URLContext *cc = NULL;
+ ret = ffurl_accept(sc, &cc);
+ if (ret < 0)
+ return ret;
+ return ffio_fdopen(c, cc);
+}
+
+int avio_handshake(AVIOContext *c)
+{
+ AVIOInternal *internal = c->opaque;
+ URLContext *cc = internal->h;
+ return ffurl_handshake(cc);
+}
+
/* output in a dynamic buffer */
typedef struct DynBuffer {
@@ -1169,6 +1326,23 @@ int ffio_open_dyn_packet_buf(AVIOContext **s, int max_packet_size)
return url_open_dyn_buf_internal(s, max_packet_size);
}
+int avio_get_dyn_buf(AVIOContext *s, uint8_t **pbuffer)
+{
+ DynBuffer *d;
+
+ if (!s) {
+ *pbuffer = NULL;
+ return 0;
+ }
+
+ avio_flush(s);
+
+ d = s->opaque;
+ *pbuffer = d->buffer;
+
+ return d->size;
+}
+
int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer)
{
DynBuffer *d;
diff --git a/libavformat/avisynth.c b/libavformat/avisynth.c
index d0fe0fd..5670028 100644
--- a/libavformat/avisynth.c
+++ b/libavformat/avisynth.c
@@ -2,20 +2,20 @@
* AviSynth/AvxSynth support
* Copyright (c) 2012 AvxSynth Team
*
- * This file is part of Libav.
+ * This file is part of FFmpeg
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free 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,20 +32,17 @@
#define AVSC_NO_DECLSPEC
/* Platform-specific directives for AviSynth vs AvxSynth. */
-#if CONFIG_AVISYNTH
- #include <windows.h>
+#ifdef _WIN32
+ #include "compat/w32dlfcn.h"
#undef EXTERN_C
- #include <avisynth/avisynth_c.h>
+ #include "compat/avisynth/avisynth_c.h"
#define AVISYNTH_LIB "avisynth"
+ #define USING_AVISYNTH
#else
#include <dlfcn.h>
- #include <avxsynth/avxsynth_c.h>
+ #include "compat/avisynth/avxsynth_c.h"
#define AVISYNTH_NAME "libavxsynth"
#define AVISYNTH_LIB AVISYNTH_NAME SLIBSUF
-
- #define LoadLibrary(x) dlopen(x, RTLD_NOW | RTLD_LOCAL)
- #define GetProcAddress dlsym
- #define FreeLibrary dlclose
#endif
typedef struct AviSynthLibrary {
@@ -65,7 +62,7 @@ typedef struct AviSynthLibrary {
AVSC_DECLARE_FUNC(avs_release_value);
AVSC_DECLARE_FUNC(avs_release_video_frame);
AVSC_DECLARE_FUNC(avs_take_clip);
-#if CONFIG_AVISYNTH
+#ifdef USING_AVISYNTH
AVSC_DECLARE_FUNC(avs_bits_per_pixel);
AVSC_DECLARE_FUNC(avs_get_height_p);
AVSC_DECLARE_FUNC(avs_get_pitch_p);
@@ -121,13 +118,12 @@ static av_cold void avisynth_atexit_handler(void);
static av_cold int avisynth_load_library(void)
{
- avs_library.library = LoadLibrary(AVISYNTH_LIB);
+ avs_library.library = dlopen(AVISYNTH_LIB, RTLD_NOW | RTLD_LOCAL);
if (!avs_library.library)
return AVERROR_UNKNOWN;
#define LOAD_AVS_FUNC(name, continue_on_fail) \
- avs_library.name = (name ## _func) \
- GetProcAddress(avs_library.library, #name); \
+ avs_library.name = dlsym(avs_library.library, #name); \
if (!continue_on_fail && !avs_library.name) \
goto fail;
@@ -145,7 +141,7 @@ static av_cold int avisynth_load_library(void)
LOAD_AVS_FUNC(avs_release_value, 0);
LOAD_AVS_FUNC(avs_release_video_frame, 0);
LOAD_AVS_FUNC(avs_take_clip, 0);
-#if CONFIG_AVISYNTH
+#ifdef USING_AVISYNTH
LOAD_AVS_FUNC(avs_bits_per_pixel, 1);
LOAD_AVS_FUNC(avs_get_height_p, 1);
LOAD_AVS_FUNC(avs_get_pitch_p, 1);
@@ -160,7 +156,7 @@ static av_cold int avisynth_load_library(void)
return 0;
fail:
- FreeLibrary(avs_library.library);
+ dlclose(avs_library.library);
return AVERROR_UNKNOWN;
}
@@ -228,7 +224,7 @@ static av_cold void avisynth_atexit_handler(void)
avisynth_context_destroy(avs);
avs = next;
}
- FreeLibrary(avs_library.library);
+ dlclose(avs_library.library);
avs_atexit_called = 1;
}
@@ -252,7 +248,7 @@ static int avisynth_create_stream_video(AVFormatContext *s, AVStream *st)
avpriv_set_pts_info(st, 32, avs->vi->fps_denominator, avs->vi->fps_numerator);
switch (avs->vi->pixel_type) {
-#if CONFIG_AVISYNTH
+#ifdef USING_AVISYNTH
/* 10~16-bit YUV pix_fmts (AviSynth+) */
case AVS_CS_YUV444P10:
st->codecpar->format = AV_PIX_FMT_YUV444P10;
@@ -278,6 +274,18 @@ static int avisynth_create_stream_video(AVFormatContext *s, AVStream *st)
st->codecpar->format = AV_PIX_FMT_YUV420P12;
planar = 1;
break;
+ case AVS_CS_YUV444P14:
+ st->codecpar->format = AV_PIX_FMT_YUV444P14;
+ planar = 1;
+ break;
+ case AVS_CS_YUV422P14:
+ st->codecpar->format = AV_PIX_FMT_YUV422P14;
+ planar = 1;
+ break;
+ case AVS_CS_YUV420P14:
+ st->codecpar->format = AV_PIX_FMT_YUV420P14;
+ planar = 1;
+ break;
case AVS_CS_YUV444P16:
st->codecpar->format = AV_PIX_FMT_YUV444P16;
planar = 1;
@@ -340,6 +348,10 @@ static int avisynth_create_stream_video(AVFormatContext *s, AVStream *st)
st->codecpar->format = AV_PIX_FMT_GBRP12;
planar = 3;
break;
+ case AVS_CS_RGBP14:
+ st->codecpar->format = AV_PIX_FMT_GBRP14;
+ planar = 3;
+ break;
case AVS_CS_RGBP16:
st->codecpar->format = AV_PIX_FMT_GBRP16;
planar = 3;
@@ -349,6 +361,10 @@ static int avisynth_create_stream_video(AVFormatContext *s, AVStream *st)
st->codecpar->format = AV_PIX_FMT_GBRAP;
planar = 5;
break;
+ case AVS_CS_RGBAP10:
+ st->codecpar->format = AV_PIX_FMT_GBRAP10;
+ planar = 5;
+ break;
case AVS_CS_RGBAP12:
st->codecpar->format = AV_PIX_FMT_GBRAP12;
planar = 5;
@@ -508,7 +524,7 @@ static int avisynth_open_file(AVFormatContext *s)
AviSynthContext *avs = s->priv_data;
AVS_Value arg, val;
int ret;
-#if CONFIG_AVISYNTH
+#ifdef USING_AVISYNTH
char filename_ansi[MAX_PATH * 4];
wchar_t filename_wc[MAX_PATH * 4];
#endif
@@ -516,7 +532,7 @@ static int avisynth_open_file(AVFormatContext *s)
if (ret = avisynth_context_create(s))
return ret;
-#if CONFIG_AVISYNTH
+#ifdef USING_AVISYNTH
/* Convert UTF-8 to ANSI code page */
MultiByteToWideChar(CP_UTF8, 0, s->filename, -1, filename_wc, MAX_PATH * 4);
WideCharToMultiByte(CP_THREAD_ACP, 0, filename_wc, -1, filename_ansi,
@@ -540,8 +556,8 @@ 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);
-#if CONFIG_AVISYNTH
- /* On Windows, libav supports AviSynth interface version 6 or higher.
+#ifdef USING_AVISYNTH
+ /* On Windows, FFmpeg supports AviSynth interface version 6 or higher.
* This includes AviSynth 2.6 RC1 or higher, and AviSynth+ r1718 or higher,
* and excludes 2.5 and the 2.6 alphas. Since AvxSynth identifies itself
* as interface version 3 like 2.5.8, this needs to be special-cased. */
@@ -604,7 +620,7 @@ static int avisynth_read_packet_video(AVFormatContext *s, AVPacket *pkt,
if (discard)
return 0;
-#if CONFIG_AVISYNTH
+#ifdef USING_AVISYNTH
/* Detect whether we're using AviSynth 2.6 or AviSynth+ by
* looking for whether avs_is_planar_rgb exists. */
if (GetProcAddress(avs_library.library, "avs_is_planar_rgb") == NULL)
@@ -648,7 +664,7 @@ static int avisynth_read_packet_video(AVFormatContext *s, AVPacket *pkt,
dst_p = pkt->data;
for (i = 0; i < avs->n_planes; i++) {
plane = avs->planes[i];
-#if CONFIG_AVISYNTH
+#ifdef USING_AVISYNTH
src_p = avs_library.avs_get_read_ptr_p(frame, plane);
pitch = avs_library.avs_get_pitch_p(frame, plane);
diff --git a/libavformat/avlanguage.c b/libavformat/avlanguage.c
index e606ef2..f5d2ddf 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
*/
@@ -733,7 +733,7 @@ static int lang_table_compare(const void *lhs, const void *rhs)
return strcmp(lhs, ((const LangEntry *)rhs)->str);
}
-const char *av_convert_lang_to(const char *lang, enum AVLangCodespace target_codespace)
+const char *ff_convert_lang_to(const char *lang, enum AVLangCodespace target_codespace)
{
int i;
const LangEntry *entry = NULL;
@@ -759,7 +759,14 @@ const char *av_convert_lang_to(const char *lang, enum AVLangCodespace target_cod
entry = lang_table + entry->next_equivalent;
if (target_codespace == AV_LANG_ISO639_2_TERM)
- return av_convert_lang_to(lang, AV_LANG_ISO639_2_BIBL);
+ return ff_convert_lang_to(lang, AV_LANG_ISO639_2_BIBL);
return NULL;
}
+
+#if LIBAVFORMAT_VERSION_MAJOR < 58
+const char *av_convert_lang_to(const char *lang, enum AVLangCodespace target_codespace)
+{
+ return ff_convert_lang_to(lang, target_codespace);
+}
+#endif
diff --git a/libavformat/avlanguage.h b/libavformat/avlanguage.h
index 128fb1c..1d72dcb 100644
--- a/libavformat/avlanguage.h
+++ b/libavformat/avlanguage.h
@@ -1,26 +1,29 @@
/*
* 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
*/
#ifndef AVFORMAT_AVLANGUAGE_H
#define AVFORMAT_AVLANGUAGE_H
+#include "libavutil/attributes.h"
+#include "libavformat/version.h"
+
/**
* Known language codespaces
*/
@@ -34,6 +37,10 @@ enum AVLangCodespace {
* Convert a language code to a target codespace. The source codespace is guessed.
* @return NULL if the provided lang is null or invalid.
*/
+const char *ff_convert_lang_to(const char *lang, enum AVLangCodespace target_codespace);
+#if LIBAVFORMAT_VERSION_MAJOR < 58
+attribute_deprecated
const char *av_convert_lang_to(const char *lang, enum AVLangCodespace target_codespace);
+#endif
#endif /* AVFORMAT_AVLANGUAGE_H */
diff --git a/libavformat/avr.c b/libavformat/avr.c
new file mode 100644
index 0000000..294160e
--- /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->codecpar->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->codecpar->channels = 1;
+ } else if (chan == 0xFFFFu) {
+ st->codecpar->channels = 2;
+ } else {
+ avpriv_request_sample(s, "chan %d", chan);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ st->codecpar->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->codecpar->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->codecpar->codec_id = ff_get_pcm_codec_id(bps, 0, 1, sign);
+ if (st->codecpar->codec_id == AV_CODEC_ID_NONE) {
+ avpriv_request_sample(s, "Bps %d and sign %d", bps, sign);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ st->codecpar->block_align = bps * st->codecpar->channels / 8;
+
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->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 c825a3a..763ba63 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->codecpar->height = avs->height;
avs->st_video->codecpar->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 8ed9d4f..f516806 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 99371bd..6c98e33 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,17 +81,25 @@ static int bfi_read_header(AVFormatContext * s)
/*Load the palette to extradata */
avio_skip(pb, 8);
vstream->codecpar->extradata = av_malloc(768);
+ if (!vstream->codecpar->extradata)
+ return AVERROR(ENOMEM);
vstream->codecpar->extradata_size = 768;
avio_read(pb, vstream->codecpar->extradata,
- vstream->codecpar->extradata_size);
+ vstream->codecpar->extradata_size);
astream->codecpar->sample_rate = avio_rl32(pb);
+ if (astream->codecpar->sample_rate <= 0) {
+ av_log(s, AV_LOG_ERROR, "Invalid sample rate %d\n", astream->codecpar->sample_rate);
+ return AVERROR_INVALIDDATA;
+ }
/* Set up the video codec... */
avpriv_set_pts_info(vstream, 32, 1, fps);
vstream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
vstream->codecpar->codec_id = AV_CODEC_ID_BFI;
vstream->codecpar->format = AV_PIX_FMT_PAL8;
+ vstream->nb_frames =
+ vstream->duration = bfi->nframes;
/* Set up the audio codec now... */
astream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
@@ -100,7 +108,7 @@ static int bfi_read_header(AVFormatContext * s)
astream->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
astream->codecpar->bits_per_coded_sample = 8;
astream->codecpar->bit_rate =
- astream->codecpar->sample_rate * astream->codecpar->bits_per_coded_sample;
+ (int64_t)astream->codecpar->sample_rate * astream->codecpar->bits_per_coded_sample;
avio_seek(pb, chunk_header - 3, SEEK_SET);
avpriv_set_pts_info(astream, 64, 1, astream->codecpar->sample_rate);
return 0;
@@ -112,15 +120,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 8f28212..8a05082 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
*/
@@ -45,6 +45,7 @@ enum BinkAudFlags {
#define BINK_MAX_AUDIO_TRACKS 256
#define BINK_MAX_WIDTH 7680
#define BINK_MAX_HEIGHT 4800
+#define SMUSH_BLOCK_SIZE 512
typedef struct BinkDemuxContext {
uint32_t file_size;
@@ -55,19 +56,28 @@ typedef struct BinkDemuxContext {
int64_t audio_pts[BINK_MAX_AUDIO_TRACKS];
uint32_t remain_packet_size;
+ int smush_size;
} BinkDemuxContext;
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') &&
- 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 &&
- AV_RL32(b+28) > 0 && AV_RL32(b+32) > 0) // fps num,den
- return AVPROBE_SCORE_MAX;
+ int smush = AV_RN32(p->buf) == AV_RN32("SMUS");
+
+ do {
+ if (((b[0] == 'B' && b[1] == 'I' && b[2] == 'K' && /* Bink 1 */
+ (b[3] == 'b' || b[3] == 'f' || b[3] == 'g' || b[3] == 'h' || b[3] == 'i' ||
+ b[3] == 'k')) ||
+ (b[0] == 'K' && b[1] == 'B' && b[2] == '2' && /* Bink 2 */
+ (b[3] == 'a' || b[3] == 'd' || b[3] == 'f' || b[3] == 'g' || b[3] == 'h' ||
+ b[3] == 'i' || b[3] == 'j' || b[3] == 'k'))) &&
+ 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 &&
+ AV_RL32(b+28) > 0 && AV_RL32(b+32) > 0) // fps num,den
+ return AVPROBE_SCORE_MAX;
+ b += SMUSH_BLOCK_SIZE;
+ } while (smush && b < p->buf + p->buf_size - 32);
return 0;
}
@@ -81,12 +91,24 @@ 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)
return AVERROR(ENOMEM);
vst->codecpar->codec_tag = avio_rl32(pb);
+ if (vst->codecpar->codec_tag == AV_RL32("SMUS")) {
+ do {
+ bink->smush_size += SMUSH_BLOCK_SIZE;
+ avio_skip(pb, SMUSH_BLOCK_SIZE - 4);
+ vst->codecpar->codec_tag = avio_rl32(pb);
+ } while (!avio_feof(pb) && (vst->codecpar->codec_tag & 0xFFFFFF) != AV_RL32("BIK"));
+ if (avio_feof(pb)) {
+ av_log(s, AV_LOG_ERROR, "invalid SMUSH header: BIK not found\n");
+ return AVERROR_INVALIDDATA;
+ }
+ }
bink->file_size = avio_rl32(pb) + 8;
vst->duration = avio_rl32(pb);
@@ -120,11 +142,14 @@ static int read_header(AVFormatContext *s)
vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
vst->codecpar->codec_id = AV_CODEC_ID_BINKVIDEO;
- vst->codecpar->extradata = av_mallocz(4 + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!vst->codecpar->extradata)
+
+ if ((vst->codecpar->codec_tag & 0xFFFFFF) == MKTAG('K', 'B', '2', 0)) {
+ av_log(s, AV_LOG_WARNING, "Bink 2 video is not implemented\n");
+ vst->codecpar->codec_id = AV_CODEC_ID_NONE;
+ }
+
+ if (ff_get_extradata(s, vst->codecpar, pb, 4) < 0)
return AVERROR(ENOMEM);
- vst->codecpar->extradata_size = 4;
- avio_read(pb, vst->codecpar->extradata, 4);
bink->num_audio_tracks = avio_rl32(pb);
@@ -136,7 +161,14 @@ static int read_header(AVFormatContext *s)
}
if (bink->num_audio_tracks) {
- avio_skip(pb, 4 * bink->num_audio_tracks);
+ uint32_t signature = (vst->codecpar->codec_tag & 0xFFFFFF);
+ uint8_t revision = ((vst->codecpar->codec_tag >> 24) % 0xFF);
+
+ if ((signature == AV_RL32("BIK") && (revision == 0x6b)) || /* k */
+ (signature == AV_RL32("KB2") && (revision == 0x69 || revision == 0x6a || revision == 0x6b))) /* i,j,k */
+ avio_skip(pb, 4); /* unknown new field */
+
+ avio_skip(pb, 4 * bink->num_audio_tracks); /* max decoded size */
for (i = 0; i < bink->num_audio_tracks; i++) {
ast = avformat_new_stream(s, NULL);
@@ -156,10 +188,8 @@ static int read_header(AVFormatContext *s)
ast->codecpar->channels = 1;
ast->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
}
- ast->codecpar->extradata = av_mallocz(4 + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!ast->codecpar->extradata)
+ if (ff_alloc_extradata(ast->codecpar, 4))
return AVERROR(ENOMEM);
- ast->codecpar->extradata_size = 4;
AV_WL32(ast->codecpar->extradata, vst->codecpar->codec_tag);
}
@@ -185,11 +215,15 @@ 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);
+ if (vst->index_entries)
+ avio_seek(pb, vst->index_entries[0].pos + bink->smush_size, SEEK_SET);
+ else
+ avio_skip(pb, 4);
bink->current_track = -1;
return 0;
@@ -206,7 +240,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);
@@ -271,7 +305,7 @@ static int read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, in
return -1;
/* seek to the first frame */
- if (avio_seek(s->pb, vst->index_entries[0].pos, SEEK_SET) < 0)
+ if (avio_seek(s->pb, vst->index_entries[0].pos + bink->smush_size, SEEK_SET) < 0)
return -1;
bink->video_pts = 0;
@@ -288,4 +322,5 @@ AVInputFormat ff_bink_demuxer = {
.read_header = read_header,
.read_packet = read_packet,
.read_seek = read_seek,
+ .flags = AVFMT_SHOW_IDS,
};
diff --git a/libavformat/bintext.c b/libavformat/bintext.c
new file mode 100644
index 0000000..12e3bfd
--- /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->codecpar->codec_tag = 0;
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+
+ if (!bin->width) {
+ st->codecpar->width = (80<<3);
+ st->codecpar->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(AVCodecParameters *par, uint64_t fsize)
+{
+ par->height = (fsize / ((par->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(AVCodecParameters *par, uint64_t fsize, int got_width)
+{
+ /** attempt to guess width */
+ if (!got_width)
+ par->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->codecpar->codec_id = AV_CODEC_ID_BINTEXT;
+
+ if (ff_alloc_extradata(st->codecpar, 2))
+ return AVERROR(ENOMEM);
+ st->codecpar->extradata[0] = 16;
+ st->codecpar->extradata[1] = 0;
+
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ 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->codecpar, bin->fsize, got_width);
+ calculate_height(st->codecpar, 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->codecpar->width = avio_rl16(pb)<<3;
+ st->codecpar->height = avio_rl16(pb);
+ fontheight = avio_r8(pb);
+ st->codecpar->height *= fontheight;
+ flags = avio_r8(pb);
+
+ st->codecpar->extradata_size = 2;
+ if ((flags & BINTEXT_PALETTE))
+ st->codecpar->extradata_size += 48;
+ if ((flags & BINTEXT_FONT))
+ st->codecpar->extradata_size += fontheight * (flags & 0x10 ? 512 : 256);
+ st->codecpar->codec_id = flags & 4 ? AV_CODEC_ID_XBIN : AV_CODEC_ID_BINTEXT;
+
+ if (ff_alloc_extradata(st->codecpar, st->codecpar->extradata_size))
+ return AVERROR(ENOMEM);
+ st->codecpar->extradata[0] = fontheight;
+ st->codecpar->extradata[1] = flags;
+ if (avio_read(pb, st->codecpar->extradata + 2, st->codecpar->extradata_size - 2) < 0)
+ return AVERROR(EIO);
+
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ bin->fsize = avio_size(pb) - 9 - st->codecpar->extradata_size;
+ ff_sauce_read(s, &bin->fsize, NULL, 0);
+ avio_seek(pb, 9 + st->codecpar->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->codecpar->codec_id = AV_CODEC_ID_BINTEXT;
+
+ if (ff_alloc_extradata(st->codecpar, 2 + 48 + 4096))
+ return AVERROR(ENOMEM);
+ st->codecpar->extradata[0] = 16;
+ st->codecpar->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT;
+
+ if (avio_read(pb, st->codecpar->extradata + 2, 24) < 0)
+ return AVERROR(EIO);
+ avio_skip(pb, 144);
+ if (avio_read(pb, st->codecpar->extradata + 2 + 24, 24) < 0)
+ return AVERROR(EIO);
+ if (avio_read(pb, st->codecpar->extradata + 2 + 48, 4096) < 0)
+ return AVERROR(EIO);
+
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ int got_width = 0;
+ bin->fsize = avio_size(pb) - 1 - 192 - 4096;
+ st->codecpar->width = 80<<3;
+ ff_sauce_read(s, &bin->fsize, &got_width, 0);
+ if (!bin->width)
+ calculate_height(st->codecpar, 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 & AVIO_SEEKABLE_NORMAL))
+ return AVERROR(EIO);
+
+ st = init_stream(s);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codecpar->codec_id = AV_CODEC_ID_IDF;
+
+ if (ff_alloc_extradata(st->codecpar, 2 + 48 + 4096))
+ return AVERROR(ENOMEM);
+ st->codecpar->extradata[0] = 16;
+ st->codecpar->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT;
+
+ avio_seek(pb, avio_size(pb) - 4096 - 48, SEEK_SET);
+
+ if (avio_read(pb, st->codecpar->extradata + 2 + 48, 4096) < 0)
+ return AVERROR(EIO);
+ if (avio_read(pb, st->codecpar->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->codecpar, 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, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+#define CLASS(name) \
+(const AVClass[1]){{ \
+ .class_name = name, \
+ .item_name = av_default_item_name, \
+ .option = options, \
+ .version = LIBAVUTIL_VERSION_INT, \
+}}
+
+#if CONFIG_BINTEXT_DEMUXER
+AVInputFormat ff_bintext_demuxer = {
+ .name = "bin",
+ .long_name = NULL_IF_CONFIG_SMALL("Binary text"),
+ .priv_data_size = sizeof(BinDemuxContext),
+ .read_header = bintext_read_header,
+ .read_packet = read_packet,
+ .extensions = "bin",
+ .priv_class = CLASS("Binary text demuxer"),
+};
+#endif
+
+#if CONFIG_XBIN_DEMUXER
+AVInputFormat ff_xbin_demuxer = {
+ .name = "xbin",
+ .long_name = NULL_IF_CONFIG_SMALL("eXtended BINary text (XBIN)"),
+ .priv_data_size = sizeof(BinDemuxContext),
+ .read_probe = xbin_probe,
+ .read_header = xbin_read_header,
+ .read_packet = read_packet,
+ .priv_class = CLASS("eXtended BINary text (XBIN) demuxer"),
+};
+#endif
+
+#if CONFIG_ADF_DEMUXER
+AVInputFormat ff_adf_demuxer = {
+ .name = "adf",
+ .long_name = NULL_IF_CONFIG_SMALL("Artworx Data Format"),
+ .priv_data_size = sizeof(BinDemuxContext),
+ .read_header = adf_read_header,
+ .read_packet = read_packet,
+ .extensions = "adf",
+ .priv_class = CLASS("Artworx Data Format demuxer"),
+};
+#endif
+
+#if CONFIG_IDF_DEMUXER
+AVInputFormat ff_idf_demuxer = {
+ .name = "idf",
+ .long_name = NULL_IF_CONFIG_SMALL("iCE Draw File"),
+ .priv_data_size = sizeof(BinDemuxContext),
+ .read_probe = idf_probe,
+ .read_header = idf_read_header,
+ .read_packet = read_packet,
+ .extensions = "idf",
+ .priv_class = CLASS("iCE Draw File demuxer"),
+};
+#endif
diff --git a/libavformat/bit.c b/libavformat/bit.c
new file mode 100644
index 0000000..76aae2d
--- /dev/null
+++ b/libavformat/bit.c
@@ -0,0 +1,168 @@
+/*
+ * 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
+
+#if CONFIG_BIT_DEMUXER
+static int probe(AVProbeData *p)
+{
+ int i = 0, j, valid = 0;
+
+ while (2 * i + 3 < p->buf_size){
+ if (AV_RL16(&p->buf[2 * i++]) != SYNC_WORD)
+ return 0;
+ j = AV_RL16(&p->buf[2 * i++]);
+ if (j != 0 && j != 0x10 && j != 0x40 && j != 0x50 && j != 0x76)
+ return 0;
+ if (j)
+ valid++;
+ i += j;
+ }
+ if (valid > 10)
+ return AVPROBE_SCORE_MAX;
+ if (valid > 2)
+ return AVPROBE_SCORE_EXTENSION - 1;
+ return 0;
+}
+
+static int read_header(AVFormatContext *s)
+{
+ AVStream* st;
+
+ st=avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id=AV_CODEC_ID_G729;
+ st->codecpar->sample_rate=8000;
+ st->codecpar->block_align = 16;
+ st->codecpar->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",
+};
+#endif
+
+#if CONFIG_BIT_MUXER
+static int write_header(AVFormatContext *s)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+
+ if ((par->codec_id != AV_CODEC_ID_G729) || par->channels != 1) {
+ av_log(s, AV_LOG_ERROR,
+ "only codec g729 with 1 channel is supported by this format\n");
+ return AVERROR(EINVAL);
+ }
+
+ par->bits_per_coded_sample = 16;
+ par->block_align = (par->bits_per_coded_sample * par->channels) >> 3;
+
+ return 0;
+}
+
+static int write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVIOContext *pb = s->pb;
+ GetBitContext gb;
+ int i;
+
+ if (pkt->size != 10)
+ return AVERROR(EINVAL);
+
+ avio_wl16(pb, SYNC_WORD);
+ avio_wl16(pb, 8 * pkt->size);
+
+ init_get_bits(&gb, pkt->data, 8 * pkt->size);
+ for (i = 0; i < 8 * pkt->size; i++)
+ avio_wl16(pb, get_bits1(&gb) ? BIT_1 : BIT_0);
+
+ return 0;
+}
+
+AVOutputFormat ff_bit_muxer = {
+ .name = "bit",
+ .long_name = NULL_IF_CONFIG_SMALL("G.729 BIT file format"),
+ .mime_type = "audio/bit",
+ .extensions = "bit",
+ .audio_codec = AV_CODEC_ID_G729,
+ .video_codec = AV_CODEC_ID_NONE,
+ .write_header = write_header,
+ .write_packet = write_packet,
+};
+#endif
diff --git a/libavformat/bluray.c b/libavformat/bluray.c
new file mode 100644
index 0000000..9282bf9
--- /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);
+}
+
+
+const 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 9bc8dda..ac567c2 100644
--- a/libavformat/bmv.c
+++ b/libavformat/bmv.c
@@ -2,20 +2,20 @@
* Discworld II BMV demuxer
* Copyright (c) 2011 Konstantin Shishkov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/boadec.c b/libavformat/boadec.c
new file mode 100644
index 0000000..730e957
--- /dev/null
+++ b/libavformat/boadec.c
@@ -0,0 +1,85 @@
+/*
+ * 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 "libavcodec/internal.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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_MS;
+
+ avio_rl32(s->pb);
+ avio_rl32(s->pb);
+ st->codecpar->sample_rate = avio_rl32(s->pb);
+ st->codecpar->channels = avio_rl32(s->pb);
+ if (st->codecpar->channels > FF_SANE_NB_CHANNELS)
+ return AVERROR(ENOSYS);
+ s->internal->data_offset = avio_rl32(s->pb);
+ avio_r8(s->pb);
+ st->codecpar->block_align = avio_rl32(s->pb);
+ if (st->codecpar->block_align > INT_MAX / FF_SANE_NB_CHANNELS)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->block_align *= st->codecpar->channels;
+
+ 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->codecpar->block_align);
+}
+
+AVInputFormat ff_boa_demuxer = {
+ .name = "boa",
+ .long_name = NULL_IF_CONFIG_SMALL("Black Ops Audio"),
+ .read_probe = probe,
+ .read_header = read_header,
+ .read_packet = read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/brstm.c b/libavformat/brstm.c
new file mode 100644
index 0000000..87690e3
--- /dev/null
+++ b/libavformat/brstm.c
@@ -0,0 +1,483 @@
+/*
+ * 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;
+ uint32_t last_block_size;
+ uint32_t last_block_samples;
+ uint32_t data_start;
+ uint8_t *table;
+ uint8_t *adpc;
+ int little_endian;
+} 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 probe_bfstm(AVProbeData *p)
+{
+ if ((AV_RL32(p->buf) == MKTAG('F','S','T','M') ||
+ AV_RL32(p->buf) == MKTAG('C','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 av_always_inline unsigned int read16(AVFormatContext *s)
+{
+ BRSTMDemuxContext *b = s->priv_data;
+ if (b->little_endian)
+ return avio_rl16(s->pb);
+ else
+ return avio_rb16(s->pb);
+}
+
+static av_always_inline unsigned int read32(AVFormatContext *s)
+{
+ BRSTMDemuxContext *b = s->priv_data;
+ if (b->little_endian)
+ return avio_rl32(s->pb);
+ else
+ return avio_rb32(s->pb);
+}
+
+static int read_header(AVFormatContext *s)
+{
+ BRSTMDemuxContext *b = s->priv_data;
+ int bom, major, minor, codec, chunk;
+ int64_t h1offset, pos, toffset;
+ uint32_t size, asize, start = 0;
+ AVStream *st;
+ int ret = AVERROR_EOF;
+ int loop = 0;
+ int bfstm = !strcmp("bfstm", s->iformat->name);
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codecpar->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)
+ b->little_endian = 1;
+
+ if (!bfstm) {
+ major = avio_r8(s->pb);
+ minor = avio_r8(s->pb);
+ avio_skip(s->pb, 4); // size of file
+ size = read16(s);
+ 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;
+ } else {
+ uint32_t info_offset = 0;
+ uint16_t section_count, header_size, i;
+
+ header_size = read16(s); // 6
+
+ avio_skip(s->pb, 4); // Unknown constant 0x00030000
+ avio_skip(s->pb, 4); // size of file
+ section_count = read16(s);
+ avio_skip(s->pb, 2); // padding
+ for (i = 0; avio_tell(s->pb) < header_size
+ && !(start && info_offset)
+ && i < section_count; i++) {
+ uint16_t flag = read16(s);
+ avio_skip(s->pb, 2);
+ switch (flag) {
+ case 0x4000:
+ info_offset = read32(s);
+ /*info_size =*/ read32(s);
+ break;
+ case 0x4001:
+ avio_skip(s->pb, 4); // seek offset
+ avio_skip(s->pb, 4); // seek size
+ break;
+ case 0x4002:
+ start = read32(s) + 8;
+ avio_skip(s->pb, 4); //data_size = read32(s);
+ break;
+ case 0x4003:
+ avio_skip(s->pb, 4); // REGN offset
+ avio_skip(s->pb, 4); // REGN size
+ break;
+ }
+ }
+
+ if (!info_offset || !start)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(s->pb, info_offset - avio_tell(s->pb));
+ pos = avio_tell(s->pb);
+ if (avio_rl32(s->pb) != MKTAG('I','N','F','O'))
+ return AVERROR_INVALIDDATA;
+ }
+
+ size = read32(s);
+ if (size < 40)
+ return AVERROR_INVALIDDATA;
+ avio_skip(s->pb, 4); // unknown
+ h1offset = read32(s);
+ if (h1offset > size)
+ return AVERROR_INVALIDDATA;
+ avio_skip(s->pb, 12);
+ toffset = read32(s) + 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 = b->little_endian ?
+ AV_CODEC_ID_PCM_S16LE_PLANAR :
+ AV_CODEC_ID_PCM_S16BE_PLANAR; break;
+ case 2: codec = b->little_endian ?
+ AV_CODEC_ID_ADPCM_THP_LE :
+ AV_CODEC_ID_ADPCM_THP; break;
+ default:
+ avpriv_request_sample(s, "codec %d", codec);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ loop = avio_r8(s->pb); // loop flag
+ st->codecpar->codec_id = codec;
+ st->codecpar->channels = avio_r8(s->pb);
+ if (!st->codecpar->channels)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(s->pb, 1); // padding
+
+ st->codecpar->sample_rate = bfstm ? read32(s) : read16(s);
+ if (st->codecpar->sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+
+ if (!bfstm)
+ avio_skip(s->pb, 2); // padding
+
+ if (loop) {
+ if (av_dict_set_int(&s->metadata, "loop_start",
+ av_rescale(read32(s), AV_TIME_BASE,
+ st->codecpar->sample_rate),
+ 0) < 0)
+ return AVERROR(ENOMEM);
+ } else {
+ avio_skip(s->pb, 4);
+ }
+
+ st->start_time = 0;
+ st->duration = read32(s);
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ if (!bfstm)
+ start = read32(s);
+ b->current_block = 0;
+ b->block_count = read32(s);
+ if (b->block_count > UINT16_MAX) {
+ av_log(s, AV_LOG_WARNING, "too many blocks: %"PRIu32"\n", b->block_count);
+ return AVERROR_INVALIDDATA;
+ }
+
+ b->block_size = read32(s);
+ if (b->block_size > UINT32_MAX / st->codecpar->channels)
+ return AVERROR_INVALIDDATA;
+
+ b->samples_per_block = read32(s);
+ b->last_block_used_bytes = read32(s);
+ b->last_block_samples = read32(s);
+ b->last_block_size = read32(s);
+ if (b->last_block_size > UINT32_MAX / st->codecpar->channels)
+ return AVERROR_INVALIDDATA;
+ if (b->last_block_used_bytes > b->last_block_size)
+ return AVERROR_INVALIDDATA;
+
+
+ if (codec == AV_CODEC_ID_ADPCM_THP || codec == AV_CODEC_ID_ADPCM_THP_LE) {
+ int ch;
+
+ avio_skip(s->pb, pos + toffset - avio_tell(s->pb));
+ if (!bfstm)
+ toffset = read32(s) + 16LL;
+ else
+ toffset = toffset + read32(s) + st->codecpar->channels * 8 - 8;
+ if (toffset > size)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(s->pb, pos + toffset - avio_tell(s->pb));
+ b->table = av_mallocz(32 * st->codecpar->channels);
+ if (!b->table)
+ return AVERROR(ENOMEM);
+
+ for (ch = 0; ch < st->codecpar->channels; ch++) {
+ if (avio_read(s->pb, b->table + ch * 32, 32) != 32) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ avio_skip(s->pb, bfstm ? 14 : 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 = read32(s);
+ if (size < 8) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ size -= 8;
+ switch (chunk) {
+ case MKTAG('S','E','E','K'):
+ case MKTAG('A','D','P','C'):
+ if (codec != AV_CODEC_ID_ADPCM_THP &&
+ codec != AV_CODEC_ID_ADPCM_THP_LE)
+ goto skip;
+
+ asize = b->block_count * st->codecpar->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;
+ }
+ if (bfstm && codec != AV_CODEC_ID_ADPCM_THP_LE) {
+ // Big-endian BFSTMs have little-endian SEEK tables
+ // for some strange reason.
+ int i;
+ for (i = 0; i < asize; i += 2) {
+ b->adpc[i+1] = avio_r8(s->pb);
+ b->adpc[i] = avio_r8(s->pb);
+ }
+ } else {
+ 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 ||
+ codec == AV_CODEC_ID_ADPCM_THP_LE))) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ avio_skip(s->pb, start - avio_tell(s->pb));
+
+ if (bfstm && (codec == AV_CODEC_ID_ADPCM_THP ||
+ codec == AV_CODEC_ID_ADPCM_THP_LE))
+ avio_skip(s->pb, 24);
+
+ b->data_start = avio_tell(s->pb);
+
+ if (!bfstm && (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)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ BRSTMDemuxContext *b = s->priv_data;
+ uint32_t samples, size, skip = 0;
+ int ret, i;
+
+ 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 = b->last_block_samples;
+ skip = b->last_block_size - b->last_block_used_bytes;
+
+ if (samples < size * 14 / 8) {
+ uint32_t adjusted_size = samples / 14 * 8;
+ if (samples % 14)
+ adjusted_size += (samples % 14 + 1) / 2 + 1;
+
+ skip += size - adjusted_size;
+ size = adjusted_size;
+ }
+ } else if (b->current_block < b->block_count) {
+ size = b->block_size;
+ samples = b->samples_per_block;
+ } else {
+ return AVERROR_EOF;
+ }
+
+ if (par->codec_id == AV_CODEC_ID_ADPCM_THP ||
+ par->codec_id == AV_CODEC_ID_ADPCM_THP_LE) {
+ uint8_t *dst;
+
+ if (!b->adpc) {
+ av_log(s, AV_LOG_ERROR, "adpcm_thp requires ADPC chunk, but none was found.\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if (!b->table) {
+ b->table = av_mallocz(32 * par->channels);
+ if (!b->table)
+ return AVERROR(ENOMEM);
+ }
+
+ if (size > (INT_MAX - 32 - 4) ||
+ (32 + 4 + size) > (INT_MAX / par->channels) ||
+ (32 + 4 + size) * par->channels > INT_MAX - 8)
+ return AVERROR_INVALIDDATA;
+ if (av_new_packet(pkt, 8 + (32 + 4 + size) * par->channels) < 0)
+ return AVERROR(ENOMEM);
+ dst = pkt->data;
+ if (par->codec_id == AV_CODEC_ID_ADPCM_THP_LE) {
+ bytestream_put_le32(&dst, size * par->channels);
+ bytestream_put_le32(&dst, samples);
+ } else {
+ bytestream_put_be32(&dst, size * par->channels);
+ bytestream_put_be32(&dst, samples);
+ }
+ bytestream_put_buffer(&dst, b->table, 32 * par->channels);
+ bytestream_put_buffer(&dst, b->adpc + 4 * par->channels *
+ (b->current_block - 1), 4 * par->channels);
+
+ for (i = 0; i < par->channels; i++) {
+ ret = avio_read(s->pb, dst, size);
+ dst += size;
+ avio_skip(s->pb, skip);
+ if (ret != size) {
+ av_packet_unref(pkt);
+ break;
+ }
+ }
+ pkt->duration = samples;
+ } else {
+ size *= par->channels;
+ ret = av_get_packet(s->pb, pkt, size);
+ }
+
+ pkt->stream_index = 0;
+
+ if (ret != size)
+ ret = AVERROR(EIO);
+
+ return ret;
+}
+
+static int read_seek(AVFormatContext *s, int stream_index,
+ int64_t timestamp, int flags)
+{
+ AVStream *st = s->streams[stream_index];
+ BRSTMDemuxContext *b = s->priv_data;
+ int64_t ret = 0;
+
+ timestamp /= b->samples_per_block;
+ ret = avio_seek(s->pb, b->data_start + timestamp * b->block_size *
+ st->codecpar->channels, SEEK_SET);
+ if (ret < 0)
+ return ret;
+
+ b->current_block = timestamp;
+ ff_update_cur_dts(s, st, timestamp * b->samples_per_block);
+ return 0;
+}
+
+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,
+ .read_seek = read_seek,
+ .extensions = "brstm",
+};
+
+AVInputFormat ff_bfstm_demuxer = {
+ .name = "bfstm",
+ .long_name = NULL_IF_CONFIG_SMALL("BFSTM (Binary Cafe Stream)"),
+ .priv_data_size = sizeof(BRSTMDemuxContext),
+ .read_probe = probe_bfstm,
+ .read_header = read_header,
+ .read_packet = read_packet,
+ .read_close = read_close,
+ .read_seek = read_seek,
+ .extensions = "bfstm,bcstm",
+};
diff --git a/libavformat/c93.c b/libavformat/c93.c
index c213b06..b1a245a 100644
--- a/libavformat/c93.c
+++ b/libavformat/c93.c
@@ -2,20 +2,20 @@
* Interplay C93 demuxer
* Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -133,7 +133,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt)
}
if (c93->current_frame >= br->frames) {
if (c93->current_block >= 511 || !br[1].length)
- return AVERROR(EIO);
+ return AVERROR_EOF;
br++;
c93->current_block++;
c93->current_frame = 0;
diff --git a/libavformat/cache.c b/libavformat/cache.c
new file mode 100644
index 0000000..66bbbf5
--- /dev/null
+++ b/libavformat/cache.c
@@ -0,0 +1,330 @@
+/*
+ * 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/internal.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(const void *key, const void *node)
+{
+ return FFDIFFSIGN(*(const 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 = avpriv_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_whitelist(&c->inner, arg, flags, &h->interrupt_callback,
+ options, h->protocol_whitelist, h->protocol_blacklist, h);
+}
+
+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 isn't 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};
+ int64_t 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 == AVERROR_EOF && 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 == AVERROR_EOF && 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 enu_free(void *opaque, void *elem)
+{
+ av_free(elem);
+ return 0;
+}
+
+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_enumerate(c->root, NULL, NULL, enu_free);
+ 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 isn't 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,
+};
+
+const 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 cf128d5..fe242ff 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,49 @@
* 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_AAC, MKTAG('a','a','c','l') },
+ { 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_OPUS, MKTAG('o','p','u','s') },
+ { 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_QDMC, 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_PCM_S8, MKTAG('l','p','c','m') },
+ { AV_CODEC_ID_PCM_S16LE, MKTAG('l','p','c','m') },
+ { AV_CODEC_ID_PCM_S16BE, MKTAG('l','p','c','m') },
+ { AV_CODEC_ID_PCM_S24LE, MKTAG('l','p','c','m') },
+ { AV_CODEC_ID_PCM_S24BE, MKTAG('l','p','c','m') },
+ { AV_CODEC_ID_PCM_S32LE, MKTAG('l','p','c','m') },
+ { AV_CODEC_ID_PCM_S32BE, MKTAG('l','p','c','m') },
+ { AV_CODEC_ID_PCM_F32LE, MKTAG('l','p','c','m') },
+ { AV_CODEC_ID_PCM_F32BE, MKTAG('l','p','c','m') },
+ { AV_CODEC_ID_PCM_F64LE, MKTAG('l','p','c','m') },
+ { AV_CODEC_ID_PCM_F64BE, MKTAG('l','p','c','m') },
{ AV_CODEC_ID_NONE, 0 },
};
+
diff --git a/libavformat/caf.h b/libavformat/caf.h
index 7ca4dc5..9c25f2c 100644
--- a/libavformat/caf.h
+++ b/libavformat/caf.h
@@ -2,20 +2,20 @@
* CAF common code
* Copyright (c) 2007 Justin Ruggles
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/cafdec.c b/libavformat/cafdec.c
index efc8c49..7652d9e 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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->sample_rate = av_int2double(avio_rb64(pb));
- st->codecpar->codec_tag = avio_rb32(pb);
+ st->codecpar->codec_tag = avio_rl32(pb);
flags = avio_rb32(pb);
caf->bytes_per_packet = avio_rb32(pb);
st->codecpar->block_align = caf->bytes_per_packet;
@@ -88,7 +88,7 @@ static int read_desc_chunk(AVFormatContext *s)
}
/* determine codec */
- if (st->codecpar->codec_tag == MKBETAG('l','p','c','m'))
+ if (st->codecpar->codec_tag == MKTAG('l','p','c','m'))
st->codecpar->codec_id = ff_mov_get_lpcm_codec_id(st->codecpar->bits_per_coded_sample, (flags ^ 0x2) | 0x4);
else
st->codecpar->codec_id = ff_codec_get_id(ff_codec_caf_tags, st->codecpar->codec_tag);
@@ -129,10 +129,13 @@ static int read_kuki_chunk(AVFormatContext *s, int64_t size)
avio_skip(pb, size);
return AVERROR_INVALIDDATA;
}
- avio_read(pb, preamble, ALAC_PREAMBLE);
+ if (avio_read(pb, preamble, ALAC_PREAMBLE) != ALAC_PREAMBLE) {
+ av_log(s, AV_LOG_ERROR, "failed to read preamble\n");
+ return AVERROR_INVALIDDATA;
+ }
- st->codecpar->extradata = av_mallocz(ALAC_HEADER + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
+ av_freep(&st->codecpar->extradata);
+ if (ff_alloc_extradata(st->codecpar, ALAC_HEADER))
return AVERROR(ENOMEM);
/* For the old style cookie, we skip 12 bytes, then read 36 bytes.
@@ -145,23 +148,36 @@ static int read_kuki_chunk(AVFormatContext *s, int64_t size)
av_freep(&st->codecpar->extradata);
return AVERROR_INVALIDDATA;
}
- avio_read(pb, st->codecpar->extradata, ALAC_HEADER);
+ if (avio_read(pb, st->codecpar->extradata, ALAC_HEADER) != ALAC_HEADER) {
+ av_log(s, AV_LOG_ERROR, "failed to read kuki header\n");
+ av_freep(&st->codecpar->extradata);
+ return AVERROR_INVALIDDATA;
+ }
avio_skip(pb, size - ALAC_PREAMBLE - ALAC_HEADER);
} else {
AV_WB32(st->codecpar->extradata, 36);
memcpy(&st->codecpar->extradata[4], "alac", 4);
AV_WB32(&st->codecpar->extradata[8], 0);
memcpy(&st->codecpar->extradata[12], preamble, 12);
- avio_read(pb, &st->codecpar->extradata[24], ALAC_NEW_KUKI - 12);
+ if (avio_read(pb, &st->codecpar->extradata[24], ALAC_NEW_KUKI - 12) != ALAC_NEW_KUKI - 12) {
+ av_log(s, AV_LOG_ERROR, "failed to read new kuki header\n");
+ av_freep(&st->codecpar->extradata);
+ return AVERROR_INVALIDDATA;
+ }
avio_skip(pb, size - ALAC_NEW_KUKI);
}
- st->codecpar->extradata_size = ALAC_HEADER;
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_OPUS) {
+ // The data layout for Opus is currently unknown, so we do not export
+ // extradata at all. Multichannel streams are not supported.
+ if (st->codecpar->channels > 2) {
+ avpriv_request_sample(s, "multichannel Opus in CAF");
+ return AVERROR_PATCHWELCOME;
+ }
+ avio_skip(pb, size);
} else {
- st->codecpar->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
+ av_freep(&st->codecpar->extradata);
+ if (ff_get_extradata(s, st->codecpar, pb, size) < 0)
return AVERROR(ENOMEM);
- avio_read(pb, st->codecpar->extradata, size);
- st->codecpar->extradata_size = size;
}
return 0;
@@ -209,10 +225,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 +241,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 +261,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 +270,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) {
@@ -289,17 +306,20 @@ static int read_header(AVFormatContext *s)
break;
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));
-#undef _
+ "skipping CAF chunk: %08"PRIX32" (%s), size %"PRId64"\n",
+ tag, av_fourcc2str(av_bswap32(tag)), size);
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,9 +328,14 @@ 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) {
- st->codecpar->bit_rate = st->codecpar->sample_rate * caf->data_size * 8 /
- st->duration;
+ } else if (st->nb_index_entries && st->duration > 0) {
+ if (st->codecpar->sample_rate && caf->data_size / st->duration > INT64_MAX / st->codecpar->sample_rate / 8) {
+ av_log(s, AV_LOG_ERROR, "Overflow during bit rate calculation %d * 8 * %"PRId64"\n",
+ st->codecpar->sample_rate, caf->data_size / st->duration);
+ return AVERROR_INVALIDDATA;
+ }
+ st->codecpar->bit_rate = st->codecpar->sample_rate * 8LL *
+ (caf->data_size / st->duration);
} else {
av_log(s, AV_LOG_ERROR, "Missing packet table. It is required when "
"block size or frame size are variable.\n");
@@ -337,13 +362,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 +421,7 @@ static int read_seek(AVFormatContext *s, int stream_index,
if (caf->frames_per_packet > 0 && caf->bytes_per_packet > 0) {
/* calculate new byte position based on target frame position */
- pos = caf->bytes_per_packet * timestamp / caf->frames_per_packet;
+ pos = caf->bytes_per_packet * (timestamp / caf->frames_per_packet);
if (caf->data_size > 0)
pos = FFMIN(pos, caf->data_size);
packet_cnt = pos / caf->bytes_per_packet;
diff --git a/libavformat/cafenc.c b/libavformat/cafenc.c
new file mode 100644
index 0000000..0f7c4eb
--- /dev/null
+++ b/libavformat/cafenc.c
@@ -0,0 +1,280 @@
+/*
+ * Core Audio Format muxer
+ * Copyright (c) 2011 Carl Eugen Hoyos
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "caf.h"
+#include "isom.h"
+#include "avio_internal.h"
+#include "libavutil/intfloat.h"
+#include "libavutil/dict.h"
+
+typedef struct {
+ int64_t data;
+ uint8_t *pkt_sizes;
+ int size_buffer_size;
+ int size_entries_used;
+ int packets;
+} CAFContext;
+
+static uint32_t codec_flags(enum AVCodecID codec_id) {
+ switch (codec_id) {
+ case AV_CODEC_ID_PCM_F32BE:
+ case AV_CODEC_ID_PCM_F64BE:
+ return 1; //< kCAFLinearPCMFormatFlagIsFloat
+ case AV_CODEC_ID_PCM_S16LE:
+ case AV_CODEC_ID_PCM_S24LE:
+ case AV_CODEC_ID_PCM_S32LE:
+ return 2; //< kCAFLinearPCMFormatFlagIsLittleEndian
+ case AV_CODEC_ID_PCM_F32LE:
+ case AV_CODEC_ID_PCM_F64LE:
+ return 3; //< kCAFLinearPCMFormatFlagIsFloat | kCAFLinearPCMFormatFlagIsLittleEndian
+ default:
+ return 0;
+ }
+}
+
+static uint32_t samples_per_packet(enum AVCodecID codec_id, int channels, 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_OPUS:
+ return 960;
+ case AV_CODEC_ID_MP2:
+ case AV_CODEC_ID_MP3:
+ return 1152;
+ case AV_CODEC_ID_AC3:
+ return 1536;
+ case AV_CODEC_ID_QDM2:
+ case AV_CODEC_ID_QDMC:
+ 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;
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ CAFContext *caf = s->priv_data;
+ AVDictionaryEntry *t = NULL;
+ unsigned int codec_tag = ff_codec_get_tag(ff_codec_caf_tags, par->codec_id);
+ int64_t chunk_size = 0;
+ int frame_size = par->frame_size;
+
+ if (s->nb_streams != 1) {
+ av_log(s, AV_LOG_ERROR, "CAF files have exactly one stream\n");
+ return AVERROR(EINVAL);
+ }
+
+ switch (par->codec_id) {
+ case AV_CODEC_ID_AAC:
+ av_log(s, AV_LOG_ERROR, "muxing codec currently unsupported\n");
+ return AVERROR_PATCHWELCOME;
+ }
+
+ if (par->codec_id == AV_CODEC_ID_OPUS && par->channels > 2) {
+ av_log(s, AV_LOG_ERROR, "Only mono and stereo are supported for Opus\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (!codec_tag) {
+ av_log(s, AV_LOG_ERROR, "unsupported codec\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (!par->block_align && !(pb->seekable & AVIO_SEEKABLE_NORMAL)) {
+ av_log(s, AV_LOG_ERROR, "Muxing variable packet size not supported on non seekable output\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (par->codec_id != AV_CODEC_ID_MP3 || frame_size != 576)
+ frame_size = samples_per_packet(par->codec_id, par->channels, par->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(par->sample_rate)); //< mSampleRate
+ avio_wl32(pb, codec_tag); //< mFormatID
+ avio_wb32(pb, codec_flags(par->codec_id)); //< mFormatFlags
+ avio_wb32(pb, par->block_align); //< mBytesPerPacket
+ avio_wb32(pb, frame_size); //< mFramesPerPacket
+ avio_wb32(pb, par->channels); //< mChannelsPerFrame
+ avio_wb32(pb, av_get_bits_per_sample(par->codec_id)); //< mBitsPerChannel
+
+ if (par->channel_layout) {
+ ffio_wfourcc(pb, "chan");
+ avio_wb64(pb, 12);
+ ff_mov_write_chan(pb, par->channel_layout);
+ }
+
+ if (par->codec_id == AV_CODEC_ID_ALAC) {
+ ffio_wfourcc(pb, "kuki");
+ avio_wb64(pb, 12 + par->extradata_size);
+ avio_write(pb, "\0\0\0\14frmaalac", 12);
+ avio_write(pb, par->extradata, par->extradata_size);
+ } else if (par->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 (par->codec_id == AV_CODEC_ID_QDM2 || par->codec_id == AV_CODEC_ID_QDMC) {
+ ffio_wfourcc(pb, "kuki");
+ avio_wb64(pb, par->extradata_size);
+ avio_write(pb, par->extradata, par->extradata_size);
+ }
+
+ ff_standardize_creation_time(s);
+ 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]->codecpar->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;
+ AVCodecParameters *par = s->streams[0]->codecpar;
+
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ 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 (!par->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(par->codec_id, par->channels, par->block_align)); ///< mNumberValidFrames
+ avio_wb32(pb, 0); ///< mPrimingFrames
+ avio_wb32(pb, 0); ///< mRemainderFrames
+ avio_write(pb, caf->pkt_sizes, caf->size_entries_used);
+ caf->size_buffer_size = 0;
+ }
+ avio_flush(pb);
+ }
+ av_freep(&caf->pkt_sizes);
+ return 0;
+}
+
+AVOutputFormat ff_caf_muxer = {
+ .name = "caf",
+ .long_name = NULL_IF_CONFIG_SMALL("Apple CAF (Core Audio Format)"),
+ .mime_type = "audio/x-caf",
+ .extensions = "caf",
+ .priv_data_size = sizeof(CAFContext),
+ .audio_codec = AV_CODEC_ID_PCM_S16BE,
+ .video_codec = AV_CODEC_ID_NONE,
+ .write_header = caf_write_header,
+ .write_packet = caf_write_packet,
+ .write_trailer = caf_write_trailer,
+ .codec_tag = (const AVCodecTag* const []){ff_codec_caf_tags, 0},
+};
diff --git a/libavformat/cavsvideodec.c b/libavformat/cavsvideodec.c
index fcca9e1..b4da58e 100644
--- a/libavformat/cavsvideodec.c
+++ b/libavformat/cavsvideodec.c
@@ -2,25 +2,26 @@
* 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
*/
#include "avformat.h"
#include "rawdec.h"
+#include "libavcodec/internal.h"
#define CAVS_SEQ_START_CODE 0x000001b0
#define CAVS_PIC_I_START_CODE 0x000001b3
@@ -33,10 +34,10 @@ static int cavsvideo_probe(AVProbeData *p)
{
uint32_t code= -1;
int pic=0, seq=0, slice_pos = 0;
- int i;
+ const uint8_t *ptr = p->buf, *end = p->buf + p->buf_size;
- for(i=0; i<p->buf_size; i++){
- code = (code<<8) + p->buf[i];
+ while (ptr < end) {
+ ptr = avpriv_find_start_code(ptr, end, &code);
if ((code & 0xffffff00) == 0x100) {
if(code < CAVS_SEQ_START_CODE) {
/* slices have to be consecutive */
@@ -49,7 +50,7 @@ static int cavsvideo_probe(AVProbeData *p)
if (code == CAVS_SEQ_START_CODE) {
seq++;
/* check for the only currently supported profile */
- if(p->buf[i+1] != CAVS_PROFILE_JIZHUN)
+ if (*ptr != CAVS_PROFILE_JIZHUN)
return 0;
} else if ((code == CAVS_PIC_I_START_CODE) ||
(code == CAVS_PIC_PB_START_CODE)) {
@@ -61,7 +62,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 3f11286..05cac6e 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 26a5f81..94a063c 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 format, frames, ret;
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR_EOF;
pos = avio_tell(pb);
@@ -80,12 +125,18 @@ static int cdxl_read_packet(AVFormatContext *s, AVPacket *pkt)
return AVERROR_INVALIDDATA;
}
+ format = cdxl->header[1] & 0xE0;
current_size = AV_RB32(&cdxl->header[2]);
width = AV_RB16(&cdxl->header[14]);
height = AV_RB16(&cdxl->header[16]);
palette_size = AV_RB16(&cdxl->header[20]);
audio_size = AV_RB16(&cdxl->header[22]);
- image_size = FFALIGN(width, 16) * height * cdxl->header[19] / 8;
+ if (FFALIGN(width, 16) * (uint64_t)height * cdxl->header[19] > INT_MAX)
+ return AVERROR_INVALIDDATA;
+ if (format == 0x20)
+ image_size = width * height * cdxl->header[19] / 8;
+ else
+ image_size = FFALIGN(width, 16) * height * cdxl->header[19] / 8;
video_size = palette_size + image_size;
if (palette_size > 512)
@@ -133,6 +184,15 @@ static int cdxl_read_packet(AVFormatContext *s, AVPacket *pkt)
st->codecpar->codec_id = AV_CODEC_ID_CDXL;
st->codecpar->width = width;
st->codecpar->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 +240,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/chromaprint.c b/libavformat/chromaprint.c
new file mode 100644
index 0000000..4da02be
--- /dev/null
+++ b/libavformat/chromaprint.c
@@ -0,0 +1,190 @@
+/*
+ * Chromaprint fingerprinting muxer
+ * Copyright (c) 2015 Rodger Combs
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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 "libavutil/opt.h"
+#include "libavcodec/internal.h"
+#include <chromaprint.h>
+
+#define CPR_VERSION_INT AV_VERSION_INT(CHROMAPRINT_VERSION_MAJOR, \
+ CHROMAPRINT_VERSION_MINOR, \
+ CHROMAPRINT_VERSION_PATCH)
+
+typedef enum FingerprintFormat {
+ FINGERPRINT_RAW,
+ FINGERPRINT_COMPRESSED,
+ FINGERPRINT_BASE64,
+} FingerprintFormat;
+
+typedef struct ChromaprintMuxContext {
+ const AVClass *class;
+ int silence_threshold;
+ int algorithm;
+ FingerprintFormat fp_format;
+#if CPR_VERSION_INT >= AV_VERSION_INT(1, 4, 0)
+ ChromaprintContext *ctx;
+#else
+ ChromaprintContext ctx;
+#endif
+} ChromaprintMuxContext;
+
+static void cleanup(ChromaprintMuxContext *cpr)
+{
+ if (cpr->ctx) {
+ avpriv_lock_avformat();
+ chromaprint_free(cpr->ctx);
+ avpriv_unlock_avformat();
+ }
+}
+
+static int write_header(AVFormatContext *s)
+{
+ ChromaprintMuxContext *cpr = s->priv_data;
+ AVStream *st;
+
+ avpriv_lock_avformat();
+ cpr->ctx = chromaprint_new(cpr->algorithm);
+ avpriv_unlock_avformat();
+
+ if (!cpr->ctx) {
+ av_log(s, AV_LOG_ERROR, "Failed to create chromaprint context.\n");
+ return AVERROR(ENOMEM);
+ }
+
+ if (cpr->silence_threshold != -1) {
+#if CPR_VERSION_INT >= AV_VERSION_INT(0, 7, 0)
+ if (!chromaprint_set_option(cpr->ctx, "silence_threshold", cpr->silence_threshold)) {
+ av_log(s, AV_LOG_ERROR, "Failed to set silence threshold.\n");
+ goto fail;
+ }
+#else
+ av_log(s, AV_LOG_ERROR, "Setting the silence threshold requires Chromaprint "
+ "version 0.7.0 or later.\n");
+ goto fail;
+#endif
+ }
+
+ if (s->nb_streams != 1) {
+ av_log(s, AV_LOG_ERROR, "Only one stream is supported\n");
+ goto fail;
+ }
+
+ st = s->streams[0];
+
+ if (st->codecpar->channels > 2) {
+ av_log(s, AV_LOG_ERROR, "Only up to 2 channels are supported\n");
+ goto fail;
+ }
+
+ if (st->codecpar->sample_rate < 1000) {
+ av_log(s, AV_LOG_ERROR, "Sampling rate must be at least 1000\n");
+ goto fail;
+ }
+
+ if (!chromaprint_start(cpr->ctx, st->codecpar->sample_rate, st->codecpar->channels)) {
+ av_log(s, AV_LOG_ERROR, "Failed to start chromaprint\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ cleanup(cpr);
+ return AVERROR(EINVAL);
+}
+
+static int write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ ChromaprintMuxContext *cpr = s->priv_data;
+ return chromaprint_feed(cpr->ctx, pkt->data, pkt->size / 2) ? 0 : AVERROR(EINVAL);
+}
+
+static int write_trailer(AVFormatContext *s)
+{
+ ChromaprintMuxContext *cpr = s->priv_data;
+ AVIOContext *pb = s->pb;
+ void *fp = NULL, *enc_fp = NULL;
+ int size, enc_size, ret = AVERROR(EINVAL);
+
+ if (!chromaprint_finish(cpr->ctx)) {
+ av_log(s, AV_LOG_ERROR, "Failed to generate fingerprint\n");
+ goto fail;
+ }
+
+ if (!chromaprint_get_raw_fingerprint(cpr->ctx, &fp, &size)) {
+ av_log(s, AV_LOG_ERROR, "Failed to retrieve fingerprint\n");
+ goto fail;
+ }
+
+ switch (cpr->fp_format) {
+ case FINGERPRINT_RAW:
+ avio_write(pb, fp, size);
+ break;
+ case FINGERPRINT_COMPRESSED:
+ case FINGERPRINT_BASE64:
+ if (!chromaprint_encode_fingerprint(fp, size, cpr->algorithm, &enc_fp, &enc_size,
+ cpr->fp_format == FINGERPRINT_BASE64)) {
+ av_log(s, AV_LOG_ERROR, "Failed to encode fingerprint\n");
+ goto fail;
+ }
+ avio_write(pb, enc_fp, enc_size);
+ break;
+ }
+
+ ret = 0;
+fail:
+ if (fp)
+ chromaprint_dealloc(fp);
+ if (enc_fp)
+ chromaprint_dealloc(enc_fp);
+ cleanup(cpr);
+ return ret;
+}
+
+#define OFFSET(x) offsetof(ChromaprintMuxContext, x)
+#define FLAGS AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ { "silence_threshold", "threshold for detecting silence", OFFSET(silence_threshold), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 32767, FLAGS },
+ { "algorithm", "version of the fingerprint algorithm", OFFSET(algorithm), AV_OPT_TYPE_INT, { .i64 = CHROMAPRINT_ALGORITHM_DEFAULT }, CHROMAPRINT_ALGORITHM_TEST1, INT_MAX, FLAGS },
+ { "fp_format", "fingerprint format to write", OFFSET(fp_format), AV_OPT_TYPE_INT, { .i64 = FINGERPRINT_BASE64 }, FINGERPRINT_RAW, FINGERPRINT_BASE64, FLAGS },
+ { "raw", "binary raw fingerprint", 0, AV_OPT_TYPE_CONST, {.i64 = FINGERPRINT_RAW }, INT_MIN, INT_MAX, FLAGS, "fp_format"},
+ { "compressed", "binary compressed fingerprint", 0, AV_OPT_TYPE_CONST, {.i64 = FINGERPRINT_COMPRESSED }, INT_MIN, INT_MAX, FLAGS, "fp_format"},
+ { "base64", "Base64 compressed fingerprint", 0, AV_OPT_TYPE_CONST, {.i64 = FINGERPRINT_BASE64 }, INT_MIN, INT_MAX, FLAGS, "fp_format"},
+ { NULL },
+};
+
+static const AVClass chromaprint_class = {
+ .class_name = "chromaprint muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVOutputFormat ff_chromaprint_muxer = {
+ .name = "chromaprint",
+ .long_name = NULL_IF_CONFIG_SMALL("Chromaprint"),
+ .priv_data_size = sizeof(ChromaprintMuxContext),
+ .audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE),
+ .write_header = write_header,
+ .write_packet = write_packet,
+ .write_trailer = write_trailer,
+ .flags = AVFMT_NOTIMESTAMPS,
+ .priv_class = &chromaprint_class,
+};
diff --git a/libavformat/cinedec.c b/libavformat/cinedec.c
new file mode 100644
index 0000000..de34fb9
--- /dev/null
+++ b/libavformat/cinedec.c
@@ -0,0 +1,329 @@
+/*
+ * 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 */
+};
+
+#define CFA_TLGRAY 0x80000000U
+#define CFA_TRGRAY 0x40000000U
+#define CFA_BLGRAY 0x20000000U
+#define CFA_BRGRAY 0x10000000U
+
+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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
+ st->codecpar->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, "unknown 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->codecpar->width = avio_rl32(pb);
+ st->codecpar->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->codecpar->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->codecpar->extradata = av_strdup("BottomUp");
+ st->codecpar->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->codecpar->bits_per_coded_sample = avio_rl32(pb);
+
+ if (compression == CC_RGB) {
+ if (biBitCount == 8) {
+ st->codecpar->format = AV_PIX_FMT_GRAY8;
+ } else if (biBitCount == 16) {
+ st->codecpar->format = AV_PIX_FMT_GRAY16LE;
+ } else if (biBitCount == 24) {
+ st->codecpar->format = AV_PIX_FMT_BGR24;
+ } else if (biBitCount == 48) {
+ st->codecpar->format = 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->codecpar->format = AV_PIX_FMT_BAYER_GBRG8;
+ } else if (biBitCount == 16) {
+ st->codecpar->format = 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->codecpar->format = AV_PIX_FMT_BAYER_RGGB8;
+ } else if (biBitCount == 16) {
+ st->codecpar->format = 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++) {
+ if (avio_feof(pb))
+ return AVERROR_INVALIDDATA;
+
+ 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 & AVIO_SEEKABLE_NORMAL))
+ 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 a338df6..19c83c3 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
*/
@@ -65,7 +65,10 @@ static av_cold int concat_open(URLContext *h, const char *uri, int flags)
struct concat_data *data = h->priv_data;
struct concat_nodes *nodes;
- av_strstart(uri, "concat:", &uri);
+ if (!av_strstart(uri, "concat:", &uri)) {
+ av_log(h, AV_LOG_ERROR, "URL %s lacks prefix\n", uri);
+ return AVERROR(EINVAL);
+ }
for (i = 0, len = 1; uri[i]; i++) {
if (uri[i] == *AV_CAT_SEPARATOR) {
@@ -94,8 +97,9 @@ static av_cold int concat_open(URLContext *h, const char *uri, int flags)
uri += len + strspn(uri + len, AV_CAT_SEPARATOR);
/* creating URLContext */
- if ((err = ffurl_open(&uc, node_uri, flags,
- &h->interrupt_callback, NULL, h->protocols, h)) < 0)
+ err = ffurl_open_whitelist(&uc, node_uri, flags,
+ &h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist, h);
+ if (err < 0)
break;
/* creating size */
@@ -114,9 +118,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;
}
@@ -130,19 +135,20 @@ static int concat_read(URLContext *h, unsigned char *buf, int size)
while (size > 0) {
result = ffurl_read(nodes[i].uc, buf, size);
- if (result < 0)
- return total ? total : result;
- if (!result) {
+ if (result == AVERROR_EOF) {
if (i + 1 == data->length ||
ffurl_seek(nodes[++i].uc, 0, SEEK_SET) < 0)
break;
+ result = 0;
}
+ if (result < 0)
+ return total ? total : result;
total += result;
buf += result;
size -= result;
}
data->current = i;
- return total;
+ return total ? total : result;
}
static int64_t concat_seek(URLContext *h, int64_t pos, int whence)
@@ -188,4 +194,5 @@ const URLProtocol ff_concat_protocol = {
.url_seek = concat_seek,
.url_close = concat_close,
.priv_data_size = sizeof(struct concat_data),
+ .default_whitelist = "concat,file,subfile",
};
diff --git a/libavformat/concatdec.c b/libavformat/concatdec.c
new file mode 100644
index 0000000..0e18901
--- /dev/null
+++ b/libavformat/concatdec.c
@@ -0,0 +1,780 @@
+/*
+ * 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 {
+ AVBSFContext *bsf;
+ int out_stream_index;
+} ConcatStream;
+
+typedef struct {
+ char *url;
+ int64_t start_time;
+ int64_t file_start_time;
+ int64_t file_inpoint;
+ int64_t duration;
+ int64_t next_dts;
+ ConcatStream *streams;
+ int64_t inpoint;
+ int64_t outpoint;
+ AVDictionary *metadata;
+ int nb_streams;
+} ConcatFile;
+
+typedef struct {
+ AVClass *class;
+ ConcatFile *files;
+ ConcatFile *cur_file;
+ unsigned nb_files;
+ AVFormatContext *avf;
+ int safe;
+ int seekable;
+ int eof;
+ ConcatMatchMode stream_match_mode;
+ unsigned auto_convert;
+ int segment_time_metadata;
+} 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 (proto && !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;
+ file->next_dts = AV_NOPTS_VALUE;
+ file->inpoint = AV_NOPTS_VALUE;
+ file->outpoint = 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->codecpar->codec_id || !source_st->codecpar->codec_id) {
+ if (st->codecpar->extradata_size < source_st->codecpar->extradata_size) {
+ if (st->codecpar->extradata) {
+ av_freep(&st->codecpar->extradata);
+ st->codecpar->extradata_size = 0;
+ }
+ ret = ff_alloc_extradata(st->codecpar,
+ source_st->codecpar->extradata_size);
+ if (ret < 0)
+ return ret;
+ }
+ memcpy(st->codecpar->extradata, source_st->codecpar->extradata,
+ source_st->codecpar->extradata_size);
+ return 0;
+ }
+ if ((ret = avcodec_parameters_copy(st->codecpar, source_st->codecpar)) < 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;
+
+ av_dict_copy(&st->metadata, source_st->metadata, 0);
+ 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];
+ const AVBitStreamFilter *filter;
+ AVBSFContext *bsf;
+ int ret;
+
+ if (cat->auto_convert && st->codecpar->codec_id == AV_CODEC_ID_H264) {
+ if (!st->codecpar->extradata_size ||
+ (st->codecpar->extradata_size >= 3 && AV_RB24(st->codecpar->extradata) == 1) ||
+ (st->codecpar->extradata_size >= 4 && AV_RB32(st->codecpar->extradata) == 1))
+ return 0;
+ av_log(cat->avf, AV_LOG_INFO,
+ "Auto-inserting h264_mp4toannexb bitstream filter\n");
+ filter = av_bsf_get_by_name("h264_mp4toannexb");
+ if (!filter) {
+ av_log(avf, AV_LOG_ERROR, "h264_mp4toannexb bitstream filter "
+ "required for H.264 streams\n");
+ return AVERROR_BSF_NOT_FOUND;
+ }
+ ret = av_bsf_alloc(filter, &bsf);
+ if (ret < 0)
+ return ret;
+ cs->bsf = bsf;
+
+ ret = avcodec_parameters_copy(bsf->par_in, st->codecpar);
+ if (ret < 0)
+ return ret;
+
+ ret = av_bsf_init(bsf);
+ if (ret < 0)
+ return ret;
+
+ ret = avcodec_parameters_copy(st->codecpar, bsf->par_out);
+ if (ret < 0)
+ return ret;
+ }
+ 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;
+ if ((ret = detect_stream_specific(avf, i)) < 0)
+ return ret;
+ }
+ 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;
+ 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->flags |= avf->flags & ~AVFMT_FLAG_CUSTOM_IO;
+ cat->avf->interrupt_callback = avf->interrupt_callback;
+
+ if ((ret = ff_copy_whiteblacklists(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;
+ file->file_start_time = (cat->avf->start_time == AV_NOPTS_VALUE) ? 0 : cat->avf->start_time;
+ file->file_inpoint = (file->inpoint == AV_NOPTS_VALUE) ? file->file_start_time : file->inpoint;
+ if (file->duration == AV_NOPTS_VALUE && file->outpoint != AV_NOPTS_VALUE)
+ file->duration = file->outpoint - file->file_inpoint;
+
+ if (cat->segment_time_metadata) {
+ av_dict_set_int(&file->metadata, "lavf.concatdec.start_time", file->start_time, 0);
+ if (file->duration != AV_NOPTS_VALUE)
+ av_dict_set_int(&file->metadata, "lavf.concatdec.duration", file->duration, 0);
+ }
+
+ if ((ret = match_streams(avf)) < 0)
+ return ret;
+ if (file->inpoint != AV_NOPTS_VALUE) {
+ if ((ret = avformat_seek_file(cat->avf, -1, INT64_MIN, file->inpoint, file->inpoint, 0)) < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int concat_read_close(AVFormatContext *avf)
+{
+ ConcatContext *cat = avf->priv_data;
+ unsigned i, j;
+
+ for (i = 0; i < cat->nb_files; i++) {
+ av_freep(&cat->files[i].url);
+ for (j = 0; j < cat->files[i].nb_streams; j++) {
+ if (cat->files[i].streams[j].bsf)
+ av_bsf_free(&cat->files[i].streams[j].bsf);
+ }
+ av_freep(&cat->files[i].streams);
+ av_dict_free(&cat->files[i].metadata);
+ }
+ if (cat->avf)
+ avformat_close_input(&cat->avf);
+ 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") || !strcmp(keyword, "inpoint") || !strcmp(keyword, "outpoint")) {
+ char *dur_str = get_keyword(&cursor);
+ int64_t dur;
+ if (!file) {
+ av_log(avf, AV_LOG_ERROR, "Line %d: %s without file\n",
+ line, keyword);
+ FAIL(AVERROR_INVALIDDATA);
+ }
+ if ((ret = av_parse_time(&dur, dur_str, 1)) < 0) {
+ av_log(avf, AV_LOG_ERROR, "Line %d: invalid %s '%s'\n",
+ line, keyword, dur_str);
+ goto fail;
+ }
+ if (!strcmp(keyword, "duration"))
+ file->duration = dur;
+ else if (!strcmp(keyword, "inpoint"))
+ file->inpoint = dur;
+ else if (!strcmp(keyword, "outpoint"))
+ file->outpoint = dur;
+ } else if (!strcmp(keyword, "file_packet_metadata")) {
+ char *metadata;
+ if (!file) {
+ av_log(avf, AV_LOG_ERROR, "Line %d: %s without file\n",
+ line, keyword);
+ FAIL(AVERROR_INVALIDDATA);
+ }
+ metadata = av_get_token((const char **)&cursor, SPACE_CHARS);
+ if (!metadata) {
+ av_log(avf, AV_LOG_ERROR, "Line %d: packet metadata required\n", line);
+ FAIL(AVERROR_INVALIDDATA);
+ }
+ if ((ret = av_dict_parse_string(&file->metadata, metadata, "=", "", 0)) < 0) {
+ av_log(avf, AV_LOG_ERROR, "Line %d: failed to parse metadata string\n", line);
+ av_freep(&metadata);
+ FAIL(AVERROR_INVALIDDATA);
+ }
+ av_freep(&metadata);
+ } 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) {
+ if (cat->files[i].inpoint == AV_NOPTS_VALUE || cat->files[i].outpoint == AV_NOPTS_VALUE)
+ break;
+ cat->files[i].duration = cat->files[i].outpoint - cat->files[i].inpoint;
+ }
+ 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) {
+ if (cat->avf->duration > 0 || cat->cur_file->next_dts == AV_NOPTS_VALUE) {
+ cat->cur_file->duration = cat->avf->duration;
+ } else {
+ cat->cur_file->duration = cat->cur_file->next_dts;
+ }
+ cat->cur_file->duration -= (cat->cur_file->file_inpoint - cat->cur_file->file_start_time);
+ }
+
+ if (++fileno >= cat->nb_files) {
+ cat->eof = 1;
+ return AVERROR_EOF;
+ }
+ return open_file(avf, fileno);
+}
+
+static int filter_packet(AVFormatContext *avf, ConcatStream *cs, AVPacket *pkt)
+{
+ int ret;
+
+ if (cs->bsf) {
+ ret = av_bsf_send_packet(cs->bsf, pkt);
+ if (ret < 0) {
+ av_log(avf, AV_LOG_ERROR, "h264_mp4toannexb filter "
+ "failed to send input packet\n");
+ av_packet_unref(pkt);
+ return ret;
+ }
+
+ while (!ret)
+ ret = av_bsf_receive_packet(cs->bsf, pkt);
+
+ if (ret < 0 && (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)) {
+ av_log(avf, AV_LOG_ERROR, "h264_mp4toannexb filter "
+ "failed to receive output packet\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/* Returns true if the packet dts is greater or equal to the specified outpoint. */
+static int packet_after_outpoint(ConcatContext *cat, AVPacket *pkt)
+{
+ if (cat->cur_file->outpoint != AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE) {
+ return av_compare_ts(pkt->dts, cat->avf->streams[pkt->stream_index]->time_base,
+ cat->cur_file->outpoint, AV_TIME_BASE_Q) >= 0;
+ }
+ return 0;
+}
+
+static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt)
+{
+ ConcatContext *cat = avf->priv_data;
+ int ret;
+ int64_t delta;
+ ConcatStream *cs;
+ AVStream *st;
+
+ if (cat->eof)
+ return AVERROR_EOF;
+
+ 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;
+ }
+ if (packet_after_outpoint(cat, pkt)) {
+ av_packet_unref(pkt);
+ if ((ret = open_next_file(avf)) < 0)
+ return ret;
+ continue;
+ }
+ 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));
+
+ delta = av_rescale_q(cat->cur_file->start_time - cat->cur_file->file_inpoint,
+ 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));
+ if (cat->cur_file->metadata) {
+ uint8_t* metadata;
+ int metadata_len;
+ char* packed_metadata = av_packet_pack_dictionary(cat->cur_file->metadata, &metadata_len);
+ if (!packed_metadata)
+ return AVERROR(ENOMEM);
+ if (!(metadata = av_packet_new_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, metadata_len))) {
+ av_freep(&packed_metadata);
+ return AVERROR(ENOMEM);
+ }
+ memcpy(metadata, packed_metadata, metadata_len);
+ av_freep(&packed_metadata);
+ }
+
+ if (cat->cur_file->duration == AV_NOPTS_VALUE && st->cur_dts != AV_NOPTS_VALUE) {
+ int64_t next_dts = av_rescale_q(st->cur_dts, st->time_base, AV_TIME_BASE_Q);
+ if (cat->cur_file->next_dts == AV_NOPTS_VALUE || next_dts > cat->cur_file->next_dts) {
+ cat->cur_file->next_dts = next_dts;
+ }
+ }
+
+ 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->cur_file->file_inpoint;
+
+ 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, AVFormatContext *cur_avf)
+{
+ 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 (cat->cur_file != &cat->files[left]) {
+ if ((ret = open_file(avf, left)) < 0)
+ return ret;
+ } else {
+ cat->avf = cur_avf;
+ }
+
+ 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 (cat->cur_file == &cat->files[left])
+ cat->avf = NULL;
+ 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, cur_avf_saved)) < 0) {
+ if (cat->cur_file != cur_file_saved) {
+ if (cat->avf)
+ avformat_close_input(&cat->avf);
+ }
+ cat->avf = cur_avf_saved;
+ cat->cur_file = cur_file_saved;
+ } else {
+ if (cat->cur_file != cur_file_saved) {
+ avformat_close_input(&cur_avf_saved);
+ }
+ cat->eof = 0;
+ }
+ 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_BOOL, {.i64 = 1}, -1, 1, DEC },
+ { "auto_convert", "automatically convert bitstream format",
+ OFFSET(auto_convert), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, DEC },
+ { "segment_time_metadata", "output file segment start time and duration as packet metadata",
+ OFFSET(segment_time_metadata), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DEC },
+ { NULL }
+};
+
+static const AVClass concat_class = {
+ .class_name = "concat demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+
+AVInputFormat ff_concat_demuxer = {
+ .name = "concat",
+ .long_name = NULL_IF_CONFIG_SMALL("Virtual concatenation script"),
+ .priv_data_size = sizeof(ConcatContext),
+ .read_probe = concat_probe,
+ .read_header = concat_read_header,
+ .read_packet = concat_read_packet,
+ .read_close = concat_read_close,
+ .read_seek2 = concat_seek,
+ .priv_class = &concat_class,
+};
diff --git a/libavformat/crcenc.c b/libavformat/crcenc.c
index 925646b..3fdfea5 100644
--- a/libavformat/crcenc.c
+++ b/libavformat/crcenc.c
@@ -2,20 +2,20 @@
* CRC encoder (for codec/format testing)
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -59,7 +59,6 @@ static int crc_write_trailer(struct AVFormatContext *s)
AVOutputFormat ff_crc_muxer = {
.name = "crc",
.long_name = NULL_IF_CONFIG_SMALL("CRC testing"),
- .extensions = "",
.priv_data_size = sizeof(CRCState),
.audio_codec = AV_CODEC_ID_PCM_S16LE,
.video_codec = AV_CODEC_ID_RAWVIDEO,
diff --git a/libavformat/crypto.c b/libavformat/crypto.c
index bb4adb6..9a48f2e 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
*/
@@ -26,7 +26,8 @@
#include "internal.h"
#include "url.h"
-#define MAX_BUFFER_BLOCKS 150
+// encourage reads of 4096 bytes - 1 block is always retained.
+#define MAX_BUFFER_BLOCKS 257
#define BLOCKSIZE 16
typedef struct CryptoContext {
@@ -36,6 +37,8 @@ typedef struct CryptoContext {
outbuffer[BLOCKSIZE*MAX_BUFFER_BLOCKS];
uint8_t *outptr;
int indata, indata_used, outdata;
+ int64_t position; // position in file - used in seek
+ int flags;
int eof;
uint8_t *key;
int keylen;
@@ -91,10 +94,9 @@ static int set_aes_arg(URLContext *h, uint8_t **buf, int *buf_len,
desc, default_buf_len, BLOCKSIZE);
return AVERROR(EINVAL);
}
- *buf = av_malloc(default_buf_len);
+ *buf = av_memdup(default_buf, default_buf_len);
if (!*buf)
return AVERROR(ENOMEM);
- memcpy(*buf, default_buf, default_buf_len);
*buf_len = default_buf_len;
} else if (*buf_len != BLOCKSIZE) {
av_log(h, AV_LOG_ERROR,
@@ -105,11 +107,12 @@ static int set_aes_arg(URLContext *h, uint8_t **buf, int *buf_len,
return 0;
}
-static int crypto_open(URLContext *h, const char *uri, int flags)
+static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary **options)
{
const char *nested_url;
int ret = 0;
CryptoContext *c = h->priv_data;
+ c->flags = flags;
if (!av_strstart(uri, "crypto+", &nested_url) &&
!av_strstart(uri, "crypto:", &nested_url)) {
@@ -137,8 +140,9 @@ static int crypto_open(URLContext *h, const char *uri, int flags)
goto err;
}
- if ((ret = ffurl_open(&c->hd, nested_url, flags,
- &h->interrupt_callback, NULL, h->protocols, h)) < 0) {
+ if ((ret = ffurl_open_whitelist(&c->hd, nested_url, flags,
+ &h->interrupt_callback, options,
+ h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {
av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", nested_url);
goto err;
}
@@ -152,6 +156,10 @@ static int crypto_open(URLContext *h, const char *uri, int flags)
ret = av_aes_init(c->aes_decrypt, c->decrypt_key, BLOCKSIZE * 8, 1);
if (ret < 0)
goto err;
+
+ // pass back information about the context we openned
+ if (c->hd->is_streamed)
+ h->is_streamed = c->hd->is_streamed;
}
if (flags & AVIO_FLAG_WRITE) {
@@ -163,10 +171,11 @@ static int crypto_open(URLContext *h, const char *uri, int flags)
ret = av_aes_init(c->aes_encrypt, c->encrypt_key, BLOCKSIZE * 8, 0);
if (ret < 0)
goto err;
+ // for write, we must be streamed
+ // - linear write only for crytpo aes-128-cbc
+ h->is_streamed = 1;
}
- h->is_streamed = 1;
-
err:
return ret;
}
@@ -181,6 +190,7 @@ retry:
memcpy(buf, c->outptr, size);
c->outptr += size;
c->outdata -= size;
+ c->position = c->position + size;
return size;
}
// We avoid using the last block until we've found EOF,
@@ -220,7 +230,107 @@ retry:
goto retry;
}
-static int crypto_write(URLContext *h, const uint8_t *buf, int size)
+static int64_t crypto_seek(URLContext *h, int64_t pos, int whence)
+{
+ CryptoContext *c = h->priv_data;
+ int64_t block;
+ int64_t newpos;
+
+ if (c->flags & AVIO_FLAG_WRITE) {
+ av_log(h, AV_LOG_ERROR,
+ "Crypto: seek not supported for write\r\n");
+ /* seems the most appropriate error to return */
+ return AVERROR(ESPIPE);
+ }
+
+ // reset eof, else we won't read it correctly if we already hit eof.
+ c->eof = 0;
+
+ switch (whence) {
+ case SEEK_SET:
+ break;
+ case SEEK_CUR:
+ pos = pos + c->position;
+ break;
+ case SEEK_END: {
+ int64_t newpos = ffurl_seek( c->hd, pos, AVSEEK_SIZE );
+ if (newpos < 0) {
+ av_log(h, AV_LOG_ERROR,
+ "Crypto: seek_end - can't get file size (pos=%lld)\r\n", (long long int)pos);
+ return newpos;
+ }
+ pos = newpos - pos;
+ }
+ break;
+ case AVSEEK_SIZE: {
+ int64_t newpos = ffurl_seek( c->hd, pos, AVSEEK_SIZE );
+ return newpos;
+ }
+ break;
+ default:
+ av_log(h, AV_LOG_ERROR,
+ "Crypto: no support for seek where 'whence' is %d\r\n", whence);
+ return AVERROR(EINVAL);
+ }
+
+ c->outdata = 0;
+ c->indata = 0;
+ c->indata_used = 0;
+ c->outptr = c->outbuffer;
+
+ // identify the block containing the IV for the
+ // next block we will decrypt
+ block = pos/BLOCKSIZE;
+ if (block == 0) {
+ // restore the iv to the seed one - this is the iv for the FIRST block
+ memcpy( c->decrypt_iv, c->iv, c->ivlen );
+ c->position = 0;
+ } else {
+ // else, go back one block - we will get av_cyrpt to read this block
+ // which it will then store use as the iv.
+ // note that the DECRYPTED result will not be correct,
+ // but will be discarded
+ block--;
+ c->position = (block * BLOCKSIZE);
+ }
+
+ newpos = ffurl_seek( c->hd, c->position, SEEK_SET );
+ if (newpos < 0) {
+ av_log(h, AV_LOG_ERROR,
+ "Crypto: nested protocol no support for seek or seek failed\n");
+ return newpos;
+ }
+
+ // read and discard from here up to required position
+ // (which will set the iv correctly to it).
+ if (pos - c->position) {
+ uint8_t buff[BLOCKSIZE*2]; // maximum size of pos-c->position
+ int len = pos - c->position;
+ int res;
+
+ while (len > 0) {
+ // note: this may not return all the bytes first time
+ res = crypto_read(h, buff, len);
+ if (res < 0)
+ break;
+ len -= res;
+ }
+
+ // if we did not get all the bytes
+ if (len != 0) {
+ char errbuf[100] = "unknown error";
+ av_strerror(res, errbuf, sizeof(errbuf));
+ av_log(h, AV_LOG_ERROR,
+ "Crypto: discard read did not get all the bytes (%d remain) - read returned (%d)-%s\n",
+ len, res, errbuf);
+ return AVERROR(EINVAL);
+ }
+ }
+
+ return c->position;
+}
+
+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;
@@ -285,7 +395,8 @@ static int crypto_close(URLContext *h)
const URLProtocol ff_crypto_protocol = {
.name = "crypto",
- .url_open = crypto_open,
+ .url_open2 = crypto_open2,
+ .url_seek = crypto_seek,
.url_read = crypto_read,
.url_write = crypto_write,
.url_close = crypto_close,
diff --git a/libavformat/cutils.c b/libavformat/cutils.c
index f5339a9..d86ba05 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/dash.c b/libavformat/dash.c
new file mode 100644
index 0000000..d2501f0
--- /dev/null
+++ b/libavformat/dash.c
@@ -0,0 +1,157 @@
+/*
+ * MPEG-DASH ISO BMFF segmenter
+ * Copyright (c) 2014 Martin Storsjo
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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 "config.h"
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/mathematics.h"
+#include "libavutil/opt.h"
+#include "libavutil/rational.h"
+#include "libavutil/time_internal.h"
+
+#include "avc.h"
+#include "avformat.h"
+#include "avio_internal.h"
+#include "internal.h"
+#include "isom.h"
+#include "os_support.h"
+#include "url.h"
+#include "dash.h"
+
+static DASHTmplId dash_read_tmpl_id(const char *identifier, char *format_tag,
+ size_t format_tag_size, const char **ptr) {
+ const char *next_ptr;
+ DASHTmplId id_type = DASH_TMPL_ID_UNDEFINED;
+
+ if (av_strstart(identifier, "$$", &next_ptr)) {
+ id_type = DASH_TMPL_ID_ESCAPE;
+ *ptr = next_ptr;
+ } else if (av_strstart(identifier, "$RepresentationID$", &next_ptr)) {
+ id_type = DASH_TMPL_ID_REP_ID;
+ // default to basic format, as $RepresentationID$ identifiers
+ // are not allowed to have custom format-tags.
+ av_strlcpy(format_tag, "%d", format_tag_size);
+ *ptr = next_ptr;
+ } else { // the following identifiers may have an explicit format_tag
+ if (av_strstart(identifier, "$Number", &next_ptr))
+ id_type = DASH_TMPL_ID_NUMBER;
+ else if (av_strstart(identifier, "$Bandwidth", &next_ptr))
+ id_type = DASH_TMPL_ID_BANDWIDTH;
+ else if (av_strstart(identifier, "$Time", &next_ptr))
+ id_type = DASH_TMPL_ID_TIME;
+ else
+ id_type = DASH_TMPL_ID_UNDEFINED;
+
+ // next parse the dash format-tag and generate a c-string format tag
+ // (next_ptr now points at the first '%' at the beginning of the format-tag)
+ if (id_type != DASH_TMPL_ID_UNDEFINED) {
+ const char *number_format = (id_type == DASH_TMPL_ID_TIME) ? PRId64 : "d";
+ if (next_ptr[0] == '$') { // no dash format-tag
+ snprintf(format_tag, format_tag_size, "%%%s", number_format);
+ *ptr = &next_ptr[1];
+ } else {
+ const char *width_ptr;
+ // only tolerate single-digit width-field (i.e. up to 9-digit width)
+ if (av_strstart(next_ptr, "%0", &width_ptr) &&
+ av_isdigit(width_ptr[0]) &&
+ av_strstart(&width_ptr[1], "d$", &next_ptr)) {
+ // yes, we're using a format tag to build format_tag.
+ snprintf(format_tag, format_tag_size, "%s%c%s", "%0", width_ptr[0], number_format);
+ *ptr = next_ptr;
+ } else {
+ av_log(NULL, AV_LOG_WARNING, "Failed to parse format-tag beginning with %s. Expected either a "
+ "closing '$' character or a format-string like '%%0[width]d', "
+ "where width must be a single digit\n", next_ptr);
+ id_type = DASH_TMPL_ID_UNDEFINED;
+ }
+ }
+ }
+ }
+ return id_type;
+}
+
+void ff_dash_fill_tmpl_params(char *dst, size_t buffer_size,
+ const char *template, int rep_id,
+ int number, int bit_rate,
+ int64_t time) {
+ int dst_pos = 0;
+ const char *t_cur = template;
+ while (dst_pos < buffer_size - 1 && *t_cur) {
+ char format_tag[7]; // May be "%d", "%0Xd", or "%0Xlld" (for $Time$), where X is in [0-9]
+ int n = 0;
+ DASHTmplId id_type;
+ const char *t_next = strchr(t_cur, '$'); // copy over everything up to the first '$' character
+ if (t_next) {
+ int num_copy_bytes = FFMIN(t_next - t_cur, buffer_size - dst_pos - 1);
+ av_strlcpy(&dst[dst_pos], t_cur, num_copy_bytes + 1);
+ // advance
+ dst_pos += num_copy_bytes;
+ t_cur = t_next;
+ } else { // no more DASH identifiers to substitute - just copy the rest over and break
+ av_strlcpy(&dst[dst_pos], t_cur, buffer_size - dst_pos);
+ break;
+ }
+
+ if (dst_pos >= buffer_size - 1 || !*t_cur)
+ break;
+
+ // t_cur is now pointing to a '$' character
+ id_type = dash_read_tmpl_id(t_cur, format_tag, sizeof(format_tag), &t_next);
+ switch (id_type) {
+ case DASH_TMPL_ID_ESCAPE:
+ av_strlcpy(&dst[dst_pos], "$", 2);
+ n = 1;
+ break;
+ case DASH_TMPL_ID_REP_ID:
+ n = snprintf(&dst[dst_pos], buffer_size - dst_pos, format_tag, rep_id);
+ break;
+ case DASH_TMPL_ID_NUMBER:
+ n = snprintf(&dst[dst_pos], buffer_size - dst_pos, format_tag, number);
+ break;
+ case DASH_TMPL_ID_BANDWIDTH:
+ n = snprintf(&dst[dst_pos], buffer_size - dst_pos, format_tag, bit_rate);
+ break;
+ case DASH_TMPL_ID_TIME:
+ n = snprintf(&dst[dst_pos], buffer_size - dst_pos, format_tag, time);
+ break;
+ case DASH_TMPL_ID_UNDEFINED:
+ // copy over one byte and advance
+ av_strlcpy(&dst[dst_pos], t_cur, 2);
+ n = 1;
+ t_next = &t_cur[1];
+ break;
+ }
+ // t_next points just past the processed identifier
+ // n is the number of bytes that were attempted to be written to dst
+ // (may have failed to write all because buffer_size).
+
+ // advance
+ dst_pos += FFMIN(n, buffer_size - dst_pos - 1);
+ t_cur = t_next;
+ }
+}
+
+
diff --git a/libavformat/dash.h b/libavformat/dash.h
new file mode 100644
index 0000000..c6d9d2c
--- /dev/null
+++ b/libavformat/dash.h
@@ -0,0 +1,39 @@
+/*
+ * MPEG-DASH ISO BMFF segmenter
+ * Copyright (c) 2014 Martin Storsjo
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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_DASH_H
+#define AVFORMAT_DASH_H
+#include "avformat.h"
+
+// See ISO/IEC 23009-1:2014 5.3.9.4.4
+typedef enum {
+ DASH_TMPL_ID_UNDEFINED = -1,
+ DASH_TMPL_ID_ESCAPE,
+ DASH_TMPL_ID_REP_ID,
+ DASH_TMPL_ID_NUMBER,
+ DASH_TMPL_ID_BANDWIDTH,
+ DASH_TMPL_ID_TIME,
+} DASHTmplId;
+
+
+void ff_dash_fill_tmpl_params(char *dst, size_t buffer_size, const char *template, int rep_id, int number, int bit_rate, int64_t time);
+
+#endif /* AVFORMAT_DASH_H */
diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c
new file mode 100644
index 0000000..f63f1ff
--- /dev/null
+++ b/libavformat/dashdec.c
@@ -0,0 +1,1848 @@
+/*
+ * Dynamic Adaptive Streaming over HTTP demux
+ * Copyright (c) 2017 samsamsam@o2.pl based on HLS demux
+ * Copyright (c) 2017 Steven Liu
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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 <libxml/parser.h>
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+#include "libavutil/time.h"
+#include "libavutil/parseutils.h"
+#include "internal.h"
+#include "avio_internal.h"
+#include "dash.h"
+
+#define INITIAL_BUFFER_SIZE 32768
+
+struct fragment {
+ int64_t url_offset;
+ int64_t size;
+ char *url;
+};
+
+/*
+ * reference to : ISO_IEC_23009-1-DASH-2012
+ * Section: 5.3.9.6.2
+ * Table: Table 17 — Semantics of SegmentTimeline element
+ * */
+struct timeline {
+ /* starttime: Element or Attribute Name
+ * specifies the MPD start time, in @timescale units,
+ * the first Segment in the series starts relative to the beginning of the Period.
+ * The value of this attribute must be equal to or greater than the sum of the previous S
+ * element earliest presentation time and the sum of the contiguous Segment durations.
+ * If the value of the attribute is greater than what is expressed by the previous S element,
+ * it expresses discontinuities in the timeline.
+ * If not present then the value shall be assumed to be zero for the first S element
+ * and for the subsequent S elements, the value shall be assumed to be the sum of
+ * the previous S element's earliest presentation time and contiguous duration
+ * (i.e. previous S@starttime + @duration * (@repeat + 1)).
+ * */
+ int64_t starttime;
+ /* repeat: Element or Attribute Name
+ * specifies the repeat count of the number of following contiguous Segments with
+ * the same duration expressed by the value of @duration. This value is zero-based
+ * (e.g. a value of three means four Segments in the contiguous series).
+ * */
+ int64_t repeat;
+ /* duration: Element or Attribute Name
+ * specifies the Segment duration, in units of the value of the @timescale.
+ * */
+ int64_t duration;
+};
+
+/*
+ * Each playlist has its own demuxer. If it is currently active,
+ * it has an opened AVIOContext too, and potentially an AVPacket
+ * containing the next packet from this stream.
+ */
+struct representation {
+ char *url_template;
+ AVIOContext pb;
+ AVIOContext *input;
+ AVFormatContext *parent;
+ AVFormatContext *ctx;
+ AVPacket pkt;
+ int rep_idx;
+ int rep_count;
+ int stream_index;
+
+ enum AVMediaType type;
+
+ int n_fragments;
+ struct fragment **fragments; /* VOD list of fragment for profile */
+
+ int n_timelines;
+ struct timeline **timelines;
+
+ int64_t first_seq_no;
+ int64_t last_seq_no;
+ int64_t start_number; /* used in case when we have dynamic list of segment to know which segments are new one*/
+
+ int64_t fragment_duration;
+ int64_t fragment_timescale;
+
+ int64_t presentation_timeoffset;
+
+ int64_t cur_seq_no;
+ int64_t cur_seg_offset;
+ int64_t cur_seg_size;
+ struct fragment *cur_seg;
+
+ /* Currently active Media Initialization Section */
+ struct fragment *init_section;
+ uint8_t *init_sec_buf;
+ uint32_t init_sec_buf_size;
+ uint32_t init_sec_data_len;
+ uint32_t init_sec_buf_read_offset;
+ int64_t cur_timestamp;
+ int is_restart_needed;
+};
+
+typedef struct DASHContext {
+ const AVClass *class;
+ char *base_url;
+ struct representation *cur_video;
+ struct representation *cur_audio;
+
+ /* MediaPresentationDescription Attribute */
+ uint64_t media_presentation_duration;
+ uint64_t suggested_presentation_delay;
+ uint64_t availability_start_time;
+ uint64_t publish_time;
+ uint64_t minimum_update_period;
+ uint64_t time_shift_buffer_depth;
+ uint64_t min_buffer_time;
+
+ /* Period Attribute */
+ uint64_t period_duration;
+ uint64_t period_start;
+
+ int is_live;
+ 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
+ char *allowed_extensions;
+ AVDictionary *avio_opts;
+} DASHContext;
+
+static uint64_t get_current_time_in_sec(void)
+{
+ return av_gettime() / 1000000;
+}
+
+static uint64_t get_utc_date_time_insec(AVFormatContext *s, const char *datetime)
+{
+ struct tm timeinfo;
+ int year = 0;
+ int month = 0;
+ int day = 0;
+ int hour = 0;
+ int minute = 0;
+ int ret = 0;
+ float second = 0.0;
+
+ /* ISO-8601 date parser */
+ if (!datetime)
+ return 0;
+
+ ret = sscanf(datetime, "%d-%d-%dT%d:%d:%fZ", &year, &month, &day, &hour, &minute, &second);
+ /* year, month, day, hour, minute, second 6 arguments */
+ if (ret != 6) {
+ av_log(s, AV_LOG_WARNING, "get_utc_date_time_insec get a wrong time format\n");
+ }
+ timeinfo.tm_year = year - 1900;
+ timeinfo.tm_mon = month - 1;
+ timeinfo.tm_mday = day;
+ timeinfo.tm_hour = hour;
+ timeinfo.tm_min = minute;
+ timeinfo.tm_sec = (int)second;
+
+ return av_timegm(&timeinfo);
+}
+
+static uint32_t get_duration_insec(AVFormatContext *s, const char *duration)
+{
+ /* ISO-8601 duration parser */
+ uint32_t days = 0;
+ uint32_t hours = 0;
+ uint32_t mins = 0;
+ uint32_t secs = 0;
+ int size = 0;
+ float value = 0;
+ char type = '\0';
+ const char *ptr = duration;
+
+ while (*ptr) {
+ if (*ptr == 'P' || *ptr == 'T') {
+ ptr++;
+ continue;
+ }
+
+ if (sscanf(ptr, "%f%c%n", &value, &type, &size) != 2) {
+ av_log(s, AV_LOG_WARNING, "get_duration_insec get a wrong time format\n");
+ return 0; /* parser error */
+ }
+ switch (type) {
+ case 'D':
+ days = (uint32_t)value;
+ break;
+ case 'H':
+ hours = (uint32_t)value;
+ break;
+ case 'M':
+ mins = (uint32_t)value;
+ break;
+ case 'S':
+ secs = (uint32_t)value;
+ break;
+ default:
+ // handle invalid type
+ break;
+ }
+ ptr += size;
+ }
+ return ((days * 24 + hours) * 60 + mins) * 60 + secs;
+}
+
+static int64_t get_segment_start_time_based_on_timeline(struct representation *pls, int64_t cur_seq_no)
+{
+ int64_t start_time = 0;
+ int64_t i = 0;
+ int64_t j = 0;
+ int64_t num = 0;
+
+ if (pls->n_timelines) {
+ for (i = 0; i < pls->n_timelines; i++) {
+ if (pls->timelines[i]->starttime > 0) {
+ start_time = pls->timelines[i]->starttime;
+ }
+ if (num == cur_seq_no)
+ goto finish;
+
+ start_time += pls->timelines[i]->duration;
+ for (j = 0; j < pls->timelines[i]->repeat; j++) {
+ num++;
+ if (num == cur_seq_no)
+ goto finish;
+ start_time += pls->timelines[i]->duration;
+ }
+ num++;
+ }
+ }
+finish:
+ return start_time;
+}
+
+static int64_t calc_next_seg_no_from_timelines(struct representation *pls, int64_t cur_time)
+{
+ int64_t i = 0;
+ int64_t j = 0;
+ int64_t num = 0;
+ int64_t start_time = 0;
+
+ for (i = 0; i < pls->n_timelines; i++) {
+ if (pls->timelines[i]->starttime > 0) {
+ start_time = pls->timelines[i]->starttime;
+ }
+ if (start_time > cur_time)
+ goto finish;
+
+ start_time += pls->timelines[i]->duration;
+ for (j = 0; j < pls->timelines[i]->repeat; j++) {
+ num++;
+ if (start_time > cur_time)
+ goto finish;
+ start_time += pls->timelines[i]->duration;
+ }
+ num++;
+ }
+
+ return -1;
+
+finish:
+ return num;
+}
+
+static void free_fragment(struct fragment **seg)
+{
+ if (!(*seg)) {
+ return;
+ }
+ av_freep(&(*seg)->url);
+ av_freep(seg);
+}
+
+static void free_fragment_list(struct representation *pls)
+{
+ int i;
+
+ for (i = 0; i < pls->n_fragments; i++) {
+ free_fragment(&pls->fragments[i]);
+ }
+ av_freep(&pls->fragments);
+ pls->n_fragments = 0;
+}
+
+static void free_timelines_list(struct representation *pls)
+{
+ int i;
+
+ for (i = 0; i < pls->n_timelines; i++) {
+ av_freep(&pls->timelines[i]);
+ }
+ av_freep(&pls->timelines);
+ pls->n_timelines = 0;
+}
+
+static void free_representation(struct representation *pls)
+{
+ free_fragment_list(pls);
+ free_timelines_list(pls);
+ free_fragment(&pls->cur_seg);
+ free_fragment(&pls->init_section);
+ av_freep(&pls->init_sec_buf);
+ av_freep(&pls->pb.buffer);
+ if (pls->input)
+ ff_format_io_close(pls->parent, &pls->input);
+ if (pls->ctx) {
+ pls->ctx->pb = NULL;
+ avformat_close_input(&pls->ctx);
+ }
+
+ av_freep(&pls->url_template);
+ av_freep(pls);
+}
+
+static void set_httpheader_options(DASHContext *c, AVDictionary *opts)
+{
+ // 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);
+ if (c->is_live) {
+ av_dict_set(&opts, "seekable", "0", 0);
+ }
+}
+static void update_options(char **dest, const char *name, void *src)
+{
+ av_freep(dest);
+ av_opt_get(src, name, AV_OPT_SEARCH_CHILDREN, (uint8_t**)dest);
+ if (*dest)
+ av_freep(dest);
+}
+
+static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
+ AVDictionary *opts, AVDictionary *opts2, int *is_http)
+{
+ DASHContext *c = s->priv_data;
+ AVDictionary *tmp = NULL;
+ const char *proto_name = NULL;
+ int ret;
+
+ av_dict_copy(&tmp, opts, 0);
+ av_dict_copy(&tmp, opts2, 0);
+
+ if (av_strstart(url, "crypto", NULL)) {
+ if (url[6] == '+' || url[6] == ':')
+ proto_name = avio_find_protocol_name(url + 7);
+ }
+
+ if (!proto_name)
+ proto_name = avio_find_protocol_name(url);
+
+ if (!proto_name)
+ return AVERROR_INVALIDDATA;
+
+ // only http(s) & file are allowed
+ if (av_strstart(proto_name, "file", NULL)) {
+ if (strcmp(c->allowed_extensions, "ALL") && !av_match_ext(url, c->allowed_extensions)) {
+ av_log(s, AV_LOG_ERROR,
+ "Filename extension of \'%s\' is not a common multimedia extension, blocked for security reasons.\n"
+ "If you wish to override this adjust allowed_extensions, you can set it to \'ALL\' to allow all\n",
+ url);
+ return AVERROR_INVALIDDATA;
+ }
+ } else if (av_strstart(proto_name, "http", NULL)) {
+ ;
+ } else
+ return AVERROR_INVALIDDATA;
+
+ if (!strncmp(proto_name, url, strlen(proto_name)) && url[strlen(proto_name)] == ':')
+ ;
+ else if (av_strstart(url, "crypto", NULL) && !strncmp(proto_name, url + 7, strlen(proto_name)) && url[7 + strlen(proto_name)] == ':')
+ ;
+ else if (strcmp(proto_name, "file") || !strncmp(url, "file,", 5))
+ return AVERROR_INVALIDDATA;
+
+ ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp);
+ if (ret >= 0) {
+ // update cookies on http response with setcookies.
+ char *new_cookies = NULL;
+
+ if (!(s->flags & AVFMT_FLAG_CUSTOM_IO))
+ av_opt_get(*pb, "cookies", AV_OPT_SEARCH_CHILDREN, (uint8_t**)&new_cookies);
+
+ if (new_cookies) {
+ av_free(c->cookies);
+ c->cookies = new_cookies;
+ }
+
+ av_dict_set(&opts, "cookies", c->cookies, 0);
+ }
+
+ av_dict_free(&tmp);
+
+ if (is_http)
+ *is_http = av_strstart(proto_name, "http", NULL);
+
+ return ret;
+}
+
+static char *get_content_url(xmlNodePtr *baseurl_nodes,
+ int n_baseurl_nodes,
+ char *rep_id_val,
+ char *rep_bandwidth_val,
+ char *val)
+{
+ int i;
+ char *text;
+ char *url = NULL;
+ char tmp_str[MAX_URL_SIZE];
+ char tmp_str_2[MAX_URL_SIZE];
+
+ memset(tmp_str, 0, sizeof(tmp_str));
+
+ for (i = 0; i < n_baseurl_nodes; ++i) {
+ if (baseurl_nodes[i] &&
+ baseurl_nodes[i]->children &&
+ baseurl_nodes[i]->children->type == XML_TEXT_NODE) {
+ text = xmlNodeGetContent(baseurl_nodes[i]->children);
+ if (text) {
+ memset(tmp_str, 0, sizeof(tmp_str));
+ memset(tmp_str_2, 0, sizeof(tmp_str_2));
+ ff_make_absolute_url(tmp_str_2, MAX_URL_SIZE, tmp_str, text);
+ av_strlcpy(tmp_str, tmp_str_2, sizeof(tmp_str));
+ xmlFree(text);
+ }
+ }
+ }
+
+ if (val)
+ av_strlcat(tmp_str, (const char*)val, sizeof(tmp_str));
+
+ if (rep_id_val) {
+ url = av_strireplace(tmp_str, "$RepresentationID$", (const char*)rep_id_val);
+ if (!url) {
+ return NULL;
+ }
+ av_strlcpy(tmp_str, url, sizeof(tmp_str));
+ av_free(url);
+ }
+ if (rep_bandwidth_val && tmp_str[0] != '\0') {
+ url = av_strireplace(tmp_str, "$Bandwidth$", (const char*)rep_bandwidth_val);
+ if (!url) {
+ return NULL;
+ }
+ }
+ return url;
+}
+
+static char *get_val_from_nodes_tab(xmlNodePtr *nodes, const int n_nodes, const char *attrname)
+{
+ int i;
+ char *val;
+
+ for (i = 0; i < n_nodes; ++i) {
+ if (nodes[i]) {
+ val = xmlGetProp(nodes[i], attrname);
+ if (val)
+ return val;
+ }
+ }
+
+ return NULL;
+}
+
+static xmlNodePtr find_child_node_by_name(xmlNodePtr rootnode, const char *nodename)
+{
+ xmlNodePtr node = rootnode;
+ if (!node) {
+ return NULL;
+ }
+
+ node = xmlFirstElementChild(node);
+ while (node) {
+ if (!av_strcasecmp(node->name, nodename)) {
+ return node;
+ }
+ node = xmlNextElementSibling(node);
+ }
+ return NULL;
+}
+
+static enum AVMediaType get_content_type(xmlNodePtr node)
+{
+ enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
+ int i = 0;
+ const char *attr;
+ char *val = NULL;
+
+ if (node) {
+ for (i = 0; i < 2; i++) {
+ attr = i ? "mimeType" : "contentType";
+ val = xmlGetProp(node, attr);
+ if (val) {
+ if (av_stristr((const char *)val, "video")) {
+ type = AVMEDIA_TYPE_VIDEO;
+ } else if (av_stristr((const char *)val, "audio")) {
+ type = AVMEDIA_TYPE_AUDIO;
+ }
+ xmlFree(val);
+ }
+ }
+ }
+ return type;
+}
+
+static int parse_manifest_segmenturlnode(AVFormatContext *s, struct representation *rep,
+ xmlNodePtr fragmenturl_node,
+ xmlNodePtr *baseurl_nodes,
+ char *rep_id_val,
+ char *rep_bandwidth_val)
+{
+ char *initialization_val = NULL;
+ char *media_val = NULL;
+
+ if (!av_strcasecmp(fragmenturl_node->name, (const char *)"Initialization")) {
+ initialization_val = xmlGetProp(fragmenturl_node, "sourceURL");
+ if (initialization_val) {
+ rep->init_section = av_mallocz(sizeof(struct fragment));
+ if (!rep->init_section) {
+ xmlFree(initialization_val);
+ return AVERROR(ENOMEM);
+ }
+ rep->init_section->url = get_content_url(baseurl_nodes, 4,
+ rep_id_val,
+ rep_bandwidth_val,
+ initialization_val);
+ if (!rep->init_section->url) {
+ av_free(rep->init_section);
+ xmlFree(initialization_val);
+ return AVERROR(ENOMEM);
+ }
+ rep->init_section->size = -1;
+ xmlFree(initialization_val);
+ }
+ } else if (!av_strcasecmp(fragmenturl_node->name, (const char *)"SegmentURL")) {
+ media_val = xmlGetProp(fragmenturl_node, "media");
+ if (media_val) {
+ struct fragment *seg = av_mallocz(sizeof(struct fragment));
+ if (!seg) {
+ xmlFree(media_val);
+ return AVERROR(ENOMEM);
+ }
+ seg->url = get_content_url(baseurl_nodes, 4,
+ rep_id_val,
+ rep_bandwidth_val,
+ media_val);
+ if (!seg->url) {
+ av_free(seg);
+ xmlFree(media_val);
+ return AVERROR(ENOMEM);
+ }
+ seg->size = -1;
+ dynarray_add(&rep->fragments, &rep->n_fragments, seg);
+ xmlFree(media_val);
+ }
+ }
+
+ return 0;
+}
+
+static int parse_manifest_segmenttimeline(AVFormatContext *s, struct representation *rep,
+ xmlNodePtr fragment_timeline_node)
+{
+ xmlAttrPtr attr = NULL;
+ char *val = NULL;
+
+ if (!av_strcasecmp(fragment_timeline_node->name, (const char *)"S")) {
+ struct timeline *tml = av_mallocz(sizeof(struct timeline));
+ if (!tml) {
+ return AVERROR(ENOMEM);
+ }
+ attr = fragment_timeline_node->properties;
+ while (attr) {
+ val = xmlGetProp(fragment_timeline_node, attr->name);
+
+ if (!val) {
+ av_log(s, AV_LOG_WARNING, "parse_manifest_segmenttimeline attr->name = %s val is NULL\n", attr->name);
+ continue;
+ }
+
+ if (!av_strcasecmp(attr->name, (const char *)"t")) {
+ tml->starttime = (int64_t)strtoll(val, NULL, 10);
+ } else if (!av_strcasecmp(attr->name, (const char *)"r")) {
+ tml->repeat =(int64_t) strtoll(val, NULL, 10);
+ } else if (!av_strcasecmp(attr->name, (const char *)"d")) {
+ tml->duration = (int64_t)strtoll(val, NULL, 10);
+ }
+ attr = attr->next;
+ xmlFree(val);
+ }
+ dynarray_add(&rep->timelines, &rep->n_timelines, tml);
+ }
+
+ return 0;
+}
+
+static int parse_manifest_representation(AVFormatContext *s, const char *url,
+ xmlNodePtr node,
+ xmlNodePtr adaptionset_node,
+ xmlNodePtr mpd_baseurl_node,
+ xmlNodePtr period_baseurl_node,
+ xmlNodePtr fragment_template_node,
+ xmlNodePtr content_component_node,
+ xmlNodePtr adaptionset_baseurl_node)
+{
+ int32_t ret = 0;
+ int32_t audio_rep_idx = 0;
+ int32_t video_rep_idx = 0;
+ DASHContext *c = s->priv_data;
+ struct representation *rep = NULL;
+ struct fragment *seg = NULL;
+ xmlNodePtr representation_segmenttemplate_node = NULL;
+ xmlNodePtr representation_baseurl_node = NULL;
+ xmlNodePtr representation_segmentlist_node = NULL;
+ xmlNodePtr fragment_timeline_node = NULL;
+ xmlNodePtr fragment_templates_tab[2];
+ char *duration_val = NULL;
+ char *presentation_timeoffset_val = NULL;
+ char *startnumber_val = NULL;
+ char *timescale_val = NULL;
+ char *initialization_val = NULL;
+ char *media_val = NULL;
+ xmlNodePtr baseurl_nodes[4];
+ xmlNodePtr representation_node = node;
+ char *rep_id_val = xmlGetProp(representation_node, "id");
+ char *rep_bandwidth_val = xmlGetProp(representation_node, "bandwidth");
+ enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
+
+ // try get information from representation
+ if (type == AVMEDIA_TYPE_UNKNOWN)
+ type = get_content_type(representation_node);
+ // try get information from contentComponen
+ if (type == AVMEDIA_TYPE_UNKNOWN)
+ type = get_content_type(content_component_node);
+ // try get information from adaption set
+ if (type == AVMEDIA_TYPE_UNKNOWN)
+ type = get_content_type(adaptionset_node);
+ if (type == AVMEDIA_TYPE_UNKNOWN) {
+ av_log(s, AV_LOG_VERBOSE, "Parsing '%s' - skipp not supported representation type\n", url);
+ } else if ((type == AVMEDIA_TYPE_VIDEO && !c->cur_video) || (type == AVMEDIA_TYPE_AUDIO && !c->cur_audio)) {
+ // convert selected representation to our internal struct
+ rep = av_mallocz(sizeof(struct representation));
+ if (!rep) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ representation_segmenttemplate_node = find_child_node_by_name(representation_node, "SegmentTemplate");
+ representation_baseurl_node = find_child_node_by_name(representation_node, "BaseURL");
+ representation_segmentlist_node = find_child_node_by_name(representation_node, "SegmentList");
+
+ baseurl_nodes[0] = mpd_baseurl_node;
+ baseurl_nodes[1] = period_baseurl_node;
+ baseurl_nodes[2] = adaptionset_baseurl_node;
+ baseurl_nodes[3] = representation_baseurl_node;
+
+ if (representation_segmenttemplate_node || fragment_template_node) {
+ fragment_timeline_node = NULL;
+ fragment_templates_tab[0] = representation_segmenttemplate_node;
+ fragment_templates_tab[1] = fragment_template_node;
+
+ presentation_timeoffset_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "presentationTimeOffset");
+ duration_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "duration");
+ startnumber_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "startNumber");
+ timescale_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "timescale");
+ initialization_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "initialization");
+ media_val = get_val_from_nodes_tab(fragment_templates_tab, 2, "media");
+
+ if (initialization_val) {
+ rep->init_section = av_mallocz(sizeof(struct fragment));
+ if (!rep->init_section) {
+ av_free(rep);
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ rep->init_section->url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, initialization_val);
+ if (!rep->init_section->url) {
+ av_free(rep->init_section);
+ av_free(rep);
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ rep->init_section->size = -1;
+ xmlFree(initialization_val);
+ }
+
+ if (media_val) {
+ rep->url_template = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val);
+ xmlFree(media_val);
+ }
+
+ if (presentation_timeoffset_val) {
+ rep->presentation_timeoffset = (int64_t) strtoll(presentation_timeoffset_val, NULL, 10);
+ xmlFree(presentation_timeoffset_val);
+ }
+ if (duration_val) {
+ rep->fragment_duration = (int64_t) strtoll(duration_val, NULL, 10);
+ xmlFree(duration_val);
+ }
+ if (timescale_val) {
+ rep->fragment_timescale = (int64_t) strtoll(timescale_val, NULL, 10);
+ xmlFree(timescale_val);
+ }
+ if (startnumber_val) {
+ rep->first_seq_no = (int64_t) strtoll(startnumber_val, NULL, 10);
+ xmlFree(startnumber_val);
+ }
+
+ fragment_timeline_node = find_child_node_by_name(representation_segmenttemplate_node, "SegmentTimeline");
+
+ if (!fragment_timeline_node)
+ fragment_timeline_node = find_child_node_by_name(fragment_template_node, "SegmentTimeline");
+ if (fragment_timeline_node) {
+ fragment_timeline_node = xmlFirstElementChild(fragment_timeline_node);
+ while (fragment_timeline_node) {
+ ret = parse_manifest_segmenttimeline(s, rep, fragment_timeline_node);
+ if (ret < 0) {
+ return ret;
+ }
+ fragment_timeline_node = xmlNextElementSibling(fragment_timeline_node);
+ }
+ }
+ } else if (representation_baseurl_node && !representation_segmentlist_node) {
+ seg = av_mallocz(sizeof(struct fragment));
+ if (!seg) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ seg->url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, NULL);
+ if (!seg->url) {
+ av_free(seg);
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ seg->size = -1;
+ dynarray_add(&rep->fragments, &rep->n_fragments, seg);
+ } else if (representation_segmentlist_node) {
+ // TODO: https://www.brendanlong.com/the-structure-of-an-mpeg-dash-mpd.html
+ // http://www-itec.uni-klu.ac.at/dash/ddash/mpdGenerator.php?fragmentlength=15&type=full
+ xmlNodePtr fragmenturl_node = NULL;
+ duration_val = xmlGetProp(representation_segmentlist_node, "duration");
+ timescale_val = xmlGetProp(representation_segmentlist_node, "timescale");
+ if (duration_val) {
+ rep->fragment_duration = (int64_t) strtoll(duration_val, NULL, 10);
+ xmlFree(duration_val);
+ }
+ if (timescale_val) {
+ rep->fragment_timescale = (int64_t) strtoll(timescale_val, NULL, 10);
+ xmlFree(timescale_val);
+ }
+ fragmenturl_node = xmlFirstElementChild(representation_segmentlist_node);
+ while (fragmenturl_node) {
+ ret = parse_manifest_segmenturlnode(s, rep, fragmenturl_node,
+ baseurl_nodes,
+ rep_id_val,
+ rep_bandwidth_val);
+ if (ret < 0) {
+ return ret;
+ }
+ fragmenturl_node = xmlNextElementSibling(fragmenturl_node);
+ }
+
+ fragment_timeline_node = find_child_node_by_name(representation_segmenttemplate_node, "SegmentTimeline");
+
+ if (!fragment_timeline_node)
+ fragment_timeline_node = find_child_node_by_name(fragment_template_node, "SegmentTimeline");
+ if (fragment_timeline_node) {
+ fragment_timeline_node = xmlFirstElementChild(fragment_timeline_node);
+ while (fragment_timeline_node) {
+ ret = parse_manifest_segmenttimeline(s, rep, fragment_timeline_node);
+ if (ret < 0) {
+ return ret;
+ }
+ fragment_timeline_node = xmlNextElementSibling(fragment_timeline_node);
+ }
+ }
+ } else {
+ free_representation(rep);
+ rep = NULL;
+ av_log(s, AV_LOG_ERROR, "Unknown format of Representation node id[%s] \n", (const char *)rep_id_val);
+ }
+
+ if (rep) {
+ if (rep->fragment_duration > 0 && !rep->fragment_timescale)
+ rep->fragment_timescale = 1;
+ if (type == AVMEDIA_TYPE_VIDEO) {
+ rep->rep_idx = video_rep_idx;
+ c->cur_video = rep;
+ } else {
+ rep->rep_idx = audio_rep_idx;
+ c->cur_audio = rep;
+ }
+ }
+ }
+
+ video_rep_idx += type == AVMEDIA_TYPE_VIDEO;
+ audio_rep_idx += type == AVMEDIA_TYPE_AUDIO;
+
+end:
+ if (rep_id_val)
+ xmlFree(rep_id_val);
+ if (rep_bandwidth_val)
+ xmlFree(rep_bandwidth_val);
+
+ return ret;
+}
+
+static int parse_manifest_adaptationset(AVFormatContext *s, const char *url,
+ xmlNodePtr adaptionset_node,
+ xmlNodePtr mpd_baseurl_node,
+ xmlNodePtr period_baseurl_node)
+{
+ int ret = 0;
+ xmlNodePtr fragment_template_node = NULL;
+ xmlNodePtr content_component_node = NULL;
+ xmlNodePtr adaptionset_baseurl_node = NULL;
+ xmlNodePtr node = NULL;
+
+ node = xmlFirstElementChild(adaptionset_node);
+ while (node) {
+ if (!av_strcasecmp(node->name, (const char *)"SegmentTemplate")) {
+ fragment_template_node = node;
+ } else if (!av_strcasecmp(node->name, (const char *)"ContentComponent")) {
+ content_component_node = node;
+ } else if (!av_strcasecmp(node->name, (const char *)"BaseURL")) {
+ adaptionset_baseurl_node = node;
+ } else if (!av_strcasecmp(node->name, (const char *)"Representation")) {
+ ret = parse_manifest_representation(s, url, node,
+ adaptionset_node,
+ mpd_baseurl_node,
+ period_baseurl_node,
+ fragment_template_node,
+ content_component_node,
+ adaptionset_baseurl_node);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ node = xmlNextElementSibling(node);
+ }
+ return 0;
+}
+
+static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in)
+{
+ DASHContext *c = s->priv_data;
+ int ret = 0;
+ int close_in = 0;
+ uint8_t *new_url = NULL;
+ int64_t filesize = 0;
+ char *buffer = NULL;
+ AVDictionary *opts = NULL;
+ xmlDoc *doc = NULL;
+ xmlNodePtr root_element = NULL;
+ xmlNodePtr node = NULL;
+ xmlNodePtr period_node = NULL;
+ xmlNodePtr mpd_baseurl_node = NULL;
+ xmlNodePtr period_baseurl_node = NULL;
+ xmlNodePtr adaptionset_node = NULL;
+ xmlAttrPtr attr = NULL;
+ char *val = NULL;
+ uint32_t perdiod_duration_sec = 0;
+ uint32_t perdiod_start_sec = 0;
+ int32_t audio_rep_idx = 0;
+ int32_t video_rep_idx = 0;
+
+ if (!in) {
+ close_in = 1;
+
+ set_httpheader_options(c, opts);
+ ret = avio_open2(&in, url, AVIO_FLAG_READ, c->interrupt_callback, &opts);
+ av_dict_free(&opts);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >= 0) {
+ c->base_url = av_strdup(new_url);
+ } else {
+ c->base_url = av_strdup(url);
+ }
+
+ filesize = avio_size(in);
+ if (filesize <= 0) {
+ filesize = 8 * 1024;
+ }
+
+ buffer = av_mallocz(filesize);
+ if (!buffer) {
+ av_free(c->base_url);
+ return AVERROR(ENOMEM);
+ }
+
+ filesize = avio_read(in, buffer, filesize);
+ if (filesize <= 0) {
+ av_log(s, AV_LOG_ERROR, "Unable to read to offset '%s'\n", url);
+ ret = AVERROR_INVALIDDATA;
+ } else {
+ LIBXML_TEST_VERSION
+
+ doc = xmlReadMemory(buffer, filesize, c->base_url, NULL, 0);
+ root_element = xmlDocGetRootElement(doc);
+ node = root_element;
+
+ if (!node) {
+ ret = AVERROR_INVALIDDATA;
+ av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing root node\n", url);
+ goto cleanup;
+ }
+
+ if (node->type != XML_ELEMENT_NODE ||
+ av_strcasecmp(node->name, (const char *)"MPD")) {
+ ret = AVERROR_INVALIDDATA;
+ av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - wrong root node name[%s] type[%d]\n", url, node->name, (int)node->type);
+ goto cleanup;
+ }
+
+ val = xmlGetProp(node, "type");
+ if (!val) {
+ av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing type attrib\n", url);
+ ret = AVERROR_INVALIDDATA;
+ goto cleanup;
+ }
+ if (!av_strcasecmp(val, (const char *)"dynamic"))
+ c->is_live = 1;
+ xmlFree(val);
+
+ attr = node->properties;
+ while (attr) {
+ val = xmlGetProp(node, attr->name);
+
+ if (!av_strcasecmp(attr->name, (const char *)"availabilityStartTime")) {
+ c->availability_start_time = get_utc_date_time_insec(s, (const char *)val);
+ } else if (!av_strcasecmp(attr->name, (const char *)"publishTime")) {
+ c->publish_time = get_utc_date_time_insec(s, (const char *)val);
+ } else if (!av_strcasecmp(attr->name, (const char *)"minimumUpdatePeriod")) {
+ c->minimum_update_period = get_duration_insec(s, (const char *)val);
+ } else if (!av_strcasecmp(attr->name, (const char *)"timeShiftBufferDepth")) {
+ c->time_shift_buffer_depth = get_duration_insec(s, (const char *)val);
+ } else if (!av_strcasecmp(attr->name, (const char *)"minBufferTime")) {
+ c->min_buffer_time = get_duration_insec(s, (const char *)val);
+ } else if (!av_strcasecmp(attr->name, (const char *)"suggestedPresentationDelay")) {
+ c->suggested_presentation_delay = get_duration_insec(s, (const char *)val);
+ } else if (!av_strcasecmp(attr->name, (const char *)"mediaPresentationDuration")) {
+ c->media_presentation_duration = get_duration_insec(s, (const char *)val);
+ }
+ attr = attr->next;
+ xmlFree(val);
+ }
+
+ mpd_baseurl_node = find_child_node_by_name(node, "BaseURL");
+
+ // at now we can handle only one period, with the longest duration
+ node = xmlFirstElementChild(node);
+ while (node) {
+ if (!av_strcasecmp(node->name, (const char *)"Period")) {
+ perdiod_duration_sec = 0;
+ perdiod_start_sec = 0;
+ attr = node->properties;
+ while (attr) {
+ val = xmlGetProp(node, attr->name);
+ if (!av_strcasecmp(attr->name, (const char *)"duration")) {
+ perdiod_duration_sec = get_duration_insec(s, (const char *)val);
+ } else if (!av_strcasecmp(attr->name, (const char *)"start")) {
+ perdiod_start_sec = get_duration_insec(s, (const char *)val);
+ }
+ attr = attr->next;
+ xmlFree(val);
+ }
+ if ((perdiod_duration_sec) >= (c->period_duration)) {
+ period_node = node;
+ c->period_duration = perdiod_duration_sec;
+ c->period_start = perdiod_start_sec;
+ if (c->period_start > 0)
+ c->media_presentation_duration = c->period_duration;
+ }
+ }
+ node = xmlNextElementSibling(node);
+ }
+ if (!period_node) {
+ av_log(s, AV_LOG_ERROR, "Unable to parse '%s' - missing Period node\n", url);
+ ret = AVERROR_INVALIDDATA;
+ goto cleanup;
+ }
+
+ adaptionset_node = xmlFirstElementChild(period_node);
+ while (adaptionset_node) {
+ if (!av_strcasecmp(adaptionset_node->name, (const char *)"BaseURL")) {
+ period_baseurl_node = adaptionset_node;
+ } else if (!av_strcasecmp(adaptionset_node->name, (const char *)"AdaptationSet")) {
+ parse_manifest_adaptationset(s, url, adaptionset_node, mpd_baseurl_node, period_baseurl_node);
+ }
+ adaptionset_node = xmlNextElementSibling(adaptionset_node);
+ }
+ if (c->cur_video) {
+ c->cur_video->rep_count = video_rep_idx;
+ av_log(s, AV_LOG_VERBOSE, "rep_idx[%d]\n", (int)c->cur_video->rep_idx);
+ av_log(s, AV_LOG_VERBOSE, "rep_count[%d]\n", (int)video_rep_idx);
+ }
+ if (c->cur_audio) {
+ c->cur_audio->rep_count = audio_rep_idx;
+ }
+cleanup:
+ /*free the document */
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ }
+
+ av_free(new_url);
+ av_free(buffer);
+ if (close_in) {
+ avio_close(in);
+ }
+ return ret;
+}
+
+static int64_t calc_cur_seg_no(AVFormatContext *s, struct representation *pls)
+{
+ DASHContext *c = s->priv_data;
+ int64_t num = 0;
+ int64_t start_time_offset = 0;
+
+ if (c->is_live) {
+ if (pls->n_fragments) {
+ num = pls->first_seq_no;
+ } else if (pls->n_timelines) {
+ start_time_offset = get_segment_start_time_based_on_timeline(pls, 0xFFFFFFFF) - pls->timelines[pls->first_seq_no]->starttime; // total duration of playlist
+ if (start_time_offset < 60 * pls->fragment_timescale)
+ start_time_offset = 0;
+ else
+ start_time_offset = start_time_offset - 60 * pls->fragment_timescale;
+
+ num = calc_next_seg_no_from_timelines(pls, pls->timelines[pls->first_seq_no]->starttime + start_time_offset);
+ if (num == -1)
+ num = pls->first_seq_no;
+ } else if (pls->fragment_duration){
+ if (pls->presentation_timeoffset) {
+ num = pls->presentation_timeoffset * pls->fragment_timescale / pls->fragment_duration;
+ } else if (c->publish_time > 0 && !c->availability_start_time) {
+ num = pls->first_seq_no + (((c->publish_time - c->availability_start_time) - c->suggested_presentation_delay) * pls->fragment_timescale) / pls->fragment_duration;
+ } else {
+ num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->suggested_presentation_delay) * pls->fragment_timescale) / pls->fragment_duration;
+ }
+ }
+ } else {
+ num = pls->first_seq_no;
+ }
+ return num;
+}
+
+static int64_t calc_min_seg_no(AVFormatContext *s, struct representation *pls)
+{
+ DASHContext *c = s->priv_data;
+ int64_t num = 0;
+
+ if (c->is_live && pls->fragment_duration) {
+ num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time) - c->time_shift_buffer_depth) * pls->fragment_timescale) / pls->fragment_duration;
+ } else {
+ num = pls->first_seq_no;
+ }
+ return num;
+}
+
+static int64_t calc_max_seg_no(struct representation *pls)
+{
+ DASHContext *c = pls->parent->priv_data;
+ int64_t num = 0;
+
+ if (pls->n_fragments) {
+ num = pls->first_seq_no + pls->n_fragments - 1;
+ } else if (pls->n_timelines) {
+ int i = 0;
+ num = pls->first_seq_no + pls->n_timelines - 1;
+ for (i = 0; i < pls->n_timelines; i++) {
+ num += pls->timelines[i]->repeat;
+ }
+ } else if (c->is_live && pls->fragment_duration) {
+ num = pls->first_seq_no + (((get_current_time_in_sec() - c->availability_start_time)) * pls->fragment_timescale) / pls->fragment_duration;
+ } else if (pls->fragment_duration) {
+ num = pls->first_seq_no + (c->media_presentation_duration * pls->fragment_timescale) / pls->fragment_duration;
+ }
+
+ return num;
+}
+
+static void move_timelines(struct representation *rep_src, struct representation *rep_dest)
+{
+ if (rep_dest && rep_src ) {
+ free_timelines_list(rep_dest);
+ rep_dest->timelines = rep_src->timelines;
+ rep_dest->n_timelines = rep_src->n_timelines;
+ rep_dest->first_seq_no = rep_src->first_seq_no;
+ rep_dest->last_seq_no = calc_max_seg_no(rep_dest);
+ rep_src->timelines = NULL;
+ rep_src->n_timelines = 0;
+ rep_dest->cur_seq_no = rep_src->cur_seq_no;
+ }
+}
+
+static void move_segments(struct representation *rep_src, struct representation *rep_dest)
+{
+ if (rep_dest && rep_src ) {
+ free_fragment_list(rep_dest);
+ if (rep_src->start_number > (rep_dest->start_number + rep_dest->n_fragments))
+ rep_dest->cur_seq_no = 0;
+ else
+ rep_dest->cur_seq_no += rep_src->start_number - rep_dest->start_number;
+ rep_dest->fragments = rep_src->fragments;
+ rep_dest->n_fragments = rep_src->n_fragments;
+ rep_dest->parent = rep_src->parent;
+ rep_dest->last_seq_no = calc_max_seg_no(rep_dest);
+ rep_src->fragments = NULL;
+ rep_src->n_fragments = 0;
+ }
+}
+
+
+static int refresh_manifest(AVFormatContext *s)
+{
+
+ int ret = 0;
+ DASHContext *c = s->priv_data;
+
+ // save current context
+ struct representation *cur_video = c->cur_video;
+ struct representation *cur_audio = c->cur_audio;
+ char *base_url = c->base_url;
+
+ c->base_url = NULL;
+ c->cur_video = NULL;
+ c->cur_audio = NULL;
+ ret = parse_manifest(s, s->filename, NULL);
+ if (ret)
+ goto finish;
+
+ if (cur_video && cur_video->timelines || cur_audio && cur_audio->timelines) {
+ // calc current time
+ int64_t currentVideoTime = 0;
+ int64_t currentAudioTime = 0;
+ if (cur_video && cur_video->timelines)
+ currentVideoTime = get_segment_start_time_based_on_timeline(cur_video, cur_video->cur_seq_no) / cur_video->fragment_timescale;
+ if (cur_audio && cur_audio->timelines)
+ currentAudioTime = get_segment_start_time_based_on_timeline(cur_audio, cur_audio->cur_seq_no) / cur_audio->fragment_timescale;
+ // update segments
+ if (cur_video && cur_video->timelines) {
+ c->cur_video->cur_seq_no = calc_next_seg_no_from_timelines(c->cur_video, currentVideoTime * cur_video->fragment_timescale - 1);
+ if (c->cur_video->cur_seq_no >= 0) {
+ move_timelines(c->cur_video, cur_video);
+ }
+ }
+ if (cur_audio && cur_audio->timelines) {
+ c->cur_audio->cur_seq_no = calc_next_seg_no_from_timelines(c->cur_audio, currentAudioTime * cur_audio->fragment_timescale - 1);
+ if (c->cur_audio->cur_seq_no >= 0) {
+ move_timelines(c->cur_audio, cur_audio);
+ }
+ }
+ }
+ if (cur_video && cur_video->fragments) {
+ move_segments(c->cur_video, cur_video);
+ }
+ if (cur_audio && cur_audio->fragments) {
+ move_segments(c->cur_audio, cur_audio);
+ }
+
+finish:
+ // restore context
+ if (c->base_url)
+ av_free(base_url);
+ else
+ c->base_url = base_url;
+ if (c->cur_audio)
+ free_representation(c->cur_audio);
+ if (c->cur_video)
+ free_representation(c->cur_video);
+ c->cur_audio = cur_audio;
+ c->cur_video = cur_video;
+ return ret;
+}
+
+static struct fragment *get_current_fragment(struct representation *pls)
+{
+ int64_t min_seq_no = 0;
+ int64_t max_seq_no = 0;
+ struct fragment *seg = NULL;
+ struct fragment *seg_ptr = NULL;
+ DASHContext *c = pls->parent->priv_data;
+
+ while (( !ff_check_interrupt(c->interrupt_callback)&& pls->n_fragments > 0)) {
+ if (pls->cur_seq_no < pls->n_fragments) {
+ seg_ptr = pls->fragments[pls->cur_seq_no];
+ seg = av_mallocz(sizeof(struct fragment));
+ if (!seg) {
+ return NULL;
+ }
+ seg->url = av_strdup(seg_ptr->url);
+ if (!seg->url) {
+ av_free(seg);
+ return NULL;
+ }
+ seg->size = seg_ptr->size;
+ seg->url_offset = seg_ptr->url_offset;
+ return seg;
+ } else if (c->is_live) {
+ refresh_manifest(pls->parent);
+ } else {
+ break;
+ }
+ }
+ if (c->is_live) {
+ min_seq_no = calc_min_seg_no(pls->parent, pls);
+ max_seq_no = calc_max_seg_no(pls);
+
+ if (pls->timelines || pls->fragments) {
+ refresh_manifest(pls->parent);
+ }
+ if (pls->cur_seq_no <= min_seq_no) {
+ av_log(pls->parent, AV_LOG_VERBOSE, "old fragment: cur[%"PRId64"] min[%"PRId64"] max[%"PRId64"], playlist %d\n", (int64_t)pls->cur_seq_no, min_seq_no, max_seq_no, (int)pls->rep_idx);
+ pls->cur_seq_no = calc_cur_seg_no(pls->parent, pls);
+ } else if (pls->cur_seq_no > max_seq_no) {
+ av_log(pls->parent, AV_LOG_VERBOSE, "new fragment: min[%"PRId64"] max[%"PRId64"], playlist %d\n", min_seq_no, max_seq_no, (int)pls->rep_idx);
+ }
+ seg = av_mallocz(sizeof(struct fragment));
+ if (!seg) {
+ return NULL;
+ }
+ } else if (pls->cur_seq_no <= pls->last_seq_no) {
+ seg = av_mallocz(sizeof(struct fragment));
+ if (!seg) {
+ return NULL;
+ }
+ }
+ if (seg) {
+ char tmpfilename[MAX_URL_SIZE];
+
+ ff_dash_fill_tmpl_params(tmpfilename, sizeof(tmpfilename), pls->url_template, 0, pls->cur_seq_no, 0, get_segment_start_time_based_on_timeline(pls, pls->cur_seq_no));
+ seg->url = av_strireplace(pls->url_template, pls->url_template, tmpfilename);
+ if (!seg->url) {
+ av_log(pls->parent, AV_LOG_WARNING, "Unable to resolve template url '%s', try to use origin template\n", pls->url_template);
+ seg->url = av_strdup(pls->url_template);
+ if (!seg->url) {
+ av_log(pls->parent, AV_LOG_ERROR, "Cannot resolve template url '%s'\n", pls->url_template);
+ return NULL;
+ }
+ }
+
+ seg->size = -1;
+ }
+
+ return seg;
+}
+
+enum ReadFromURLMode {
+ READ_NORMAL,
+ READ_COMPLETE,
+};
+
+static int read_from_url(struct representation *pls, struct fragment *seg,
+ uint8_t *buf, int buf_size,
+ enum ReadFromURLMode mode)
+{
+ int ret;
+
+ /* limit read if the fragment was only a part of a file */
+ if (seg->size >= 0)
+ buf_size = FFMIN(buf_size, pls->cur_seg_size - pls->cur_seg_offset);
+
+ if (mode == READ_COMPLETE) {
+ ret = avio_read(pls->input, buf, buf_size);
+ if (ret < buf_size) {
+ av_log(pls->parent, AV_LOG_WARNING, "Could not read complete fragment.\n");
+ }
+ } else {
+ ret = avio_read(pls->input, buf, buf_size);
+ }
+ if (ret > 0)
+ pls->cur_seg_offset += ret;
+
+ return ret;
+}
+
+static int open_input(DASHContext *c, struct representation *pls, struct fragment *seg)
+{
+ AVDictionary *opts = NULL;
+ char url[MAX_URL_SIZE];
+ int ret;
+
+ set_httpheader_options(c, opts);
+ 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);
+ }
+
+ ff_make_absolute_url(url, MAX_URL_SIZE, c->base_url, seg->url);
+ av_log(pls->parent, AV_LOG_VERBOSE, "DASH request for url '%s', offset %"PRId64", playlist %d\n",
+ url, seg->url_offset, pls->rep_idx);
+ ret = open_url(pls->parent, &pls->input, url, c->avio_opts, opts, NULL);
+ if (ret < 0) {
+ goto cleanup;
+ }
+
+ /* 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 && seg->url_offset) {
+ int64_t seekret = avio_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 DASH fragment '%s'\n", seg->url_offset, seg->url);
+ ret = (int) seekret;
+ ff_format_io_close(pls->parent, &pls->input);
+ }
+ }
+
+cleanup:
+ av_dict_free(&opts);
+ pls->cur_seg_offset = 0;
+ pls->cur_seg_size = seg->size;
+ return ret;
+}
+
+static int update_init_section(struct representation *pls)
+{
+ static const int max_init_section_size = 1024 * 1024;
+ DASHContext *c = pls->parent->priv_data;
+ int64_t sec_size;
+ int64_t urlsize;
+ int ret;
+
+ if (!pls->init_section || pls->init_sec_buf)
+ return 0;
+
+ ret = open_input(c, pls, pls->init_section);
+ if (ret < 0) {
+ av_log(pls->parent, AV_LOG_WARNING,
+ "Failed to open an initialization section in playlist %d\n",
+ pls->rep_idx);
+ return ret;
+ }
+
+ if (pls->init_section->size >= 0)
+ sec_size = pls->init_section->size;
+ else if ((urlsize = avio_size(pls->input)) >= 0)
+ sec_size = urlsize;
+ else
+ sec_size = max_init_section_size;
+
+ av_log(pls->parent, AV_LOG_DEBUG,
+ "Downloading an initialization section of size %"PRId64"\n",
+ sec_size);
+
+ sec_size = FFMIN(sec_size, max_init_section_size);
+
+ av_fast_malloc(&pls->init_sec_buf, &pls->init_sec_buf_size, sec_size);
+
+ ret = read_from_url(pls, pls->init_section, pls->init_sec_buf,
+ pls->init_sec_buf_size, READ_COMPLETE);
+ ff_format_io_close(pls->parent, &pls->input);
+
+ if (ret < 0)
+ return ret;
+
+ pls->init_sec_data_len = ret;
+ pls->init_sec_buf_read_offset = 0;
+
+ return 0;
+}
+
+static int64_t seek_data(void *opaque, int64_t offset, int whence)
+{
+ struct representation *v = opaque;
+ if (v->n_fragments && !v->init_sec_data_len) {
+ return avio_seek(v->input, offset, whence);
+ }
+
+ return AVERROR(ENOSYS);
+}
+
+static int read_data(void *opaque, uint8_t *buf, int buf_size)
+{
+ int ret = 0;
+ struct representation *v = opaque;
+ DASHContext *c = v->parent->priv_data;
+
+restart:
+ if (!v->input) {
+ free_fragment(&v->cur_seg);
+ v->cur_seg = get_current_fragment(v);
+ if (!v->cur_seg) {
+ ret = AVERROR_EOF;
+ goto end;
+ }
+
+ /* load/update Media Initialization Section, if any */
+ ret = update_init_section(v);
+ if (ret)
+ goto end;
+
+ ret = open_input(c, v, v->cur_seg);
+ if (ret < 0) {
+ if (ff_check_interrupt(c->interrupt_callback)) {
+ goto end;
+ ret = AVERROR_EXIT;
+ }
+ av_log(v->parent, AV_LOG_WARNING, "Failed to open fragment of playlist %d\n", v->rep_idx);
+ v->cur_seq_no++;
+ goto restart;
+ }
+ }
+
+ if (v->init_sec_buf_read_offset < v->init_sec_data_len) {
+ /* Push init section out first before first actual fragment */
+ int copy_size = FFMIN(v->init_sec_data_len - v->init_sec_buf_read_offset, buf_size);
+ memcpy(buf, v->init_sec_buf, copy_size);
+ v->init_sec_buf_read_offset += copy_size;
+ ret = copy_size;
+ goto end;
+ }
+
+ /* check the v->cur_seg, if it is null, get current and double check if the new v->cur_seg*/
+ if (!v->cur_seg) {
+ v->cur_seg = get_current_fragment(v);
+ }
+ if (!v->cur_seg) {
+ ret = AVERROR_EOF;
+ goto end;
+ }
+ ret = read_from_url(v, v->cur_seg, buf, buf_size, READ_NORMAL);
+ if (ret > 0)
+ goto end;
+
+ if (!v->is_restart_needed)
+ v->cur_seq_no++;
+ v->is_restart_needed = 1;
+
+end:
+ return ret;
+}
+
+static int save_avio_options(AVFormatContext *s)
+{
+ DASHContext *c = s->priv_data;
+ const char *opts[] = { "headers", "user_agent", "user-agent", "cookies", NULL }, **opt = opts;
+ uint8_t *buf = NULL;
+ int ret = 0;
+
+ while (*opt) {
+ if (av_opt_get(s->pb, *opt, AV_OPT_SEARCH_CHILDREN, &buf) >= 0) {
+ if (buf[0] != '\0') {
+ ret = av_dict_set(&c->avio_opts, *opt, buf, AV_DICT_DONT_STRDUP_VAL);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ opt++;
+ }
+
+ return ret;
+}
+
+static int nested_io_open(AVFormatContext *s, AVIOContext **pb, const char *url,
+ int flags, AVDictionary **opts)
+{
+ av_log(s, AV_LOG_ERROR,
+ "A DASH playlist item '%s' referred to an external file '%s'. "
+ "Opening this file was forbidden for security reasons\n",
+ s->filename, url);
+ return AVERROR(EPERM);
+}
+
+static int reopen_demux_for_component(AVFormatContext *s, struct representation *pls)
+{
+ DASHContext *c = s->priv_data;
+ AVInputFormat *in_fmt = NULL;
+ AVDictionary *in_fmt_opts = NULL;
+ uint8_t *avio_ctx_buffer = NULL;
+ int ret = 0;
+
+ if (pls->ctx) {
+ /* note: the internal buffer could have changed, and be != avio_ctx_buffer */
+ av_freep(&pls->pb.buffer);
+ memset(&pls->pb, 0x00, sizeof(AVIOContext));
+ pls->ctx->pb = NULL;
+ avformat_close_input(&pls->ctx);
+ pls->ctx = NULL;
+ }
+ if (!(pls->ctx = avformat_alloc_context())) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ avio_ctx_buffer = av_malloc(INITIAL_BUFFER_SIZE);
+ if (!avio_ctx_buffer ) {
+ ret = AVERROR(ENOMEM);
+ avformat_free_context(pls->ctx);
+ pls->ctx = NULL;
+ goto fail;
+ }
+ if (c->is_live) {
+ ffio_init_context(&pls->pb, avio_ctx_buffer , INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, NULL);
+ } else {
+ ffio_init_context(&pls->pb, avio_ctx_buffer , INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, seek_data);
+ }
+ pls->pb.seekable = 0;
+
+ if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0)
+ goto fail;
+
+ pls->ctx->flags = AVFMT_FLAG_CUSTOM_IO;
+ pls->ctx->probesize = 1024 * 4;
+ pls->ctx->max_analyze_duration = 4 * AV_TIME_BASE;
+ ret = av_probe_input_buffer(&pls->pb, &in_fmt, "", NULL, 0, 0);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "Error when loading first fragment, playlist %d\n", (int)pls->rep_idx);
+ avformat_free_context(pls->ctx);
+ pls->ctx = NULL;
+ goto fail;
+ }
+
+ pls->ctx->pb = &pls->pb;
+ pls->ctx->io_open = nested_io_open;
+
+ // provide additional information from mpd if available
+ ret = avformat_open_input(&pls->ctx, "", in_fmt, &in_fmt_opts); //pls->init_section->url
+ av_dict_free(&in_fmt_opts);
+ if (ret < 0)
+ goto fail;
+ if (pls->n_fragments) {
+ ret = avformat_find_stream_info(pls->ctx, NULL);
+ if (ret < 0)
+ goto fail;
+ }
+
+fail:
+ return ret;
+}
+
+static int open_demux_for_component(AVFormatContext *s, struct representation *pls)
+{
+ int ret = 0;
+ int i;
+
+ pls->parent = s;
+ pls->cur_seq_no = calc_cur_seg_no(s, pls);
+ pls->last_seq_no = calc_max_seg_no(pls);
+
+ ret = reopen_demux_for_component(s, pls);
+ if (ret < 0) {
+ goto fail;
+ }
+ for (i = 0; i < pls->ctx->nb_streams; i++) {
+ AVStream *st = avformat_new_stream(s, NULL);
+ AVStream *ist = pls->ctx->streams[i];
+ if (!st) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ st->id = i;
+ avcodec_parameters_copy(st->codecpar, pls->ctx->streams[i]->codecpar);
+ avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num, ist->time_base.den);
+ }
+
+ return 0;
+fail:
+ return ret;
+}
+
+static int dash_read_header(AVFormatContext *s)
+{
+ void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
+ DASHContext *c = s->priv_data;
+ int ret = 0;
+ int stream_index = 0;
+
+ c->interrupt_callback = &s->interrupt_callback;
+ // if the URL context is good, read important options we must broker later
+ if (u) {
+ update_options(&c->user_agent, "user-agent", u);
+ update_options(&c->cookies, "cookies", u);
+ update_options(&c->headers, "headers", u);
+ }
+
+ if ((ret = parse_manifest(s, s->filename, s->pb)) < 0)
+ goto fail;
+
+ if ((ret = save_avio_options(s)) < 0)
+ goto fail;
+
+ /* If this isn't a live stream, fill the total duration of the
+ * stream. */
+ if (!c->is_live) {
+ s->duration = (int64_t) c->media_presentation_duration * AV_TIME_BASE;
+ }
+
+ /* Open the demuxer for curent video and current audio components if available */
+ if (!ret && c->cur_video) {
+ ret = open_demux_for_component(s, c->cur_video);
+ if (!ret) {
+ c->cur_video->stream_index = stream_index;
+ ++stream_index;
+ } else {
+ free_representation(c->cur_video);
+ c->cur_video = NULL;
+ }
+ }
+
+ if (!ret && c->cur_audio) {
+ ret = open_demux_for_component(s, c->cur_audio);
+ if (!ret) {
+ c->cur_audio->stream_index = stream_index;
+ ++stream_index;
+ } else {
+ free_representation(c->cur_audio);
+ c->cur_audio = NULL;
+ }
+ }
+
+ if (!stream_index) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ /* Create a program */
+ if (!ret) {
+ AVProgram *program;
+ program = av_new_program(s, 0);
+ if (!program) {
+ goto fail;
+ }
+
+ if (c->cur_video) {
+ av_program_add_stream_index(s, 0, c->cur_video->stream_index);
+ }
+ if (c->cur_audio) {
+ av_program_add_stream_index(s, 0, c->cur_audio->stream_index);
+ }
+ }
+
+ return 0;
+fail:
+ return ret;
+}
+
+static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ DASHContext *c = s->priv_data;
+ int ret = 0;
+ struct representation *cur = NULL;
+
+ if (!c->cur_audio && !c->cur_video ) {
+ return AVERROR_INVALIDDATA;
+ }
+ if (c->cur_audio && !c->cur_video) {
+ cur = c->cur_audio;
+ } else if (!c->cur_audio && c->cur_video) {
+ cur = c->cur_video;
+ } else if (c->cur_video->cur_timestamp < c->cur_audio->cur_timestamp) {
+ cur = c->cur_video;
+ } else {
+ cur = c->cur_audio;
+ }
+
+ if (cur->ctx) {
+ while (!ff_check_interrupt(c->interrupt_callback) && !ret) {
+ ret = av_read_frame(cur->ctx, pkt);
+ if (ret >= 0) {
+ /* If we got a packet, return it */
+ cur->cur_timestamp = av_rescale(pkt->pts, (int64_t)cur->ctx->streams[0]->time_base.num * 90000, cur->ctx->streams[0]->time_base.den);
+ pkt->stream_index = cur->stream_index;
+ return 0;
+ }
+ if (cur->is_restart_needed) {
+ cur->cur_seg_offset = 0;
+ cur->init_sec_buf_read_offset = 0;
+ if (cur->input)
+ ff_format_io_close(cur->parent, &cur->input);
+ ret = reopen_demux_for_component(s, cur);
+ cur->is_restart_needed = 0;
+ }
+
+ }
+ }
+ return AVERROR_EOF;
+}
+
+static int dash_close(AVFormatContext *s)
+{
+ DASHContext *c = s->priv_data;
+ if (c->cur_audio) {
+ free_representation(c->cur_audio);
+ }
+ if (c->cur_video) {
+ free_representation(c->cur_video);
+ }
+
+ av_freep(&c->cookies);
+ av_freep(&c->user_agent);
+ av_dict_free(&c->avio_opts);
+ av_freep(&c->base_url);
+ return 0;
+}
+
+static int dash_seek(AVFormatContext *s, struct representation *pls, int64_t seek_pos_msec, int flags)
+{
+ int ret = 0;
+ int i = 0;
+ int j = 0;
+ int64_t duration = 0;
+
+ av_log(pls->parent, AV_LOG_VERBOSE, "DASH seek pos[%"PRId64"ms], playlist %d\n", seek_pos_msec, pls->rep_idx);
+
+ // single fragment mode
+ if (pls->n_fragments == 1) {
+ pls->cur_timestamp = 0;
+ pls->cur_seg_offset = 0;
+ ff_read_frame_flush(pls->ctx);
+ return av_seek_frame(pls->ctx, -1, seek_pos_msec * 1000, flags);
+ }
+
+ if (pls->input)
+ ff_format_io_close(pls->parent, &pls->input);
+
+ // find the nearest fragment
+ if (pls->n_timelines > 0 && pls->fragment_timescale > 0) {
+ int64_t num = pls->first_seq_no;
+ av_log(pls->parent, AV_LOG_VERBOSE, "dash_seek with SegmentTimeline start n_timelines[%d] "
+ "last_seq_no[%"PRId64"], playlist %d.\n",
+ (int)pls->n_timelines, (int64_t)pls->last_seq_no, (int)pls->rep_idx);
+ for (i = 0; i < pls->n_timelines; i++) {
+ if (pls->timelines[i]->starttime > 0) {
+ duration = pls->timelines[i]->starttime;
+ }
+ duration += pls->timelines[i]->duration;
+ if (seek_pos_msec < ((duration * 1000) / pls->fragment_timescale)) {
+ goto set_seq_num;
+ }
+ for (j = 0; j < pls->timelines[i]->repeat; j++) {
+ duration += pls->timelines[i]->duration;
+ num++;
+ if (seek_pos_msec < ((duration * 1000) / pls->fragment_timescale)) {
+ goto set_seq_num;
+ }
+ }
+ num++;
+ }
+
+set_seq_num:
+ pls->cur_seq_no = num > pls->last_seq_no ? pls->last_seq_no : num;
+ av_log(pls->parent, AV_LOG_VERBOSE, "dash_seek with SegmentTimeline end cur_seq_no[%"PRId64"], playlist %d.\n",
+ (int64_t)pls->cur_seq_no, (int)pls->rep_idx);
+ } else if (pls->fragment_duration > 0) {
+ pls->cur_seq_no = pls->first_seq_no + ((seek_pos_msec * pls->fragment_timescale) / pls->fragment_duration) / 1000;
+ } else {
+ av_log(pls->parent, AV_LOG_ERROR, "dash_seek missing fragment_duration\n");
+ pls->cur_seq_no = pls->first_seq_no;
+ }
+ pls->cur_timestamp = 0;
+ pls->cur_seg_offset = 0;
+ pls->init_sec_buf_read_offset = 0;
+ ret = reopen_demux_for_component(s, pls);
+
+ return ret;
+}
+
+static int dash_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
+{
+ int ret = 0;
+ DASHContext *c = s->priv_data;
+ int64_t seek_pos_msec = av_rescale_rnd(timestamp, 1000,
+ s->streams[stream_index]->time_base.den,
+ flags & AVSEEK_FLAG_BACKWARD ?
+ AV_ROUND_DOWN : AV_ROUND_UP);
+ if ((flags & AVSEEK_FLAG_BYTE) || c->is_live)
+ return AVERROR(ENOSYS);
+ if (c->cur_audio) {
+ ret = dash_seek(s, c->cur_audio, seek_pos_msec, flags);
+ }
+ if (!ret && c->cur_video) {
+ ret = dash_seek(s, c->cur_video, seek_pos_msec, flags);
+ }
+ return ret;
+}
+
+static int dash_probe(AVProbeData *p)
+{
+ if (!av_stristr(p->buf, "<MPD"))
+ return 0;
+
+ if (av_stristr(p->buf, "dash:profile:isoff-on-demand:2011") ||
+ av_stristr(p->buf, "dash:profile:isoff-live:2011") ||
+ av_stristr(p->buf, "dash:profile:isoff-live:2012") ||
+ av_stristr(p->buf, "dash:profile:isoff-main:2011")) {
+ return AVPROBE_SCORE_MAX;
+ }
+ if (av_stristr(p->buf, "dash:profile")) {
+ return AVPROBE_SCORE_MAX;
+ }
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(DASHContext, x)
+#define FLAGS AV_OPT_FLAG_DECODING_PARAM
+static const AVOption dash_options[] = {
+ {"allowed_extensions", "List of file extensions that dash is allowed to access",
+ OFFSET(allowed_extensions), AV_OPT_TYPE_STRING,
+ {.str = "aac,m4a,m4s,m4v,mov,mp4"},
+ INT_MIN, INT_MAX, FLAGS},
+ {NULL}
+};
+
+static const AVClass dash_class = {
+ .class_name = "dash",
+ .item_name = av_default_item_name,
+ .option = dash_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_dash_demuxer = {
+ .name = "dash",
+ .long_name = NULL_IF_CONFIG_SMALL("Dynamic Adaptive Streaming over HTTP"),
+ .priv_class = &dash_class,
+ .priv_data_size = sizeof(DASHContext),
+ .read_probe = dash_probe,
+ .read_header = dash_read_header,
+ .read_packet = dash_read_packet,
+ .read_close = dash_close,
+ .read_seek = dash_read_seek,
+ .flags = AVFMT_NO_BYTE_SEEK,
+};
diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c
index 7134af4..240ff41 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
*/
@@ -24,11 +24,13 @@
#include <unistd.h>
#endif
+#include "libavutil/avassert.h"
#include "libavutil/avutil.h"
#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
+#include "libavutil/rational.h"
#include "libavutil/time_internal.h"
#include "avc.h"
@@ -38,16 +40,7 @@
#include "isom.h"
#include "os_support.h"
#include "url.h"
-
-// See ISO/IEC 23009-1:2014 5.3.9.4.4
-typedef enum {
- DASH_TMPL_ID_UNDEFINED = -1,
- DASH_TMPL_ID_ESCAPE,
- DASH_TMPL_ID_REP_ID,
- DASH_TMPL_ID_NUMBER,
- DASH_TMPL_ID_BANDWIDTH,
- DASH_TMPL_ID_TIME,
-} DASHTmplId;
+#include "dash.h"
typedef struct Segment {
char file[1024];
@@ -104,6 +97,8 @@ typedef struct DASHContext {
const char *single_file_name;
const char *init_seg_name;
const char *media_seg_name;
+ AVRational min_frame_rate, max_frame_rate;
+ int ambiguous_frame_rate;
const char *utc_timing_url;
} DASHContext;
@@ -153,7 +148,7 @@ static void set_codec_str(AVFormatContext *s, AVCodecParameters *par,
tags[0] = ff_mp4_obj_type;
oti = av_codec_get_tag(tags, par->codec_id);
if (oti)
- av_strlcatf(str, size, ".%02"SCNx32, oti);
+ av_strlcatf(str, size, ".%02"PRIx32, oti);
else
return;
@@ -322,119 +317,6 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext
}
}
-static DASHTmplId dash_read_tmpl_id(const char *identifier, char *format_tag,
- size_t format_tag_size, const char **ptr) {
- const char *next_ptr;
- DASHTmplId id_type = DASH_TMPL_ID_UNDEFINED;
-
- if (av_strstart(identifier, "$$", &next_ptr)) {
- id_type = DASH_TMPL_ID_ESCAPE;
- *ptr = next_ptr;
- } else if (av_strstart(identifier, "$RepresentationID$", &next_ptr)) {
- id_type = DASH_TMPL_ID_REP_ID;
- // default to basic format, as $RepresentationID$ identifiers
- // are not allowed to have custom format-tags.
- av_strlcpy(format_tag, "%d", format_tag_size);
- *ptr = next_ptr;
- } else { // the following identifiers may have an explicit format_tag
- if (av_strstart(identifier, "$Number", &next_ptr))
- id_type = DASH_TMPL_ID_NUMBER;
- else if (av_strstart(identifier, "$Bandwidth", &next_ptr))
- id_type = DASH_TMPL_ID_BANDWIDTH;
- else if (av_strstart(identifier, "$Time", &next_ptr))
- id_type = DASH_TMPL_ID_TIME;
- else
- id_type = DASH_TMPL_ID_UNDEFINED;
-
- // next parse the dash format-tag and generate a c-string format tag
- // (next_ptr now points at the first '%' at the beginning of the format-tag)
- if (id_type != DASH_TMPL_ID_UNDEFINED) {
- const char *number_format = (id_type == DASH_TMPL_ID_TIME) ? PRId64 : "d";
- if (next_ptr[0] == '$') { // no dash format-tag
- snprintf(format_tag, format_tag_size, "%%%s", number_format);
- *ptr = &next_ptr[1];
- } else {
- const char *width_ptr;
- // only tolerate single-digit width-field (i.e. up to 9-digit width)
- if (av_strstart(next_ptr, "%0", &width_ptr) &&
- av_isdigit(width_ptr[0]) &&
- av_strstart(&width_ptr[1], "d$", &next_ptr)) {
- // yes, we're using a format tag to build format_tag.
- snprintf(format_tag, format_tag_size, "%s%c%s", "%0", width_ptr[0], number_format);
- *ptr = next_ptr;
- } else {
- av_log(NULL, AV_LOG_WARNING, "Failed to parse format-tag beginning with %s. Expected either a "
- "closing '$' character or a format-string like '%%0[width]d', "
- "where width must be a single digit\n", next_ptr);
- id_type = DASH_TMPL_ID_UNDEFINED;
- }
- }
- }
- }
- return id_type;
-}
-
-static void dash_fill_tmpl_params(char *dst, size_t buffer_size,
- const char *template, int rep_id,
- int number, int bit_rate,
- int64_t time) {
- int dst_pos = 0;
- const char *t_cur = template;
- while (dst_pos < buffer_size - 1 && *t_cur) {
- char format_tag[7]; // May be "%d", "%0Xd", or "%0Xlld" (for $Time$), where X is in [0-9]
- int n = 0;
- DASHTmplId id_type;
- const char *t_next = strchr(t_cur, '$'); // copy over everything up to the first '$' character
- if (t_next) {
- int num_copy_bytes = FFMIN(t_next - t_cur, buffer_size - dst_pos - 1);
- av_strlcpy(&dst[dst_pos], t_cur, num_copy_bytes + 1);
- // advance
- dst_pos += num_copy_bytes;
- t_cur = t_next;
- } else { // no more DASH identifiers to substitute - just copy the rest over and break
- av_strlcpy(&dst[dst_pos], t_cur, buffer_size - dst_pos);
- break;
- }
-
- if (dst_pos >= buffer_size - 1 || !*t_cur)
- break;
-
- // t_cur is now pointing to a '$' character
- id_type = dash_read_tmpl_id(t_cur, format_tag, sizeof(format_tag), &t_next);
- switch (id_type) {
- case DASH_TMPL_ID_ESCAPE:
- av_strlcpy(&dst[dst_pos], "$", 2);
- n = 1;
- break;
- case DASH_TMPL_ID_REP_ID:
- n = snprintf(&dst[dst_pos], buffer_size - dst_pos, format_tag, rep_id);
- break;
- case DASH_TMPL_ID_NUMBER:
- n = snprintf(&dst[dst_pos], buffer_size - dst_pos, format_tag, number);
- break;
- case DASH_TMPL_ID_BANDWIDTH:
- n = snprintf(&dst[dst_pos], buffer_size - dst_pos, format_tag, bit_rate);
- break;
- case DASH_TMPL_ID_TIME:
- n = snprintf(&dst[dst_pos], buffer_size - dst_pos, format_tag, time);
- break;
- case DASH_TMPL_ID_UNDEFINED:
- // copy over one byte and advance
- av_strlcpy(&dst[dst_pos], t_cur, 2);
- n = 1;
- t_next = &t_cur[1];
- break;
- }
- // t_next points just past the processed identifier
- // n is the number of bytes that were attempted to be written to dst
- // (may have failed to write all because buffer_size).
-
- // advance
- dst_pos += FFMIN(n, buffer_size - dst_pos - 1);
- t_cur = t_next;
- }
-}
-
static char *xmlescape(const char *str) {
int outlen = strlen(str)*3/2 + 6;
char *out = av_realloc(NULL, outlen + 1);
@@ -511,6 +393,8 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind
avio_printf(out, "\t\t<AdaptationSet id=\"%s\" contentType=\"%s\" segmentAlignment=\"true\" bitstreamSwitching=\"true\"",
as->id, as->media_type == AVMEDIA_TYPE_VIDEO ? "video" : "audio");
+ if (as->media_type == AVMEDIA_TYPE_VIDEO && c->max_frame_rate.num && !c->ambiguous_frame_rate)
+ avio_printf(out, " %s=\"%d/%d\"", (av_cmp_q(c->min_frame_rate, c->max_frame_rate) < 0) ? "maxFrameRate" : "frameRate", c->max_frame_rate.num, c->max_frame_rate.den);
lang = av_dict_get(as->metadata, "language", NULL, 0);
if (lang)
avio_printf(out, " lang=\"%s\"", lang->value);
@@ -527,8 +411,12 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind
continue;
if (as->media_type == AVMEDIA_TYPE_VIDEO) {
- avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"video/%s\" codecs=\"%s\"%s width=\"%d\" height=\"%d\">\n",
+ AVStream *st = s->streams[i];
+ avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"video/%s\" codecs=\"%s\"%s width=\"%d\" height=\"%d\"",
i, os->format_name, os->codec_str, os->bandwidth_str, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height);
+ if (st->avg_frame_rate.num)
+ avio_printf(out, " frameRate=\"%d/%d\"", st->avg_frame_rate.num, st->avg_frame_rate.den);
+ avio_printf(out, ">\n");
} else {
avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"audio/%s\" codecs=\"%s\"%s audioSamplingRate=\"%d\">\n",
i, os->format_name, os->codec_str, os->bandwidth_str, s->streams[i]->codecpar->sample_rate);
@@ -683,9 +571,15 @@ static int write_manifest(AVFormatContext *s, int final)
AVIOContext *out;
char temp_filename[1024];
int ret, i;
+ const char *proto = avio_find_protocol_name(s->filename);
+ int use_rename = proto && !strcmp(proto, "file");
+ static unsigned int warned_non_file = 0;
AVDictionaryEntry *title = av_dict_get(s->metadata, "title", NULL, 0);
- snprintf(temp_filename, sizeof(temp_filename), "%s.tmp", s->filename);
+ if (!use_rename && !warned_non_file++)
+ av_log(s, AV_LOG_ERROR, "Cannot use rename on non file protocol, this may lead to races and temporary partial files\n");
+
+ snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", s->filename);
ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, NULL);
if (ret < 0) {
av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename);
@@ -755,7 +649,11 @@ static int write_manifest(AVFormatContext *s, int final)
avio_printf(out, "</MPD>\n");
avio_flush(out);
ff_format_io_close(s, &out);
- return ff_rename(temp_filename, s->filename);
+
+ if (use_rename)
+ return avpriv_io_move(temp_filename, s->filename);
+
+ return 0;
}
static int dict_copy_entry(AVDictionary **dst, const AVDictionary *src, const char *key)
@@ -774,7 +672,7 @@ static int dict_set_int(AVDictionary **pm, const char *key, int64_t value, int f
return av_dict_set(pm, key, valuestr, flags);
}
-static int dash_write_header(AVFormatContext *s)
+static int dash_init(AVFormatContext *s)
{
DASHContext *c = s->priv_data;
int ret = 0, i;
@@ -785,6 +683,7 @@ static int dash_write_header(AVFormatContext *s)
c->single_file = 1;
if (c->single_file)
c->use_template = 0;
+ c->ambiguous_frame_rate = 0;
av_strlcpy(c->dirname, s->filename, sizeof(c->dirname));
ptr = strrchr(c->dirname, '/');
@@ -801,13 +700,11 @@ static int dash_write_header(AVFormatContext *s)
*ptr = '\0';
c->streams = av_mallocz(sizeof(*c->streams) * s->nb_streams);
- if (!c->streams) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
+ if (!c->streams)
+ return AVERROR(ENOMEM);
if ((ret = parse_adaptation_sets(s)) < 0)
- goto fail;
+ return ret;
for (i = 0; i < s->nb_streams; i++) {
OutputStream *os = &c->streams[i];
@@ -825,10 +722,8 @@ static int dash_write_header(AVFormatContext *s)
int level = s->strict_std_compliance >= FF_COMPLIANCE_STRICT ?
AV_LOG_ERROR : AV_LOG_WARNING;
av_log(s, level, "No bit rate set for stream %d\n", i);
- if (s->strict_std_compliance >= FF_COMPLIANCE_STRICT) {
- ret = AVERROR(EINVAL);
- goto fail;
- }
+ if (s->strict_std_compliance >= FF_COMPLIANCE_STRICT)
+ return AVERROR(EINVAL);
}
// copy AdaptationSet language and role from stream metadata
@@ -836,10 +731,8 @@ static int dash_write_header(AVFormatContext *s)
dict_copy_entry(&as->metadata, s->streams[i]->metadata, "role");
ctx = avformat_alloc_context();
- if (!ctx) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
+ if (!ctx)
+ return AVERROR(ENOMEM);
// choose muxer based on codec: webm for VP8/9 and opus, mp4 otherwise
// note: os->format_name is also used as part of the mimetype of the
@@ -853,40 +746,37 @@ static int dash_write_header(AVFormatContext *s)
snprintf(os->format_name, sizeof(os->format_name), "mp4");
}
ctx->oformat = av_guess_format(os->format_name, NULL, NULL);
- if (!ctx->oformat) {
- ret = AVERROR_MUXER_NOT_FOUND;
- goto fail;
- }
+ if (!ctx->oformat)
+ return AVERROR_MUXER_NOT_FOUND;
os->ctx = ctx;
ctx->interrupt_callback = s->interrupt_callback;
ctx->opaque = s->opaque;
ctx->io_close = s->io_close;
ctx->io_open = s->io_open;
- if (!(st = avformat_new_stream(ctx, NULL))) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
+ if (!(st = avformat_new_stream(ctx, NULL)))
+ return AVERROR(ENOMEM);
avcodec_parameters_copy(st->codecpar, s->streams[i]->codecpar);
st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
st->time_base = s->streams[i]->time_base;
ctx->avoid_negative_ts = s->avoid_negative_ts;
+ ctx->flags = s->flags;
if ((ret = avio_open_dyn_buf(&ctx->pb)) < 0)
- goto fail;
+ return ret;
if (c->single_file) {
if (c->single_file_name)
- dash_fill_tmpl_params(os->initfile, sizeof(os->initfile), c->single_file_name, i, 0, os->bit_rate, 0);
+ ff_dash_fill_tmpl_params(os->initfile, sizeof(os->initfile), c->single_file_name, i, 0, os->bit_rate, 0);
else
snprintf(os->initfile, sizeof(os->initfile), "%s-stream%d.m4s", basename, i);
} else {
- dash_fill_tmpl_params(os->initfile, sizeof(os->initfile), c->init_seg_name, i, 0, os->bit_rate, 0);
+ ff_dash_fill_tmpl_params(os->initfile, sizeof(os->initfile), c->init_seg_name, i, 0, os->bit_rate, 0);
}
snprintf(filename, sizeof(filename), "%s%s", c->dirname, os->initfile);
ret = s->io_open(s, &os->out, filename, AVIO_FLAG_WRITE, NULL);
if (ret < 0)
- goto fail;
+ return ret;
os->init_start_pos = 0;
if (!strcmp(os->format_name, "mp4")) {
@@ -895,9 +785,8 @@ static int dash_write_header(AVFormatContext *s)
dict_set_int(&opts, "cluster_time_limit", c->min_seg_duration / 1000, 0);
dict_set_int(&opts, "cluster_size_limit", 5 * 1024 * 1024, 0); // set a large cluster size limit
}
- if ((ret = avformat_write_header(ctx, &opts)) < 0) {
- goto fail;
- }
+ if ((ret = avformat_write_header(ctx, &opts)) < 0)
+ return ret;
os->ctx_inited = 1;
avio_flush(ctx->pb);
av_dict_free(&opts);
@@ -916,8 +805,18 @@ static int dash_write_header(AVFormatContext *s)
// already before being handed to this muxer, so we don't have mismatches
// between the MPD and the actual segments.
s->avoid_negative_ts = ctx->avoid_negative_ts;
- if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ AVRational avg_frame_rate = s->streams[i]->avg_frame_rate;
+ if (avg_frame_rate.num > 0) {
+ if (av_cmp_q(avg_frame_rate, c->min_frame_rate) < 0)
+ c->min_frame_rate = avg_frame_rate;
+ if (av_cmp_q(c->max_frame_rate, avg_frame_rate) < 0)
+ c->max_frame_rate = avg_frame_rate;
+ } else {
+ c->ambiguous_frame_rate = 1;
+ }
c->has_video = 1;
+ }
set_codec_str(s, st->codecpar, os->codec_str, sizeof(os->codec_str));
os->first_pts = AV_NOPTS_VALUE;
@@ -928,15 +827,25 @@ static int dash_write_header(AVFormatContext *s)
if (!c->has_video && c->min_seg_duration <= 0) {
av_log(s, AV_LOG_WARNING, "no video stream and no min seg duration set\n");
- ret = AVERROR(EINVAL);
+ return AVERROR(EINVAL);
+ }
+ return 0;
+}
+
+static int dash_write_header(AVFormatContext *s)
+{
+ DASHContext *c = s->priv_data;
+ int i, ret;
+ for (i = 0; i < s->nb_streams; i++) {
+ OutputStream *os = &c->streams[i];
+ if ((ret = avformat_write_header(os->ctx, NULL)) < 0) {
+ dash_free(s);
+ return ret;
+ }
}
ret = write_manifest(s, 0);
if (!ret)
av_log(s, AV_LOG_VERBOSE, "Manifest written to: %s\n", s->filename);
-
-fail:
- if (ret)
- dash_free(s);
return ret;
}
@@ -1034,6 +943,10 @@ static int dash_flush(AVFormatContext *s, int final, int stream)
{
DASHContext *c = s->priv_data;
int i, ret = 0;
+
+ const char *proto = avio_find_protocol_name(s->filename);
+ int use_rename = proto && !strcmp(proto, "file");
+
int cur_flush_segment_index = 0;
if (stream >= 0)
cur_flush_segment_index = c->streams[stream].segment_index;
@@ -1063,9 +976,9 @@ static int dash_flush(AVFormatContext *s, int final, int stream)
}
if (!c->single_file) {
- dash_fill_tmpl_params(filename, sizeof(filename), c->media_seg_name, i, os->segment_index, os->bit_rate, os->start_pts);
+ ff_dash_fill_tmpl_params(filename, sizeof(filename), c->media_seg_name, i, os->segment_index, os->bit_rate, os->start_pts);
snprintf(full_path, sizeof(full_path), "%s%s", c->dirname, filename);
- snprintf(temp_path, sizeof(temp_path), "%s.tmp", full_path);
+ snprintf(temp_path, sizeof(temp_path), use_rename ? "%s.tmp" : "%s", full_path);
ret = s->io_open(s, &os->out, temp_path, AVIO_FLAG_WRITE, NULL);
if (ret < 0)
break;
@@ -1084,9 +997,12 @@ static int dash_flush(AVFormatContext *s, int final, int stream)
find_index_range(s, full_path, os->pos, &index_length);
} else {
ff_format_io_close(s, &os->out);
- ret = ff_rename(temp_path, full_path);
- if (ret < 0)
- break;
+
+ if (use_rename) {
+ ret = avpriv_io_move(temp_path, full_path);
+ if (ret < 0)
+ break;
+ }
}
if (!os->bit_rate) {
@@ -1201,7 +1117,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)
@@ -1233,10 +1149,32 @@ static int dash_write_trailer(AVFormatContext *s)
unlink(s->filename);
}
- dash_free(s);
return 0;
}
+static int dash_check_bitstream(struct AVFormatContext *s, const AVPacket *avpkt)
+{
+ DASHContext *c = s->priv_data;
+ OutputStream *os = &c->streams[avpkt->stream_index];
+ AVFormatContext *oc = os->ctx;
+ if (oc->oformat->check_bitstream) {
+ int ret;
+ AVPacket pkt = *avpkt;
+ pkt.stream_index = 0;
+ ret = oc->oformat->check_bitstream(oc, &pkt);
+ if (ret == 1) {
+ AVStream *st = s->streams[avpkt->stream_index];
+ AVStream *ost = oc->streams[0];
+ st->internal->bsfcs = ost->internal->bsfcs;
+ st->internal->nb_bsfcs = ost->internal->nb_bsfcs;
+ ost->internal->bsfcs = NULL;
+ ost->internal->nb_bsfcs = 0;
+ }
+ return ret;
+ }
+ return 1;
+}
+
#define OFFSET(x) offsetof(DASHContext, x)
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
@@ -1244,14 +1182,14 @@ static const AVOption options[] = {
{ "window_size", "number of segments kept in the manifest", OFFSET(window_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, E },
{ "extra_window_size", "number of segments kept outside of the manifest before removing from disk", OFFSET(extra_window_size), AV_OPT_TYPE_INT, { .i64 = 5 }, 0, INT_MAX, E },
{ "min_seg_duration", "minimum segment duration (in microseconds)", OFFSET(min_seg_duration), AV_OPT_TYPE_INT64, { .i64 = 5000000 }, 0, INT_MAX, E },
- { "remove_at_exit", "remove all segments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, E },
- { "use_template", "Use SegmentTemplate instead of SegmentList", OFFSET(use_template), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, E },
- { "use_timeline", "Use SegmentTimeline in SegmentTemplate", OFFSET(use_timeline), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, E },
- { "single_file", "Store all segments in one file, accessed using byte ranges", OFFSET(single_file), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, E },
+ { "remove_at_exit", "remove all segments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
+ { "use_template", "Use SegmentTemplate instead of SegmentList", OFFSET(use_template), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E },
+ { "use_timeline", "Use SegmentTimeline in SegmentTemplate", OFFSET(use_timeline), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E },
+ { "single_file", "Store all segments in one file, accessed using byte ranges", OFFSET(single_file), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
{ "single_file_name", "DASH-templated name to be used for baseURL. Implies storing all segments in one file, accessed using byte ranges", OFFSET(single_file_name), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
{ "init_seg_name", "DASH-templated name to used for the initialization segment", OFFSET(init_seg_name), AV_OPT_TYPE_STRING, {.str = "init-stream$RepresentationID$.m4s"}, 0, 0, E },
{ "media_seg_name", "DASH-templated name to used for the media segments", OFFSET(media_seg_name), AV_OPT_TYPE_STRING, {.str = "chunk-stream$RepresentationID$-$Number%05d$.m4s"}, 0, 0, E },
- { "utc_timing_url", "URL of the page that will return the UTC timestamp in ISO format", OFFSET(utc_timing_url), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
+ { "utc_timing_url", "URL of the page that will return the UTC timestamp in ISO format", OFFSET(utc_timing_url), AV_OPT_TYPE_STRING, { 0 }, 0, 0, E },
{ NULL },
};
@@ -1269,9 +1207,11 @@ AVOutputFormat ff_dash_muxer = {
.audio_codec = AV_CODEC_ID_AAC,
.video_codec = AV_CODEC_ID_H264,
.flags = AVFMT_GLOBALHEADER | AVFMT_NOFILE | AVFMT_TS_NEGATIVE,
+ .init = dash_init,
.write_header = dash_write_header,
.write_packet = dash_write_packet,
.write_trailer = dash_write_trailer,
- .codec_tag = (const AVCodecTag* const []){ ff_mp4_obj_type, 0 },
+ .deinit = dash_free,
+ .check_bitstream = dash_check_bitstream,
.priv_class = &dash_class,
};
diff --git a/libavformat/data_uri.c b/libavformat/data_uri.c
new file mode 100644
index 0000000..1863830
--- /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;
+}
+
+const 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 98f8a57..69196b0 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 93608e3..15a5afd 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/dcstr.c b/libavformat/dcstr.c
new file mode 100644
index 0000000..6035dd4
--- /dev/null
+++ b/libavformat/dcstr.c
@@ -0,0 +1,87 @@
+/*
+ * DC STR demuxer
+ * Copyright (c) 2015 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"
+
+static int dcstr_probe(AVProbeData *p)
+{
+ if (p->buf_size < 224 || memcmp(p->buf + 213, "Sega Stream", 11))
+ return 0;
+
+ return AVPROBE_SCORE_MAX;
+}
+
+static int dcstr_read_header(AVFormatContext *s)
+{
+ unsigned codec, align;
+ int mult;
+ AVStream *st;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->channels = avio_rl32(s->pb);
+ st->codecpar->sample_rate = avio_rl32(s->pb);
+ codec = avio_rl32(s->pb);
+ align = avio_rl32(s->pb);
+ avio_skip(s->pb, 4);
+ st->duration = avio_rl32(s->pb);
+ mult = avio_rl32(s->pb);
+ if (st->codecpar->channels <= 0 || mult <= 0 || mult > INT_MAX / st->codecpar->channels) {
+ av_log(s, AV_LOG_ERROR, "invalid number of channels %d x %d\n", st->codecpar->channels, mult);
+ return AVERROR_INVALIDDATA;
+ }
+ st->codecpar->channels *= mult;
+ if (!align || align > INT_MAX / st->codecpar->channels)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->block_align = align * st->codecpar->channels;
+
+ switch (codec) {
+ case 4: st->codecpar->codec_id = AV_CODEC_ID_ADPCM_AICA; break;
+ case 16: st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE_PLANAR; break;
+ default: avpriv_request_sample(s, "codec %X", codec);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ avio_skip(s->pb, 0x800 - avio_tell(s->pb));
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ return 0;
+}
+
+static int dcstr_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ return av_get_packet(s->pb, pkt, par->block_align);
+}
+
+AVInputFormat ff_dcstr_demuxer = {
+ .name = "dcstr",
+ .long_name = NULL_IF_CONFIG_SMALL("Sega DC STR"),
+ .read_probe = dcstr_probe,
+ .read_header = dcstr_read_header,
+ .read_packet = dcstr_read_packet,
+ .extensions = "str",
+ .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK | AVFMT_NOBINSEARCH,
+};
diff --git a/libavformat/dfa.c b/libavformat/dfa.c
index cecf6b6..9858ee7 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->codecpar, 2))
+ return AVERROR(ENOMEM);
+ AV_WL16(st->codecpar->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) {
@@ -90,6 +101,7 @@ static int dfa_read_packet(AVFormatContext *s, AVPacket *pkt)
frame_size = AV_RL32(pkt->data + pkt->size - 8);
if (frame_size > INT_MAX - 4) {
av_log(s, AV_LOG_ERROR, "Too large chunk size: %"PRIu32"\n", frame_size);
+ av_packet_unref(pkt);
return AVERROR(EIO);
}
if (AV_RL32(pkt->data + pkt->size - 12) == MKTAG('E', 'O', 'F', 'R')) {
diff --git a/libavformat/diracdec.c b/libavformat/diracdec.c
index f275212..e061ba5 100644
--- a/libavformat/diracdec.c
+++ b/libavformat/diracdec.c
@@ -2,20 +2,20 @@
* RAW Dirac demuxer
* Copyright (c) 2007 Marco Gerards <marco@gnu.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -25,10 +25,19 @@
static int dirac_probe(AVProbeData *p)
{
- if (AV_RL32(p->buf) == MKTAG('B', 'B', 'C', 'D'))
- return AVPROBE_SCORE_MAX;
- else
+ unsigned size;
+ if (AV_RL32(p->buf) != MKTAG('B', 'B', 'C', 'D'))
return 0;
+
+ size = AV_RB32(p->buf+5);
+ if (size < 13)
+ return 0;
+ if (size + 13LL > p->buf_size)
+ return AVPROBE_SCORE_MAX / 4;
+ if (AV_RL32(p->buf + size) != MKTAG('B', 'B', 'C', 'D'))
+ return 0;
+
+ return AVPROBE_SCORE_MAX;
}
FF_DEF_RAWVIDEO_DEMUXER(dirac, "raw Dirac", dirac_probe, NULL, AV_CODEC_ID_DIRAC)
diff --git a/libavformat/dnxhddec.c b/libavformat/dnxhddec.c
index 8bb6814..0ad51b5 100644
--- a/libavformat/dnxhddec.c
+++ b/libavformat/dnxhddec.c
@@ -3,41 +3,42 @@
* 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
*/
#include "libavutil/intreadwrite.h"
#include "avformat.h"
#include "rawdec.h"
+#include "libavcodec/dnxhddata.h"
static int dnxhd_probe(AVProbeData *p)
{
- static const uint8_t header[] = {0x00,0x00,0x02,0x80,0x01};
int w, h, compression_id;
if (p->buf_size < 0x2c)
return 0;
- if (memcmp(p->buf, header, 5))
+ if (ff_dnxhd_parse_header_prefix(p->buf) == 0)
return 0;
h = AV_RB16(p->buf + 0x18);
w = AV_RB16(p->buf + 0x1a);
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 > 1260) &&
+ (compression_id < 1270 || compression_id > 1274))
return 0;
return AVPROBE_SCORE_MAX;
}
diff --git a/libavformat/dsfdec.c b/libavformat/dsfdec.c
new file mode 100644
index 0000000..41538fd
--- /dev/null
+++ b/libavformat/dsfdec.c
@@ -0,0 +1,165 @@
+/*
+ * 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_parse_chapters(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 & AVIO_SEEKABLE_NORMAL) {
+ 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->codecpar->channel_layout = dsf_channel_layout[channel_type];
+ if (!st->codecpar->channel_layout)
+ avpriv_request_sample(s, "channel type %i", channel_type);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->channels = avio_rl32(pb);
+ st->codecpar->sample_rate = avio_rl32(pb) / 8;
+
+ if (st->codecpar->channels <= 0)
+ return AVERROR_INVALIDDATA;
+
+ switch(avio_rl32(pb)) {
+ case 1: st->codecpar->codec_id = AV_CODEC_ID_DSD_LSBF_PLANAR; break;
+ case 8: st->codecpar->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->codecpar->block_align = avio_rl32(pb);
+ if (st->codecpar->block_align > INT_MAX / st->codecpar->channels) {
+ avpriv_request_sample(s, "block_align overflow");
+ return AVERROR_INVALIDDATA;
+ }
+ st->codecpar->block_align *= st->codecpar->channels;
+ st->codecpar->bit_rate = st->codecpar->channels * st->codecpar->sample_rate * 8LL;
+ 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->codecpar->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 fa5943d..bd4f3ad 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/dss.c b/libavformat/dss.c
index a9b2ebf..083eb4a 100644
--- a/libavformat/dss.c
+++ b/libavformat/dss.c
@@ -2,20 +2,20 @@
* Digital Speech Standard (DSS) demuxer
* Copyright (c) 2014 Oleksij Rempel <linux@rempel-privat.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
*/
@@ -42,7 +42,6 @@
#define DSS_COMMENT_SIZE 64
#define DSS_BLOCK_SIZE 512
-#define DSS_HEADER_SIZE (DSS_BLOCK_SIZE * 2)
#define DSS_AUDIO_BLOCK_HEADER_SIZE 6
#define DSS_FRAME_SIZE 42
@@ -54,11 +53,15 @@ typedef struct DSSDemuxContext {
int swap;
int dss_sp_swap_byte;
int8_t *dss_sp_buf;
+
+ int packet_size;
+ int dss_header_size;
} DSSDemuxContext;
static int dss_probe(AVProbeData *p)
{
- if (AV_RL32(p->buf) != MKTAG(0x2, 'd', 's', 's'))
+ if ( AV_RL32(p->buf) != MKTAG(0x2, 'd', 's', 's')
+ && AV_RL32(p->buf) != MKTAG(0x3, 'd', 's', 's'))
return 0;
return AVPROBE_SCORE_MAX;
@@ -78,7 +81,8 @@ static int dss_read_metadata_date(AVFormatContext *s, unsigned int offset,
if (ret < DSS_TIME_SIZE)
return ret < 0 ? ret : AVERROR_EOF;
- sscanf(string, "%2d%2d%2d%2d%2d%2d", &y, &month, &d, &h, &minute, &sec);
+ if (sscanf(string, "%2d%2d%2d%2d%2d%2d", &y, &month, &d, &h, &minute, &sec) != 6)
+ return AVERROR_INVALIDDATA;
/* We deal with a two-digit year here, so set the default date to 2000
* and hope it will never be used in the next century. */
snprintf(datetime, sizeof(datetime), "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d",
@@ -117,12 +121,15 @@ static int dss_read_header(AVFormatContext *s)
DSSDemuxContext *ctx = s->priv_data;
AVIOContext *pb = s->pb;
AVStream *st;
- int ret;
+ int ret, version;
st = avformat_new_stream(s, NULL);
if (!st)
return AVERROR(ENOMEM);
+ version = avio_r8(pb);
+ ctx->dss_header_size = version * DSS_BLOCK_SIZE;
+
ret = dss_read_metadata_string(s, DSS_HEAD_OFFSET_AUTHOR,
DSS_AUTHOR_SIZE, "author");
if (ret)
@@ -142,7 +149,7 @@ static int dss_read_header(AVFormatContext *s)
if (ctx->audio_codec == DSS_ACODEC_DSS_SP) {
st->codecpar->codec_id = AV_CODEC_ID_DSS_SP;
- st->codecpar->sample_rate = 12000;
+ st->codecpar->sample_rate = 11025;
} else if (ctx->audio_codec == DSS_ACODEC_G723_1) {
st->codecpar->codec_id = AV_CODEC_ID_G723_1;
st->codecpar->sample_rate = 8000;
@@ -161,7 +168,7 @@ static int dss_read_header(AVFormatContext *s)
/* Jump over header */
- if (avio_seek(pb, DSS_HEADER_SIZE, SEEK_SET) != DSS_HEADER_SIZE)
+ if (avio_seek(pb, ctx->dss_header_size, SEEK_SET) != ctx->dss_header_size)
return AVERROR(EIO);
ctx->counter = 0;
@@ -209,13 +216,13 @@ static void dss_sp_byte_swap(DSSDemuxContext *ctx,
static int dss_sp_read_packet(AVFormatContext *s, AVPacket *pkt)
{
DSSDemuxContext *ctx = s->priv_data;
+ AVStream *st = s->streams[0];
int read_size, ret, offset = 0, buff_offset = 0;
+ int64_t pos = avio_tell(s->pb);
if (ctx->counter == 0)
dss_skip_audio_header(s, pkt);
- pkt->pos = avio_tell(s->pb);
-
if (ctx->swap) {
read_size = DSS_FRAME_SIZE - 2;
buff_offset = 3;
@@ -223,13 +230,16 @@ static int dss_sp_read_packet(AVFormatContext *s, AVPacket *pkt)
read_size = DSS_FRAME_SIZE;
ctx->counter -= read_size;
+ ctx->packet_size = DSS_FRAME_SIZE - 1;
ret = av_new_packet(pkt, DSS_FRAME_SIZE);
if (ret < 0)
return ret;
- pkt->duration = 0;
+ pkt->duration = 264;
+ pkt->pos = pos;
pkt->stream_index = 0;
+ s->bit_rate = 8LL * ctx->packet_size * st->codecpar->sample_rate * 512 / (506 * pkt->duration);
if (ctx->counter < 0) {
int size2 = ctx->counter + read_size;
@@ -250,8 +260,10 @@ static int dss_sp_read_packet(AVFormatContext *s, AVPacket *pkt)
dss_sp_byte_swap(ctx, pkt->data, ctx->dss_sp_buf);
- if (pkt->data[0] == 0xff)
- return AVERROR_INVALIDDATA;
+ if (ctx->dss_sp_swap_byte < 0) {
+ ret = AVERROR(EAGAIN);
+ goto error_eof;
+ }
return pkt->size;
@@ -263,12 +275,13 @@ error_eof:
static int dss_723_1_read_packet(AVFormatContext *s, AVPacket *pkt)
{
DSSDemuxContext *ctx = s->priv_data;
+ AVStream *st = s->streams[0];
int size, byte, ret, offset;
+ int64_t pos = avio_tell(s->pb);
if (ctx->counter == 0)
dss_skip_audio_header(s, pkt);
- pkt->pos = avio_tell(s->pb);
/* We make one byte-step here. Don't forget to add offset. */
byte = avio_r8(s->pb);
if (byte == 0xff)
@@ -276,15 +289,18 @@ static int dss_723_1_read_packet(AVFormatContext *s, AVPacket *pkt)
size = frame_size[byte & 3];
+ ctx->packet_size = size;
ctx->counter -= size;
ret = av_new_packet(pkt, size);
if (ret < 0)
return ret;
+ pkt->pos = pos;
pkt->data[0] = byte;
offset = 1;
pkt->duration = 240;
+ s->bit_rate = 8LL * size * st->codecpar->sample_rate * 512 / (506 * pkt->duration);
pkt->stream_index = 0;
@@ -325,11 +341,50 @@ static int dss_read_close(AVFormatContext *s)
{
DSSDemuxContext *ctx = s->priv_data;
- av_free(ctx->dss_sp_buf);
+ av_freep(&ctx->dss_sp_buf);
+
+ return 0;
+}
+
+static int dss_read_seek(AVFormatContext *s, int stream_index,
+ int64_t timestamp, int flags)
+{
+ DSSDemuxContext *ctx = s->priv_data;
+ int64_t ret, seekto;
+ uint8_t header[DSS_AUDIO_BLOCK_HEADER_SIZE];
+ int offset;
+
+ if (ctx->audio_codec == DSS_ACODEC_DSS_SP)
+ seekto = timestamp / 264 * 41 / 506 * 512;
+ else
+ seekto = timestamp / 240 * ctx->packet_size / 506 * 512;
+ if (seekto < 0)
+ seekto = 0;
+
+ seekto += ctx->dss_header_size;
+
+ ret = avio_seek(s->pb, seekto, SEEK_SET);
+ if (ret < 0)
+ return ret;
+
+ avio_read(s->pb, header, DSS_AUDIO_BLOCK_HEADER_SIZE);
+ ctx->swap = !!(header[0] & 0x80);
+ offset = 2*header[1] + 2*ctx->swap;
+ if (offset < DSS_AUDIO_BLOCK_HEADER_SIZE)
+ return AVERROR_INVALIDDATA;
+ if (offset == DSS_AUDIO_BLOCK_HEADER_SIZE) {
+ ctx->counter = 0;
+ offset = avio_skip(s->pb, -DSS_AUDIO_BLOCK_HEADER_SIZE);
+ } else {
+ ctx->counter = DSS_BLOCK_SIZE - offset;
+ offset = avio_skip(s->pb, offset - DSS_AUDIO_BLOCK_HEADER_SIZE);
+ }
+ ctx->dss_sp_swap_byte = -1;
return 0;
}
+
AVInputFormat ff_dss_demuxer = {
.name = "dss",
.long_name = NULL_IF_CONFIG_SMALL("Digital Speech Standard (DSS)"),
@@ -338,5 +393,6 @@ AVInputFormat ff_dss_demuxer = {
.read_header = dss_read_header,
.read_packet = dss_read_packet,
.read_close = dss_read_close,
+ .read_seek = dss_read_seek,
.extensions = "dss"
};
diff --git a/libavformat/dtsdec.c b/libavformat/dtsdec.c
index 90e9d38..a3e52cd 100644
--- a/libavformat/dtsdec.c
+++ b/libavformat/dtsdec.c
@@ -2,25 +2,29 @@
* 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 "libavutil/crc.h"
+
#include "libavcodec/bytestream.h"
+#include "libavcodec/dca.h"
#include "libavcodec/dca_syncwords.h"
+#include "libavcodec/get_bits.h"
#include "avformat.h"
#include "rawdec.h"
@@ -29,34 +33,95 @@ 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 exss_markers = 0, exss_nextpos = 0;
+ int sum, max, pos, ret, i;
+ int64_t diff = 0;
+ uint8_t hdr[DCA_CORE_FRAME_HEADER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE] = { 0 };
- buf = p->buf;
+ for (pos = FFMIN(4096, p->buf_size); pos < p->buf_size - 2; pos += 2) {
+ int marker, wide_hdr, hdr_size, framesize;
+ DCACoreFrameHeader h;
+ GetBitContext gb;
- for(; buf < (p->buf+p->buf_size)-2; buf+=2) {
- bufp = buf;
+ bufp = buf = p->buf + pos;
state = (state << 16) | bytestream_get_be16(&bufp);
+ if (pos >= 4)
+ diff += FFABS(((int16_t)AV_RL16(buf)) - (int16_t)AV_RL16(buf-4));
+
+ /* extension substream (EXSS) */
+ if (state == DCA_SYNCWORD_SUBSTREAM) {
+ if (pos < exss_nextpos)
+ continue;
+
+ init_get_bits(&gb, buf - 2, 96);
+ skip_bits_long(&gb, 42);
+
+ wide_hdr = get_bits1(&gb);
+ hdr_size = get_bits(&gb, 8 + 4 * wide_hdr) + 1;
+ framesize = get_bits(&gb, 16 + 4 * wide_hdr) + 1;
+ if (hdr_size & 3 || framesize & 3)
+ continue;
+ if (hdr_size < 16 || framesize < hdr_size)
+ continue;
+ if (pos - 2 + hdr_size > p->buf_size)
+ continue;
+ if (av_crc(av_crc_get_table(AV_CRC_16_CCITT), 0xffff, buf + 3, hdr_size - 5))
+ continue;
+
+ if (pos == exss_nextpos)
+ exss_markers++;
+ else
+ exss_markers = FFMAX(1, exss_markers - 1);
+ exss_nextpos = pos + framesize;
+ continue;
+ }
+
/* regular bitstream */
- if (state == DCA_SYNCWORD_CORE_BE || state == DCA_SYNCWORD_CORE_LE)
- markers[0]++;
+ if (state == DCA_SYNCWORD_CORE_BE &&
+ (bytestream_get_be16(&bufp) & 0xFC00) == 0xFC00)
+ marker = 0;
+ else if (state == DCA_SYNCWORD_CORE_LE &&
+ (bytestream_get_be16(&bufp) & 0x00FC) == 0x00FC)
+ marker = 1;
/* 14 bits big-endian bitstream */
- if (state == DCA_SYNCWORD_CORE_14B_BE)
- if ((bytestream_get_be16(&bufp) & 0xFFF0) == 0x07F0)
- markers[1]++;
+ else if (state == DCA_SYNCWORD_CORE_14B_BE &&
+ (bytestream_get_be16(&bufp) & 0xFFF0) == 0x07F0)
+ marker = 2;
/* 14 bits little-endian bitstream */
- if (state == DCA_SYNCWORD_CORE_14B_LE)
- if ((bytestream_get_be16(&bufp) & 0xF0FF) == 0xF007)
- markers[2]++;
+ else if (state == DCA_SYNCWORD_CORE_14B_LE &&
+ (bytestream_get_be16(&bufp) & 0xF0FF) == 0xF007)
+ marker = 3;
+ else
+ continue;
+
+ if ((ret = avpriv_dca_convert_bitstream(buf - 2, DCA_CORE_FRAME_HEADER_SIZE,
+ hdr, DCA_CORE_FRAME_HEADER_SIZE)) < 0)
+ continue;
+ if (avpriv_dca_parse_core_frame_header(&h, hdr, ret) < 0)
+ continue;
+
+ marker += 4 * h.sr_code;
+
+ markers[marker] ++;
}
- sum = markers[0] + markers[1] + markers[2];
- max = markers[1] > markers[0];
- max = markers[2] > markers[max] ? 2 : max;
+
+ if (exss_markers > 3)
+ return AVPROBE_SCORE_EXTENSION + 1;
+
+ 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 0000000..1bd403c
--- /dev/null
+++ b/libavformat/dtshddec.c
@@ -0,0 +1,172 @@
+/*
+ * 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 "libavcodec/dca.h"
+#include "avformat.h"
+#include "internal.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;
+ int64_t duration, data_start;
+ AVStream *st;
+ int ret;
+ char *value;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_DTS;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
+
+ for (;;) {
+ chunk_type = avio_rb64(pb);
+ chunk_size = avio_rb64(pb);
+
+ if (avio_feof(pb))
+ break;
+
+ 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:
+ data_start = avio_tell(pb);
+ dtshd->data_end = data_start + chunk_size;
+ if (dtshd->data_end <= chunk_size)
+ return AVERROR_INVALIDDATA;
+ if (!(pb->seekable & AVIO_SEEKABLE_NORMAL))
+ goto break_loop;
+ goto skip;
+ break;
+ case AUPR_HDR:
+ if (chunk_size < 21)
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, 3);
+ st->codecpar->sample_rate = avio_rb24(pb);
+ if (!st->codecpar->sample_rate)
+ return AVERROR_INVALIDDATA;
+ duration = avio_rb32(pb); // num_frames
+ duration *= avio_rb16(pb); // samples_per_frames
+ st->duration = duration;
+ avio_skip(pb, 5);
+ st->codecpar->channels = ff_dca_count_chs_for_mask(avio_rb16(pb));
+ st->codecpar->initial_padding = avio_rb16(pb);
+ avio_skip(pb, chunk_size - 21);
+ 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;
+ };
+ }
+
+ if (!dtshd->data_end)
+ return AVERROR_EOF;
+
+ avio_seek(pb, data_start, SEEK_SET);
+
+break_loop:
+ if (st->codecpar->sample_rate)
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ return 0;
+}
+
+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 7514aee..77043e3 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
*/
@@ -25,7 +26,10 @@
#include "libavutil/display.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
+#include "libavutil/mastering_display_metadata.h"
#include "libavutil/mathematics.h"
+#include "libavutil/opt.h"
+#include "libavutil/avstring.h"
#include "libavutil/replaygain.h"
#include "libavutil/spherical.h"
#include "libavutil/stereo3d.h"
@@ -77,7 +81,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);
@@ -98,16 +102,16 @@ static void pkt_dump_internal(void *avcl, FILE *f, int level, AVPacket *pkt,
HEXDUMP_PRINT("\n");
HEXDUMP_PRINT(" size=%d\n", pkt->size);
if (dump_payload)
- av_hex_dump(f, pkt->data, pkt->size);
+ hex_dump_internal(avcl, f, level, 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);
}
@@ -116,7 +120,9 @@ void av_pkt_dump_log2(void *avcl, int level, AVPacket *pkt, int dump_payload,
static void print_fps(double d, const char *postfix)
{
uint64_t v = lrintf(d * 100);
- if (v % 100)
+ if (!v)
+ av_log(NULL, AV_LOG_INFO, "%1.4f %s", d, postfix);
+ else if (v % 100)
av_log(NULL, AV_LOG_INFO, "%3.2f %s", d, postfix);
else if (v % (100 * 1000))
av_log(NULL, AV_LOG_INFO, "%1.0f %s", d, postfix);
@@ -131,9 +137,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");
+ }
}
}
@@ -307,6 +326,31 @@ static void dump_cpb(void *ctx, AVPacketSideData *sd)
cpb->vbv_delay);
}
+static void dump_mastering_display_metadata(void *ctx, AVPacketSideData* sd) {
+ AVMasteringDisplayMetadata* metadata = (AVMasteringDisplayMetadata*)sd->data;
+ av_log(ctx, AV_LOG_INFO, "Mastering Display Metadata, "
+ "has_primaries:%d has_luminance:%d "
+ "r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f) "
+ "min_luminance=%f, max_luminance=%f",
+ metadata->has_primaries, metadata->has_luminance,
+ av_q2d(metadata->display_primaries[0][0]),
+ av_q2d(metadata->display_primaries[0][1]),
+ av_q2d(metadata->display_primaries[1][0]),
+ av_q2d(metadata->display_primaries[1][1]),
+ av_q2d(metadata->display_primaries[2][0]),
+ av_q2d(metadata->display_primaries[2][1]),
+ av_q2d(metadata->white_point[0]), av_q2d(metadata->white_point[1]),
+ av_q2d(metadata->min_luminance), av_q2d(metadata->max_luminance));
+}
+
+static void dump_content_light_metadata(void *ctx, AVPacketSideData* sd)
+{
+ AVContentLightMetadata* metadata = (AVContentLightMetadata*)sd->data;
+ av_log(ctx, AV_LOG_INFO, "Content Light Level Metadata, "
+ "MaxCLL=%d, MaxFALL=%d",
+ metadata->MaxCLL, metadata->MaxFALL);
+}
+
static void dump_spherical(void *ctx, AVCodecParameters *par, AVPacketSideData *sd)
{
AVSphericalMapping *spherical = (AVSphericalMapping *)sd->data;
@@ -317,16 +361,7 @@ static void dump_spherical(void *ctx, AVCodecParameters *par, AVPacketSideData *
return;
}
- if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR)
- av_log(ctx, AV_LOG_INFO, "equirectangular ");
- else if (spherical->projection == AV_SPHERICAL_CUBEMAP)
- av_log(ctx, AV_LOG_INFO, "cubemap ");
- else if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE)
- av_log(ctx, AV_LOG_INFO, "tiled equirectangular ");
- else {
- av_log(ctx, AV_LOG_WARNING, "unknown");
- return;
- }
+ av_log(ctx, AV_LOG_INFO, "%s ", av_spherical_projection_name(spherical->projection));
yaw = ((double)spherical->yaw) / (1 << 16);
pitch = ((double)spherical->pitch) / (1 << 16);
@@ -339,7 +374,7 @@ static void dump_spherical(void *ctx, AVCodecParameters *par, AVPacketSideData *
&l, &t, &r, &b);
av_log(ctx, AV_LOG_INFO, "[%zu, %zu, %zu, %zu] ", l, t, r, b);
} else if (spherical->projection == AV_SPHERICAL_CUBEMAP) {
- av_log(ctx, AV_LOG_INFO, "[pad %zu] ", spherical->padding);
+ av_log(ctx, AV_LOG_INFO, "[pad %"PRIu32"] ", spherical->padding);
}
}
@@ -384,19 +419,26 @@ static void dump_sidedata(void *ctx, AVStream *st, const char *indent)
av_log(ctx, AV_LOG_INFO, "audio service type: ");
dump_audioservicetype(ctx, &sd);
break;
- case AV_PKT_DATA_QUALITY_FACTOR:
- av_log(ctx, AV_LOG_INFO, "quality factor: %d", *(int *)sd.data);
+ case AV_PKT_DATA_QUALITY_STATS:
+ av_log(ctx, AV_LOG_INFO, "quality factor: %"PRId32", pict_type: %c",
+ AV_RL32(sd.data), av_get_picture_type_char(sd.data[4]));
break;
case AV_PKT_DATA_CPB_PROPERTIES:
av_log(ctx, AV_LOG_INFO, "cpb: ");
dump_cpb(ctx, &sd);
break;
+ case AV_PKT_DATA_MASTERING_DISPLAY_METADATA:
+ dump_mastering_display_metadata(ctx, &sd);
+ break;
case AV_PKT_DATA_SPHERICAL:
av_log(ctx, AV_LOG_INFO, "spherical: ");
dump_spherical(ctx, st->codecpar, &sd);
break;
+ case AV_PKT_DATA_CONTENT_LIGHT_LEVEL:
+ dump_content_light_metadata(ctx, &sd);
+ break;
default:
- av_log(ctx, AV_LOG_WARNING,
+ av_log(ctx, AV_LOG_INFO,
"unknown side data type %d (%d bytes)", sd.type, sd.size);
break;
}
@@ -413,6 +455,7 @@ 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;
AVCodecContext *avctx;
int ret;
@@ -426,6 +469,16 @@ static void dump_stream_format(AVFormatContext *ic, int i,
return;
}
+ // Fields which are missing from AVCodecParameters need to be taken from the AVCodecContext
+ avctx->properties = st->codec->properties;
+ avctx->codec = st->codec->codec;
+ avctx->qmin = st->codec->qmin;
+ avctx->qmax = st->codec->qmax;
+ avctx->coded_width = st->codec->coded_width;
+ avctx->coded_height = st->codec->coded_height;
+
+ if (separator)
+ av_opt_set(avctx, "dump_separator", separator, 0);
avcodec_string(buf, sizeof(buf), avctx, is_output);
avcodec_free_context(&avctx);
@@ -441,27 +494,35 @@ static void dump_stream_format(AVFormatContext *ic, int i,
st->time_base.num, st->time_base.den);
av_log(NULL, AV_LOG_INFO, ": %s", buf);
- if (st->sample_aspect_ratio.num) {
+ if (st->sample_aspect_ratio.num &&
+ av_cmp_q(st->sample_aspect_ratio, st->codecpar->sample_aspect_ratio)) {
AVRational display_aspect_ratio;
av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
- st->codecpar->width * st->sample_aspect_ratio.num,
- st->codecpar->height * st->sample_aspect_ratio.den,
+ st->codecpar->width * (int64_t)st->sample_aspect_ratio.num,
+ st->codecpar->height * (int64_t)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->codecpar->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 || tbr || tbn || tbc)
+ av_log(NULL, AV_LOG_INFO, "%s", separator);
- if (fps || tbn)
- av_log(NULL, AV_LOG_INFO, "\n ");
if (fps)
- print_fps(av_q2d(st->avg_frame_rate), tbn ? "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), "tbn");
+ print_fps(1 / av_q2d(st->time_base), tbc ? "tbn, " : "tbn");
+ if (tbc)
+ print_fps(1 / av_q2d(st->codec->time_base), "tbc");
}
if (st->disposition & AV_DISPOSITION_DEFAULT)
@@ -510,8 +571,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 + (ic->duration <= INT64_MAX - 5000 ? 5000 : 0);
+ secs = duration / AV_TIME_BASE;
+ us = duration % AV_TIME_BASE;
mins = secs / 60;
secs %= 60;
hours = mins / 60;
@@ -524,14 +586,16 @@ void av_dump_format(AVFormatContext *ic, int index,
if (ic->start_time != AV_NOPTS_VALUE) {
int secs, us;
av_log(NULL, AV_LOG_INFO, ", start: ");
- secs = ic->start_time / AV_TIME_BASE;
+ secs = llabs(ic->start_time / AV_TIME_BASE);
us = llabs(ic->start_time % AV_TIME_BASE);
- av_log(NULL, AV_LOG_INFO, "%d.%06d",
- secs, (int) av_rescale(us, 1000000, AV_TIME_BASE));
+ av_log(NULL, AV_LOG_INFO, "%s%d.%06d",
+ ic->start_time >= 0 ? "" : "-",
+ secs,
+ (int) av_rescale(us, 1000000, AV_TIME_BASE));
}
av_log(NULL, AV_LOG_INFO, ", bitrate: ");
if (ic->bit_rate)
- av_log(NULL, AV_LOG_INFO, "%d kb/s", ic->bit_rate / 1000);
+ av_log(NULL, AV_LOG_INFO, "%"PRId64" kb/s", ic->bit_rate / 1000);
else
av_log(NULL, AV_LOG_INFO, "N/A");
av_log(NULL, AV_LOG_INFO, "\n");
diff --git a/libavformat/dv.c b/libavformat/dv.c
index 748b5e8..06de044 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 16-bit linear samples: 12-bit nonlinear samples
* are converted into 16-bit 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 (12-bit 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,36 +279,44 @@ 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;
+ AVCodecParameters *par;
int apt, is16_9;
- int size = 0;
- if (c->sys) {
- AVCodecParameters *par = c->vst->codecpar;
+ par = c->vst->codecpar;
+
+ 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);
+
+ /* finding out SAR is a little bit messy */
+ vsc_pack = dv_extract_pack(frame, dv_video_control);
+ apt = frame[4] & 0x07;
+ is16_9 = (vsc_pack && ((vsc_pack[2] & 0x07) == 0x02 ||
+ (!apt && (vsc_pack[2] & 0x07) == 0x07)));
+ c->vst->sample_aspect_ratio = c->sys->sar[is16_9];
+ par->bit_rate = av_rescale_q(c->sys->frame_size,
+ (AVRational) { 8, 1 },
+ c->sys->time_base);
+ return c->sys->frame_size;
+}
- 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 (!par->width) {
- par->width = c->sys->width;
- par->height = c->sys->height;
- }
- par->format = c->sys->pix_fmt;
-
- /* finding out SAR is a little bit messy */
- vsc_pack = dv_extract_pack(frame, dv_video_control);
- apt = frame[4] & 0x07;
- is16_9 = (vsc_pack && ((vsc_pack[2] & 0x07) == 0x02 ||
- (!apt && (vsc_pack[2] & 0x07) == 0x07)));
- c->vst->sample_aspect_ratio = c->sys->sar[is16_9];
- par->bit_rate = av_rescale_q(c->sys->frame_size,
- (AVRational) { 8, 1 },
- c->sys->time_base);
- size = c->sys->frame_size;
- }
- 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 */
@@ -340,7 +362,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 };
@@ -355,6 +377,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]->codecpar->bit_rate;
@@ -380,6 +403,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;
@@ -394,13 +418,12 @@ 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->codecpar->width, c->vst->codecpar->height,
- c->vst->codecpar->format);
+ const int frame_size = c->sys->frame_size;
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;
+ int64_t max_offset = ((size - 1) / frame_size) * frame_size;
- offset = sys->frame_size * timestamp;
+ offset = frame_size * timestamp;
if (size >= 0 && offset > max_offset)
offset = max_offset;
@@ -413,10 +436,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]->codecpar->bit_rate });
-
+ } else
+ av_log(c->fctx, AV_LOG_ERROR, "cannot adjust audio bytes\n");
+ }
c->audio_pkt[0].size = c->audio_pkt[1].size = 0;
c->audio_pkt[2].size = c->audio_pkt[3].size = 0;
}
@@ -430,6 +456,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;
@@ -441,7 +502,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;
}
@@ -456,7 +517,7 @@ static int dv_read_header(AVFormatContext *s)
}
AV_WB32(c->buf, state);
- if (avio_read(s->pb, c->buf + 4, DV_PROFILE_BYTES - 4) <= 0 ||
+ if (avio_read(s->pb, c->buf + 4, DV_PROFILE_BYTES - 4) != DV_PROFILE_BYTES - 4 ||
avio_seek(s->pb, -DV_PROFILE_BYTES, SEEK_CUR) < 0)
return AVERROR(EIO);
@@ -473,6 +534,9 @@ static int dv_read_header(AVFormatContext *s)
(AVRational) { 8, 1 },
c->dv_demux->sys->time_base);
+ if (s->pb->seekable & AVIO_SEEKABLE_NORMAL)
+ dv_read_timecode(s);
+
return 0;
}
@@ -485,15 +549,18 @@ static int dv_read_packet(AVFormatContext *s, AVPacket *pkt)
if (size < 0) {
int ret;
-
+ int64_t pos = avio_tell(s->pb);
if (!c->dv_demux->sys)
return AVERROR(EIO);
size = c->dv_demux->sys->frame_size;
ret = avio_read(s->pb, c->buf, size);
- if (ret <= 0)
+ if (ret < 0) {
return ret;
+ } else if (ret == 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;
@@ -516,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 e8b2d37..160c6ab 100644
--- a/libavformat/dv.h
+++ b/libavformat/dv.h
@@ -8,20 +8,20 @@
* Raw DV format
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -33,7 +33,7 @@
typedef struct DVDemuxContext DVDemuxContext;
DVDemuxContext* avpriv_dv_init_demux(AVFormatContext* s);
int avpriv_dv_get_packet(DVDemuxContext*, AVPacket *);
-int avpriv_dv_produce_packet(DVDemuxContext*, AVPacket*, uint8_t*, int);
+int avpriv_dv_produce_packet(DVDemuxContext*, AVPacket*, uint8_t*, int, int64_t);
void ff_dv_offset_reset(DVDemuxContext *c, int64_t frame_offset);
typedef struct DVMuxContext DVMuxContext;
diff --git a/libavformat/dvbsub.c b/libavformat/dvbsub.c
new file mode 100644
index 0000000..3d2f704
--- /dev/null
+++ b/libavformat/dvbsub.c
@@ -0,0 +1,70 @@
+/*
+ * 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; 6 < end - ptr; 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;
+ if (6 + len > end - ptr)
+ 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/dvbtxt.c b/libavformat/dvbtxt.c
new file mode 100644
index 0000000..6828738
--- /dev/null
+++ b/libavformat/dvbtxt.c
@@ -0,0 +1,50 @@
+/*
+ * RAW dvb teletext demuxer
+ * Copyright (c) 2016 Marton Balnt <cus@passwd.hu>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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/dvbtxt.h"
+
+#include "avformat.h"
+#include "rawdec.h"
+
+static int dvbtxt_probe(AVProbeData *p)
+{
+ const uint8_t *end = p->buf + p->buf_size;
+ const uint8_t *buf;
+
+ /* The purpose of this is demuxer is to detect DVB teletext streams in
+ * mpegts, so we reject invalid buffer sizes */
+ if ((p->buf_size + 45) % 184 != 0)
+ return 0;
+
+ if (!ff_data_identifier_is_teletext(p->buf[0]))
+ return 0;
+
+ for (buf = p->buf + 1; buf < end; buf += 46) {
+ if (!ff_data_unit_id_is_teletext(buf[0]) && buf[0] != 0xff)
+ return 0;
+ if (buf[1] != 0x2c) // data_unit_length
+ return 0;
+ }
+
+ return AVPROBE_SCORE_MAX / 2;
+}
+
+FF_DEF_RAWSUB_DEMUXER(dvbtxt, "dvbtxt", dvbtxt_probe, NULL, AV_CODEC_ID_DVB_TELETEXT, 0)
diff --git a/libavformat/dvenc.c b/libavformat/dvenc.c
index cf79534..93c103b 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>
@@ -35,12 +35,17 @@
#include "libavcodec/dv_profile.h"
#include "libavcodec/dv.h"
#include "dv.h"
+#include "libavutil/avassert.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 32-bit 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 +55,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 +73,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 +91,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]->codecpar->sample_rate == 44100) {
+ audio_type = 1;
+ } else if (c->ast[channel]->codecpar->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]->codecpar->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 +125,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 -- 16-bit linear, 1 -- 12-bit nonlinear */
+
va_end(ap);
break;
case dv_audio_control:
@@ -184,12 +187,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]->codecpar->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)
@@ -243,7 +246,6 @@ static int dv_assemble_frame(AVFormatContext *s,
int i, reqasize;
*frame = &c->frame_buf[0];
- reqasize = 4 * dv_audio_frame_size(c->sys, c->frames);
switch (st->codecpar->codec_type) {
case AVMEDIA_TYPE_VIDEO:
@@ -267,6 +269,8 @@ static int dv_assemble_frame(AVFormatContext *s,
av_log(s, 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->codecpar->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);
@@ -281,6 +285,7 @@ static int dv_assemble_frame(AVFormatContext *s,
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]->codecpar->sample_rate);
av_fifo_drain(c->audio_data[i], reqasize);
c->has_audio |= ((reqasize <= av_fifo_size(c->audio_data[i])) << i);
}
@@ -299,7 +304,6 @@ static DVMuxContext* dv_init_mux(AVFormatContext* s)
{
DVMuxContext *c = s->priv_data;
AVStream *vst = NULL;
- AVDictionaryEntry *t;
int i;
/* we support at most 1 video and 2 audio streams */
@@ -329,15 +333,28 @@ static DVMuxContext* dv_init_mux(AVFormatContext* s)
if (!vst || vst->codecpar->codec_id != AV_CODEC_ID_DVVIDEO)
goto bail_out;
for (i=0; i<c->n_ast; i++) {
- if (c->ast[i] && (c->ast[i]->codecpar->codec_id != AV_CODEC_ID_PCM_S16LE ||
- c->ast[i]->codecpar->sample_rate != 48000 ||
- c->ast[i]->codecpar->channels != 2))
- goto bail_out;
+ if (c->ast[i]) {
+ if(c->ast[i]->codecpar->codec_id != AV_CODEC_ID_PCM_S16LE ||
+ c->ast[i]->codecpar->channels != 2)
+ goto bail_out;
+ if (c->ast[i]->codecpar->sample_rate != 48000 &&
+ c->ast[i]->codecpar->sample_rate != 44100 &&
+ c->ast[i]->codecpar->sample_rate != 32000 )
+ goto bail_out;
+ }
}
- c->sys = av_dv_codec_profile(vst->codecpar->width, vst->codecpar->height, vst->codecpar->format);
+ c->sys = av_dv_codec_profile2(vst->codecpar->width, vst->codecpar->height,
+ vst->codecpar->format, vst->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]->codecpar->sample_rate != 48000)
+ goto bail_out;
+ if (c->ast[1] && c->ast[1]->codecpar->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;
@@ -347,14 +364,13 @@ static DVMuxContext* dv_init_mux(AVFormatContext* s)
c->frames = 0;
c->has_audio = 0;
c->has_video = 0;
- if (t = av_dict_get(s->metadata, "creation_time", NULL, 0))
- c->start_time = ff_iso8601_to_unix_time(t->value);
+ ff_parse_creation_time_metadata(s, &c->start_time, 1);
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;
}
@@ -370,19 +386,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 a86b90e..5019390 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(s, pb, ast->codecpar, fsize);
+ return AVERROR(ENOMEM);
+ ret = ff_get_wav_header(s, pb, ast->codecpar, fsize, 0);
if (ret < 0)
return ret;
if (ast->codecpar->sample_rate > 0)
avpriv_set_pts_info(ast, 64, 1, ast->codecpar->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,9 +170,14 @@ 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);
- switch(AV_RL32(buf)){
+ while(!avio_feof(s->pb) && c->frames){
+ uint32_t tag;
+ 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;
+ }
+ tag = AV_RL32(buf);
+ switch (tag) {
case MKTAG('N', 'U', 'L', 'L'):
if(av_new_packet(pkt, 4 + pal_size) < 0)
return AVERROR(ENOMEM);
@@ -189,12 +194,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);
@@ -211,11 +219,11 @@ static int dxa_read_packet(AVFormatContext *s, AVPacket *pkt)
c->readvid = 0;
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;
+ av_log(s, AV_LOG_ERROR, "Unknown tag %s\n", av_fourcc2str(tag));
+ return AVERROR_INVALIDDATA;
}
}
- return AVERROR(EIO);
+ return AVERROR_EOF;
}
AVInputFormat ff_dxa_demuxer = {
diff --git a/libavformat/eacdata.c b/libavformat/eacdata.c
index d3f27a4..97eb66e 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->codecpar->codec_tag = 0; /* no fourcc */
st->codecpar->codec_id = AV_CODEC_ID_ADPCM_EA_XAS;
st->codecpar->channels = cdata->channels;
+ st->codecpar->channel_layout = channel_layout;
st->codecpar->sample_rate = sample_rate;
avpriv_set_pts_info(st, 64, 1, sample_rate);
diff --git a/libavformat/electronicarts.c b/libavformat/electronicarts.c
index 2df27e4..bfd3fed 100644
--- a/libavformat/electronicarts.c
+++ b/libavformat/electronicarts.c
@@ -2,20 +2,20 @@
* Copyright (c) 2004 The FFmpeg project
* Copyright (c) 2006-2008 Peter Ross
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -59,16 +59,25 @@
#define MVhd_TAG MKTAG('M', 'V', 'h', 'd')
#define MV0K_TAG MKTAG('M', 'V', '0', 'K')
#define MV0F_TAG MKTAG('M', 'V', '0', 'F')
+#define AVhd_TAG MKTAG('A', 'V', 'h', 'd')
+#define AV0K_TAG MKTAG('A', 'V', '0', 'K')
+#define AV0F_TAG MKTAG('A', 'V', '0', 'F')
#define MVIh_TAG MKTAG('M', 'V', 'I', 'h') /* CMV header */
#define MVIf_TAG MKTAG('M', 'V', 'I', 'f') /* CMV I-frame */
+#define AVP6_TAG MKTAG('A', 'V', 'P', '6')
+
+typedef struct VideoProperties {
+ enum AVCodecID codec;
+ AVRational time_base;
+ int width, height;
+ int nb_frames;
+ int stream_index;
+} VideoProperties;
typedef struct EaDemuxContext {
int big_endian;
- enum AVCodecID video_codec;
- AVRational time_base;
- int width, height;
- int video_stream_index;
+ VideoProperties video, alpha;
enum AVCodecID audio_codec;
int audio_stream_index;
@@ -77,6 +86,8 @@ typedef struct EaDemuxContext {
int sample_rate;
int num_channels;
int num_samples;
+
+ int platform;
} EaDemuxContext;
static uint32_t read_arbitrary(AVIOContext *pb)
@@ -108,7 +119,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 +128,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 +222,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,8 +230,16 @@ 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 15:
case 16:
ea->audio_codec = AV_CODEC_ID_MP3;
break;
@@ -229,18 +247,19 @@ 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;
}
+ if (ea->audio_codec == AV_CODEC_ID_NONE && ea->platform == 0x01)
+ ea->audio_codec = AV_CODEC_ID_ADPCM_PSX;
if (ea->sample_rate == -1)
ea->sample_rate = revision == 3 ? 48000 : 22050;
@@ -278,9 +297,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);
}
}
@@ -295,38 +314,43 @@ static void process_audio_header_sead(AVFormatContext *s)
ea->audio_codec = AV_CODEC_ID_ADPCM_IMA_EA_SEAD;
}
-static void process_video_header_mdec(AVFormatContext *s)
+static void process_video_header_mdec(AVFormatContext *s, VideoProperties *video)
{
- EaDemuxContext *ea = s->priv_data;
AVIOContext *pb = s->pb;
avio_skip(pb, 4);
- ea->width = avio_rl16(pb);
- ea->height = avio_rl16(pb);
- ea->time_base = (AVRational) { 1, 15 };
- ea->video_codec = AV_CODEC_ID_MDEC;
+ video->width = avio_rl16(pb);
+ video->height = avio_rl16(pb);
+ video->time_base = (AVRational) { 1, 15 };
+ video->codec = AV_CODEC_ID_MDEC;
}
-static void process_video_header_vp6(AVFormatContext *s)
+static int process_video_header_vp6(AVFormatContext *s, VideoProperties *video)
{
- EaDemuxContext *ea = s->priv_data;
- AVIOContext *pb = s->pb;
+ AVIOContext *pb = s->pb;
- avio_skip(pb, 16);
- ea->time_base.den = avio_rl32(pb);
- ea->time_base.num = avio_rl32(pb);
- ea->video_codec = AV_CODEC_ID_VP6;
+ avio_skip(pb, 8);
+ video->nb_frames = avio_rl32(pb);
+ avio_skip(pb, 4);
+ video->time_base.den = avio_rl32(pb);
+ video->time_base.num = avio_rl32(pb);
+ if (video->time_base.den <= 0 || video->time_base.num <= 0) {
+ av_log(s, AV_LOG_ERROR, "Timebase is invalid\n");
+ return AVERROR_INVALIDDATA;
+ }
+ video->codec = AV_CODEC_ID_VP6;
+
+ return 1;
}
-static void process_video_header_cmv(AVFormatContext *s)
+static void process_video_header_cmv(AVFormatContext *s, VideoProperties *video)
{
- EaDemuxContext *ea = s->priv_data;
int fps;
avio_skip(s->pb, 10);
fps = avio_rl16(s->pb);
if (fps)
- ea->time_base = (AVRational) { 1, fps };
- ea->video_codec = AV_CODEC_ID_CMV;
+ video->time_base = (AVRational) { 1, fps };
+ video->codec = AV_CODEC_ID_CMV;
}
/* Process EA file header.
@@ -338,21 +362,26 @@ static int process_ea_header(AVFormatContext *s)
AVIOContext *pb = s->pb;
int i;
- for (i = 0; i < 5 && (!ea->audio_codec || !ea->video_codec); i++) {
- unsigned int startpos = avio_tell(pb);
+ for (i = 0; i < 5 && (!ea->audio_codec || !ea->video.codec); i++) {
+ 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);
@@ -363,10 +392,10 @@ static int process_ea_header(AVFormatContext *s)
blockid = avio_rl32(pb);
if (blockid == GSTR_TAG) {
avio_skip(pb, 4);
- } else if ((blockid & 0xFFFF) != PT00_TAG) {
- av_log(s, AV_LOG_ERROR, "unknown SCHl headerid\n");
- return 0;
+ } else if ((blockid & 0xFF) != (PT00_TAG & 0xFF)) {
+ blockid = avio_rl32(pb);
}
+ ea->platform = (blockid >> 16) & 0xFF;
err = process_audio_header_elements(s);
break;
@@ -375,41 +404,44 @@ static int process_ea_header(AVFormatContext *s)
break;
case MVIh_TAG:
- process_video_header_cmv(s);
+ process_video_header_cmv(s, &ea->video);
break;
case kVGT_TAG:
- ea->video_codec = AV_CODEC_ID_TGV;
- ea->time_base = (AVRational) { 1, 15 };
+ ea->video.codec = AV_CODEC_ID_TGV;
break;
case mTCD_TAG:
- process_video_header_mdec(s);
+ process_video_header_mdec(s, &ea->video);
break;
case MPCh_TAG:
- ea->video_codec = AV_CODEC_ID_MPEG2VIDEO;
+ ea->video.codec = AV_CODEC_ID_MPEG2VIDEO;
break;
case pQGT_TAG:
case TGQs_TAG:
- ea->video_codec = AV_CODEC_ID_TGQ;
- ea->time_base = (AVRational) { 1, 15 };
+ ea->video.codec = AV_CODEC_ID_TGQ;
+ ea->video.time_base = (AVRational) { 1, 15 };
break;
case pIQT_TAG:
- ea->video_codec = AV_CODEC_ID_TQI;
- ea->time_base = (AVRational) { 1, 15 };
+ ea->video.codec = AV_CODEC_ID_TQI;
+ ea->video.time_base = (AVRational) { 1, 15 };
break;
case MADk_TAG:
- ea->video_codec = AV_CODEC_ID_MAD;
+ ea->video.codec = AV_CODEC_ID_MAD;
avio_skip(pb, 6);
- ea->time_base = (AVRational) { avio_rl16(pb), 1000 };
+ ea->video.time_base = (AVRational) { avio_rl16(pb), 1000 };
break;
case MVhd_TAG:
- process_video_header_vp6(s);
+ err = process_video_header_vp6(s, &ea->video);
+ break;
+
+ case AVhd_TAG:
+ err = process_video_header_vp6(s, &ea->alpha);
break;
}
@@ -428,6 +460,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:
@@ -438,39 +472,59 @@ static int ea_probe(AVProbeData *p)
case MPCh_TAG:
case MVhd_TAG:
case MVIh_TAG:
+ case AVP6_TAG:
break;
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;
}
+static int init_video_stream(AVFormatContext *s, VideoProperties *video)
+{
+ AVStream *st;
+
+ if (!video->codec)
+ return 0;
+
+ /* initialize the video decoder stream */
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ video->stream_index = st->index;
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_id = video->codec;
+ // parsing is necessary to make FFmpeg generate correct timestamps
+ if (st->codecpar->codec_id == AV_CODEC_ID_MPEG2VIDEO)
+ st->need_parsing = AVSTREAM_PARSE_HEADERS;
+ st->codecpar->codec_tag = 0; /* no fourcc */
+ st->codecpar->width = video->width;
+ st->codecpar->height = video->height;
+ st->duration = st->nb_frames = video->nb_frames;
+ if (video->time_base.num)
+ avpriv_set_pts_info(st, 64, video->time_base.num, video->time_base.den);
+ st->r_frame_rate =
+ st->avg_frame_rate = av_inv_q(video->time_base);
+ return 0;
+}
+
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) {
- /* initialize the video decoder stream */
- st = avformat_new_stream(s, NULL);
- if (!st)
- return AVERROR(ENOMEM);
- ea->video_stream_index = st->index;
- st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
- st->codecpar->codec_id = ea->video_codec;
- st->codecpar->codec_tag = 0; /* no fourcc */
- st->codecpar->width = ea->width;
- st->codecpar->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 };
- }
+ if (init_video_stream(s, &ea->video) || init_video_stream(s, &ea->alpha))
+ return AVERROR(ENOMEM);
if (ea->audio_codec) {
if (ea->num_channels <= 0 || ea->num_channels > 2) {
@@ -485,7 +539,7 @@ static int ea_read_header(AVFormatContext *s)
ea->audio_codec = 0;
return 1;
}
- if (ea->bytes <= 0) {
+ if (ea->bytes <= 0 || ea->bytes > 2) {
av_log(s, AV_LOG_ERROR,
"Invalid number of bytes per sample: %d\n", ea->bytes);
ea->audio_codec = AV_CODEC_ID_NONE;
@@ -503,7 +557,7 @@ static int ea_read_header(AVFormatContext *s)
st->codecpar->channels = ea->num_channels;
st->codecpar->sample_rate = ea->sample_rate;
st->codecpar->bits_per_coded_sample = ea->bytes * 8;
- st->codecpar->bit_rate = st->codecpar->channels *
+ st->codecpar->bit_rate = (int64_t)st->codecpar->channels *
st->codecpar->sample_rate *
st->codecpar->bits_per_coded_sample / 4;
st->codecpar->block_align = st->codecpar->channels *
@@ -519,11 +573,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)
@@ -550,7 +605,17 @@ static int ea_read_packet(AVFormatContext *s, AVPacket *pkt)
num_samples = avio_rl32(pb);
avio_skip(pb, 8);
chunk_size -= 12;
+ } else if (ea->audio_codec == AV_CODEC_ID_ADPCM_PSX) {
+ avio_skip(pb, 8);
+ chunk_size -= 8;
}
+
+ if (partial_packet) {
+ avpriv_request_sample(s, "video header followed by audio packet");
+ av_packet_unref(pkt);
+ partial_packet = 0;
+ }
+
if (!chunk_size)
continue;
@@ -582,6 +647,9 @@ static int ea_read_packet(AVFormatContext *s, AVPacket *pkt)
case AV_CODEC_ID_MP3:
pkt->duration = num_samples;
break;
+ case AV_CODEC_ID_ADPCM_PSX:
+ pkt->duration = chunk_size / (16 * ea->num_channels) * 28;
+ break;
default:
pkt->duration = chunk_size / (ea->bytes * ea->num_channels);
}
@@ -595,7 +663,19 @@ static int ea_read_packet(AVFormatContext *s, AVPacket *pkt)
case SCEl_TAG:
case SEND_TAG:
case SEEN_TAG:
- ret = AVERROR(EIO);
+ while (!avio_feof(pb)) {
+ int tag = avio_rl32(pb);
+
+ if (tag == ISNh_TAG ||
+ tag == SCHl_TAG ||
+ tag == SEAD_TAG ||
+ tag == SHEN_TAG) {
+ avio_skip(pb, -4);
+ break;
+ }
+ }
+ if (avio_feof(pb))
+ ret = AVERROR_EOF;
packet_read = 1;
break;
@@ -622,18 +702,29 @@ static int ea_read_packet(AVFormatContext *s, AVPacket *pkt)
goto get_video_packet;
case MV0K_TAG:
+ case AV0K_TAG:
case MPCh_TAG:
case pIQT_TAG:
key = AV_PKT_FLAG_KEY;
case MV0F_TAG:
+ case AV0F_TAG:
get_video_packet:
if (!chunk_size)
continue;
- ret = av_get_packet(pb, pkt, chunk_size);
- if (ret < 0)
- return ret;
- pkt->stream_index = ea->video_stream_index;
+ 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;
+ if (chunk_type == AV0K_TAG || chunk_type == AV0F_TAG)
+ pkt->stream_index = ea->alpha.stream_index;
+ else
+ pkt->stream_index = ea->video.stream_index;
pkt->flags |= key;
packet_read = 1;
break;
@@ -644,6 +735,8 @@ get_video_packet:
}
}
+ if (ret < 0 && partial_packet)
+ av_packet_unref(pkt);
return ret;
}
diff --git a/libavformat/epafdec.c b/libavformat/epafdec.c
new file mode 100644
index 0000000..0cd9627
--- /dev/null
+++ b/libavformat/epafdec.c
@@ -0,0 +1,105 @@
+/*
+ * 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 "libavcodec/internal.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 <= 0 || channels > FF_SANE_NB_CHANNELS || sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->channels = channels;
+ st->codecpar->sample_rate = sample_rate;
+ switch (codec) {
+ case 0:
+ st->codecpar->codec_id = le ? AV_CODEC_ID_PCM_S16LE : AV_CODEC_ID_PCM_S16BE;
+ break;
+ case 2:
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_S8;
+ break;
+ case 1:
+ avpriv_request_sample(s, "24-bit Paris PCM format");
+ default:
+ return AVERROR_INVALIDDATA;
+ }
+
+ st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id);
+ st->codecpar->block_align = st->codecpar->bits_per_coded_sample * st->codecpar->channels / 8;
+
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ if (avio_skip(s->pb, 2024) < 0)
+ return AVERROR_INVALIDDATA;
+ return 0;
+}
+
+AVInputFormat ff_epaf_demuxer = {
+ .name = "epaf",
+ .long_name = NULL_IF_CONFIG_SMALL("Ensoniq Paris Audio File"),
+ .read_probe = epaf_probe,
+ .read_header = epaf_read_header,
+ .read_packet = ff_pcm_read_packet,
+ .read_seek = ff_pcm_read_seek,
+ .extensions = "paf,fap",
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/ffm.h b/libavformat/ffm.h
new file mode 100644
index 0000000..c445f47
--- /dev/null
+++ b/libavformat/ffm.h
@@ -0,0 +1,62 @@
+/*
+ * 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 {
+ const AVClass *class;
+ /* 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;
+ int server_attached;
+} FFMContext;
+
+#endif /* AVFORMAT_FFM_H */
diff --git a/libavformat/ffmdec.c b/libavformat/ffmdec.c
new file mode 100644
index 0000000..de6ac27
--- /dev/null
+++ b/libavformat/ffmdec.c
@@ -0,0 +1,878 @@
+/*
+ * 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/imgutils.h"
+#include "libavutil/internal.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/intfloat.h"
+#include "libavutil/opt.h"
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/pixdesc.h"
+#include "libavcodec/internal.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;
+ ptrdiff_t 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 */
+ if (ffm->server_attached)
+ return AVERROR(EAGAIN);
+ else
+ return AVERROR_INVALIDDATA;
+ } 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 if (ffm->server_attached)
+ return AVERROR(EAGAIN);
+ else
+ return AVERROR_INVALIDDATA;
+}
+
+static int ffm_resync(AVFormatContext *s, uint32_t 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 fill_size, size1, frame_offset;
+ uint32_t id;
+ ptrdiff_t len;
+ int64_t last_pos = -1;
+
+ 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) {
+ if (ffm->server_attached) {
+ avio_seek(pb, ffm->packet_size, SEEK_SET);
+ } else
+ return AVERROR_EOF;
+ }
+ retry_read:
+ if (pb->buffer_size != ffm->packet_size) {
+ int64_t tell = avio_tell(pb);
+ int ret = ffio_set_buf_size(pb, ffm->packet_size);
+ if (ret < 0)
+ return ret;
+ avio_seek(pb, tell, SEEK_SET);
+ }
+ id = avio_rb16(pb); /* PACKET_ID */
+ if (id != PACKET_ID) {
+ if (ffm_resync(s, id) < 0)
+ return -1;
+ last_pos = avio_tell(pb);
+ }
+ 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);
+ if (ffm->packet_size < FFM_HEADER_SIZE + fill_size || frame_offset < 0) {
+ return -1;
+ }
+ ffm->packet_end = ffm->packet + (ffm->packet_size - FFM_HEADER_SIZE - fill_size);
+ /* 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) {
+ int64_t seekback = FFMIN(ffm->packet_size * 2LL, avio_tell(pb) - last_pos);
+ seekback = FFMAX(seekback, 0);
+ avio_seek(pb, -seekback, 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) {
+ ffm->packet_end = ffm->packet_ptr;
+ 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 actual 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);
+ ff_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);
+ ff_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_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;
+}
+
+#define VALIDATE_PARAMETER(parameter, name, check) { \
+ if (check) { \
+ av_log(s, AV_LOG_ERROR, "Invalid " name " %d\n", codecpar->parameter); \
+ ret = AVERROR_INVALIDDATA; \
+ goto fail; \
+ } \
+}
+
+static int ffm2_read_header(AVFormatContext *s)
+{
+ FFMContext *ffm = s->priv_data;
+ AVStream *st = NULL;
+ AVIOContext *pb = s->pb;
+ AVCodecContext *dummy_codec = NULL;
+ AVCodecParameters *codecpar = NULL;
+ const AVCodecDescriptor *codec_desc;
+ int ret;
+ int f_main = 0, f_cprv = -1, f_stvi = -1, f_stau = -1;
+ 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 & AVIO_SEEKABLE_NORMAL) {
+ ffm->file_size = avio_size(pb);
+ if (ffm->write_index && 0)
+ adjust_write_index(s);
+ } else {
+ ffm->file_size = (UINT64_C(1) << 63) - 1;
+ }
+ dummy_codec = avcodec_alloc_context3(NULL);
+
+ 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];
+ int flags;
+
+ 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);
+
+ codecpar = st->codecpar;
+ /* generic info */
+ codecpar->codec_id = avio_rb32(pb);
+ codec_desc = avcodec_descriptor_get(codecpar->codec_id);
+ if (!codec_desc) {
+ av_log(s, AV_LOG_ERROR, "Invalid codec id: %d\n", codecpar->codec_id);
+ codecpar->codec_id = AV_CODEC_ID_NONE;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ codecpar->codec_type = avio_r8(pb);
+ if (codecpar->codec_type != codec_desc->type) {
+ av_log(s, AV_LOG_ERROR, "Codec type mismatch: expected %d, found %d\n",
+ codec_desc->type, codecpar->codec_type);
+ codecpar->codec_id = AV_CODEC_ID_NONE;
+ codecpar->codec_type = AVMEDIA_TYPE_UNKNOWN;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ codecpar->bit_rate = avio_rb32(pb);
+ if (codecpar->bit_rate < 0) {
+ av_log(s, AV_LOG_ERROR, "Invalid bit rate %"PRId64"\n", codecpar->bit_rate);
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ flags = avio_rb32(pb);
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ st->codec->flags = flags;
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+ avio_rb32(pb); // flags2
+ avio_rb32(pb); // debug
+ if (flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
+ int size = avio_rb32(pb);
+ if (size < 0 || size >= FF_MAX_EXTRADATA_SIZE) {
+ av_log(s, AV_LOG_ERROR, "Invalid extradata size %d\n", size);
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ codecpar->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!codecpar->extradata) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ codecpar->extradata_size = size;
+ avio_read(pb, codecpar->extradata, size);
+ }
+ break;
+ case MKBETAG('S', 'T', 'V', 'I'):
+ if (f_stvi++ || codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ avio_rb32(pb); // time_base.num
+ avio_rb32(pb); // time_base.den
+ codecpar->width = avio_rb16(pb);
+ codecpar->height = avio_rb16(pb);
+ ret = av_image_check_size(codecpar->width, codecpar->height, 0, s);
+ if (ret < 0)
+ goto fail;
+ avio_rb16(pb); // gop_size
+ codecpar->format = avio_rb32(pb);
+ if (!av_pix_fmt_desc_get(codecpar->format)) {
+ av_log(s, AV_LOG_ERROR, "Invalid pix fmt id: %d\n", codecpar->format);
+ codecpar->format = AV_PIX_FMT_NONE;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ avio_r8(pb); // qmin
+ avio_r8(pb); // qmax
+ avio_r8(pb); // max_qdiff
+ avio_rb16(pb); // qcompress / 10000.0
+ avio_rb16(pb); // qblur / 10000.0
+ avio_rb32(pb); // bit_rate_tolerance
+ avio_get_str(pb, INT_MAX, rc_eq_buf, sizeof(rc_eq_buf));
+
+ avio_rb32(pb); // rc_max_rate
+ avio_rb32(pb); // rc_min_rate
+ avio_rb32(pb); // rc_buffer_size
+ avio_rb64(pb); // i_quant_factor
+ avio_rb64(pb); // b_quant_factor
+ avio_rb64(pb); // i_quant_offset
+ avio_rb64(pb); // b_quant_offset
+ avio_rb32(pb); // dct_algo
+ avio_rb32(pb); // strict_std_compliance
+ avio_rb32(pb); // max_b_frames
+ avio_rb32(pb); // mpeg_quant
+ avio_rb32(pb); // intra_dc_precision
+ avio_rb32(pb); // me_method
+ avio_rb32(pb); // mb_decision
+ avio_rb32(pb); // nsse_weight
+ avio_rb32(pb); // frame_skip_cmp
+ avio_rb64(pb); // rc_buffer_aggressivity
+ codecpar->codec_tag = avio_rb32(pb);
+ avio_r8(pb); // thread_count
+ avio_rb32(pb); // coder_type
+ avio_rb32(pb); // me_cmp
+ avio_rb32(pb); // me_subpel_quality
+ avio_rb32(pb); // me_range
+ avio_rb32(pb); // keyint_min
+ avio_rb32(pb); // scenechange_threshold
+ avio_rb32(pb); // b_frame_strategy
+ avio_rb64(pb); // qcompress
+ avio_rb64(pb); // qblur
+ avio_rb32(pb); // max_qdiff
+ avio_rb32(pb); // refs
+ break;
+ case MKBETAG('S', 'T', 'A', 'U'):
+ if (f_stau++ || codecpar->codec_type != AVMEDIA_TYPE_AUDIO) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ codecpar->sample_rate = avio_rb32(pb);
+ VALIDATE_PARAMETER(sample_rate, "sample rate", codecpar->sample_rate < 0)
+ codecpar->channels = avio_rl16(pb);
+ VALIDATE_PARAMETER(channels, "number of channels", codecpar->channels < 0)
+ codecpar->frame_size = avio_rl16(pb);
+ VALIDATE_PARAMETER(frame_size, "frame size", codecpar->frame_size < 0)
+ break;
+ case MKBETAG('C', 'P', 'R', 'V'):
+ if (f_cprv++) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ enc = avcodec_find_encoder(codecpar->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++ || !size || codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ buffer = av_malloc(size);
+ if (!buffer) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ avio_get_str(pb, INT_MAX, buffer, size);
+ // The lack of AVOptions support in AVCodecParameters makes this back and forth copying needed
+ avcodec_parameters_to_context(dummy_codec, codecpar);
+ av_set_options_string(dummy_codec, buffer, "=", ",");
+ avcodec_parameters_from_context(codecpar, dummy_codec);
+ if ((ret = ffm_append_recommended_configuration(st, &buffer)) < 0)
+ goto fail;
+ break;
+ case MKBETAG('S', '2', 'A', 'U'):
+ if (f_stau++ || !size || codecpar->codec_type != AVMEDIA_TYPE_AUDIO) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+ buffer = av_malloc(size);
+ if (!buffer) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ avio_get_str(pb, INT_MAX, buffer, size);
+ // The lack of AVOptions support in AVCodecParameters makes this back and forth copying needed
+ avcodec_parameters_to_context(dummy_codec, codecpar);
+ av_set_options_string(dummy_codec, buffer, "=", ",");
+ avcodec_parameters_from_context(codecpar, dummy_codec);
+ if ((ret = ffm_append_recommended_configuration(st, &buffer)) < 0)
+ goto fail;
+ break;
+ }
+ avio_seek(pb, next, SEEK_SET);
+ }
+
+ /* get until end of block reached */
+ while ((avio_tell(pb) % ffm->packet_size) != 0 && !pb->eof_reached)
+ 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;
+ avcodec_free_context(&dummy_codec);
+ return 0;
+ fail:
+ avcodec_free_context(&dummy_codec);
+ return ret;
+}
+
+static int ffm_read_header(AVFormatContext *s)
+{
+ FFMContext *ffm = s->priv_data;
+ AVStream *st;
+ AVIOContext *pb = s->pb;
+ AVCodecContext *dummy_codec = NULL;
+ AVCodecParameters *codecpar;
+ const AVCodecDescriptor *codec_desc;
+ int i, nb_streams, ret;
+ 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')) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ ffm->packet_size = avio_rb32(pb);
+ if (ffm->packet_size != FFM_PACKET_SIZE) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ ffm->write_index = avio_rb64(pb);
+ /* get also filesize */
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ ffm->file_size = avio_size(pb);
+ if (ffm->write_index && 0)
+ adjust_write_index(s);
+ } else {
+ ffm->file_size = (UINT64_C(1) << 63) - 1;
+ }
+ dummy_codec = avcodec_alloc_context3(NULL);
+
+ 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];
+ int flags;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ avpriv_set_pts_info(st, 64, 1, 1000000);
+
+ codecpar = st->codecpar;
+ /* generic info */
+ codecpar->codec_id = avio_rb32(pb);
+ codec_desc = avcodec_descriptor_get(codecpar->codec_id);
+ if (!codec_desc) {
+ av_log(s, AV_LOG_ERROR, "Invalid codec id: %d\n", codecpar->codec_id);
+ codecpar->codec_id = AV_CODEC_ID_NONE;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ codecpar->codec_type = avio_r8(pb); /* codec_type */
+ if (codecpar->codec_type != codec_desc->type) {
+ av_log(s, AV_LOG_ERROR, "Codec type mismatch: expected %d, found %d\n",
+ codec_desc->type, codecpar->codec_type);
+ codecpar->codec_id = AV_CODEC_ID_NONE;
+ codecpar->codec_type = AVMEDIA_TYPE_UNKNOWN;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ codecpar->bit_rate = avio_rb32(pb);
+ if (codecpar->bit_rate < 0) {
+ av_log(s, AV_LOG_WARNING, "Invalid bit rate %"PRId64"\n", codecpar->bit_rate);
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ flags = avio_rb32(pb);
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ st->codec->flags = flags;
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+ avio_rb32(pb); // flags2
+ avio_rb32(pb); // debug
+ /* specific info */
+ switch(codecpar->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ avio_rb32(pb); // time_base.num
+ avio_rb32(pb); // time_base.den
+ codecpar->width = avio_rb16(pb);
+ codecpar->height = avio_rb16(pb);
+ if ((ret = av_image_check_size(codecpar->width, codecpar->height, 0, s)) < 0)
+ goto fail;
+ avio_rb16(pb); // gop_size
+ codecpar->format = avio_rb32(pb);
+ if (!av_pix_fmt_desc_get(codecpar->format)) {
+ av_log(s, AV_LOG_ERROR, "Invalid pix fmt id: %d\n", codecpar->format);
+ codecpar->format = AV_PIX_FMT_NONE;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ avio_r8(pb); // qmin
+ avio_r8(pb); // qmax
+ avio_r8(pb); // max_qdiff
+ avio_rb16(pb); // qcompress / 10000.0
+ avio_rb16(pb); // qblur / 10000.0
+ avio_rb32(pb); // bit_rate_tolerance
+ avio_get_str(pb, INT_MAX, rc_eq_buf, sizeof(rc_eq_buf));
+
+ avio_rb32(pb); // rc_max_rate
+ avio_rb32(pb); // rc_min_rate
+ avio_rb32(pb); // rc_buffer_size
+ avio_rb64(pb); // i_quant_factor
+ avio_rb64(pb); // b_quant_factor
+ avio_rb64(pb); // i_quant_offset
+ avio_rb64(pb); // b_quant_offset
+ avio_rb32(pb); // dct_algo
+ avio_rb32(pb); // strict_std_compliance
+ avio_rb32(pb); // max_b_frames
+ avio_rb32(pb); // mpeg_quant
+ avio_rb32(pb); // intra_dc_precision
+ avio_rb32(pb); // me_method
+ avio_rb32(pb); // mb_decision
+ avio_rb32(pb); // nsse_weight
+ avio_rb32(pb); // frame_skip_cmp
+ avio_rb64(pb); // rc_buffer_aggressivity
+ codecpar->codec_tag = avio_rb32(pb);
+ avio_r8(pb); // thread_count
+ avio_rb32(pb); // coder_type
+ avio_rb32(pb); // me_cmp
+ avio_rb32(pb); // me_subpel_quality
+ avio_rb32(pb); // me_range
+ avio_rb32(pb); // keyint_min
+ avio_rb32(pb); // scenechange_threshold
+ avio_rb32(pb); // b_frame_strategy
+ avio_rb64(pb); // qcompress
+ avio_rb64(pb); // qblur
+ avio_rb32(pb); // max_qdiff
+ avio_rb32(pb); // refs
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ codecpar->sample_rate = avio_rb32(pb);
+ VALIDATE_PARAMETER(sample_rate, "sample rate", codecpar->sample_rate < 0)
+ codecpar->channels = avio_rl16(pb);
+ VALIDATE_PARAMETER(channels, "number of channels", codecpar->channels < 0)
+ codecpar->frame_size = avio_rl16(pb);
+ VALIDATE_PARAMETER(frame_size, "frame size", codecpar->frame_size < 0)
+ break;
+ default:
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ if (flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
+ int size = avio_rb32(pb);
+ if (size < 0 || size >= FF_MAX_EXTRADATA_SIZE) {
+ av_log(s, AV_LOG_ERROR, "Invalid extradata size %d\n", size);
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ codecpar->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!codecpar->extradata) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ codecpar->extradata_size = size;
+ avio_read(pb, codecpar->extradata, size);
+ }
+ }
+
+ /* get until end of block reached */
+ while ((avio_tell(pb) % ffm->packet_size) != 0 && !pb->eof_reached)
+ 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;
+ avcodec_free_context(&dummy_codec);
+ return 0;
+ fail:
+ avcodec_free_context(&dummy_codec);
+ return ret;
+}
+
+/* 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;
+
+ ff_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_packet_unref(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_packet_unref(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;
+
+ ff_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;
+}
+
+static const AVOption options[] = {
+ {"server_attached", NULL, offsetof(FFMContext, server_attached), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_EXPORT },
+ {"ffm_write_index", NULL, offsetof(FFMContext, write_index), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, AV_OPT_FLAG_EXPORT },
+ {"ffm_file_size", NULL, offsetof(FFMContext, file_size), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, AV_OPT_FLAG_EXPORT },
+ { NULL },
+};
+
+static const AVClass ffm_class = {
+ .class_name = "ffm demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+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_seek = ffm_seek,
+ .priv_class = &ffm_class,
+};
diff --git a/libavformat/ffmenc.c b/libavformat/ffmenc.c
new file mode 100644
index 0000000..ef7dc3a
--- /dev/null
+++ b/libavformat/ffmenc.c
@@ -0,0 +1,362 @@
+/*
+ * 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 "avio_internal.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_ctx(AVIOContext *pb, AVCodecParameters *ctxpar, unsigned tag, int type)
+{
+ AVIOContext *tmp;
+ char *buf = NULL;
+ int ret, need_coma = 0;
+ AVCodecContext *ctx = NULL;
+
+#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);
+
+ // AVCodecParameters does not suport AVOptions, we thus must copy it over to a context that does
+ // otherwise it could be used directly and this would be much simpler
+ ctx = avcodec_alloc_context3(NULL);
+ if (!ctx) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ avcodec_parameters_to_context(ctx, ctxpar);
+
+ 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_freep(&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_freep(&buf);
+ avio_w8(tmp, 0);
+ write_header_chunk(pb, tmp, tag);
+ avcodec_free_context(&ctx);
+ return 0;
+ fail:
+ av_free(buf);
+ ffio_free_dyn_buf(&tmp);
+ avcodec_free_context(&ctx);
+ return ret;
+
+#undef SKIP_DEFAULTS
+#undef OPT_FLAGS_EXACT
+#undef ENC
+}
+
+static int ffm_write_recommended_config(AVIOContext *pb, AVCodecParameters *codecpar, unsigned tag,
+ const char *configuration)
+{
+ int ret;
+ const AVCodec *enc = avcodec_find_encoder(codecpar->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;
+ AVStream *st;
+ AVIOContext *pb = s->pb;
+ AVCodecParameters *codecpar;
+ int bit_rate, i, ret;
+
+ if ((ret = ff_parse_creation_time_metadata(s, &ffm->start_time, 0)) < 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->codecpar->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++) {
+ int flags = 0;
+ st = s->streams[i];
+ avpriv_set_pts_info(st, 64, 1, 1000000);
+ if(avio_open_dyn_buf(&pb) < 0)
+ return AVERROR(ENOMEM);
+
+ codecpar = st->codecpar;
+ /* generic info */
+ avio_wb32(pb, codecpar->codec_id);
+ avio_w8(pb, codecpar->codec_type);
+ avio_wb32(pb, codecpar->bit_rate);
+ if (codecpar->extradata_size)
+ flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
+
+ // If the user is not providing us with a configuration we have to fill it in as we cannot access the encoder
+ if (!st->recommended_encoder_configuration) {
+ if (s->flags & AVFMT_FLAG_BITEXACT)
+ flags |= AV_CODEC_FLAG_BITEXACT;
+ }
+
+ avio_wb32(pb, flags);
+ avio_wb32(pb, 0); // flags2
+ avio_wb32(pb, 0); // debug
+ if (codecpar->extradata_size) {
+ avio_wb32(pb, codecpar->extradata_size);
+ avio_write(pb, codecpar->extradata, codecpar->extradata_size);
+ }
+ write_header_chunk(s->pb, pb, MKBETAG('C', 'O', 'M', 'M'));
+ /* specific info */
+ switch(codecpar->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, codecpar, MKBETAG('S', '2', 'V', 'I'),
+ st->recommended_encoder_configuration)) < 0)
+ return ret;
+ } else if ((ret = ffm_write_header_codec_ctx(s->pb, codecpar, MKBETAG('S', '2', 'V', 'I'), 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, codecpar, MKBETAG('S', '2', 'A', 'U'),
+ st->recommended_encoder_configuration)) < 0)
+ return ret;
+ } else if ((ret = ffm_write_header_codec_ctx(s->pb, codecpar, MKBETAG('S', '2', 'A', 'U'), 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 a5380ca..ae8778d 100644
--- a/libavformat/ffmeta.h
+++ b/libavformat/ffmeta.h
@@ -2,20 +2,20 @@
* Common data for metadata muxer/demuxer
* Copyright (c) 2010 Anton Khirnov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/ffmetadec.c b/libavformat/ffmetadec.c
index 42fc98f..3290b3b 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->codecpar->codec_type = AVMEDIA_TYPE_DATA;
st->codecpar->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 19fe6c9..a9adbb1 100644
--- a/libavformat/ffmetaenc.c
+++ b/libavformat/ffmetaenc.c
@@ -2,20 +2,20 @@
* Metadata muxer
* Copyright (c) 2010 Anton Khirnov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/fifo.c b/libavformat/fifo.c
new file mode 100644
index 0000000..c881f31
--- /dev/null
+++ b/libavformat/fifo.c
@@ -0,0 +1,667 @@
+/*
+ * FIFO pseudo-muxer
+ * Copyright (c) 2016 Jan Sebechlebsky
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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/opt.h"
+#include "libavutil/time.h"
+#include "libavutil/thread.h"
+#include "libavutil/threadmessage.h"
+#include "avformat.h"
+#include "internal.h"
+
+#define FIFO_DEFAULT_QUEUE_SIZE 60
+#define FIFO_DEFAULT_MAX_RECOVERY_ATTEMPTS 0
+#define FIFO_DEFAULT_RECOVERY_WAIT_TIME_USEC 5000000 // 5 seconds
+
+typedef struct FifoContext {
+ const AVClass *class;
+ AVFormatContext *avf;
+
+ char *format;
+ char *format_options_str;
+ AVDictionary *format_options;
+
+ int queue_size;
+ AVThreadMessageQueue *queue;
+
+ pthread_t writer_thread;
+
+ /* Return value of last write_trailer_call */
+ int write_trailer_ret;
+
+ /* Time to wait before next recovery attempt
+ * This can refer to the time in processed stream,
+ * or real time. */
+ int64_t recovery_wait_time;
+
+ /* Maximal number of unsuccessful successive recovery attempts */
+ int max_recovery_attempts;
+
+ /* Whether to attempt recovery from failure */
+ int attempt_recovery;
+
+ /* If >0 stream time will be used when waiting
+ * for the recovery attempt instead of real time */
+ int recovery_wait_streamtime;
+
+ /* If >0 recovery will be attempted regardless of error code
+ * (except AVERROR_EXIT, so exit request is never ignored) */
+ int recover_any_error;
+
+ /* Whether to drop packets in case the queue is full. */
+ int drop_pkts_on_overflow;
+
+ /* Whether to wait for keyframe when recovering
+ * from failure or queue overflow */
+ int restart_with_keyframe;
+
+ pthread_mutex_t overflow_flag_lock;
+ int overflow_flag_lock_initialized;
+ /* Value > 0 signals queue overflow */
+ volatile uint8_t overflow_flag;
+
+} FifoContext;
+
+typedef struct FifoThreadContext {
+ AVFormatContext *avf;
+
+ /* Timestamp of last failure.
+ * This is either pts in case stream time is used,
+ * or microseconds as returned by av_getttime_relative() */
+ int64_t last_recovery_ts;
+
+ /* Number of current recovery process
+ * Value > 0 means we are in recovery process */
+ int recovery_nr;
+
+ /* If > 0 all frames will be dropped until keyframe is received */
+ uint8_t drop_until_keyframe;
+
+ /* Value > 0 means that the previous write_header call was successful
+ * so finalization by calling write_trailer and ff_io_close must be done
+ * before exiting / reinitialization of underlying muxer */
+ uint8_t header_written;
+} FifoThreadContext;
+
+typedef enum FifoMessageType {
+ FIFO_WRITE_HEADER,
+ FIFO_WRITE_PACKET,
+ FIFO_FLUSH_OUTPUT
+} FifoMessageType;
+
+typedef struct FifoMessage {
+ FifoMessageType type;
+ AVPacket pkt;
+} FifoMessage;
+
+static int fifo_thread_write_header(FifoThreadContext *ctx)
+{
+ AVFormatContext *avf = ctx->avf;
+ FifoContext *fifo = avf->priv_data;
+ AVFormatContext *avf2 = fifo->avf;
+ AVDictionary *format_options = NULL;
+ int ret, i;
+
+ ret = av_dict_copy(&format_options, fifo->format_options, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = ff_format_output_open(avf2, avf->filename, &format_options);
+ if (ret < 0) {
+ av_log(avf, AV_LOG_ERROR, "Error opening %s: %s\n", avf->filename,
+ av_err2str(ret));
+ goto end;
+ }
+
+ for (i = 0;i < avf2->nb_streams; i++)
+ avf2->streams[i]->cur_dts = 0;
+
+ ret = avformat_write_header(avf2, &format_options);
+ if (!ret)
+ ctx->header_written = 1;
+
+ // Check for options unrecognized by underlying muxer
+ if (format_options) {
+ AVDictionaryEntry *entry = NULL;
+ while ((entry = av_dict_get(format_options, "", entry, AV_DICT_IGNORE_SUFFIX)))
+ av_log(avf2, AV_LOG_ERROR, "Unknown option '%s'\n", entry->key);
+ ret = AVERROR(EINVAL);
+ }
+
+end:
+ av_dict_free(&format_options);
+ return ret;
+}
+
+static int fifo_thread_flush_output(FifoThreadContext *ctx)
+{
+ AVFormatContext *avf = ctx->avf;
+ FifoContext *fifo = avf->priv_data;
+ AVFormatContext *avf2 = fifo->avf;
+
+ return av_write_frame(avf2, NULL);
+}
+
+static int fifo_thread_write_packet(FifoThreadContext *ctx, AVPacket *pkt)
+{
+ AVFormatContext *avf = ctx->avf;
+ FifoContext *fifo = avf->priv_data;
+ AVFormatContext *avf2 = fifo->avf;
+ AVRational src_tb, dst_tb;
+ int ret, s_idx;
+
+ if (ctx->drop_until_keyframe) {
+ if (pkt->flags & AV_PKT_FLAG_KEY) {
+ ctx->drop_until_keyframe = 0;
+ av_log(avf, AV_LOG_VERBOSE, "Keyframe received, recovering...\n");
+ } else {
+ av_log(avf, AV_LOG_VERBOSE, "Dropping non-keyframe packet\n");
+ av_packet_unref(pkt);
+ return 0;
+ }
+ }
+
+ s_idx = pkt->stream_index;
+ src_tb = avf->streams[s_idx]->time_base;
+ dst_tb = avf2->streams[s_idx]->time_base;
+ av_packet_rescale_ts(pkt, src_tb, dst_tb);
+
+ ret = av_write_frame(avf2, pkt);
+ if (ret >= 0)
+ av_packet_unref(pkt);
+ return ret;
+}
+
+static int fifo_thread_write_trailer(FifoThreadContext *ctx)
+{
+ AVFormatContext *avf = ctx->avf;
+ FifoContext *fifo = avf->priv_data;
+ AVFormatContext *avf2 = fifo->avf;
+ int ret;
+
+ if (!ctx->header_written)
+ return 0;
+
+ ret = av_write_trailer(avf2);
+ ff_format_io_close(avf2, &avf2->pb);
+
+ return ret;
+}
+
+static int fifo_thread_dispatch_message(FifoThreadContext *ctx, FifoMessage *msg)
+{
+ int ret = AVERROR(EINVAL);
+
+ if (!ctx->header_written) {
+ ret = fifo_thread_write_header(ctx);
+ if (ret < 0)
+ return ret;
+ }
+
+ switch(msg->type) {
+ case FIFO_WRITE_HEADER:
+ av_assert0(ret >= 0);
+ return ret;
+ case FIFO_WRITE_PACKET:
+ return fifo_thread_write_packet(ctx, &msg->pkt);
+ case FIFO_FLUSH_OUTPUT:
+ return fifo_thread_flush_output(ctx);
+ }
+
+ av_assert0(0);
+ return AVERROR(EINVAL);
+}
+
+static int is_recoverable(const FifoContext *fifo, int err_no) {
+ if (!fifo->attempt_recovery)
+ return 0;
+
+ if (fifo->recover_any_error)
+ return err_no != AVERROR_EXIT;
+
+ switch (err_no) {
+ case AVERROR(EINVAL):
+ case AVERROR(ENOSYS):
+ case AVERROR_EOF:
+ case AVERROR_EXIT:
+ case AVERROR_PATCHWELCOME:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+static void free_message(void *msg)
+{
+ FifoMessage *fifo_msg = msg;
+
+ if (fifo_msg->type == FIFO_WRITE_PACKET)
+ av_packet_unref(&fifo_msg->pkt);
+}
+
+static int fifo_thread_process_recovery_failure(FifoThreadContext *ctx, AVPacket *pkt,
+ int err_no)
+{
+ AVFormatContext *avf = ctx->avf;
+ FifoContext *fifo = avf->priv_data;
+ int ret;
+
+ av_log(avf, AV_LOG_INFO, "Recovery failed: %s\n",
+ av_err2str(err_no));
+
+ if (fifo->recovery_wait_streamtime) {
+ if (pkt->pts == AV_NOPTS_VALUE)
+ av_log(avf, AV_LOG_WARNING, "Packet does not contain presentation"
+ " timestamp, recovery will be attempted immediately");
+ ctx->last_recovery_ts = pkt->pts;
+ } else {
+ ctx->last_recovery_ts = av_gettime_relative();
+ }
+
+ if (fifo->max_recovery_attempts &&
+ ctx->recovery_nr >= fifo->max_recovery_attempts) {
+ av_log(avf, AV_LOG_ERROR,
+ "Maximal number of %d recovery attempts reached.\n",
+ fifo->max_recovery_attempts);
+ ret = err_no;
+ } else {
+ ret = AVERROR(EAGAIN);
+ }
+
+ return ret;
+}
+
+static int fifo_thread_attempt_recovery(FifoThreadContext *ctx, FifoMessage *msg, int err_no)
+{
+ AVFormatContext *avf = ctx->avf;
+ FifoContext *fifo = avf->priv_data;
+ AVPacket *pkt = &msg->pkt;
+ int64_t time_since_recovery;
+ int ret;
+
+ if (!is_recoverable(fifo, err_no)) {
+ ret = err_no;
+ goto fail;
+ }
+
+ if (ctx->header_written) {
+ fifo->write_trailer_ret = fifo_thread_write_trailer(ctx);
+ ctx->header_written = 0;
+ }
+
+ if (!ctx->recovery_nr) {
+ ctx->last_recovery_ts = fifo->recovery_wait_streamtime ?
+ AV_NOPTS_VALUE : 0;
+ } else {
+ if (fifo->recovery_wait_streamtime) {
+ if (ctx->last_recovery_ts == AV_NOPTS_VALUE) {
+ AVRational tb = avf->streams[pkt->stream_index]->time_base;
+ time_since_recovery = av_rescale_q(pkt->pts - ctx->last_recovery_ts,
+ tb, AV_TIME_BASE_Q);
+ } else {
+ /* Enforce recovery immediately */
+ time_since_recovery = fifo->recovery_wait_time;
+ }
+ } else {
+ time_since_recovery = av_gettime_relative() - ctx->last_recovery_ts;
+ }
+
+ if (time_since_recovery < fifo->recovery_wait_time)
+ return AVERROR(EAGAIN);
+ }
+
+ ctx->recovery_nr++;
+
+ if (fifo->max_recovery_attempts) {
+ av_log(avf, AV_LOG_VERBOSE, "Recovery attempt #%d/%d\n",
+ ctx->recovery_nr, fifo->max_recovery_attempts);
+ } else {
+ av_log(avf, AV_LOG_VERBOSE, "Recovery attempt #%d\n",
+ ctx->recovery_nr);
+ }
+
+ if (fifo->restart_with_keyframe && fifo->drop_pkts_on_overflow)
+ ctx->drop_until_keyframe = 1;
+
+ ret = fifo_thread_dispatch_message(ctx, msg);
+ if (ret < 0) {
+ if (is_recoverable(fifo, ret)) {
+ return fifo_thread_process_recovery_failure(ctx, pkt, ret);
+ } else {
+ goto fail;
+ }
+ } else {
+ av_log(avf, AV_LOG_INFO, "Recovery successful\n");
+ ctx->recovery_nr = 0;
+ }
+
+ return 0;
+
+fail:
+ free_message(msg);
+ return ret;
+}
+
+static int fifo_thread_recover(FifoThreadContext *ctx, FifoMessage *msg, int err_no)
+{
+ AVFormatContext *avf = ctx->avf;
+ FifoContext *fifo = avf->priv_data;
+ int ret;
+
+ do {
+ if (!fifo->recovery_wait_streamtime && ctx->recovery_nr > 0) {
+ int64_t time_since_recovery = av_gettime_relative() - ctx->last_recovery_ts;
+ int64_t time_to_wait = FFMAX(0, fifo->recovery_wait_time - time_since_recovery);
+ if (time_to_wait)
+ av_usleep(FFMIN(10000, time_to_wait));
+ }
+
+ ret = fifo_thread_attempt_recovery(ctx, msg, err_no);
+ } while (ret == AVERROR(EAGAIN) && !fifo->drop_pkts_on_overflow);
+
+ if (ret == AVERROR(EAGAIN) && fifo->drop_pkts_on_overflow) {
+ if (msg->type == FIFO_WRITE_PACKET)
+ av_packet_unref(&msg->pkt);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void *fifo_consumer_thread(void *data)
+{
+ AVFormatContext *avf = data;
+ FifoContext *fifo = avf->priv_data;
+ AVThreadMessageQueue *queue = fifo->queue;
+ FifoMessage msg = {FIFO_WRITE_HEADER, {0}};
+ int ret;
+
+ FifoThreadContext fifo_thread_ctx;
+ memset(&fifo_thread_ctx, 0, sizeof(FifoThreadContext));
+ fifo_thread_ctx.avf = avf;
+
+ while (1) {
+ uint8_t just_flushed = 0;
+
+ if (!fifo_thread_ctx.recovery_nr)
+ ret = fifo_thread_dispatch_message(&fifo_thread_ctx, &msg);
+
+ if (ret < 0 || fifo_thread_ctx.recovery_nr > 0) {
+ int rec_ret = fifo_thread_recover(&fifo_thread_ctx, &msg, ret);
+ if (rec_ret < 0) {
+ av_thread_message_queue_set_err_send(queue, rec_ret);
+ break;
+ }
+ }
+
+ /* If the queue is full at the moment when fifo_write_packet
+ * attempts to insert new message (packet) to the queue,
+ * it sets the fifo->overflow_flag to 1 and drops packet.
+ * Here in consumer thread, the flag is checked and if it is
+ * set, the queue is flushed and flag cleared. */
+ pthread_mutex_lock(&fifo->overflow_flag_lock);
+ if (fifo->overflow_flag) {
+ av_thread_message_flush(queue);
+ if (fifo->restart_with_keyframe)
+ fifo_thread_ctx.drop_until_keyframe = 1;
+ fifo->overflow_flag = 0;
+ just_flushed = 1;
+ }
+ pthread_mutex_unlock(&fifo->overflow_flag_lock);
+
+ if (just_flushed)
+ av_log(avf, AV_LOG_INFO, "FIFO queue flushed\n");
+
+ ret = av_thread_message_queue_recv(queue, &msg, 0);
+ if (ret < 0) {
+ av_thread_message_queue_set_err_send(queue, ret);
+ break;
+ }
+ }
+
+ fifo->write_trailer_ret = fifo_thread_write_trailer(&fifo_thread_ctx);
+
+ return NULL;
+}
+
+static int fifo_mux_init(AVFormatContext *avf, AVOutputFormat *oformat,
+ const char *filename)
+{
+ FifoContext *fifo = avf->priv_data;
+ AVFormatContext *avf2;
+ int ret = 0, i;
+
+ ret = avformat_alloc_output_context2(&avf2, oformat, NULL, filename);
+ if (ret < 0)
+ return ret;
+
+ fifo->avf = avf2;
+
+ avf2->interrupt_callback = avf->interrupt_callback;
+ avf2->max_delay = avf->max_delay;
+ ret = av_dict_copy(&avf2->metadata, avf->metadata, 0);
+ if (ret < 0)
+ return ret;
+ avf2->opaque = avf->opaque;
+ avf2->io_close = avf->io_close;
+ avf2->io_open = avf->io_open;
+ avf2->flags = avf->flags;
+
+ for (i = 0; i < avf->nb_streams; ++i) {
+ AVStream *st = avformat_new_stream(avf2, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ ret = ff_stream_encode_params_copy(st, avf->streams[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fifo_init(AVFormatContext *avf)
+{
+ FifoContext *fifo = avf->priv_data;
+ AVOutputFormat *oformat;
+ int ret = 0;
+
+ if (fifo->recovery_wait_streamtime && !fifo->drop_pkts_on_overflow) {
+ av_log(avf, AV_LOG_ERROR, "recovery_wait_streamtime can be turned on"
+ " only when drop_pkts_on_overflow is also turned on\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (fifo->format_options_str) {
+ ret = av_dict_parse_string(&fifo->format_options, fifo->format_options_str,
+ "=", ":", 0);
+ if (ret < 0) {
+ av_log(avf, AV_LOG_ERROR, "Could not parse format options list '%s'\n",
+ fifo->format_options_str);
+ return ret;
+ }
+ }
+
+ oformat = av_guess_format(fifo->format, avf->filename, NULL);
+ if (!oformat) {
+ ret = AVERROR_MUXER_NOT_FOUND;
+ return ret;
+ }
+
+ ret = fifo_mux_init(avf, oformat, avf->filename);
+ if (ret < 0)
+ return ret;
+
+ ret = av_thread_message_queue_alloc(&fifo->queue, (unsigned) fifo->queue_size,
+ sizeof(FifoMessage));
+ if (ret < 0)
+ return ret;
+
+ av_thread_message_queue_set_free_func(fifo->queue, free_message);
+
+ ret = pthread_mutex_init(&fifo->overflow_flag_lock, NULL);
+ if (ret < 0)
+ return AVERROR(ret);
+ fifo->overflow_flag_lock_initialized = 1;
+
+ return 0;
+}
+
+static int fifo_write_header(AVFormatContext *avf)
+{
+ FifoContext * fifo = avf->priv_data;
+ int ret;
+
+ ret = pthread_create(&fifo->writer_thread, NULL, fifo_consumer_thread, avf);
+ if (ret) {
+ av_log(avf, AV_LOG_ERROR, "Failed to start thread: %s\n",
+ av_err2str(AVERROR(ret)));
+ ret = AVERROR(ret);
+ }
+
+ return ret;
+}
+
+static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt)
+{
+ FifoContext *fifo = avf->priv_data;
+ FifoMessage msg = {.type = pkt ? FIFO_WRITE_PACKET : FIFO_FLUSH_OUTPUT};
+ int ret;
+
+ if (pkt) {
+ av_init_packet(&msg.pkt);
+ ret = av_packet_ref(&msg.pkt,pkt);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = av_thread_message_queue_send(fifo->queue, &msg,
+ fifo->drop_pkts_on_overflow ?
+ AV_THREAD_MESSAGE_NONBLOCK : 0);
+ if (ret == AVERROR(EAGAIN)) {
+ uint8_t overflow_set = 0;
+
+ /* Queue is full, set fifo->overflow_flag to 1
+ * to let consumer thread know the queue should
+ * be flushed. */
+ pthread_mutex_lock(&fifo->overflow_flag_lock);
+ if (!fifo->overflow_flag)
+ fifo->overflow_flag = overflow_set = 1;
+ pthread_mutex_unlock(&fifo->overflow_flag_lock);
+
+ if (overflow_set)
+ av_log(avf, AV_LOG_WARNING, "FIFO queue full\n");
+ ret = 0;
+ goto fail;
+ } else if (ret < 0) {
+ goto fail;
+ }
+
+ return ret;
+fail:
+ if (pkt)
+ av_packet_unref(&msg.pkt);
+ return ret;
+}
+
+static int fifo_write_trailer(AVFormatContext *avf)
+{
+ FifoContext *fifo= avf->priv_data;
+ int ret;
+
+ av_thread_message_queue_set_err_recv(fifo->queue, AVERROR_EOF);
+
+ ret = pthread_join(fifo->writer_thread, NULL);
+ if (ret < 0) {
+ av_log(avf, AV_LOG_ERROR, "pthread join error: %s\n",
+ av_err2str(AVERROR(ret)));
+ return AVERROR(ret);
+ }
+
+ ret = fifo->write_trailer_ret;
+ return ret;
+}
+
+static void fifo_deinit(AVFormatContext *avf)
+{
+ FifoContext *fifo = avf->priv_data;
+
+ av_dict_free(&fifo->format_options);
+ avformat_free_context(fifo->avf);
+ av_thread_message_queue_free(&fifo->queue);
+ if (fifo->overflow_flag_lock_initialized)
+ pthread_mutex_destroy(&fifo->overflow_flag_lock);
+}
+
+#define OFFSET(x) offsetof(FifoContext, x)
+static const AVOption options[] = {
+ {"fifo_format", "Target muxer", OFFSET(format),
+ AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM},
+
+ {"queue_size", "Size of fifo queue", OFFSET(queue_size),
+ AV_OPT_TYPE_INT, {.i64 = FIFO_DEFAULT_QUEUE_SIZE}, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
+
+ {"format_opts", "Options to be passed to underlying muxer", OFFSET(format_options_str),
+ AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM},
+
+ {"drop_pkts_on_overflow", "Drop packets on fifo queue overflow not to block encoder", OFFSET(drop_pkts_on_overflow),
+ AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
+
+ {"restart_with_keyframe", "Wait for keyframe when restarting output", OFFSET(restart_with_keyframe),
+ AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
+
+ {"attempt_recovery", "Attempt recovery in case of failure", OFFSET(attempt_recovery),
+ AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
+
+ {"max_recovery_attempts", "Maximal number of recovery attempts", OFFSET(max_recovery_attempts),
+ AV_OPT_TYPE_INT, {.i64 = FIFO_DEFAULT_MAX_RECOVERY_ATTEMPTS}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
+
+ {"recovery_wait_time", "Waiting time between recovery attempts", OFFSET(recovery_wait_time),
+ AV_OPT_TYPE_DURATION, {.i64 = FIFO_DEFAULT_RECOVERY_WAIT_TIME_USEC}, 0, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM},
+
+ {"recovery_wait_streamtime", "Use stream time instead of real time while waiting for recovery",
+ OFFSET(recovery_wait_streamtime), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
+
+ {"recover_any_error", "Attempt recovery regardless of type of the error", OFFSET(recover_any_error),
+ AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
+
+ {NULL},
+};
+
+static const AVClass fifo_muxer_class = {
+ .class_name = "Fifo muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVOutputFormat ff_fifo_muxer = {
+ .name = "fifo",
+ .long_name = NULL_IF_CONFIG_SMALL("FIFO queue pseudo-muxer"),
+ .priv_data_size = sizeof(FifoContext),
+ .init = fifo_init,
+ .write_header = fifo_write_header,
+ .write_packet = fifo_write_packet,
+ .write_trailer = fifo_write_trailer,
+ .deinit = fifo_deinit,
+ .priv_class = &fifo_muxer_class,
+ .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
+};
diff --git a/libavformat/file.c b/libavformat/file.c
index 27ce4de..1d321c4 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
*/
@@ -23,6 +23,9 @@
#include "libavutil/internal.h"
#include "libavutil/opt.h"
#include "avformat.h"
+#if HAVE_DIRENT_H
+#include <dirent.h>
+#endif
#include <fcntl.h>
#if HAVE_IO_H
#include <io.h>
@@ -35,6 +38,32 @@
#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
+
+/* Not available in POSIX.1-1996 */
+#ifndef S_ISLNK
+# ifdef S_IFLNK
+# define S_ISLNK(m) (((m) & S_IFLNK) == S_IFLNK)
+# else
+# define S_ISLNK(m) 0
+# endif
+#endif
+
+/* Not available in POSIX.1-1996 */
+#ifndef S_ISSOCK
+# ifdef S_IFSOCK
+# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+# else
+# define S_ISSOCK(m) 0
+# endif
+#endif
/* standard file protocol */
@@ -42,15 +71,25 @@ typedef struct FileContext {
const AVClass *class;
int fd;
int trunc;
+ int blocksize;
int follow;
+#if HAVE_DIRENT_H
+ DIR *dir;
+#endif
} 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_BOOL, { .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 },
{ "follow", "Follow a file as it is being written", offsetof(FileContext, follow), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_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 }
+};
+
static const AVClass file_class = {
.class_name = "file",
.item_name = av_default_item_name,
@@ -58,19 +97,32 @@ 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;
- int ret = read(c->fd, buf, size);
+ int ret;
+ size = FFMIN(size, c->blocksize);
+ ret = read(c->fd, buf, size);
if (ret == 0 && c->follow)
return AVERROR(EAGAIN);
+ if (ret == 0)
+ return AVERROR_EOF;
return (ret == -1) ? AVERROR(errno) : ret;
}
static int file_write(URLContext *h, const unsigned char *buf, int size)
{
FileContext *c = h->priv_data;
- int ret = write(c->fd, buf, size);
+ int ret;
+ size = FFMIN(size, c->blocksize);
+ ret = write(c->fd, buf, size);
return (ret == -1) ? AVERROR(errno) : ret;
}
@@ -82,20 +134,67 @@ static int file_get_handle(URLContext *h)
static int file_check(URLContext *h, int mask)
{
+ int ret = 0;
const char *filename = h->filename;
- struct stat st;
- int ret;
-
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;
+# ifndef _WIN32
ret = stat(filename, &st);
+# else
+ ret = win32_stat(filename, &st);
+# endif
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;
+}
+
+static int file_delete(URLContext *h)
+{
+#if HAVE_UNISTD_H
+ int ret;
+ const char *filename = h->filename;
+ av_strstart(filename, "file:", &filename);
+
+ ret = rmdir(filename);
+ if (ret < 0 && errno == ENOTDIR)
+ ret = unlink(filename);
+ if (ret < 0)
+ return AVERROR(errno);
return ret;
+#else
+ return AVERROR(ENOSYS);
+#endif /* HAVE_UNISTD_H */
+}
+
+static int file_move(URLContext *h_src, URLContext *h_dst)
+{
+ const char *filename_src = h_src->filename;
+ const char *filename_dst = h_dst->filename;
+ av_strstart(filename_src, "file:", &filename_src);
+ av_strstart(filename_dst, "file:", &filename_dst);
+
+ if (rename(filename_src, filename_dst) < 0)
+ return AVERROR(errno);
+
+ return 0;
}
#if CONFIG_FILE_PROTOCOL
@@ -105,6 +204,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);
@@ -126,6 +226,14 @@ 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);
+
+ /* Buffer writes more than the default 32k to improve throughput especially
+ * with networked file systems */
+ if (!h->is_streamed && flags & AVIO_FLAG_WRITE)
+ h->min_packet_size = h->max_packet_size = 262144;
+
return 0;
}
@@ -137,9 +245,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);
@@ -153,6 +260,90 @@ static int file_close(URLContext *h)
return close(c->fd);
}
+static int file_open_dir(URLContext *h)
+{
+#if HAVE_LSTAT
+ FileContext *c = h->priv_data;
+
+ c->dir = opendir(h->filename);
+ if (!c->dir)
+ return AVERROR(errno);
+
+ return 0;
+#else
+ return AVERROR(ENOSYS);
+#endif /* HAVE_LSTAT */
+}
+
+static int file_read_dir(URLContext *h, AVIODirEntry **next)
+{
+#if HAVE_LSTAT
+ FileContext *c = h->priv_data;
+ struct dirent *dir;
+ char *fullpath = NULL;
+
+ *next = ff_alloc_dir_entry();
+ if (!*next)
+ return AVERROR(ENOMEM);
+ do {
+ errno = 0;
+ dir = readdir(c->dir);
+ if (!dir) {
+ av_freep(next);
+ return AVERROR(errno);
+ }
+ } while (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, ".."));
+
+ fullpath = av_append_path_component(h->filename, dir->d_name);
+ if (fullpath) {
+ struct stat st;
+ if (!lstat(fullpath, &st)) {
+ if (S_ISDIR(st.st_mode))
+ (*next)->type = AVIO_ENTRY_DIRECTORY;
+ else if (S_ISFIFO(st.st_mode))
+ (*next)->type = AVIO_ENTRY_NAMED_PIPE;
+ else if (S_ISCHR(st.st_mode))
+ (*next)->type = AVIO_ENTRY_CHARACTER_DEVICE;
+ else if (S_ISBLK(st.st_mode))
+ (*next)->type = AVIO_ENTRY_BLOCK_DEVICE;
+ else if (S_ISLNK(st.st_mode))
+ (*next)->type = AVIO_ENTRY_SYMBOLIC_LINK;
+ else if (S_ISSOCK(st.st_mode))
+ (*next)->type = AVIO_ENTRY_SOCKET;
+ else if (S_ISREG(st.st_mode))
+ (*next)->type = AVIO_ENTRY_FILE;
+ else
+ (*next)->type = AVIO_ENTRY_UNKNOWN;
+
+ (*next)->group_id = st.st_gid;
+ (*next)->user_id = st.st_uid;
+ (*next)->size = st.st_size;
+ (*next)->filemode = st.st_mode & 0777;
+ (*next)->modification_timestamp = INT64_C(1000000) * st.st_mtime;
+ (*next)->access_timestamp = INT64_C(1000000) * st.st_atime;
+ (*next)->status_change_timestamp = INT64_C(1000000) * st.st_ctime;
+ }
+ av_free(fullpath);
+ }
+
+ (*next)->name = av_strdup(dir->d_name);
+ return 0;
+#else
+ return AVERROR(ENOSYS);
+#endif /* HAVE_LSTAT */
+}
+
+static int file_close_dir(URLContext *h)
+{
+#if HAVE_LSTAT
+ FileContext *c = h->priv_data;
+ closedir(c->dir);
+ return 0;
+#else
+ return AVERROR(ENOSYS);
+#endif /* HAVE_LSTAT */
+}
+
const URLProtocol ff_file_protocol = {
.name = "file",
.url_open = file_open,
@@ -162,8 +353,14 @@ const URLProtocol ff_file_protocol = {
.url_close = file_close,
.url_get_file_handle = file_get_handle,
.url_check = file_check,
+ .url_delete = file_delete,
+ .url_move = file_move,
.priv_data_size = sizeof(FileContext),
.priv_data_class = &file_class,
+ .url_open_dir = file_open_dir,
+ .url_read_dir = file_read_dir,
+ .url_close_dir = file_close_dir,
+ .default_whitelist = "file,crypto"
};
#endif /* CONFIG_FILE_PROTOCOL */
@@ -201,6 +398,8 @@ const 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,
+ .default_whitelist = "crypto"
};
#endif /* CONFIG_PIPE_PROTOCOL */
diff --git a/libavformat/filmstripdec.c b/libavformat/filmstripdec.c
index b5dad50..0bf5a80 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
*/
@@ -25,6 +25,7 @@
*/
#include "libavutil/intreadwrite.h"
+#include "libavutil/imgutils.h"
#include "avformat.h"
#include "internal.h"
@@ -45,7 +46,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 +68,10 @@ static int read_header(AVFormatContext *s)
st->codecpar->width = avio_rb16(pb);
st->codecpar->height = avio_rb16(pb);
film->leading = avio_rb16(pb);
+
+ if (av_image_check_size(st->codecpar->width, st->codecpar->height, 0, s) < 0)
+ return AVERROR_INVALIDDATA;
+
avpriv_set_pts_info(st, 64, 1, avio_rb16(pb));
avio_seek(pb, 0, SEEK_SET);
@@ -80,9 +85,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->codecpar->width * (st->codecpar->height + film->leading) * 4);
+ pkt->dts = avio_tell(s->pb) / (st->codecpar->width * (int64_t)(st->codecpar->height + film->leading) * 4);
pkt->size = av_get_packet(s->pb, pkt, st->codecpar->width * st->codecpar->height * 4);
avio_skip(s->pb, st->codecpar->width * (int64_t) film->leading * 4);
if (pkt->size < 0)
diff --git a/libavformat/filmstripenc.c b/libavformat/filmstripenc.c
index 5a09691..8ead696 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->codecpar->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/fitsdec.c b/libavformat/fitsdec.c
new file mode 100644
index 0000000..4b288b3
--- /dev/null
+++ b/libavformat/fitsdec.c
@@ -0,0 +1,231 @@
+/*
+ * FITS demuxer
+ * Copyright (c) 2017 Paras Chadha
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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
+ * FITS demuxer.
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "internal.h"
+#include "libavutil/opt.h"
+#include "libavcodec/fits.h"
+#include "libavutil/bprint.h"
+
+#define FITS_BLOCK_SIZE 2880
+
+typedef struct FITSContext {
+ const AVClass *class;
+ AVRational framerate;
+ int first_image;
+ int64_t pts;
+} FITSContext;
+
+static int fits_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+ if (!memcmp(b, "SIMPLE = T", 30))
+ return AVPROBE_SCORE_MAX - 1;
+ return 0;
+}
+
+static int fits_read_header(AVFormatContext *s)
+{
+ AVStream *st;
+ FITSContext * fits = s->priv_data;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_id = AV_CODEC_ID_FITS;
+
+ avpriv_set_pts_info(st, 64, fits->framerate.den, fits->framerate.num);
+ fits->pts = 0;
+ fits->first_image = 1;
+ return 0;
+}
+
+/**
+ * Parses header and checks that the current HDU contains image or not
+ * It also stores the header in the avbuf and stores the size of data part in data_size
+ * @param s pointer to AVFormat Context
+ * @param fits pointer to FITSContext
+ * @param header pointer to FITSHeader
+ * @param avbuf pointer to AVBPrint to store the header
+ * @param data_size to store the size of data part
+ * @return 1 if image found, 0 if any other extension and AVERROR_INVALIDDATA otherwise
+ */
+static int64_t is_image(AVFormatContext *s, FITSContext *fits, FITSHeader *header,
+ AVBPrint *avbuf, uint64_t *data_size)
+{
+ int i, ret, image = 0;
+ char buf[FITS_BLOCK_SIZE] = { 0 };
+ int64_t buf_size = 0, size = 0, t;
+
+ do {
+ ret = avio_read(s->pb, buf, FITS_BLOCK_SIZE);
+ if (ret < 0) {
+ return ret;
+ } else if (ret < FITS_BLOCK_SIZE) {
+ return AVERROR_INVALIDDATA;
+ }
+
+ av_bprint_append_data(avbuf, buf, FITS_BLOCK_SIZE);
+ ret = 0;
+ buf_size = 0;
+ while(!ret && buf_size < FITS_BLOCK_SIZE) {
+ ret = avpriv_fits_header_parse_line(s, header, buf + buf_size, NULL);
+ buf_size += 80;
+ }
+ } while (!ret);
+ if (ret < 0)
+ return ret;
+
+ image = fits->first_image || header->image_extension;
+ fits->first_image = 0;
+
+ if (header->groups) {
+ image = 0;
+ if (header->naxis > 1)
+ size = 1;
+ } else if (header->naxis) {
+ size = header->naxisn[0];
+ } else {
+ image = 0;
+ }
+
+ for (i = 1; i < header->naxis; i++) {
+ if(size && header->naxisn[i] > UINT64_MAX / size)
+ return AVERROR_INVALIDDATA;
+ size *= header->naxisn[i];
+ }
+
+ if(header->pcount > UINT64_MAX - size)
+ return AVERROR_INVALIDDATA;
+ size += header->pcount;
+
+ t = (abs(header->bitpix) >> 3) * ((int64_t) header->gcount);
+ if(size && t > UINT64_MAX / size)
+ return AVERROR_INVALIDDATA;
+ size *= t;
+
+ if (!size) {
+ image = 0;
+ } else {
+ if(FITS_BLOCK_SIZE - 1 > UINT64_MAX - size)
+ return AVERROR_INVALIDDATA;
+ size = ((size + FITS_BLOCK_SIZE - 1) / FITS_BLOCK_SIZE) * FITS_BLOCK_SIZE;
+ }
+ *data_size = size;
+ return image;
+}
+
+static int fits_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ int64_t pos, ret;
+ uint64_t size;
+ FITSContext *fits = s->priv_data;
+ FITSHeader header;
+ AVBPrint avbuf;
+ char *buf;
+
+ if (fits->first_image) {
+ avpriv_fits_header_init(&header, STATE_SIMPLE);
+ } else {
+ avpriv_fits_header_init(&header, STATE_XTENSION);
+ }
+
+ av_bprint_init(&avbuf, FITS_BLOCK_SIZE, AV_BPRINT_SIZE_UNLIMITED);
+ while ((ret = is_image(s, fits, &header, &avbuf, &size)) == 0) {
+ pos = avio_skip(s->pb, size);
+ if (pos < 0)
+ return pos;
+
+ av_bprint_finalize(&avbuf, NULL);
+ av_bprint_init(&avbuf, FITS_BLOCK_SIZE, AV_BPRINT_SIZE_UNLIMITED);
+ avpriv_fits_header_init(&header, STATE_XTENSION);
+ }
+ if (ret < 0)
+ goto fail;
+
+ if (!av_bprint_is_complete(&avbuf)) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ // Header is sent with the first line removed...
+ ret = av_new_packet(pkt, avbuf.len - 80 + size);
+ if (ret < 0)
+ goto fail;
+
+ pkt->stream_index = 0;
+ pkt->flags |= AV_PKT_FLAG_KEY;
+
+ ret = av_bprint_finalize(&avbuf, &buf);
+ if (ret < 0) {
+ av_packet_unref(pkt);
+ return ret;
+ }
+
+ memcpy(pkt->data, buf + 80, avbuf.len - 80);
+ pkt->size = avbuf.len - 80;
+ av_freep(&buf);
+ ret = avio_read(s->pb, pkt->data + pkt->size, size);
+ if (ret < 0) {
+ av_packet_unref(pkt);
+ return ret;
+ }
+
+ pkt->size += ret;
+ pkt->pts = fits->pts;
+ fits->pts++;
+
+ return 0;
+
+fail:
+ av_bprint_finalize(&avbuf, NULL);
+ return ret;
+}
+
+static const AVOption fits_options[] = {
+ { "framerate", "set the framerate", offsetof(FITSContext, framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "1"}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM},
+ { NULL },
+};
+
+static const AVClass fits_demuxer_class = {
+ .class_name = "FITS demuxer",
+ .item_name = av_default_item_name,
+ .option = fits_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_fits_demuxer = {
+ .name = "fits",
+ .long_name = NULL_IF_CONFIG_SMALL("Flexible Image Transport System"),
+ .priv_data_size = sizeof(FITSContext),
+ .read_probe = fits_probe,
+ .read_header = fits_read_header,
+ .read_packet = fits_read_packet,
+ .priv_class = &fits_demuxer_class,
+ .raw_codec_id = AV_CODEC_ID_FITS,
+};
diff --git a/libavformat/fitsenc.c b/libavformat/fitsenc.c
new file mode 100644
index 0000000..7cb1715
--- /dev/null
+++ b/libavformat/fitsenc.c
@@ -0,0 +1,183 @@
+/*
+ * FITS muxer
+ * Copyright (c) 2017 Paras Chadha
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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
+ * FITS muxer.
+ */
+
+#include "internal.h"
+
+typedef struct FITSContext {
+ int first_image;
+} FITSContext;
+
+static int fits_write_header(AVFormatContext *s)
+{
+ FITSContext *fitsctx = s->priv_data;
+ fitsctx->first_image = 1;
+ return 0;
+}
+
+/**
+ * Write one header line comprising of keyword and value(int)
+ * @param s AVFormat Context
+ * @param keyword pointer to the char array in which keyword is stored
+ * @param value the value corresponding to the keyword
+ * @param lines_written to keep track of lines written so far
+ * @return 0
+ */
+static int write_keyword_value(AVFormatContext *s, const char *keyword, int value, int *lines_written)
+{
+ int len, ret;
+ uint8_t header[80];
+
+ len = strlen(keyword);
+ memset(header, ' ', sizeof(header));
+ memcpy(header, keyword, len);
+
+ header[8] = '=';
+ header[9] = ' ';
+
+ ret = snprintf(header + 10, 70, "%d", value);
+ memset(&header[ret + 10], ' ', sizeof(header) - (ret + 10));
+
+ avio_write(s->pb, header, sizeof(header));
+ *lines_written += 1;
+ return 0;
+}
+
+static int write_image_header(AVFormatContext *s)
+{
+ AVStream *st = s->streams[0];
+ AVCodecParameters *encctx = st->codecpar;
+ FITSContext *fitsctx = s->priv_data;
+ uint8_t buffer[80];
+ int bitpix, naxis, naxis3 = 1, bzero = 0, rgb = 0, lines_written = 0, lines_left;
+
+ switch (encctx->format) {
+ case AV_PIX_FMT_GRAY8:
+ bitpix = 8;
+ naxis = 2;
+ break;
+ case AV_PIX_FMT_GRAY16BE:
+ bitpix = 16;
+ naxis = 2;
+ bzero = 32768;
+ break;
+ case AV_PIX_FMT_GBRP:
+ case AV_PIX_FMT_GBRAP:
+ bitpix = 8;
+ naxis = 3;
+ rgb = 1;
+ if (encctx->format == AV_PIX_FMT_GBRP) {
+ naxis3 = 3;
+ } else {
+ naxis3 = 4;
+ }
+ break;
+ case AV_PIX_FMT_GBRP16BE:
+ case AV_PIX_FMT_GBRAP16BE:
+ bitpix = 16;
+ naxis = 3;
+ rgb = 1;
+ if (encctx->format == AV_PIX_FMT_GBRP16BE) {
+ naxis3 = 3;
+ } else {
+ naxis3 = 4;
+ }
+ bzero = 32768;
+ break;
+ }
+
+ if (fitsctx->first_image) {
+ memcpy(buffer, "SIMPLE = ", 10);
+ memset(buffer + 10, ' ', 70);
+ buffer[29] = 'T';
+ avio_write(s->pb, buffer, sizeof(buffer));
+ } else {
+ memcpy(buffer, "XTENSION= 'IMAGE '", 20);
+ memset(buffer + 20, ' ', 60);
+ avio_write(s->pb, buffer, sizeof(buffer));
+ }
+ lines_written++;
+
+ write_keyword_value(s, "BITPIX", bitpix, &lines_written); // no of bits per pixel
+ write_keyword_value(s, "NAXIS", naxis, &lines_written); // no of dimensions of image
+ write_keyword_value(s, "NAXIS1", encctx->width, &lines_written); // first dimension i.e. width
+ write_keyword_value(s, "NAXIS2", encctx->height, &lines_written); // second dimension i.e. height
+
+ if (rgb)
+ write_keyword_value(s, "NAXIS3", naxis3, &lines_written); // third dimension to store RGBA planes
+
+ if (!fitsctx->first_image) {
+ write_keyword_value(s, "PCOUNT", 0, &lines_written);
+ write_keyword_value(s, "GCOUNT", 1, &lines_written);
+ } else {
+ fitsctx->first_image = 0;
+ }
+
+ /*
+ * Since FITS does not support unsigned 16 bit integers,
+ * BZERO = 32768 is used to store unsigned 16 bit integers as
+ * signed integers so that it can be read properly.
+ */
+ if (bitpix == 16)
+ write_keyword_value(s, "BZERO", bzero, &lines_written);
+
+ if (rgb) {
+ memcpy(buffer, "CTYPE3 = 'RGB '", 20);
+ memset(buffer + 20, ' ', 60);
+ avio_write(s->pb, buffer, sizeof(buffer));
+ lines_written++;
+ }
+
+ memcpy(buffer, "END", 3);
+ memset(buffer + 3, ' ', 77);
+ avio_write(s->pb, buffer, sizeof(buffer));
+ lines_written++;
+
+ lines_left = ((lines_written + 35) / 36) * 36 - lines_written;
+ memset(buffer, ' ', 80);
+ while (lines_left > 0) {
+ avio_write(s->pb, buffer, sizeof(buffer));
+ lines_left--;
+ }
+ return 0;
+}
+
+static int fits_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ write_image_header(s);
+ avio_write(s->pb, pkt->data, pkt->size);
+ return 0;
+}
+
+AVOutputFormat ff_fits_muxer = {
+ .name = "fits",
+ .long_name = NULL_IF_CONFIG_SMALL("Flexible Image Transport System"),
+ .extensions = "fits",
+ .priv_data_size = sizeof(FITSContext),
+ .audio_codec = AV_CODEC_ID_NONE,
+ .video_codec = AV_CODEC_ID_FITS,
+ .write_header = fits_write_header,
+ .write_packet = fits_write_packet,
+};
diff --git a/libavformat/flac_picture.c b/libavformat/flac_picture.c
index fd0bb02..38982b9 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 + AV_INPUT_BUFFER_PADDING_SIZE))) {
+ RETURN_ERROR(AVERROR(ENOMEM));
}
+ memset(data->data + len, 0, AV_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 c700582..4374b6f 100644
--- a/libavformat/flac_picture.h
+++ b/libavformat/flac_picture.h
@@ -1,20 +1,21 @@
/*
* Raw FLAC picture parser
+ * Copyright (c) 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -23,6 +24,8 @@
#include "avformat.h"
+#define RETURN_ERROR(code) do { ret = (code); goto fail; } while (0)
+
int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size);
#endif /* AVFORMAT_FLAC_PICTURE_H */
diff --git a/libavformat/flacdec.c b/libavformat/flacdec.c
index 2f4ac56..a032378 100644
--- a/libavformat/flacdec.c
+++ b/libavformat/flacdec.c
@@ -2,20 +2,20 @@
* Raw FLAC demuxer
* Copyright (c) 2001 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -28,17 +28,33 @@
#include "vorbiscomment.h"
#include "replaygain.h"
+#define SEEKPOINT_SIZE 18
+
+typedef struct FLACDecContext {
+ int found_seektable;
+} FLACDecContext;
+
+static void reset_index_position(int64_t metadata_head_size, AVStream *st)
+{
+ /* the real seek index offset should be the size of metadata blocks with the offset in the frame blocks */
+ int i;
+ for(i=0; i<st->nb_index_entries; i++) {
+ st->index_entries[i].pos += metadata_head_size;
+ }
+}
+
static int flac_read_header(AVFormatContext *s)
{
int ret, metadata_last=0, metadata_type, metadata_size, found_streaminfo=0;
uint8_t header[4];
uint8_t *buffer=NULL;
+ FLACDecContext *flac = s->priv_data;
AVStream *st = avformat_new_stream(s, NULL);
if (!st)
return AVERROR(ENOMEM);
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->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,8 +64,9 @@ static int flac_read_header(AVFormatContext *s)
}
/* process metadata blocks */
- while (!s->pb->eof_reached && !metadata_last) {
- avio_read(s->pb, header, 4);
+ while (!avio_feof(s->pb) && !metadata_last) {
+ if (avio_read(s->pb, header, 4) != 4)
+ return AVERROR(AVERROR_INVALIDDATA);
flac_parse_block_header(header, &metadata_last, &metadata_type,
&metadata_size);
switch (metadata_type) {
@@ -58,13 +75,13 @@ static int flac_read_header(AVFormatContext *s)
case FLAC_METADATA_TYPE_CUESHEET:
case FLAC_METADATA_TYPE_PICTURE:
case FLAC_METADATA_TYPE_VORBIS_COMMENT:
+ case FLAC_METADATA_TYPE_SEEKTABLE:
buffer = av_mallocz(metadata_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!buffer) {
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 +97,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->codecpar->extradata = buffer;
@@ -109,24 +124,25 @@ static int flac_read_header(AVFormatContext *s)
const uint8_t *offset;
int i, chapters, track, ti;
if (metadata_size < 431)
- return AVERROR_INVALIDDATA;
+ RETURN_ERROR(AVERROR_INVALIDDATA);
offset = buffer + 395;
chapters = bytestream_get_byte(&offset) - 1;
if (chapters <= 0)
- return AVERROR_INVALIDDATA;
+ RETURN_ERROR(AVERROR_INVALIDDATA);
for (i = 0; i < chapters; i++) {
if (offset + 36 - buffer > metadata_size)
- return AVERROR_INVALIDDATA;
+ RETURN_ERROR(AVERROR_INVALIDDATA);
start = bytestream_get_be64(&offset);
track = bytestream_get_byte(&offset);
bytestream_get_buffer(&offset, isrc, 12);
isrc[12] = 0;
offset += 14;
ti = bytestream_get_byte(&offset);
- if (ti <= 0) return AVERROR_INVALIDDATA;
+ if (ti <= 0) RETURN_ERROR(AVERROR_INVALIDDATA);
offset += ti * 12;
avpriv_new_chapter(s, track, st->time_base, start, AV_NOPTS_VALUE, isrc);
}
+ av_freep(&buffer);
} else if (metadata_type == FLAC_METADATA_TYPE_PICTURE) {
ret = ff_flac_parse_picture(s, buffer, metadata_size);
av_freep(&buffer);
@@ -134,11 +150,26 @@ static int flac_read_header(AVFormatContext *s)
av_log(s, AV_LOG_ERROR, "Error parsing attached picture.\n");
return ret;
}
- } else {
+ } else if (metadata_type == FLAC_METADATA_TYPE_SEEKTABLE) {
+ const uint8_t *seekpoint = buffer;
+ int i, seek_point_count = metadata_size/SEEKPOINT_SIZE;
+ flac->found_seektable = 1;
+ if ((s->flags&AVFMT_FLAG_FAST_SEEK)) {
+ for(i=0; i<seek_point_count; i++) {
+ int64_t timestamp = bytestream_get_be64(&seekpoint);
+ int64_t pos = bytestream_get_be64(&seekpoint);
+ /* skip number of samples */
+ bytestream_get_be16(&seekpoint);
+ av_add_index_entry(st, pos, timestamp, 0, 0, AVINDEX_KEYFRAME);
+ }
+ }
+ av_freep(&buffer);
+ }
+ 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) {
@@ -172,11 +203,35 @@ static int flac_read_header(AVFormatContext *s)
if (ret < 0)
return ret;
+ reset_index_position(avio_tell(s->pb), st);
return 0;
+
+fail:
+ av_free(buffer);
+ return ret;
+}
+
+static int raw_flac_probe(AVProbeData *p)
+{
+ if ((p->buf[2] & 0xF0) == 0) // blocksize code invalid
+ return 0;
+ if ((p->buf[2] & 0x0F) == 0x0F) // sample rate code invalid
+ return 0;
+ if ((p->buf[3] & 0xF0) >= FLAC_MAX_CHANNELS + FLAC_CHMODE_MID_SIDE << 4)
+ // channel mode invalid
+ return 0;
+ if ((p->buf[3] & 0x06) == 0x06) // bits per sample code invalid
+ return 0;
+ if ((p->buf[3] & 0x01) == 0x01) // reserved bit set
+ return 0;
+ return AVPROBE_SCORE_EXTENSION / 4 + 1;
}
static int flac_probe(AVProbeData *p)
{
+ if ((AV_RB16(p->buf) & 0xFFFE) == 0xFFF8)
+ return raw_flac_probe(p);
+
/* file header + metadata header + checked bytes of streaminfo */
if (p->buf_size >= 4 + 4 + 13) {
int type = p->buf[4] & 0x7f;
@@ -185,25 +240,103 @@ static int flac_probe(AVProbeData *p)
int max_block_size = AV_RB16(p->buf + 10);
int sample_rate = AV_RB24(p->buf + 18) >> 4;
- if (!memcmp(p->buf, "fLaC", 4) &&
- type == FLAC_METADATA_TYPE_STREAMINFO &&
+ if (memcmp(p->buf, "fLaC", 4))
+ return 0;
+ if (type == FLAC_METADATA_TYPE_STREAMINFO &&
size == FLAC_STREAMINFO_SIZE &&
min_block_size >= 16 &&
max_block_size >= min_block_size &&
sample_rate && sample_rate <= 655350)
return AVPROBE_SCORE_MAX;
+ return AVPROBE_SCORE_EXTENSION;
}
return 0;
}
+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->codecpar->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 {
+ av_packet_unref(&pkt);
+ av_assert1(!pkt.size);
+ }
+ }
+ av_init_packet(&out_pkt);
+ av_parser_parse2(parser, st->internal->avctx,
+ &out_pkt.data, &out_pkt.size, pkt.data, pkt.size,
+ pkt.pts, pkt.dts, *ppos);
+
+ av_packet_unref(&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;
+ }
+ } else if (ret < 0)
+ break;
+ }
+ av_parser_close(parser);
+ return pts;
+}
+
+static int flac_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) {
+ int index;
+ int64_t pos;
+ AVIndexEntry e;
+ FLACDecContext *flac = s->priv_data;
+
+ if (!flac->found_seektable || !(s->flags&AVFMT_FLAG_FAST_SEEK)) {
+ return -1;
+ }
+
+ index = av_index_search_timestamp(s->streams[0], timestamp, flags);
+ if(index<0 || index >= s->streams[0]->nb_index_entries)
+ return -1;
+
+ e = s->streams[0]->index_entries[index];
+ pos = avio_seek(s->pb, e.pos, SEEK_SET);
+ if (pos >= 0) {
+ return 0;
+ }
+ return -1;
+}
+
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_seek = flac_seek,
+ .read_timestamp = flac_read_timestamp,
.flags = AVFMT_GENERIC_INDEX,
.extensions = "flac",
.raw_codec_id = AV_CODEC_ID_FLAC,
+ .priv_data_size = sizeof(FLACDecContext),
};
diff --git a/libavformat/flacenc.c b/libavformat/flacenc.c
index 06985cf..b894f9e 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,23 +42,22 @@ 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;
- unsigned int len;
+ const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT;
+ int64_t len;
uint8_t *p, *p0;
ff_metadata_conv(m, ff_vorbiscomment_metadata_conv, NULL);
len = ff_vorbiscomment_length(*m, vendor);
+ if (len >= ((1<<24) - 4))
+ return AVERROR(EINVAL);
p0 = av_malloc(len+4);
if (!p0)
return AVERROR(ENOMEM);
@@ -77,12 +77,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;
AVCodecParameters *par = s->streams[0]->codecpar;
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 (par->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_uintp2(padding, 24);
+
ret = ff_flac_write_header(s->pb, par->extradata,
par->extradata_size, 0);
if (ret)
@@ -105,7 +121,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 +129,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;
}
@@ -170,7 +187,7 @@ static int flac_write_packet(struct AVFormatContext *s, AVPacket *pkt)
}
static const AVOption flacenc_options[] = {
- { "write_header", "Write the file header", offsetof(FlacMuxerContext, write_header), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
+ { "write_header", "Write the file header", offsetof(FlacMuxerContext, write_header), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
{ NULL },
};
diff --git a/libavformat/flacenc.h b/libavformat/flacenc.h
index 54dd833..d5d53a5 100644
--- a/libavformat/flacenc.h
+++ b/libavformat/flacenc.h
@@ -2,20 +2,20 @@
* raw FLAC muxer
* Copyright (C) 2009 Justin Ruggles
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/flacenc_header.c b/libavformat/flacenc_header.c
index 4f9bb20..61833cc 100644
--- a/libavformat/flacenc_header.c
+++ b/libavformat/flacenc_header.c
@@ -2,20 +2,20 @@
* raw FLAC muxer
* Copyright (C) 2009 Justin Ruggles
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/flic.c b/libavformat/flic.c
index 85e4790..7c62cb4 100644
--- a/libavformat/flic.c
+++ b/libavformat/flic.c
@@ -2,20 +2,20 @@
* FLI/FLC Animation File Demuxer
* Copyright (c) 2003 The FFmpeg project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -80,7 +80,7 @@ static int flic_probe(AVProbeData *p)
return 0;
- return AVPROBE_SCORE_MAX;
+ return AVPROBE_SCORE_MAX - 1;
}
static int flic_read_header(AVFormatContext *s)
@@ -117,7 +117,7 @@ static int flic_read_header(AVFormatContext *s)
if (!st->codecpar->width || !st->codecpar->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->codecpar->width = 640;
@@ -125,8 +125,8 @@ static int flic_read_header(AVFormatContext *s)
}
/* send over the whole 128-byte FLIC header */
- st->codecpar->extradata_size = FLIC_HEADER_SIZE;
- st->codecpar->extradata = av_malloc(FLIC_HEADER_SIZE);
+ if (ff_alloc_extradata(st->codecpar, FLIC_HEADER_SIZE))
+ return AVERROR(ENOMEM);
memcpy(st->codecpar->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->codecpar->codec_tag = 0;
ast->codecpar->sample_rate = FLIC_TFTD_SAMPLE_RATE;
ast->codecpar->channels = 1;
- ast->codecpar->format = AV_SAMPLE_FMT_U8;
ast->codecpar->bit_rate = st->codecpar->sample_rate * 8;
ast->codecpar->bits_per_coded_sample = 8;
ast->codecpar->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->codecpar->extradata);
- st->codecpar->extradata_size = 12;
- st->codecpar->extradata = av_malloc(12);
+ av_freep(&st->codecpar->extradata);
+ if (ff_alloc_extradata(st->codecpar, 12))
+ return AVERROR(ENOMEM);
memcpy(st->codecpar->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;
}
@@ -205,7 +204,7 @@ static int flic_read_packet(AVFormatContext *s,
int ret = 0;
unsigned char preamble[FLIC_PREAMBLE_SIZE];
- while (!packet_read) {
+ while (!packet_read && !avio_feof(pb)) {
if ((ret = avio_read(pb, preamble, FLIC_PREAMBLE_SIZE)) !=
FLIC_PREAMBLE_SIZE) {
@@ -257,7 +256,7 @@ static int flic_read_packet(AVFormatContext *s,
}
}
- return ret;
+ return avio_feof(pb) ? AVERROR_EOF : ret;
}
AVInputFormat ff_flic_demuxer = {
diff --git a/libavformat/flv.h b/libavformat/flv.h
index 7c08117..df5ce3d 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 FlvTagType {
};
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 b82ea33..2d89bef 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 4 bits: difference between encoded width and visible width
* - lower 4 bits: 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,16 @@
#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
+#define RESYNC_BUFFER_SIZE (1<<20)
+
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 {
@@ -58,30 +56,107 @@ typedef struct FLVContext {
int validate_next;
int validate_count;
int searched_for_end;
+
+ uint8_t resync_buffer[2*RESYNC_BUFFER_SIZE];
+
+ int broken_sizes;
+ int sum_flv_tag_size;
+
+ int last_keyframe_stream_index;
+ int keyframe_count;
+ int64_t video_bit_rate;
+ int64_t audio_bit_rate;
+ int64_t *keyframe_times;
+ int64_t *keyframe_filepositions;
+ int missing_streams;
+ AVRational framerate;
} 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 void add_keyframes_index(AVFormatContext *s)
+{
+ FLVContext *flv = s->priv_data;
+ AVStream *stream = NULL;
+ unsigned int i = 0;
+
+ if (flv->last_keyframe_stream_index < 0) {
+ av_log(s, AV_LOG_DEBUG, "keyframe stream hasn't been created\n");
+ return;
+ }
+
+ av_assert0(flv->last_keyframe_stream_index <= s->nb_streams);
+ stream = s->streams[flv->last_keyframe_stream_index];
+
+ if (stream->nb_index_entries == 0) {
+ for (i = 0; i < flv->keyframe_count; i++) {
+ av_log(s, AV_LOG_TRACE, "keyframe filepositions = %"PRId64" times = %"PRId64"\n",
+ flv->keyframe_filepositions[i], flv->keyframe_times[i] * 1000);
+ av_add_index_entry(stream, flv->keyframe_filepositions[i],
+ flv->keyframe_times[i] * 1000, 0, 0, AVINDEX_KEYFRAME);
+ }
+ } else
+ av_log(s, AV_LOG_WARNING, "Skipping duplicate index\n");
+
+ if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ av_freep(&flv->keyframe_times);
+ av_freep(&flv->keyframe_filepositions);
+ flv->keyframe_count = 0;
+ }
+}
+
static AVStream *create_stream(AVFormatContext *s, int codec_type)
{
+ FLVContext *flv = s->priv_data;
AVStream *st = avformat_new_stream(s, NULL);
if (!st)
return NULL;
st->codecpar->codec_type = codec_type;
+ if (s->nb_streams>=3 ||( s->nb_streams==2
+ && s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE
+ && s->streams[1]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE))
+ s->ctx_flags &= ~AVFMTCTX_NOHEADER;
+ if (codec_type == AVMEDIA_TYPE_AUDIO) {
+ st->codecpar->bit_rate = flv->audio_bit_rate;
+ flv->missing_streams &= ~FLV_HEADER_FLAG_HASAUDIO;
+ }
+ if (codec_type == AVMEDIA_TYPE_VIDEO) {
+ st->codecpar->bit_rate = flv->video_bit_rate;
+ flv->missing_streams &= ~FLV_HEADER_FLAG_HASVIDEO;
+ st->avg_frame_rate = flv->framerate;
+ }
+
+
avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
+ flv->last_keyframe_stream_index = s->nb_streams - 1;
+ add_keyframes_index(s);
return st;
}
@@ -190,7 +265,7 @@ static void flv_set_audio_codec(AVFormatContext *s, AVStream *astream,
apar->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);
apar->codec_tag = flv_codecid >> FLV_AUDIO_CODECID_OFFSET;
}
@@ -224,11 +299,16 @@ static int flv_same_video_codec(AVCodecParameters *vpar, int flags)
static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream,
int flv_codecid, int read)
{
+ int ret = 0;
AVCodecParameters *par = vstream->codecpar;
+ enum AVCodecID old_codec_id = vstream->codecpar->codec_id;
switch (flv_codecid) {
case FLV_CODECID_H263:
par->codec_id = AV_CODEC_ID_FLV1;
break;
+ case FLV_CODECID_REALH263:
+ par->codec_id = AV_CODEC_ID_H263;
+ break; // Really mean it this time
case FLV_CODECID_SCREEN:
par->codec_id = AV_CODEC_ID_FLASHSV;
break;
@@ -242,25 +322,35 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream,
par->codec_id = AV_CODEC_ID_VP6A;
if (read) {
if (par->extradata_size != 1) {
- par->extradata = av_malloc(1);
- if (par->extradata)
- par->extradata_size = 1;
+ ff_alloc_extradata(par, 1);
}
if (par->extradata)
par->extradata[0] = avio_r8(s->pb);
else
avio_skip(s->pb, 1);
}
- return 1; // 1 byte body size adjustment for flv_read_packet()
+ ret = 1; // 1 byte body size adjustment for flv_read_packet()
+ break;
case FLV_CODECID_H264:
par->codec_id = AV_CODEC_ID_H264;
- return 3; // not 4, reading packet type will consume one byte
+ vstream->need_parsing = AVSTREAM_PARSE_HEADERS;
+ ret = 3; // not 4, reading packet type will consume one byte
+ break;
+ case FLV_CODECID_MPEG4:
+ par->codec_id = AV_CODEC_ID_MPEG4;
+ ret = 3;
+ break;
default:
- av_log(s, AV_LOG_INFO, "Unsupported video codec (%x)\n", flv_codecid);
+ avpriv_request_sample(s, "Video codec (%x)", flv_codecid);
par->codec_tag = flv_codecid;
}
- return 0;
+ if (!vstream->internal->need_context_update && par->codec_id != old_codec_id) {
+ avpriv_request_sample(s, "Changing the codec id midstream");
+ return AVERROR_PATCHWELCOME;
+ }
+
+ return ret;
}
static int amf_get_string(AVIOContext *ioc, char *buffer, int buffsize)
@@ -278,60 +368,60 @@ static int amf_get_string(AVIOContext *ioc, char *buffer, int buffsize)
return length;
}
-static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc,
- AVStream *vstream, int64_t max_pos)
+static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, 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 (flv->keyframe_count > 0) {
+ av_log(s, AV_LOG_DEBUG, "keyframes have been paresed\n");
+ return 0;
+ }
+ av_assert0(!flv->keyframe_times);
+ av_assert0(!flv->keyframe_filepositions);
+
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
@@ -341,26 +431,26 @@ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc,
}
}
- if (!ret && timeslen == fileposlen) {
- for (i = 0; i < fileposlen; i++) {
- av_add_index_entry(vstream, filepositions[i], times[i] * 1000,
- 0, 0, AVINDEX_KEYFRAME);
- if (i < 2) {
- flv->validate_index[i].pos = filepositions[i];
- flv->validate_index[i].dts = times[i] * 1000;
- flv->validate_count = i + 1;
- }
+ if (timeslen == fileposlen && fileposlen>1 && max_pos <= filepositions[0]) {
+ for (i = 0; i < FFMIN(2,fileposlen); i++) {
+ flv->validate_index[i].pos = filepositions[i];
+ flv->validate_index[i].dts = times[i] * 1000;
+ flv->validate_count = i + 1;
}
- } else
+ flv->keyframe_times = times;
+ flv->keyframe_filepositions = filepositions;
+ flv->keyframe_count = timeslen;
+ times = NULL;
+ filepositions = NULL;
+ } 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;
}
@@ -372,7 +462,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;
@@ -387,29 +477,37 @@ 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 &&
+ if (key &&
+ (ioc->seekable & AVIO_SEEKABLE_NORMAL) &&
!strcmp(KEYFRAMES_TAG, key) && depth == 1)
- if (parse_keyframes_index(s, ioc, vstream ? vstream : astream,
+ if (parse_keyframes_index(s, ioc,
max_pos) < 0)
- return -1;
-
+ av_log(s, AV_LOG_ERROR, "Keyframe index parsing failed\n");
+ else
+ add_keyframes_index(s);
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:
case AMF_DATA_TYPE_UNSUPPORTED:
break; // these take up no additional space
case AMF_DATA_TYPE_MIXEDARRAY:
+ {
+ unsigned v;
avio_skip(ioc, 4); // skip 32-bit max array index
while (avio_tell(ioc) < max_pos - 2 &&
amf_get_string(ioc, str_val, sizeof(str_val)) > 0)
@@ -418,9 +516,13 @@ 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)
+ v = avio_r8(ioc);
+ if (v != AMF_END_OF_OBJECT) {
+ av_log(s, AV_LOG_ERROR, "Missing AMF_END_OF_OBJECT in AMF_DATA_TYPE_MIXEDARRAY, found %d\n", v);
return -1;
+ }
break;
+ }
case AMF_DATA_TYPE_ARRAY:
{
unsigned int arraylen, i;
@@ -436,33 +538,40 @@ 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) {
+ apar = astream ? astream->codecpar : NULL;
+ vpar = vstream ? vstream->codecpar : NULL;
+
// stream info doesn't live any deeper than the first object
if (depth == 1) {
- apar = astream ? astream->codecpar : NULL;
- vpar = vstream ? vstream->codecpar : NULL;
-
if (amf_type == AMF_DATA_TYPE_NUMBER ||
amf_type == AMF_DATA_TYPE_BOOL) {
if (!strcmp(key, "duration"))
s->duration = num_val * AV_TIME_BASE;
- else if (!strcmp(key, "videodatarate") && vpar &&
+ else if (!strcmp(key, "videodatarate") &&
0 <= (int)(num_val * 1024.0))
- vpar->bit_rate = num_val * 1024.0;
- else if (!strcmp(key, "audiodatarate") && apar &&
+ flv->video_bit_rate = num_val * 1024.0;
+ else if (!strcmp(key, "audiodatarate") &&
0 <= (int)(num_val * 1024.0))
- apar->bit_rate = num_val * 1024.0;
+ flv->audio_bit_rate = num_val * 1024.0;
else if (!strcmp(key, "datastream")) {
- AVStream *st = create_stream(s, AVMEDIA_TYPE_DATA);
+ AVStream *st = create_stream(s, AVMEDIA_TYPE_SUBTITLE);
if (!st)
return AVERROR(ENOMEM);
st->codecpar->codec_id = AV_CODEC_ID_TEXT;
+ } else if (!strcmp(key, "framerate")) {
+ flv->framerate = av_d2q(num_val, 1000);
+ if (vstream)
+ vstream->avg_frame_rate = flv->framerate;
} else if (flv->trust_metadata) {
if (!strcmp(key, "videocodecid") && vpar) {
- flv_set_video_codec(s, vstream, num_val, 0);
+ int ret = flv_set_video_codec(s, vstream, num_val, 0);
+ if (ret < 0)
+ return ret;
} else if (!strcmp(key, "audiocodecid") && apar) {
int id = ((int)num_val) << FLV_AUDIO_CODECID_OFFSET;
flv_set_audio_codec(s, astream, apar, id);
@@ -482,8 +591,24 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
}
}
}
+ if (amf_type == AMF_DATA_TYPE_STRING) {
+ if (!strcmp(key, "encoder")) {
+ int version = -1;
+ if (1 == sscanf(str_val, "Open Broadcaster Software v0.%d", &version)) {
+ if (version > 0 && version <= 655)
+ flv->broken_sizes = 1;
+ }
+ } else if (!strcmp(key, "metadatacreator") && !strcmp(str_val, "MEGA")) {
+ flv->broken_sizes = 1;
+ }
+ }
}
+ if (amf_type == AMF_DATA_TYPE_OBJECT && s->nb_streams == 1 &&
+ ((!apar && !strcmp(key, "audiocodecid")) ||
+ (!vpar && !strcmp(key, "videocodecid"))))
+ s->ctx_flags &= ~AVFMTCTX_NOHEADER; //If there is either audio/video missing, codecid will be an empty object
+
if (!strcmp(key, "duration") ||
!strcmp(key, "filesize") ||
!strcmp(key, "width") ||
@@ -514,40 +639,62 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
return 0;
}
+#define TYPE_ONTEXTDATA 1
+#define TYPE_ONCAPTION 2
+#define TYPE_ONCAPTIONINFO 3
+#define TYPE_UNKNOWN 9
+
static int flv_read_metabody(AVFormatContext *s, int64_t next_pos)
{
+ FLVContext *flv = s->priv_data;
AMFDataType type;
AVStream *stream, *astream, *vstream;
+ AVStream av_unused *dstream;
AVIOContext *ioc;
int i;
// only needs to hold the string "onMetaData".
// Anything longer is something we don't want.
- char buffer[11];
+ char buffer[32];
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;
+ if (!strcmp(buffer, "onCaption"))
+ return TYPE_ONCAPTION;
+
+ if (!strcmp(buffer, "onCaptionInfo"))
+ return TYPE_ONCAPTIONINFO;
+
+ if (strcmp(buffer, "onMetaData") && strcmp(buffer, "onCuePoint")) {
+ av_log(s, AV_LOG_DEBUG, "Unknown type %s\n", buffer);
+ 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->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
- astream = stream;
- else if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+ if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
vstream = stream;
+ flv->last_keyframe_stream_index = i;
+ } else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ astream = stream;
+ if (flv->last_keyframe_stream_index == -1)
+ flv->last_keyframe_stream_index = i;
+ }
+ else if (stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
+ dstream = stream;
}
// parse the second object (we want a mixed array)
@@ -559,38 +706,55 @@ static int flv_read_metabody(AVFormatContext *s, int64_t next_pos)
static int flv_read_header(AVFormatContext *s)
{
+ int flags;
+ FLVContext *flv = s->priv_data;
int offset;
+ int pre_tag_size = 0;
avio_skip(s->pb, 4);
- avio_r8(s->pb); // flags
+ flags = avio_r8(s->pb);
+
+ flv->missing_streams = flags & (FLV_HEADER_FLAG_HASVIDEO | FLV_HEADER_FLAG_HASAUDIO);
s->ctx_flags |= AVFMTCTX_NOHEADER;
offset = avio_rb32(s->pb);
avio_seek(s->pb, offset, SEEK_SET);
- avio_skip(s->pb, 4);
+
+ /* Annex E. The FLV File Format
+ * E.3 TheFLVFileBody
+ * Field Type Comment
+ * PreviousTagSize0 UI32 Always 0
+ * */
+ pre_tag_size = avio_rb32(s->pb);
+ if (pre_tag_size) {
+ av_log(s, AV_LOG_WARNING, "Read FLV header error, input file is not a standard flv format, first PreviousTagSize0 always is 0\n");
+ }
s->start_time = 0;
+ flv->sum_flv_tag_size = 0;
+ flv->last_keyframe_stream_index = -1;
return 0;
}
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]);
+ av_freep(&flv->keyframe_times);
+ av_freep(&flv->keyframe_filepositions);
return 0;
}
static int flv_get_extradata(AVFormatContext *s, AVStream *st, int size)
{
- av_free(st->codecpar->extradata);
- st->codecpar->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
+ av_freep(&st->codecpar->extradata);
+ if (ff_get_extradata(s, st->codecpar, s->pb, size) < 0)
return AVERROR(ENOMEM);
- st->codecpar->extradata_size = size;
- avio_read(s->pb, st->codecpar->extradata, st->codecpar->extradata_size);
+ st->internal->need_context_update = 1;
return 0;
}
@@ -672,8 +836,11 @@ static int flv_data_packet(AVFormatContext *s, AVPacket *pkt,
char buf[20];
int ret = AVERROR_INVALIDDATA;
int i, length = -1;
+ int array = 0;
switch (avio_r8(pb)) {
+ case AMF_DATA_TYPE_ARRAY:
+ array = 1;
case AMF_DATA_TYPE_MIXEDARRAY:
avio_seek(pb, 4, SEEK_CUR);
case AMF_DATA_TYPE_OBJECT:
@@ -682,9 +849,9 @@ static int flv_data_packet(AVFormatContext *s, AVPacket *pkt,
goto skip;
}
- while ((ret = amf_get_string(pb, buf, sizeof(buf))) > 0) {
+ while (array || (ret = amf_get_string(pb, buf, sizeof(buf))) > 0) {
AMFDataType type = avio_r8(pb);
- if (type == AMF_DATA_TYPE_STRING && !strcmp(buf, "text")) {
+ if (type == AMF_DATA_TYPE_STRING && (array || !strcmp(buf, "text"))) {
length = avio_rb16(pb);
ret = av_get_packet(pb, pkt, length);
if (ret < 0)
@@ -704,12 +871,12 @@ static int flv_data_packet(AVFormatContext *s, AVPacket *pkt,
for (i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
- if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA)
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
break;
}
if (i == s->nb_streams) {
- st = create_stream(s, AVMEDIA_TYPE_DATA);
+ st = create_stream(s, AVMEDIA_TYPE_SUBTITLE);
if (!st)
return AVERROR(ENOMEM);
st->codecpar->codec_id = AV_CODEC_ID_TEXT;
@@ -728,25 +895,61 @@ skip:
return ret;
}
+static int resync(AVFormatContext *s)
+{
+ FLVContext *flv = s->priv_data;
+ int64_t i;
+ int64_t pos = avio_tell(s->pb);
+
+ for (i=0; !avio_feof(s->pb); i++) {
+ int j = i & (RESYNC_BUFFER_SIZE-1);
+ int j1 = j + RESYNC_BUFFER_SIZE;
+ flv->resync_buffer[j ] =
+ flv->resync_buffer[j1] = avio_r8(s->pb);
+
+ if (i > 22) {
+ unsigned lsize2 = AV_RB32(flv->resync_buffer + j1 - 4);
+ if (lsize2 >= 11 && lsize2 + 8LL < FFMIN(i, RESYNC_BUFFER_SIZE)) {
+ unsigned size2 = AV_RB24(flv->resync_buffer + j1 - lsize2 + 1 - 4);
+ unsigned lsize1 = AV_RB32(flv->resync_buffer + j1 - lsize2 - 8);
+ if (lsize1 >= 11 && lsize1 + 8LL + lsize2 < FFMIN(i, RESYNC_BUFFER_SIZE)) {
+ unsigned size1 = AV_RB24(flv->resync_buffer + j1 - lsize1 + 1 - lsize2 - 8);
+ if (size1 == lsize1 - 11 && size2 == lsize2 - 11) {
+ avio_seek(s->pb, pos + i - lsize1 - lsize2 - 8, SEEK_SET);
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ return AVERROR_EOF;
+}
+
static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
{
FLVContext *flv = s->priv_data;
- int ret, i, size, flags, is_audio;
+ int ret, i, size, flags;
enum FlvTagType type;
- int64_t next, pos;
+ 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;
+ int last = -1;
+ int orig_size;
+retry:
/* 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);
+ orig_size =
size = avio_rb24(s->pb);
+ flv->sum_flv_tag_size += size + 11;
dts = avio_rb24(s->pb);
- dts |= avio_r8(s->pb) << 24;
- av_log(s, AV_LOG_TRACE, "type:%d, size:%d, dts:%"PRId64"\n", type, size, dts);
- if (s->pb->eof_reached)
+ dts |= (unsigned)avio_r8(s->pb) << 24;
+ av_log(s, AV_LOG_TRACE, "type:%d, size:%d, last:%d, dts:%"PRId64" pos:%"PRId64"\n", type, size, last, dts, avio_tell(s->pb));
+ if (avio_feof(s->pb))
return AVERROR_EOF;
avio_skip(s->pb, 3); /* stream id, always 0 */
flags = 0;
@@ -767,73 +970,106 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
}
}
- if (size == 0)
- continue;
+ if (size == 0) {
+ ret = FFERROR_REDO;
+ goto leave;
+ }
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 && size > 13 + 1 + 4)
- if (flv_read_metabody(s, next) > 0) {
+ } else if (type == FLV_TAG_TYPE_META) {
+ stream_type=FLV_STREAM_TYPE_DATA;
+ if (size > 13 + 1 + 4) { // Header-type metadata stuff
+ int type;
+ meta_pos = avio_tell(s->pb);
+ type = flv_read_metabody(s, next);
+ if (type == 0 && dts == 0 || type < 0 || type == TYPE_UNKNOWN) {
+ if (type < 0 && flv->validate_count &&
+ flv->validate_index[0].pos > next &&
+ flv->validate_index[0].pos - 4 < next
+ ) {
+ av_log(s, AV_LOG_WARNING, "Adjusting next position due to index mismatch\n");
+ next = flv->validate_index[0].pos - 4;
+ }
+ goto skip;
+ } else if (type == TYPE_ONTEXTDATA) {
+ avpriv_request_sample(s, "OnTextData packet");
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);
-
+ } else if (type == TYPE_ONCAPTION) {
+ return flv_data_packet(s, pkt, dts, next);
+ }
+ avio_seek(s->pb, meta_pos, SEEK_SET);
+ }
+ } else {
+ 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;
+ if (avio_seek(s->pb, next, SEEK_SET) != next) {
+ // This can happen if flv_read_metabody above read past
+ // next, on a non-seekable input, and the preceding data has
+ // been flushed out from the IO buffer.
+ av_log(s, AV_LOG_ERROR, "Unable to seek to the next packet\n");
+ return AVERROR_INVALIDDATA;
+ }
+ ret = FFERROR_REDO;
+ goto leave;
}
/* skip empty data packets */
- if (!size)
- continue;
+ if (!size) {
+ ret = FFERROR_REDO;
+ goto leave;
+ }
/* now find stream */
for (i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
- if (is_audio && st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
- if (flv_same_audio_codec(st->codecpar, flags))
+ if (stream_type == FLV_STREAM_TYPE_AUDIO) {
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
+ (s->audio_codec_id || flv_same_audio_codec(st->codecpar, flags)))
+ break;
+ } else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
+ (s->video_codec_id || flv_same_video_codec(st->codecpar, flags)))
break;
- } else if (!is_audio &&
- st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
- if (flv_same_video_codec(st->codecpar, flags))
+ } else if (stream_type == FLV_STREAM_TYPE_DATA) {
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
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_SUBTITLE};
+ st = create_stream(s, stream_types[stream_type]);
if (!st)
return AVERROR(ENOMEM);
+
}
- av_log(s, AV_LOG_TRACE, "%d %X %d \n", is_audio, flags, st->discard);
+ av_log(s, AV_LOG_TRACE, "%d %X %d \n", stream_type, flags, st->discard);
- if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||
- is_audio)
+ if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) &&
+ ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||
+ stream_type == FLV_STREAM_TYPE_AUDIO))
av_add_index_entry(st, pos, dts, size, 0, AVINDEX_KEYFRAME);
- 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;
+ ret = FFERROR_REDO;
+ goto leave;
}
- break;
- }
// if not streamed and no duration from metadata then seek to end to find
// the duration from the timestamps
@@ -844,7 +1080,8 @@ skip:
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);
if (size > 0 && size < fsize) {
@@ -854,14 +1091,20 @@ 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) >>
@@ -895,43 +1138,58 @@ skip:
sample_rate = par->sample_rate;
avcodec_parameters_free(&par);
}
- } else {
- size -= flv_set_video_codec(s, st, flags & FLV_VIDEO_CODECID_MASK, 1);
+ } else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
+ int ret = flv_set_video_codec(s, st, flags & FLV_VIDEO_CODECID_MASK, 1);
+ if (ret < 0)
+ return ret;
+ size -= ret;
+ } else if (stream_type == FLV_STREAM_TYPE_DATA) {
+ st->codecpar->codec_id = AV_CODEC_ID_TEXT;
}
if (st->codecpar->codec_id == AV_CODEC_ID_AAC ||
- st->codecpar->codec_id == AV_CODEC_ID_H264) {
+ st->codecpar->codec_id == AV_CODEC_ID_H264 ||
+ st->codecpar->codec_id == AV_CODEC_ID_MPEG4) {
int type = avio_r8(s->pb);
size--;
- if (st->codecpar->codec_id == AV_CODEC_ID_H264) {
+ if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->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->codecpar->extradata || st->codecpar->codec_id == AV_CODEC_ID_AAC ||
+ st->codecpar->codec_id == AV_CODEC_ID_H264)) {
+ AVDictionaryEntry *t;
+
if (st->codecpar->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);
+ ret = FFERROR_REDO;
goto leave;
}
if ((ret = flv_get_extradata(s, st, size)) < 0)
return ret;
- if (st->codecpar->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->codecpar->extradata_size = 2;
+ /* Workaround for buggy Omnia A/XE encoder */
+ t = av_dict_get(s->metadata, "Encoder", NULL, 0);
+ if (st->codecpar->codec_id == AV_CODEC_ID_AAC && t && !strcmp(t->value, "Omnia A/XE"))
+ st->codecpar->extradata_size = 2;
+
+ if (st->codecpar->codec_id == AV_CODEC_ID_AAC && 0) {
+ MPEG4AudioConfig cfg;
- avpriv_mpeg4audio_get_config(&cfg, st->codecpar->extradata,
- st->codecpar->extradata_size * 8, 1);
+ if (avpriv_mpeg4audio_get_config(&cfg, st->codecpar->extradata,
+ st->codecpar->extradata_size * 8, 1) >= 0) {
st->codecpar->channels = cfg.channels;
st->codecpar->channel_layout = 0;
if (cfg.ext_sample_rate)
@@ -939,51 +1197,65 @@ skip:
else
st->codecpar->sample_rate = cfg.sample_rate;
av_log(s, AV_LOG_TRACE, "mp4a config channels %d sample rate %d\n",
- st->codecpar->channels, st->codecpar->sample_rate);
+ st->codecpar->channels, st->codecpar->sample_rate);
+ }
}
- ret = AVERROR(EAGAIN);
+ ret = FFERROR_REDO;
goto leave;
}
}
/* skip empty data packets */
if (!size) {
- ret = AVERROR(EAGAIN);
+ ret = FFERROR_REDO;
goto leave;
}
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]) {
+ pkt->pos = pos;
+ 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:
- avio_skip(s->pb, 4);
+ last = avio_rb32(s->pb);
+ if (last != orig_size + 11 && last != orig_size + 10 &&
+ !avio_feof(s->pb) &&
+ (last != orig_size || !last) && last != flv->sum_flv_tag_size &&
+ !flv->broken_sizes) {
+ av_log(s, AV_LOG_ERROR, "Packet mismatch %d %d %d\n", last, orig_size + 11, flv->sum_flv_tag_size);
+ avio_seek(s->pb, pos + 1, SEEK_SET);
+ ret = resync(s);
+ av_packet_unref(pkt);
+ if (ret >= 0) {
+ goto retry;
+ }
+ }
return ret;
}
@@ -998,11 +1270,12 @@ static int flv_read_seek(AVFormatContext *s, int stream_index,
#define OFFSET(x) offsetof(FLVContext, x)
#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[] = {
- { "flv_metadata", "Allocate streams according to the onMetaData array", OFFSET(trust_metadata), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VD },
+ { "flv_metadata", "Allocate streams according to the onMetaData array", OFFSET(trust_metadata), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD },
+ { "missing_streams", "", OFFSET(missing_streams), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 0xFF, VD | AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY },
{ NULL }
};
-static const AVClass class = {
+static const AVClass flv_class = {
.class_name = "flvdec",
.item_name = av_default_item_name,
.option = options,
@@ -1019,5 +1292,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 00bd65c..899b07e 100644
--- a/libavformat/flvenc.c
+++ b/libavformat/flvenc.c
@@ -1,41 +1,49 @@
/*
* 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 "libavutil/mathematics.h"
+#include "avio_internal.h"
+#include "avio.h"
#include "avc.h"
#include "avformat.h"
#include "flv.h"
#include "internal.h"
#include "metadata.h"
+#include "libavutil/opt.h"
+#include "libavcodec/put_bits.h"
+#include "libavcodec/aacenctab.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 }
@@ -55,17 +63,61 @@ static const AVCodecTag flv_audio_codec_ids[] = {
{ AV_CODEC_ID_NONE, 0 }
};
+typedef enum {
+ FLV_AAC_SEQ_HEADER_DETECT = (1 << 0),
+ FLV_NO_SEQUENCE_END = (1 << 1),
+ FLV_ADD_KEYFRAME_INDEX = (1 << 2),
+ FLV_NO_METADATA = (1 << 3),
+ FLV_NO_DURATION_FILESIZE = (1 << 4),
+} FLVFlags;
+
+typedef struct FLVFileposition {
+ int64_t keyframe_position;
+ double keyframe_timestamp;
+ struct FLVFileposition *next;
+} FLVFileposition;
+
typedef struct FLVContext {
+ AVClass *av_class;
int reserved;
int64_t duration_offset;
int64_t filesize_offset;
int64_t duration;
int64_t delay; ///< first dts delay (needed for AVC & Speex)
+ int64_t datastart_offset;
+ int64_t datasize_offset;
+ int64_t datasize;
+ int64_t videosize_offset;
+ int64_t videosize;
+ int64_t audiosize_offset;
+ int64_t audiosize;
+
+ int64_t metadata_size_pos;
+ int64_t metadata_totalsize_pos;
+ int64_t metadata_totalsize;
+ int64_t keyframe_index_size;
+
+ int64_t lasttimestamp_offset;
+ double lasttimestamp;
+ int64_t lastkeyframetimestamp_offset;
+ double lastkeyframetimestamp;
+ int64_t lastkeyframelocation_offset;
+ int64_t lastkeyframelocation;
+
+ int acurframeindex;
+ int64_t keyframes_info_offset;
+
+ int64_t filepositions_count;
+ FLVFileposition *filepositions;
+ FLVFileposition *head_filepositions;
+
AVCodecParameters *audio_par;
AVCodecParameters *video_par;
double framerate;
AVCodecParameters *data_par;
+
+ int flags;
} FLVContext;
typedef struct FLVStreamContext {
@@ -83,16 +135,24 @@ static int get_audio_flags(AVFormatContext *s, AVCodecParameters *par)
else if (par->codec_id == AV_CODEC_ID_SPEEX) {
if (par->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 (par->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 {
switch (par->sample_rate) {
+ case 48000:
+ // 48khz mp3 is stored with 44k1 samplerate identifer
+ if (par->codec_id == AV_CODEC_ID_MP3) {
+ flags |= FLV_SAMPLERATE_44100HZ;
+ break;
+ } else {
+ goto error;
+ }
case 44100:
flags |= FLV_SAMPLERATE_44100HZ;
break;
@@ -110,10 +170,11 @@ static int get_audio_flags(AVFormatContext *s, AVCodecParameters *par)
break;
}
default:
+error:
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", par->sample_rate);
+ return AVERROR(EINVAL);
}
}
@@ -154,8 +215,9 @@ static int get_audio_flags(AVFormatContext *s, AVCodecParameters *par)
flags |= par->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(par->codec_id));
+ return AVERROR(EINVAL);
}
return flags;
@@ -187,6 +249,17 @@ static void put_amf_double(AVIOContext *pb, double d)
avio_wb64(pb, av_double2int(d));
}
+static void put_amf_byte(AVIOContext *pb, unsigned char abyte)
+{
+ avio_w8(pb, abyte);
+}
+
+static void put_amf_dword_array(AVIOContext *pb, uint32_t dw)
+{
+ avio_w8(pb, AMF_DATA_TYPE_ARRAY);
+ avio_wb32(pb, dw);
+}
+
static void put_amf_bool(AVIOContext *pb, int b)
{
avio_w8(pb, AMF_DATA_TYPE_BOOL);
@@ -197,13 +270,14 @@ static void write_metadata(AVFormatContext *s, unsigned int ts)
{
AVIOContext *pb = s->pb;
FLVContext *flv = s->priv_data;
+ int write_duration_filesize = !(flv->flags & FLV_NO_DURATION_FILESIZE);
int metadata_count = 0;
- int64_t metadata_size_pos, data_size, metadata_count_pos;
+ int64_t metadata_count_pos;
AVDictionaryEntry *tag = NULL;
/* write meta_tag */
- avio_w8(pb, 18); // tag type META
- metadata_size_pos = avio_tell(pb);
+ avio_w8(pb, FLV_TAG_TYPE_META); // tag type META
+ flv->metadata_size_pos = avio_tell(pb);
avio_wb24(pb, 0); // size of data part (sum of all parts below)
avio_wb24(pb, ts); // timestamp
avio_wb32(pb, 0); // reserved
@@ -219,16 +293,18 @@ static void write_metadata(AVFormatContext *s, unsigned int ts)
metadata_count_pos = avio_tell(pb);
metadata_count = 4 * !!flv->video_par +
5 * !!flv->audio_par +
- 1 * !!flv->data_par +
- 2; // +2 for duration and file size
-
+ 1 * !!flv->data_par;
+ if (write_duration_filesize) {
+ metadata_count += 2; // +2 for duration and file size
+ }
avio_wb32(pb, metadata_count);
- put_amf_string(pb, "duration");
- flv->duration_offset = avio_tell(pb);
-
- // fill in the guessed duration, it'll be corrected later if incorrect
- put_amf_double(pb, s->duration / AV_TIME_BASE);
+ if (write_duration_filesize) {
+ put_amf_string(pb, "duration");
+ flv->duration_offset = avio_tell(pb);
+ // fill in the guessed duration, it'll be corrected later if incorrect
+ put_amf_double(pb, s->duration / AV_TIME_BASE);
+ }
if (flv->video_par) {
put_amf_string(pb, "width");
@@ -272,30 +348,125 @@ static void write_metadata(AVFormatContext *s, unsigned int ts)
put_amf_double(pb, 0.0);
}
+ ff_standardize_creation_time(s);
while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
+ if( !strcmp(tag->key, "width")
+ ||!strcmp(tag->key, "height")
+ ||!strcmp(tag->key, "videodatarate")
+ ||!strcmp(tag->key, "framerate")
+ ||!strcmp(tag->key, "videocodecid")
+ ||!strcmp(tag->key, "audiodatarate")
+ ||!strcmp(tag->key, "audiosamplerate")
+ ||!strcmp(tag->key, "audiosamplesize")
+ ||!strcmp(tag->key, "stereo")
+ ||!strcmp(tag->key, "audiocodecid")
+ ||!strcmp(tag->key, "duration")
+ ||!strcmp(tag->key, "onMetaData")
+ ||!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);
metadata_count++;
}
- put_amf_string(pb, "filesize");
- flv->filesize_offset = avio_tell(pb);
- put_amf_double(pb, 0); // delayed write
+ if (write_duration_filesize) {
+ put_amf_string(pb, "filesize");
+ flv->filesize_offset = avio_tell(pb);
+ put_amf_double(pb, 0); // delayed write
+ }
+
+ if (flv->flags & FLV_ADD_KEYFRAME_INDEX) {
+ flv->acurframeindex = 0;
+ flv->keyframe_index_size = 0;
+
+ put_amf_string(pb, "hasVideo");
+ put_amf_bool(pb, !!flv->video_par);
+ metadata_count++;
+
+ put_amf_string(pb, "hasKeyframes");
+ put_amf_bool(pb, 1);
+ metadata_count++;
+
+ put_amf_string(pb, "hasAudio");
+ put_amf_bool(pb, !!flv->audio_par);
+ metadata_count++;
+
+ put_amf_string(pb, "hasMetadata");
+ put_amf_bool(pb, 1);
+ metadata_count++;
+
+ put_amf_string(pb, "canSeekToEnd");
+ put_amf_bool(pb, 1);
+ metadata_count++;
+
+ put_amf_string(pb, "datasize");
+ flv->datasize_offset = avio_tell(pb);
+ flv->datasize = 0;
+ put_amf_double(pb, flv->datasize);
+ metadata_count++;
+
+ put_amf_string(pb, "videosize");
+ flv->videosize_offset = avio_tell(pb);
+ flv->videosize = 0;
+ put_amf_double(pb, flv->videosize);
+ metadata_count++;
+
+ put_amf_string(pb, "audiosize");
+ flv->audiosize_offset = avio_tell(pb);
+ flv->audiosize = 0;
+ put_amf_double(pb, flv->audiosize);
+ metadata_count++;
+
+ put_amf_string(pb, "lasttimestamp");
+ flv->lasttimestamp_offset = avio_tell(pb);
+ flv->lasttimestamp = 0;
+ put_amf_double(pb, 0);
+ metadata_count++;
+
+ put_amf_string(pb, "lastkeyframetimestamp");
+ flv->lastkeyframetimestamp_offset = avio_tell(pb);
+ flv->lastkeyframetimestamp = 0;
+ put_amf_double(pb, 0);
+ metadata_count++;
+
+ put_amf_string(pb, "lastkeyframelocation");
+ flv->lastkeyframelocation_offset = avio_tell(pb);
+ flv->lastkeyframelocation = 0;
+ put_amf_double(pb, 0);
+ metadata_count++;
+
+ put_amf_string(pb, "keyframes");
+ put_amf_byte(pb, AMF_DATA_TYPE_OBJECT);
+ metadata_count++;
+
+ flv->keyframes_info_offset = avio_tell(pb);
+ }
put_amf_string(pb, "");
avio_w8(pb, AMF_END_OF_OBJECT);
/* write total size of tag */
- data_size = avio_tell(pb) - metadata_size_pos - 10;
+ flv->metadata_totalsize = avio_tell(pb) - flv->metadata_size_pos - 10;
avio_seek(pb, metadata_count_pos, SEEK_SET);
avio_wb32(pb, metadata_count);
- avio_seek(pb, metadata_size_pos, SEEK_SET);
- avio_wb24(pb, data_size);
- avio_skip(pb, data_size + 10 - 3);
- avio_wb32(pb, data_size + 11);
+ avio_seek(pb, flv->metadata_size_pos, SEEK_SET);
+ avio_wb24(pb, flv->metadata_totalsize);
+ avio_skip(pb, flv->metadata_totalsize + 10 - 3);
+ flv->metadata_totalsize_pos = avio_tell(pb);
+ avio_wb32(pb, flv->metadata_totalsize + 11);
}
static int unsupported_codec(AVFormatContext *s,
@@ -309,12 +480,181 @@ static int unsupported_codec(AVFormatContext *s,
return AVERROR(ENOSYS);
}
+static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par) {
+ int64_t data_size;
+ AVIOContext *pb = s->pb;
+ FLVContext *flv = s->priv_data;
+
+ if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264
+ || par->codec_id == AV_CODEC_ID_MPEG4) {
+ int64_t pos;
+ avio_w8(pb,
+ par->codec_type == AVMEDIA_TYPE_VIDEO ?
+ FLV_TAG_TYPE_VIDEO : FLV_TAG_TYPE_AUDIO);
+ avio_wb24(pb, 0); // size patched later
+ avio_wb24(pb, 0); // ts
+ avio_w8(pb, 0); // ts ext
+ avio_wb24(pb, 0); // streamid
+ pos = avio_tell(pb);
+ if (par->codec_id == AV_CODEC_ID_AAC) {
+ avio_w8(pb, get_audio_flags(s, par));
+ avio_w8(pb, 0); // AAC sequence header
+
+ if (!par->extradata_size && (flv->flags & FLV_AAC_SEQ_HEADER_DETECT)) {
+ PutBitContext pbc;
+ int samplerate_index;
+ int channels = flv->audio_par->channels
+ - (flv->audio_par->channels == 8 ? 1 : 0);
+ uint8_t data[2];
+
+ for (samplerate_index = 0; samplerate_index < 16;
+ samplerate_index++)
+ if (flv->audio_par->sample_rate
+ == mpeg4audio_sample_rates[samplerate_index])
+ break;
+
+ init_put_bits(&pbc, data, sizeof(data));
+ put_bits(&pbc, 5, flv->audio_par->profile + 1); //profile
+ put_bits(&pbc, 4, samplerate_index); //sample rate index
+ put_bits(&pbc, 4, channels);
+ put_bits(&pbc, 1, 0); //frame length - 1024 samples
+ put_bits(&pbc, 1, 0); //does not depend on core coder
+ put_bits(&pbc, 1, 0); //is not extension
+ flush_put_bits(&pbc);
+
+ avio_w8(pb, data[0]);
+ avio_w8(pb, data[1]);
+
+ av_log(s, AV_LOG_WARNING, "AAC sequence header: %02x %02x.\n",
+ data[0], data[1]);
+ }
+ avio_write(pb, par->extradata, par->extradata_size);
+ } else {
+ avio_w8(pb, par->codec_tag | FLV_FRAME_KEY); // flags
+ avio_w8(pb, 0); // AVC sequence header
+ avio_wb24(pb, 0); // composition time
+ ff_isom_write_avcc(pb, par->extradata, par->extradata_size);
+ }
+ data_size = avio_tell(pb) - pos;
+ avio_seek(pb, -data_size - 10, SEEK_CUR);
+ avio_wb24(pb, data_size);
+ avio_skip(pb, data_size + 10 - 3);
+ avio_wb32(pb, data_size + 11); // previous tag size
+ }
+}
+
+static int flv_append_keyframe_info(AVFormatContext *s, FLVContext *flv, double ts, int64_t pos)
+{
+ FLVFileposition *position = av_malloc(sizeof(FLVFileposition));
+
+ if (!position) {
+ av_log(s, AV_LOG_WARNING, "no mem for add keyframe index!\n");
+ return AVERROR(ENOMEM);
+ }
+
+ position->keyframe_timestamp = ts;
+ position->keyframe_position = pos;
+
+ if (!flv->filepositions_count) {
+ flv->filepositions = position;
+ flv->head_filepositions = flv->filepositions;
+ position->next = NULL;
+ } else {
+ flv->filepositions->next = position;
+ position->next = NULL;
+ flv->filepositions = flv->filepositions->next;
+ }
+
+ flv->filepositions_count++;
+
+ return 0;
+}
+
+static int shift_data(AVFormatContext *s)
+{
+ int ret = 0;
+ int n = 0;
+ int64_t metadata_size = 0;
+ FLVContext *flv = s->priv_data;
+ int64_t pos, pos_end = avio_tell(s->pb);
+ uint8_t *buf, *read_buf[2];
+ int read_buf_id = 0;
+ int read_size[2];
+ AVIOContext *read_pb;
+
+ metadata_size = flv->filepositions_count * 9 * 2 + 10; /* filepositions and times value */
+ metadata_size += 2 + 13; /* filepositions String */
+ metadata_size += 2 + 5; /* times String */
+ metadata_size += 3; /* Object end */
+
+ flv->keyframe_index_size = metadata_size;
+
+ if (metadata_size < 0)
+ return metadata_size;
+
+ buf = av_malloc_array(metadata_size, 2);
+ if (!buf) {
+ return AVERROR(ENOMEM);
+ }
+ read_buf[0] = buf;
+ read_buf[1] = buf + metadata_size;
+
+ avio_seek(s->pb, flv->metadata_size_pos, SEEK_SET);
+ avio_wb24(s->pb, flv->metadata_totalsize + metadata_size);
+
+ avio_seek(s->pb, flv->metadata_totalsize_pos, SEEK_SET);
+ avio_wb32(s->pb, flv->metadata_totalsize + 11 + metadata_size);
+ avio_seek(s->pb, pos_end, SEEK_SET);
+
+ /* Shift the data: the AVIO context of the output can only be used for
+ * writing, so we re-open the same output, but for reading. It also avoids
+ * a read/seek/write/seek back and forth. */
+ avio_flush(s->pb);
+ ret = s->io_open(s, &read_pb, s->filename, AVIO_FLAG_READ, NULL);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "Unable to re-open %s output file for "
+ "the second pass (add_keyframe_index)\n", s->filename);
+ goto end;
+ }
+
+ /* mark the end of the shift to up to the last data we wrote, and get ready
+ * for writing */
+ pos_end = avio_tell(s->pb);
+ avio_seek(s->pb, flv->keyframes_info_offset + metadata_size, SEEK_SET);
+
+ /* start reading at where the keyframe index information will be placed */
+ avio_seek(read_pb, flv->keyframes_info_offset, SEEK_SET);
+ pos = avio_tell(read_pb);
+
+#define READ_BLOCK do { \
+ read_size[read_buf_id] = avio_read(read_pb, read_buf[read_buf_id], metadata_size); \
+ read_buf_id ^= 1; \
+} while (0)
+
+ /* shift data by chunk of at most keyframe *filepositions* and *times* size */
+ READ_BLOCK;
+ do {
+ READ_BLOCK;
+ n = read_size[read_buf_id];
+ if (n < 0)
+ break;
+ avio_write(s->pb, read_buf[read_buf_id], n);
+ pos += n;
+ } while (pos <= pos_end);
+
+ ff_format_io_close(s, &read_pb);
+
+end:
+ av_free(buf);
+ return ret;
+}
+
+
static int flv_write_header(AVFormatContext *s)
{
int i;
AVIOContext *pb = s->pb;
FLVContext *flv = s->priv_data;
- int64_t data_size;
for (i = 0; i < s->nb_streams; i++) {
AVCodecParameters *par = s->streams[i]->codecpar;
@@ -333,6 +673,22 @@ static int flv_write_header(AVFormatContext *s)
flv->video_par = par;
if (!ff_codec_get_tag(flv_video_codec_ids, par->codec_id))
return unsupported_codec(s, "Video", par->codec_id);
+
+ if (par->codec_id == AV_CODEC_ID_MPEG4 ||
+ par->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(par->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 (par->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:
if (flv->audio_par) {
@@ -343,15 +699,27 @@ static int flv_write_header(AVFormatContext *s)
flv->audio_par = par;
if (get_audio_flags(s, par) < 0)
return unsupported_codec(s, "Audio", par->codec_id);
+ if (par->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 (par->codec_id != AV_CODEC_ID_TEXT)
+ if (par->codec_id != AV_CODEC_ID_TEXT && par->codec_id != AV_CODEC_ID_NONE)
return unsupported_codec(s, "Data", par->codec_id);
flv->data_par = par;
break;
+ case AVMEDIA_TYPE_SUBTITLE:
+ if (par->codec_id != AV_CODEC_ID_TEXT) {
+ av_log(s, AV_LOG_ERROR, "Subtitle codec '%s' for stream %d is not compatible with FLV\n",
+ avcodec_get_name(par->codec_id), i);
+ return AVERROR_INVALIDDATA;
+ }
+ flv->data_par = par;
+ 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(par->codec_type), i);
+ return AVERROR(EINVAL);
}
avpriv_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */
@@ -381,70 +749,121 @@ static int flv_write_header(AVFormatContext *s)
flv->reserved = 5;
}
- write_metadata(s, 0);
+ if (flv->flags & FLV_NO_METADATA) {
+ pb->seekable = 0;
+ } else {
+ write_metadata(s, 0);
+ }
for (i = 0; i < s->nb_streams; i++) {
- AVCodecParameters *par = s->streams[i]->codecpar;
- if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264) {
- int64_t pos;
- avio_w8(pb, par->codec_type == AVMEDIA_TYPE_VIDEO ?
- FLV_TAG_TYPE_VIDEO : FLV_TAG_TYPE_AUDIO);
- avio_wb24(pb, 0); // size patched later
- avio_wb24(pb, 0); // ts
- avio_w8(pb, 0); // ts ext
- avio_wb24(pb, 0); // streamid
- pos = avio_tell(pb);
- if (par->codec_id == AV_CODEC_ID_AAC) {
- avio_w8(pb, get_audio_flags(s, par));
- avio_w8(pb, 0); // AAC sequence header
- avio_write(pb, par->extradata, par->extradata_size);
- } else {
- avio_w8(pb, par->codec_tag | FLV_FRAME_KEY); // flags
- avio_w8(pb, 0); // AVC sequence header
- avio_wb24(pb, 0); // composition time
- ff_isom_write_avcc(pb, par->extradata, par->extradata_size);
- }
- data_size = avio_tell(pb) - pos;
- avio_seek(pb, -data_size - 10, SEEK_CUR);
- avio_wb24(pb, data_size);
- avio_skip(pb, data_size + 10 - 3);
- avio_wb32(pb, data_size + 11); // previous tag size
- }
+ flv_write_codec_header(s, s->streams[i]->codecpar);
}
+ flv->datastart_offset = avio_tell(pb);
return 0;
}
static int flv_write_trailer(AVFormatContext *s)
{
int64_t file_size;
-
AVIOContext *pb = s->pb;
FLVContext *flv = s->priv_data;
- int i;
+ int build_keyframes_idx = flv->flags & FLV_ADD_KEYFRAME_INDEX;
+ int i, res;
+ int64_t cur_pos = avio_tell(s->pb);
- /* Add EOS tag */
- for (i = 0; i < s->nb_streams; i++) {
- AVCodecParameters *par = s->streams[i]->codecpar;
- FLVStreamContext *sc = s->streams[i]->priv_data;
- if (par->codec_type == AVMEDIA_TYPE_VIDEO &&
- par->codec_id == AV_CODEC_ID_H264)
- put_avc_eos_tag(pb, sc->last_ts);
+ if (build_keyframes_idx) {
+ FLVFileposition *newflv_posinfo, *p;
+
+ avio_seek(pb, flv->videosize_offset, SEEK_SET);
+ put_amf_double(pb, flv->videosize);
+
+ avio_seek(pb, flv->audiosize_offset, SEEK_SET);
+ put_amf_double(pb, flv->audiosize);
+
+ avio_seek(pb, flv->lasttimestamp_offset, SEEK_SET);
+ put_amf_double(pb, flv->lasttimestamp);
+
+ avio_seek(pb, flv->lastkeyframetimestamp_offset, SEEK_SET);
+ put_amf_double(pb, flv->lastkeyframetimestamp);
+
+ avio_seek(pb, flv->lastkeyframelocation_offset, SEEK_SET);
+ put_amf_double(pb, flv->lastkeyframelocation + flv->keyframe_index_size);
+ avio_seek(pb, cur_pos, SEEK_SET);
+
+ res = shift_data(s);
+ if (res < 0) {
+ goto end;
+ }
+ avio_seek(pb, flv->keyframes_info_offset, SEEK_SET);
+ put_amf_string(pb, "filepositions");
+ put_amf_dword_array(pb, flv->filepositions_count);
+ for (newflv_posinfo = flv->head_filepositions; newflv_posinfo; newflv_posinfo = newflv_posinfo->next) {
+ put_amf_double(pb, newflv_posinfo->keyframe_position + flv->keyframe_index_size);
+ }
+
+ put_amf_string(pb, "times");
+ put_amf_dword_array(pb, flv->filepositions_count);
+ for (newflv_posinfo = flv->head_filepositions; newflv_posinfo; newflv_posinfo = newflv_posinfo->next) {
+ put_amf_double(pb, newflv_posinfo->keyframe_timestamp);
+ }
+
+ newflv_posinfo = flv->head_filepositions;
+ while (newflv_posinfo) {
+ p = newflv_posinfo->next;
+ if (p) {
+ newflv_posinfo->next = p->next;
+ av_free(p);
+ p = NULL;
+ } else {
+ av_free(newflv_posinfo);
+ newflv_posinfo = NULL;
+ }
+ }
+
+ put_amf_string(pb, "");
+ avio_w8(pb, AMF_END_OF_OBJECT);
+
+ avio_seek(pb, cur_pos + flv->keyframe_index_size, SEEK_SET);
+ }
+
+end:
+ if (flv->flags & FLV_NO_SEQUENCE_END) {
+ av_log(s, AV_LOG_DEBUG, "FLV no sequence end mode open\n");
+ } else {
+ /* Add EOS tag */
+ for (i = 0; i < s->nb_streams; i++) {
+ AVCodecParameters *par = s->streams[i]->codecpar;
+ FLVStreamContext *sc = s->streams[i]->priv_data;
+ if (par->codec_type == AVMEDIA_TYPE_VIDEO &&
+ (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4))
+ put_avc_eos_tag(pb, sc->last_ts);
+ }
}
file_size = avio_tell(pb);
- /* update information */
- if (avio_seek(pb, flv->duration_offset, SEEK_SET) < 0)
- av_log(s, AV_LOG_WARNING, "Failed to update header with correct duration.\n");
- else
- put_amf_double(pb, flv->duration / (double)1000);
- if (avio_seek(pb, flv->filesize_offset, SEEK_SET) < 0)
- av_log(s, AV_LOG_WARNING, "Failed to update header with correct filesize.\n");
- else
- put_amf_double(pb, file_size);
+ if (build_keyframes_idx) {
+ flv->datasize = file_size - flv->datastart_offset;
+ avio_seek(pb, flv->datasize_offset, SEEK_SET);
+ put_amf_double(pb, flv->datasize);
+ }
+ if (!(flv->flags & FLV_NO_METADATA)) {
+ if (!(flv->flags & FLV_NO_DURATION_FILESIZE)) {
+ /* update information */
+ if (avio_seek(pb, flv->duration_offset, SEEK_SET) < 0) {
+ av_log(s, AV_LOG_WARNING, "Failed to update header with correct duration.\n");
+ } else {
+ put_amf_double(pb, flv->duration / (double)1000);
+ }
+ if (avio_seek(pb, flv->filesize_offset, SEEK_SET) < 0) {
+ av_log(s, AV_LOG_WARNING, "Failed to update header with correct filesize.\n");
+ } else {
+ put_amf_double(pb, file_size);
+ }
+ }
+ }
- avio_seek(pb, file_size, SEEK_SET);
return 0;
}
@@ -457,16 +876,34 @@ 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;
+ int64_t cur_offset = avio_tell(pb);
if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A ||
- par->codec_id == AV_CODEC_ID_AAC)
+ par->codec_id == AV_CODEC_ID_VP6 || par->codec_id == AV_CODEC_ID_AAC)
flags_size = 2;
- else if (par->codec_id == AV_CODEC_ID_H264)
+ else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4)
flags_size = 5;
else
flags_size = 1;
+ if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264
+ || par->codec_id == AV_CODEC_ID_MPEG4) {
+ int side_size = 0;
+ uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size);
+ if (side && side_size > 0 && (side_size != par->extradata_size || memcmp(side, par->extradata, side_size))) {
+ av_free(par->extradata);
+ par->extradata = av_mallocz(side_size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!par->extradata) {
+ par->extradata_size = 0;
+ return AVERROR(ENOMEM);
+ }
+ memcpy(par->extradata, side, side_size);
+ par->extradata_size = side_size;
+ flv_write_codec_header(s, par);
+ }
+ }
+
if (flv->delay == AV_NOPTS_VALUE)
flv->delay = -pkt->dts;
@@ -476,7 +913,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
return AVERROR(EINVAL);
}
- ts = pkt->dts + flv->delay; // add delay to force positive dts
+ ts = pkt->dts;
if (s->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) {
write_metadata(s, ts);
@@ -497,10 +934,11 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
case AVMEDIA_TYPE_AUDIO:
flags = get_audio_flags(s, par);
- 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;
@@ -508,11 +946,21 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
return AVERROR(EINVAL);
}
- if (par->codec_id == AV_CODEC_ID_H264)
- /* check if extradata looks like MP4 */
+ if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) {
+ /* check if extradata looks like mp4 formatted */
if (par->extradata_size > 0 && *(uint8_t*)par->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 (par->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 (par->codec_id == AV_CODEC_ID_SPEEX && ts - sc->last_ts > 160)
@@ -523,26 +971,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 (par->codec_type == AVMEDIA_TYPE_DATA) {
+ if (par->codec_type == AVMEDIA_TYPE_DATA ||
+ par->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 (par->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);
@@ -550,7 +1011,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 (par->codec_id == AV_CODEC_ID_VP6)
+ avio_w8(pb,0);
if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A) {
if (par->extradata_size)
avio_w8(pb, par->extradata[0]);
@@ -559,7 +1023,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
(FFALIGN(par->height, 16) - par->height));
} else if (par->codec_id == AV_CODEC_ID_AAC)
avio_w8(pb, 1); // AAC raw
- else if (par->codec_id == AV_CODEC_ID_H264) {
+ else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) {
avio_w8(pb, 1); // AVC NALU
avio_wb24(pb, pkt->pts - pkt->dts);
}
@@ -571,11 +1035,54 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
pkt->pts + flv->delay + pkt->duration);
}
+ if (flv->flags & FLV_ADD_KEYFRAME_INDEX) {
+ switch (par->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ flv->videosize += (avio_tell(pb) - cur_offset);
+ flv->lasttimestamp = flv->acurframeindex / flv->framerate;
+ if (pkt->flags & AV_PKT_FLAG_KEY) {
+ double ts = flv->acurframeindex / flv->framerate;
+ int64_t pos = cur_offset;
+
+ flv->lastkeyframetimestamp = flv->acurframeindex / flv->framerate;
+ flv->lastkeyframelocation = pos;
+ flv_append_keyframe_info(s, flv, ts, pos);
+ }
+ flv->acurframeindex++;
+ break;
+
+ case AVMEDIA_TYPE_AUDIO:
+ flv->audiosize += (avio_tell(pb) - cur_offset);
+ break;
+
+ default:
+ av_log(s, AV_LOG_WARNING, "par->codec_type is type = [%d]\n", par->codec_type);
+ break;
+ }
+ }
+
av_free(data);
return pb->error;
}
+static const AVOption options[] = {
+ { "flvflags", "FLV muxer flags", offsetof(FLVContext, flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
+ { "aac_seq_header_detect", "Put AAC sequence header based on stream data", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_AAC_SEQ_HEADER_DETECT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
+ { "no_sequence_end", "disable sequence end for FLV", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_NO_SEQUENCE_END}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
+ { "no_metadata", "disable metadata for FLV", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_NO_METADATA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
+ { "no_duration_filesize", "disable duration and filesize zero value metadata for FLV", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_NO_DURATION_FILESIZE}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
+ { "add_keyframe_index", "Add keyframe index metadata", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_ADD_KEYFRAME_INDEX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
+ { NULL },
+};
+
+static const AVClass flv_muxer_class = {
+ .class_name = "flv muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVOutputFormat ff_flv_muxer = {
.name = "flv",
.long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
@@ -592,4 +1099,5 @@ AVOutputFormat ff_flv_muxer = {
},
.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
AVFMT_TS_NONSTRICT,
+ .priv_class = &flv_muxer_class,
};
diff --git a/libavformat/format.c b/libavformat/format.c
index 24b7205..38ca2a3 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,38 @@ AVOutputFormat *av_oformat_next(const AVOutputFormat *f)
void av_register_input_format(AVInputFormat *format)
{
- AVInputFormat **p = &first_iformat;
+ AVInputFormat **p = last_iformat;
- while (*p)
+ // Note, format could be added after the first 2 checks but that implies that *p is no longer NULL
+ while(p != &format->next && !format->next && avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))
p = &(*p)->next;
- *p = format;
- format->next = NULL;
+ if (!format->next)
+ last_iformat = &format->next;
}
void av_register_output_format(AVOutputFormat *format)
{
- AVOutputFormat **p = &first_oformat;
+ AVOutputFormat **p = last_oformat;
- while (*p)
+ // Note, format could be added after the first 2 checks but that implies that *p is no longer NULL
+ while(p != &format->next && !format->next && avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))
p = &(*p)->next;
- *p = format;
- format->next = NULL;
+ if (!format->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 +114,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 +134,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 +155,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 +170,149 @@ 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, score_max = 0;
+ const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE];
+ enum nodat {
+ NO_ID3,
+ ID3_ALMOST_GREATER_PROBE,
+ ID3_GREATER_PROBE,
+ ID3_GREATER_MAX_PROBE,
+ } nodat = NO_ID3;
+
+ if (!lpd.buf)
+ lpd.buf = (unsigned char *) 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) {
+ if (lpd.buf_size < 2LL*id3len + 16)
+ nodat = ID3_ALMOST_GREATER_PROBE;
lpd.buf += id3len;
lpd.buf_size -= id3len;
- }
- id3 = 1;
+ } else if (id3len >= PROBE_BUF_MAX) {
+ nodat = ID3_GREATER_MAX_PROBE;
+ } else
+ nodat = ID3_GREATER_PROBE;
}
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 (score)
+ av_log(NULL, AV_LOG_TRACE, "Probing %s score:%d size:%d\n", fmt1->name, score, lpd.buf_size);
+ if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {
+ switch (nodat) {
+ case NO_ID3:
+ score = FFMAX(score, 1);
+ break;
+ case ID3_GREATER_PROBE:
+ case ID3_ALMOST_GREATER_PROBE:
+ score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);
+ break;
+ case ID3_GREATER_MAX_PROBE:
+ score = FFMAX(score, AVPROBE_SCORE_EXTENSION);
+ break;
+ }
+ }
} 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)
- 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 (av_match_name(lpd.mime_type, fmt1->mime_type)) {
+ if (AVPROBE_SCORE_MIME > score) {
+ av_log(NULL, AV_LOG_DEBUG, "Probing %s score:%d increased to %d due to MIME type\n", fmt1->name, score, AVPROBE_SCORE_MIME);
+ score = AVPROBE_SCORE_MIME;
}
+ }
+ if (score > score_max) {
+ score_max = score;
+ fmt = fmt1;
+ } else if (score == score_max)
+ fmt = NULL;
}
+ if (nodat == ID3_GREATER_PROBE)
+ 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;
+ char *semi;
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;
+ semi = pd.mime_type ? strchr(pd.mime_type, ';') : NULL;
+ if (semi) {
+ *semi = '\0';
+ }
+ }
+#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 +320,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 +332,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 +353,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 470442e..a567b52 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];
+ AVCodecParameters *par = st->codecpar;
+ if (par->extradata) {
+ uint32_t crc = av_adler32_update(0, par->extradata, par->extradata_size);
+ avio_printf(s->pb, "#extradata %d: %8d, 0x%08"PRIx32"\n",
+ i, par->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", %8"PRId64", %8d, 0x%08"PRIx32"\n",
+ snprintf(buf, sizeof(buf), "%d, %10"PRId64", %10"PRId64", %8"PRId64", %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%08"PRIx32, pkt->side_data[i].size, side_data_crc);
+ }
+ }
+ av_strlcatf(buf, sizeof(buf), "\n");
avio_write(s->pb, buf, strlen(buf));
return 0;
}
@@ -39,10 +79,9 @@ static int framecrc_write_packet(struct AVFormatContext *s, AVPacket *pkt)
AVOutputFormat ff_framecrc_muxer = {
.name = "framecrc",
.long_name = NULL_IF_CONFIG_SMALL("framecrc testing"),
- .extensions = "",
.audio_codec = AV_CODEC_ID_PCM_S16LE,
.video_codec = AV_CODEC_ID_RAWVIDEO,
- .write_header = ff_framehash_write_header,
+ .write_header = framecrc_write_header,
.write_packet = framecrc_write_packet,
.flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT |
AVFMT_TS_NEGATIVE,
diff --git a/libavformat/framehash.c b/libavformat/framehash.c
index 6a6da98..3ae9092 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,9 +23,28 @@
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];
+ AVCodecParameters *avctx = st->codecpar;
+ char buf[256] = { 0 };
avio_printf(s->pb, "#tb %d: %d/%d\n", i, st->time_base.num, st->time_base.den);
+ avio_printf(s->pb, "#media_type %d: %s\n", i, av_get_media_type_string(avctx->codec_type));
+ avio_printf(s->pb, "#codec_id %d: %s\n", i, avcodec_get_name(avctx->codec_id));
+ switch (avctx->codec_type) {
+ case AVMEDIA_TYPE_AUDIO:
+ av_get_channel_layout_string(buf, sizeof(buf), avctx->channels, avctx->channel_layout);
+ avio_printf(s->pb, "#sample_rate %d: %d\n", i,avctx->sample_rate);
+ avio_printf(s->pb, "#channel_layout %d: %"PRIx64"\n", i,avctx->channel_layout);
+ avio_printf(s->pb, "#channel_layout_name %d: %s\n", i, buf);
+ break;
+ case AVMEDIA_TYPE_VIDEO:
+ avio_printf(s->pb, "#dimensions %d: %dx%d\n", i, avctx->width, avctx->height);
+ avio_printf(s->pb, "#sar %d: %d/%d\n", i, st->sample_aspect_ratio.num, st->sample_aspect_ratio.den);
+ break;
+ }
avio_flush(s->pb);
}
return 0;
diff --git a/libavformat/frmdec.c b/libavformat/frmdec.c
new file mode 100644
index 0000000..2f6b726
--- /dev/null
+++ b/libavformat/frmdec.c
@@ -0,0 +1,111 @@
+/*
+ * 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/imgutils.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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
+ avio_skip(pb, 3);
+
+ st->codecpar->format = avpriv_find_pix_fmt(frm_pix_fmt_tags, avio_r8(pb));
+ if (!st->codecpar->format)
+ return AVERROR_INVALIDDATA;
+
+ st->codecpar->codec_tag = 0;
+ st->codecpar->width = avio_rl16(pb);
+ st->codecpar->height = avio_rl16(pb);
+ return 0;
+}
+
+static int frm_read_packet(AVFormatContext *avctx, AVPacket *pkt)
+{
+ FrmContext *s = avctx->priv_data;
+ AVCodecParameters *par = avctx->streams[0]->codecpar;
+ int packet_size, ret;
+
+ if (s->count)
+ return AVERROR_EOF;
+
+ packet_size = av_image_get_buffer_size(par->format, par->width, par->height, 1);
+ if (packet_size < 0)
+ return AVERROR_INVALIDDATA;
+
+ ret = av_get_packet(avctx->pb, pkt, packet_size);
+ if (ret < 0)
+ return ret;
+
+ if (par->format == 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/fsb.c b/libavformat/fsb.c
new file mode 100644
index 0000000..4f59576
--- /dev/null
+++ b/libavformat/fsb.c
@@ -0,0 +1,210 @@
+/*
+ * FSB demuxer
+ * Copyright (c) 2015 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/avassert.h"
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "avio.h"
+#include "internal.h"
+
+static int fsb_probe(AVProbeData *p)
+{
+ if (memcmp(p->buf, "FSB", 3) || p->buf[3] - '0' < 1 || p->buf[3] - '0' > 5)
+ return 0;
+ if (AV_RL32(p->buf + 4) != 1)
+ return 0;
+ return AVPROBE_SCORE_MAX;
+}
+
+static int fsb_read_header(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ unsigned format, version, c;
+ int64_t offset;
+ AVCodecParameters *par;
+ AVStream *st = avformat_new_stream(s, NULL);
+
+ avio_skip(pb, 3); // "FSB"
+ version = avio_r8(pb) - '0';
+ if (version != 4 && version != 3) {
+ avpriv_request_sample(s, "version %d", version);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ avio_skip(pb, 4);
+
+ if (!st)
+ return AVERROR(ENOMEM);
+ par = st->codecpar;
+ par->codec_type = AVMEDIA_TYPE_AUDIO;
+ par->codec_tag = 0;
+
+ if (version == 3) {
+ offset = avio_rl32(pb) + 0x18;
+ avio_skip(pb, 44);
+ st->duration = avio_rl32(pb);
+ avio_skip(pb, 12);
+ format = avio_rl32(pb);
+ par->sample_rate = avio_rl32(pb);
+ if (par->sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, 6);
+ par->channels = avio_rl16(pb);
+ if (!par->channels)
+ return AVERROR_INVALIDDATA;
+
+ if (format & 0x00000100) {
+ par->codec_id = AV_CODEC_ID_PCM_S16LE;
+ par->block_align = 4096 * par->channels;
+ } else if (format & 0x00400000) {
+ par->bits_per_coded_sample = 4;
+ par->codec_id = AV_CODEC_ID_ADPCM_IMA_WAV;
+ par->block_align = 36 * par->channels;
+ } else if (format & 0x00800000) {
+ par->codec_id = AV_CODEC_ID_ADPCM_PSX;
+ par->block_align = 16 * par->channels;
+ } else if (format & 0x02000000) {
+ par->codec_id = AV_CODEC_ID_ADPCM_THP;
+ par->block_align = 8 * par->channels;
+ if (par->channels > INT_MAX / 32)
+ return AVERROR_INVALIDDATA;
+ ff_alloc_extradata(par, 32 * par->channels);
+ if (!par->extradata)
+ return AVERROR(ENOMEM);
+ avio_seek(pb, 0x68, SEEK_SET);
+ for (c = 0; c < par->channels; c++) {
+ avio_read(pb, par->extradata + 32 * c, 32);
+ avio_skip(pb, 14);
+ }
+ } else {
+ avpriv_request_sample(s, "format 0x%X", format);
+ return AVERROR_PATCHWELCOME;
+ }
+ } else if (version == 4) {
+ offset = avio_rl32(pb) + 0x30;
+ avio_skip(pb, 80);
+ st->duration = avio_rl32(pb);
+
+ format = avio_rb32(pb);
+ switch(format) {
+ case 0x40001001:
+ case 0x00001005:
+ case 0x40001081:
+ case 0x40200001:
+ par->codec_id = AV_CODEC_ID_XMA2;
+ break;
+ case 0x40000802:
+ par->codec_id = AV_CODEC_ID_ADPCM_THP;
+ break;
+ default:
+ avpriv_request_sample(s, "format 0x%X", format);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ par->sample_rate = avio_rl32(pb);
+ if (par->sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, 6);
+
+ par->channels = avio_rl16(pb);
+ if (!par->channels)
+ return AVERROR_INVALIDDATA;
+
+ switch (par->codec_id) {
+ case AV_CODEC_ID_XMA2:
+ ff_alloc_extradata(par, 34);
+ if (!par->extradata)
+ return AVERROR(ENOMEM);
+ memset(par->extradata, 0, 34);
+ par->block_align = 2048;
+ break;
+ case AV_CODEC_ID_ADPCM_THP:
+ if (par->channels > INT_MAX / 32)
+ return AVERROR_INVALIDDATA;
+ ff_alloc_extradata(par, 32 * par->channels);
+ if (!par->extradata)
+ return AVERROR(ENOMEM);
+ avio_seek(pb, 0x80, SEEK_SET);
+ for (c = 0; c < par->channels; c++) {
+ avio_read(pb, par->extradata + 32 * c, 32);
+ avio_skip(pb, 14);
+ }
+ par->block_align = 8 * par->channels;
+ break;
+ }
+ } else {
+ av_assert0(0);
+ }
+
+ avio_skip(pb, offset - avio_tell(pb));
+ s->internal->data_offset = avio_tell(pb);
+
+ avpriv_set_pts_info(st, 64, 1, par->sample_rate);
+
+ return 0;
+}
+
+static int fsb_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ int64_t pos;
+ int ret;
+
+ if (avio_feof(s->pb))
+ return AVERROR_EOF;
+
+ pos = avio_tell(s->pb);
+ if (par->codec_id == AV_CODEC_ID_ADPCM_THP &&
+ par->channels > 1) {
+ int i, ch;
+
+ ret = av_new_packet(pkt, par->block_align);
+ if (ret < 0)
+ return ret;
+ for (i = 0; i < 4; i++) {
+ for (ch = 0; ch < par->channels; ch++) {
+ pkt->data[ch * 8 + i * 2 + 0] = avio_r8(s->pb);
+ pkt->data[ch * 8 + i * 2 + 1] = avio_r8(s->pb);
+ }
+ }
+ ret = 0;
+ } else {
+ ret = av_get_packet(s->pb, pkt, par->block_align);
+ }
+
+ if (par->codec_id == AV_CODEC_ID_XMA2 && pkt->size >= 1)
+ pkt->duration = (pkt->data[0] >> 2) * 512;
+
+ pkt->pos = pos;
+ pkt->stream_index = 0;
+
+ return ret;
+}
+
+AVInputFormat ff_fsb_demuxer = {
+ .name = "fsb",
+ .long_name = NULL_IF_CONFIG_SMALL("FMOD Sample Bank"),
+ .read_probe = fsb_probe,
+ .read_header = fsb_read_header,
+ .read_packet = fsb_read_packet,
+ .extensions = "fsb",
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/ftp.c b/libavformat/ftp.c
new file mode 100644
index 0000000..9aa7a45
--- /dev/null
+++ b/libavformat/ftp.c
@@ -0,0 +1,1127 @@
+/*
+ * 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 "libavutil/internal.h"
+#include "libavutil/parseutils.h"
+#include "avformat.h"
+#include "internal.h"
+#include "url.h"
+#include "libavutil/opt.h"
+#include "libavutil/bprint.h"
+
+#define CONTROL_BUFFER_SIZE 1024
+#define DIR_BUFFER_SIZE 4096
+
+typedef enum {
+ UNKNOWN,
+ READY,
+ DOWNLOADING,
+ UPLOADING,
+ LISTING_DIR,
+ DISCONNECTED
+} FTPState;
+
+typedef enum {
+ UNKNOWN_METHOD,
+ NLST,
+ MLSD
+} FTPListingMethod;
+
+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; /**< Server address. */
+ char *user; /**< Server user */
+ char *password; /**< Server user's password */
+ char *path; /**< 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 */
+ FTPListingMethod listing_method; /**< Called listing method */
+ char *features; /**< List of server's features represented as raw response */
+ char *dir_buffer;
+ size_t dir_buffer_size;
+ size_t dir_buffer_offset;
+ int utf8;
+} 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_BOOL, {.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_close(URLContext *h);
+
+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;
+
+ ff_dlog(s, "%s", command);
+
+ if (response)
+ *response = NULL;
+
+ if (!s->conn_control)
+ return AVERROR(EIO);
+
+ 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)
+{
+ char buf[CONTROL_BUFFER_SIZE];
+ int err;
+ static const int user_codes[] = {331, 230, 0};
+ static const int pass_codes[] = {230, 0};
+
+ snprintf(buf, sizeof(buf), "USER %s\r\n", s->user);
+ err = ftp_send_command(s, buf, user_codes, NULL);
+ if (err == 331) {
+ if (s->password) {
+ snprintf(buf, sizeof(buf), "PASS %s\r\n", s->password);
+ 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);
+ ff_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);
+ ff_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;
+
+ *end = '\0';
+ s->path = av_strdup(start);
+
+ av_free(res);
+
+ if (!s->path)
+ return AVERROR(ENOMEM);
+ 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, 125, 0};
+ int resp_code;
+
+ snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
+ resp_code = ftp_send_command(s, command, retr_codes, NULL);
+ if (resp_code != 125 && resp_code != 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, 125, 0};
+ int resp_code;
+
+ snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
+ resp_code = ftp_send_command(s, command, stor_codes, NULL);
+ if (resp_code != 125 && resp_code != 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_set_dir(FTPContext *s)
+{
+ static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
+ char command[MAX_URL_SIZE];
+
+ snprintf(command, sizeof(command), "CWD %s\r\n", s->path);
+ if (ftp_send_command(s, command, cwd_codes, NULL) != 250)
+ return AVERROR(EIO);
+ return 0;
+}
+
+static int ftp_list_mlsd(FTPContext *s)
+{
+ static const char *command = "MLSD\r\n";
+ static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
+
+ if (ftp_send_command(s, command, mlsd_codes, NULL) != 150)
+ return AVERROR(ENOSYS);
+ s->listing_method = MLSD;
+ return 0;
+}
+
+static int ftp_list_nlst(FTPContext *s)
+{
+ static const char *command = "NLST\r\n";
+ static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0};
+
+ if (ftp_send_command(s, command, nlst_codes, NULL) != 226)
+ return AVERROR(ENOSYS);
+ s->listing_method = NLST;
+ return 0;
+}
+
+static int ftp_has_feature(FTPContext *s, const char *feature_name);
+
+static int ftp_list(FTPContext *s)
+{
+ int ret;
+ s->state = LISTING_DIR;
+
+ if ((ret = ftp_list_mlsd(s)) < 0)
+ ret = ftp_list_nlst(s);
+
+ return ret;
+}
+
+static int ftp_has_feature(FTPContext *s, const char *feature_name)
+{
+ if (!s->features)
+ return 0;
+
+ return av_stristr(s->features, feature_name) != NULL;
+}
+
+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, 0};
+
+ av_freep(&s->features);
+ if (ftp_send_command(s, feat_command, feat_codes, &s->features) != 211) {
+ av_freep(&s->features);
+ }
+
+ if (ftp_has_feature(s, "UTF8")) {
+ if (ftp_send_command(s, enable_utf8_command, opts_codes, NULL) == 200)
+ s->utf8 = 1;
+ }
+
+ 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_whitelist(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, &opts,
+ h->protocol_whitelist, h->protocol_blacklist, h);
+ 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_whitelist(&s->conn_data, buf, h->flags,
+ &h->interrupt_callback, &opts,
+ h->protocol_whitelist, h->protocol_blacklist, h);
+ 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_connect(URLContext *h, const char *url)
+{
+ char proto[10], path[MAX_URL_SIZE], credencials[MAX_URL_SIZE], hostname[MAX_URL_SIZE];
+ const char *tok_user = NULL, *tok_pass = NULL;
+ char *end = NULL, *newpath = NULL;
+ int err;
+ FTPContext *s = h->priv_data;
+
+ s->state = DISCONNECTED;
+ s->listing_method = UNKNOWN_METHOD;
+ s->filesize = -1;
+ s->position = 0;
+ s->features = NULL;
+
+ av_url_split(proto, sizeof(proto),
+ credencials, sizeof(credencials),
+ hostname, sizeof(hostname),
+ &s->server_control_port,
+ path, sizeof(path),
+ url);
+
+ tok_user = av_strtok(credencials, ":", &end);
+ tok_pass = av_strtok(end, ":", &end);
+ if (!tok_user) {
+ tok_user = "anonymous";
+ tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
+ }
+ s->user = av_strdup(tok_user);
+ s->password = av_strdup(tok_pass);
+ s->hostname = av_strdup(hostname);
+ if (!s->hostname || !s->user || (tok_pass && !s->password)) {
+ return AVERROR(ENOMEM);
+ }
+
+ if (s->server_control_port < 0 || s->server_control_port > 65535)
+ s->server_control_port = 21;
+
+ if ((err = ftp_connect_control_connection(h)) < 0)
+ return err;
+
+ if ((err = ftp_current_dir(s)) < 0)
+ return err;
+
+ newpath = av_append_path_component(s->path, path);
+ if (!newpath)
+ return AVERROR(ENOMEM);
+ av_free(s->path);
+ s->path = newpath;
+
+ return 0;
+}
+
+static int ftp_open(URLContext *h, const char *url, int flags)
+{
+ FTPContext *s = h->priv_data;
+ int err;
+
+ ff_dlog(h, "ftp protocol open\n");
+
+ if ((err = ftp_connect(h, url)) < 0)
+ goto fail;
+
+ 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");
+ ftp_close(h);
+ 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;
+
+ ff_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;
+
+ ff_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;
+
+ ff_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)
+{
+ FTPContext *s = h->priv_data;
+
+ ff_dlog(h, "ftp protocol close\n");
+
+ ftp_close_both_connections(s);
+ av_freep(&s->user);
+ av_freep(&s->password);
+ av_freep(&s->hostname);
+ av_freep(&s->path);
+ av_freep(&s->features);
+
+ return 0;
+}
+
+static int ftp_get_file_handle(URLContext *h)
+{
+ FTPContext *s = h->priv_data;
+
+ ff_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;
+
+ ff_dlog(h, "ftp protocol shutdown\n");
+
+ if (s->conn_data)
+ return ffurl_shutdown(s->conn_data, flags);
+
+ return AVERROR(EIO);
+}
+
+static int ftp_open_dir(URLContext *h)
+{
+ FTPContext *s = h->priv_data;
+ int ret;
+
+ if ((ret = ftp_connect(h, h->filename)) < 0)
+ goto fail;
+ if ((ret = ftp_set_dir(s)) < 0)
+ goto fail;
+ if ((ret = ftp_connect_data_connection(h)) < 0)
+ goto fail;
+ if ((ret = ftp_list(s)) < 0)
+ goto fail;
+ s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
+ if (!s->dir_buffer) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ s->dir_buffer[0] = 0;
+ if (s->conn_data && s->state == LISTING_DIR)
+ return 0;
+ fail:
+ ffurl_closep(&s->conn_control);
+ ffurl_closep(&s->conn_data);
+ return ret;
+}
+
+static int64_t ftp_parse_date(const char *date)
+{
+ struct tm tv;
+ memset(&tv, 0, sizeof(struct tm));
+ av_small_strptime(date, "%Y%m%d%H%M%S", &tv);
+ return INT64_C(1000000) * av_timegm(&tv);
+}
+
+static int ftp_parse_entry_nlst(char *line, AVIODirEntry *next)
+{
+ next->name = av_strdup(line);
+ return 0;
+}
+
+static int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next)
+{
+ char *fact, *value;
+ ff_dlog(NULL, "%s\n", mlsd);
+ while(fact = av_strtok(mlsd, ";", &mlsd)) {
+ if (fact[0] == ' ') {
+ next->name = av_strdup(&fact[1]);
+ continue;
+ }
+ fact = av_strtok(fact, "=", &value);
+ if (!av_strcasecmp(fact, "type")) {
+ if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
+ return 1;
+ if (!av_strcasecmp(value, "dir"))
+ next->type = AVIO_ENTRY_DIRECTORY;
+ else if (!av_strcasecmp(value, "file"))
+ next->type = AVIO_ENTRY_FILE;
+ else if (!av_strcasecmp(value, "OS.unix=slink:"))
+ next->type = AVIO_ENTRY_SYMBOLIC_LINK;
+ } else if (!av_strcasecmp(fact, "modify")) {
+ next->modification_timestamp = ftp_parse_date(value);
+ } else if (!av_strcasecmp(fact, "UNIX.mode")) {
+ next->filemode = strtoumax(value, NULL, 8);
+ } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner"))
+ next->user_id = strtoumax(value, NULL, 10);
+ else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group"))
+ next->group_id = strtoumax(value, NULL, 10);
+ else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd"))
+ next->size = strtoll(value, NULL, 10);
+ }
+ return 0;
+}
+
+/**
+ * @return 0 on success, negative on error, positive on entry to discard.
+ */
+static int ftp_parse_entry(URLContext *h, char *line, AVIODirEntry *next)
+{
+ FTPContext *s = h->priv_data;
+
+ switch (s->listing_method) {
+ case MLSD:
+ return ftp_parse_entry_mlsd(line, next);
+ case NLST:
+ return ftp_parse_entry_nlst(line, next);
+ case UNKNOWN_METHOD:
+ default:
+ return -1;
+ }
+}
+
+static int ftp_read_dir(URLContext *h, AVIODirEntry **next)
+{
+ FTPContext *s = h->priv_data;
+ char *start, *found;
+ int ret, retried;
+
+ do {
+ retried = 0;
+ start = s->dir_buffer + s->dir_buffer_offset;
+ while (!(found = strstr(start, "\n"))) {
+ if (retried)
+ return AVERROR(EIO);
+ s->dir_buffer_size -= s->dir_buffer_offset;
+ s->dir_buffer_offset = 0;
+ if (s->dir_buffer_size)
+ memmove(s->dir_buffer, start, s->dir_buffer_size);
+ ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1));
+ if (ret < 0)
+ return ret;
+ if (!ret) {
+ *next = NULL;
+ return 0;
+ }
+ s->dir_buffer_size += ret;
+ s->dir_buffer[s->dir_buffer_size] = 0;
+ start = s->dir_buffer;
+ retried = 1;
+ }
+ s->dir_buffer_offset += (found + 1 - start);
+ found[0] = 0;
+ if (found > start && found[-1] == '\r')
+ found[-1] = 0;
+
+ *next = ff_alloc_dir_entry();
+ if (!*next)
+ return AVERROR(ENOMEM);
+ (*next)->utf8 = s->utf8;
+ ret = ftp_parse_entry(h, start, *next);
+ if (ret) {
+ avio_free_directory_entry(next);
+ if (ret < 0)
+ return ret;
+ }
+ } while (ret > 0);
+ return 0;
+}
+
+static int ftp_close_dir(URLContext *h)
+{
+ FTPContext *s = h->priv_data;
+ av_freep(&s->dir_buffer);
+ ffurl_closep(&s->conn_control);
+ ffurl_closep(&s->conn_data);
+ return 0;
+}
+
+static int ftp_delete(URLContext *h)
+{
+ FTPContext *s = h->priv_data;
+ char command[MAX_URL_SIZE];
+ static const int del_codes[] = {250, 421, 450, 500, 501, 502, 530, 550, 0};
+ static const int rmd_codes[] = {250, 421, 500, 501, 502, 530, 550, 0};
+ int ret;
+
+ if ((ret = ftp_connect(h, h->filename)) < 0)
+ goto cleanup;
+
+ snprintf(command, sizeof(command), "DELE %s\r\n", s->path);
+ if (ftp_send_command(s, command, del_codes, NULL) == 250) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ snprintf(command, sizeof(command), "RMD %s\r\n", s->path);
+ if (ftp_send_command(s, command, rmd_codes, NULL) == 250)
+ ret = 0;
+ else
+ ret = AVERROR(EIO);
+
+cleanup:
+ ftp_close(h);
+ return ret;
+}
+
+static int ftp_move(URLContext *h_src, URLContext *h_dst)
+{
+ FTPContext *s = h_src->priv_data;
+ char command[MAX_URL_SIZE], path[MAX_URL_SIZE];
+ static const int rnfr_codes[] = {350, 421, 450, 500, 501, 502, 503, 530, 0};
+ static const int rnto_codes[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0};
+ int ret;
+
+ if ((ret = ftp_connect(h_src, h_src->filename)) < 0)
+ goto cleanup;
+
+ snprintf(command, sizeof(command), "RNFR %s\r\n", s->path);
+ if (ftp_send_command(s, command, rnfr_codes, NULL) != 350) {
+ ret = AVERROR(EIO);
+ goto cleanup;
+ }
+
+ av_url_split(0, 0, 0, 0, 0, 0, 0,
+ path, sizeof(path),
+ h_dst->filename);
+ snprintf(command, sizeof(command), "RNTO %s\r\n", path);
+ if (ftp_send_command(s, command, rnto_codes, NULL) == 250)
+ ret = 0;
+ else
+ ret = AVERROR(EIO);
+
+cleanup:
+ ftp_close(h_src);
+ return ret;
+}
+
+const 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,
+ .url_open_dir = ftp_open_dir,
+ .url_read_dir = ftp_read_dir,
+ .url_close_dir = ftp_close_dir,
+ .url_delete = ftp_delete,
+ .url_move = ftp_move,
+ .flags = URL_PROTOCOL_FLAG_NETWORK,
+ .default_whitelist = "tcp",
+};
diff --git a/libavformat/g722.c b/libavformat/g722.c
index 7e60e4e..2feec01 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->codecpar->bits_per_coded_sample =
av_get_bits_per_sample(st->codecpar->codec_id);
- assert(st->codecpar->bits_per_coded_sample > 0);
+ av_assert0(st->codecpar->bits_per_coded_sample > 0);
avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
return 0;
diff --git a/libavformat/g723_1.c b/libavformat/g723_1.c
index 9eca3f9..27c8c39 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/g726.c b/libavformat/g726.c
new file mode 100644
index 0000000..eca9404
--- /dev/null
+++ b/libavformat/g726.c
@@ -0,0 +1,105 @@
+/*
+ * G.726 raw demuxer
+ * Copyright 2017 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 "internal.h"
+#include "libavutil/opt.h"
+
+typedef struct G726Context {
+ AVClass *class;
+ int code_size;
+ int sample_rate;
+} G726Context;
+
+static int g726_read_header(AVFormatContext *s)
+{
+ G726Context *c = s->priv_data;
+ AVStream *st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = s->iformat->raw_codec_id;
+
+ st->codecpar->sample_rate = c->sample_rate;
+ st->codecpar->bits_per_coded_sample = c->code_size;
+ st->codecpar->bit_rate = ((int[]){ 16000, 24000, 32000, 40000 })[c->code_size - 2];
+ st->codecpar->channels = 1;
+
+ return 0;
+}
+
+static int g726_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ int res;
+ res = av_get_packet(s->pb, pkt, 1020); // a size similar to RAW_PACKET_SIZE divisible by all code_size values
+ if (res < 0)
+ return res;
+ return 0;
+}
+
+#define OFFSET(x) offsetof(G726Context, x)
+static const AVOption options[] = {
+ { "code_size", "Bits per G.726 code",
+ OFFSET(code_size), AV_OPT_TYPE_INT, {.i64 = 4}, 2, 5, AV_OPT_FLAG_DECODING_PARAM },
+ { "sample_rate", "",
+ OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64 = 8000}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+#if CONFIG_G726_DEMUXER
+static const AVClass g726le_demuxer_class = {
+ .class_name = "G.726 big-endian demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_g726_demuxer = {
+ .name = "g726",
+ .long_name = NULL_IF_CONFIG_SMALL("raw big-endian G.726 (\"left aligned\")"),
+ .read_header = g726_read_header,
+ .read_packet = g726_read_packet,
+ .priv_data_size = sizeof(G726Context),
+ .priv_class = &g726le_demuxer_class,
+ .raw_codec_id = AV_CODEC_ID_ADPCM_G726,
+};
+#endif
+
+#if CONFIG_G726LE_DEMUXER
+static const AVClass g726_demuxer_class = {
+ .class_name = "G.726 little-endian demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_g726le_demuxer = {
+ .name = "g726le",
+ .long_name = NULL_IF_CONFIG_SMALL("raw little-endian G.726 (\"right aligned\")"),
+ .read_header = g726_read_header,
+ .read_packet = g726_read_packet,
+ .priv_data_size = sizeof(G726Context),
+ .priv_class = &g726_demuxer_class,
+ .raw_codec_id = AV_CODEC_ID_ADPCM_G726LE,
+};
+#endif
+
diff --git a/libavformat/g729dec.c b/libavformat/g729dec.c
index c2beb60..7b67fc4 100644
--- a/libavformat/g729dec.c
+++ b/libavformat/g729dec.c
@@ -2,20 +2,20 @@
* G.729 raw format demuxer
* Copyright (c) 2011 Vladimir Voroshilov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -56,7 +56,7 @@ static int g729_read_header(AVFormatContext *s)
st->codecpar->block_align = 10;
break;
default:
- av_log(s, AV_LOG_ERROR, "Invalid bit_rate value %d. "
+ av_log(s, AV_LOG_ERROR, "Invalid bit_rate value %"PRId64". "
"Only 6400 and 8000 b/s are supported.", s->bit_rate);
return AVERROR(EINVAL);
}
diff --git a/libavformat/gdv.c b/libavformat/gdv.c
new file mode 100644
index 0000000..a69c349
--- /dev/null
+++ b/libavformat/gdv.c
@@ -0,0 +1,201 @@
+/*
+ * Gremlin Digital Video demuxer
+ * Copyright (c) 2017 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.h"
+#include "internal.h"
+
+typedef struct GDVContext {
+ int is_first_video;
+ int is_audio;
+ int audio_size;
+ int audio_stream_index;
+ int video_stream_index;
+ unsigned pal[256];
+} GDVContext;
+
+static int gdv_read_probe(AVProbeData *p)
+{
+ if (AV_RL32(p->buf) == 0x29111994)
+ return AVPROBE_SCORE_MAX;
+
+ return 0;
+}
+
+static struct {
+ uint16_t id;
+ uint16_t width;
+ uint16_t height;
+} FixedSize[] = {
+ { 0, 320, 200},
+ { 1, 640, 200},
+ { 2, 320, 167},
+ { 3, 320, 180},
+ { 4, 320, 400},
+ { 5, 320, 170},
+ { 6, 160, 85},
+ { 7, 160, 83},
+ { 8, 160, 90},
+ { 9, 280, 128},
+ {10, 320, 240},
+ {11, 320, 201},
+ {16, 640, 400},
+ {17, 640, 200},
+ {18, 640, 180},
+ {19, 640, 167},
+ {20, 640, 170},
+ {21, 320, 240}
+};
+
+static int gdv_read_header(AVFormatContext *ctx)
+{
+ GDVContext *gdv = ctx->priv_data;
+ AVIOContext *pb = ctx->pb;
+ AVStream *vst, *ast;
+ unsigned fps, snd_flags, vid_depth, size_id;
+
+ avio_skip(pb, 4);
+ size_id = avio_rl16(pb);
+
+ vst = avformat_new_stream(ctx, 0);
+ if (!vst)
+ return AVERROR(ENOMEM);
+
+ vst->start_time = 0;
+ vst->duration =
+ vst->nb_frames = avio_rl16(pb);
+
+ fps = avio_rl16(pb);
+ snd_flags = avio_rl16(pb);
+ if (snd_flags & 1) {
+ ast = avformat_new_stream(ctx, 0);
+ if (!ast)
+ return AVERROR(ENOMEM);
+
+ ast->start_time = 0;
+ ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ ast->codecpar->codec_tag = 0;
+ ast->codecpar->sample_rate = avio_rl16(pb);
+ ast->codecpar->channels = 1 + !!(snd_flags & 2);
+ if (snd_flags & 8) {
+ ast->codecpar->codec_id = AV_CODEC_ID_GREMLIN_DPCM;
+ } else {
+ ast->codecpar->codec_id = (snd_flags & 4) ? AV_CODEC_ID_PCM_S16LE : AV_CODEC_ID_PCM_U8;
+ }
+
+ avpriv_set_pts_info(ast, 64, 1, ast->codecpar->sample_rate);
+ gdv->audio_size = (ast->codecpar->sample_rate / fps) *
+ ast->codecpar->channels * (1 + !!(snd_flags & 4)) / (1 + !!(snd_flags & 8));
+ gdv->is_audio = 1;
+ } else {
+ avio_skip(pb, 2);
+ }
+ vid_depth = avio_rl16(pb);
+ avio_skip(pb, 4);
+
+ vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ vst->codecpar->codec_id = AV_CODEC_ID_GDV;
+ vst->codecpar->codec_tag = 0;
+ vst->codecpar->width = avio_rl16(pb);
+ vst->codecpar->height = avio_rl16(pb);
+
+ if (vst->codecpar->width == 0 || vst->codecpar->height == 0) {
+ int i;
+
+ for (i = 0; i < FF_ARRAY_ELEMS(FixedSize) - 1; i++) {
+ if (FixedSize[i].id == size_id)
+ break;
+ }
+
+ vst->codecpar->width = FixedSize[i].width;
+ vst->codecpar->height = FixedSize[i].height;
+ }
+
+ avpriv_set_pts_info(vst, 64, 1, fps);
+
+ if (vid_depth & 1) {
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ unsigned r = avio_r8(pb);
+ unsigned g = avio_r8(pb);
+ unsigned b = avio_r8(pb);
+ gdv->pal[i] = 0xFFU << 24 | r << 18 | g << 10 | b << 2;
+ }
+ }
+
+ gdv->is_first_video = 1;
+
+ return 0;
+}
+
+static int gdv_read_packet(AVFormatContext *ctx, AVPacket *pkt)
+{
+ GDVContext *gdv = ctx->priv_data;
+ AVIOContext *pb = ctx->pb;
+ int ret;
+
+ if (avio_feof(pb))
+ return pb->error ? pb->error : AVERROR_EOF;
+
+ if (gdv->audio_size && gdv->is_audio) {
+ ret = av_get_packet(pb, pkt, gdv->audio_size);
+ if (ret < 0)
+ return ret;
+ pkt->stream_index = 1;
+ gdv->is_audio = 0;
+ } else {
+ uint8_t *pal;
+
+ if (avio_rl16(pb) != 0x1305)
+ return AVERROR_INVALIDDATA;
+ ret = av_get_packet(pb, pkt, 4 + avio_rl16(pb));
+ if (ret < 0)
+ return ret;
+ pkt->stream_index = 0;
+ gdv->is_audio = 1;
+
+ if (gdv->is_first_video) {
+ pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE,
+ AVPALETTE_SIZE);
+ if (!pal) {
+ av_packet_unref(pkt);
+ return AVERROR(ENOMEM);
+ }
+ memcpy(pal, gdv->pal, AVPALETTE_SIZE);
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ gdv->is_first_video = 0;
+ }
+ }
+
+ return 0;
+}
+
+AVInputFormat ff_gdv_demuxer = {
+ .name = "gdv",
+ .long_name = NULL_IF_CONFIG_SMALL("Gremlin Digital Video"),
+ .priv_data_size = sizeof(GDVContext),
+ .read_probe = gdv_read_probe,
+ .read_header = gdv_read_header,
+ .read_packet = gdv_read_packet,
+};
diff --git a/libavformat/genh.c b/libavformat/genh.c
new file mode 100644
index 0000000..dd4e76d
--- /dev/null
+++ b/libavformat/genh.c
@@ -0,0 +1,195 @@
+/*
+ * GENH demuxer
+ * Copyright (c) 2015 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/internal.h"
+#include "avformat.h"
+#include "internal.h"
+
+typedef struct GENHDemuxContext {
+ unsigned dsp_int_type;
+ unsigned interleave_size;
+} GENHDemuxContext;
+
+static int genh_probe(AVProbeData *p)
+{
+ if (AV_RL32(p->buf) != MKTAG('G','E','N','H'))
+ return 0;
+ if (AV_RL32(p->buf+4) <= 0 || AV_RL32(p->buf+4) > 0xFFFF) // channels
+ return 0;
+
+ return AVPROBE_SCORE_MAX / 3 * 2;
+}
+
+static int genh_read_header(AVFormatContext *s)
+{
+ unsigned start_offset, header_size, codec, coef_type, coef[2];
+ GENHDemuxContext *c = s->priv_data;
+ av_unused unsigned coef_splitted[2];
+ int align, ch, ret;
+ AVStream *st;
+
+ avio_skip(s->pb, 4);
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->channels = avio_rl32(s->pb);
+ if (st->codecpar->channels <= 0 || st->codecpar->channels > FF_SANE_NB_CHANNELS)
+ return AVERROR_INVALIDDATA;
+ if (st->codecpar->channels == 1)
+ st->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
+ else if (st->codecpar->channels == 2)
+ st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
+ align =
+ c->interleave_size = avio_rl32(s->pb);
+ if (align < 0 || align > INT_MAX / st->codecpar->channels)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->block_align = align * st->codecpar->channels;
+ st->codecpar->sample_rate = avio_rl32(s->pb);
+ avio_skip(s->pb, 4);
+ st->duration = avio_rl32(s->pb);
+
+ codec = avio_rl32(s->pb);
+ switch (codec) {
+ case 0: st->codecpar->codec_id = AV_CODEC_ID_ADPCM_PSX; break;
+ case 1:
+ case 11: st->codecpar->bits_per_coded_sample = 4;
+ st->codecpar->block_align = 36 * st->codecpar->channels;
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_WAV; break;
+ case 2: st->codecpar->codec_id = AV_CODEC_ID_ADPCM_DTK; break;
+ case 3: st->codecpar->codec_id = st->codecpar->block_align > 0 ?
+ AV_CODEC_ID_PCM_S16BE_PLANAR :
+ AV_CODEC_ID_PCM_S16BE; break;
+ case 4: st->codecpar->codec_id = st->codecpar->block_align > 0 ?
+ AV_CODEC_ID_PCM_S16LE_PLANAR :
+ AV_CODEC_ID_PCM_S16LE; break;
+ case 5: st->codecpar->codec_id = st->codecpar->block_align > 0 ?
+ AV_CODEC_ID_PCM_S8_PLANAR :
+ AV_CODEC_ID_PCM_S8; break;
+ case 6: st->codecpar->codec_id = AV_CODEC_ID_SDX2_DPCM; break;
+ case 7: ret = ff_alloc_extradata(st->codecpar, 2);
+ if (ret < 0)
+ return ret;
+ AV_WL16(st->codecpar->extradata, 3);
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_WS; break;
+ case 10: st->codecpar->codec_id = AV_CODEC_ID_ADPCM_AICA; break;
+ case 12: st->codecpar->codec_id = AV_CODEC_ID_ADPCM_THP; break;
+ case 13: st->codecpar->codec_id = AV_CODEC_ID_PCM_U8; break;
+ case 17: st->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_QT; break;
+ default:
+ avpriv_request_sample(s, "codec %d", codec);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ start_offset = avio_rl32(s->pb);
+ header_size = avio_rl32(s->pb);
+
+ if (header_size > start_offset)
+ return AVERROR_INVALIDDATA;
+
+ if (header_size == 0)
+ start_offset = 0x800;
+
+ coef[0] = avio_rl32(s->pb);
+ coef[1] = avio_rl32(s->pb);
+ c->dsp_int_type = avio_rl32(s->pb);
+ coef_type = avio_rl32(s->pb);
+ coef_splitted[0] = avio_rl32(s->pb);
+ coef_splitted[1] = avio_rl32(s->pb);
+
+ if (st->codecpar->codec_id == AV_CODEC_ID_ADPCM_THP) {
+ if (st->codecpar->channels > 2) {
+ avpriv_request_sample(s, "channels %d>2", st->codecpar->channels);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ ff_alloc_extradata(st->codecpar, 32 * st->codecpar->channels);
+ for (ch = 0; ch < st->codecpar->channels; ch++) {
+ if (coef_type & 1) {
+ avpriv_request_sample(s, "coef_type & 1");
+ return AVERROR_PATCHWELCOME;
+ } else {
+ avio_seek(s->pb, coef[ch], SEEK_SET);
+ avio_read(s->pb, st->codecpar->extradata + 32 * ch, 32);
+ }
+ }
+
+ if (c->dsp_int_type == 1) {
+ st->codecpar->block_align = 8 * st->codecpar->channels;
+ if (c->interleave_size != 1 &&
+ c->interleave_size != 2 &&
+ c->interleave_size != 4)
+ return AVERROR_INVALIDDATA;
+ }
+ }
+
+ avio_skip(s->pb, start_offset - avio_tell(s->pb));
+
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ return 0;
+}
+
+static int genh_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ GENHDemuxContext *c = s->priv_data;
+ int ret;
+
+ if (c->dsp_int_type == 1 && par->codec_id == AV_CODEC_ID_ADPCM_THP &&
+ par->channels > 1) {
+ int i, ch;
+
+ if (avio_feof(s->pb))
+ return AVERROR_EOF;
+ ret = av_new_packet(pkt, 8 * par->channels);
+ if (ret < 0)
+ return ret;
+ for (i = 0; i < 8 / c->interleave_size; i++) {
+ for (ch = 0; ch < par->channels; ch++) {
+ pkt->data[ch * 8 + i*c->interleave_size+0] = avio_r8(s->pb);
+ pkt->data[ch * 8 + i*c->interleave_size+1] = avio_r8(s->pb);
+ }
+ }
+ ret = 0;
+ } else if (par->codec_id == AV_CODEC_ID_SDX2_DPCM) {
+ ret = av_get_packet(s->pb, pkt, par->block_align * 1024);
+
+ } else {
+ ret = av_get_packet(s->pb, pkt, par->block_align ? par->block_align : 1024 * par->channels);
+ }
+
+ pkt->stream_index = 0;
+ return ret;
+}
+
+AVInputFormat ff_genh_demuxer = {
+ .name = "genh",
+ .long_name = NULL_IF_CONFIG_SMALL("GENeric Header"),
+ .priv_data_size = sizeof(GENHDemuxContext),
+ .read_probe = genh_probe,
+ .read_header = genh_read_header,
+ .read_packet = genh_read_packet,
+ .extensions = "genh",
+};
diff --git a/libavformat/gif.c b/libavformat/gif.c
index e7fa37c..01d98a2 100644
--- a/libavformat/gif.c
+++ b/libavformat/gif.c
@@ -4,348 +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, AVStream *st,
+ 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 = st->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, st->codecpar->width);
+ avio_wl16(pb, st->codecpar->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;
- AVCodecParameters *par, *video_par;
- int i, width, height /*, rate*/;
+ AVCodecParameters *video_par;
+ 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_par = NULL;
- for (i = 0; i < s->nb_streams; i++) {
- par = s->streams[i]->codecpar;
- if (par->codec_type != AVMEDIA_TYPE_AUDIO)
- video_par = par;
+ if (s->nb_streams != 1 ||
+ s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO ||
+ s->streams[0]->codecpar->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_par) {
- av_free(gif);
- return -1;
- } else {
- width = video_par->width;
- height = video_par->height;
-// rate = video_enc->time_base.den;
- }
+ video_par = s->streams[0]->codecpar;
- if (video_par->format != 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_par->format) < 0) {
+ av_assert0(video_par->format == AV_PIX_FMT_PAL8);
+ /* delay header writing: we wait for the first palette to put it
+ * globally */
+ } else {
+ gif_image_write_header(s->pb, s->streams[0], 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, AVStream *st,
- const uint8_t *buf, int size)
+static int flush_packet(AVFormatContext *s, AVPacket *new)
{
- AVCodecParameters *par = st->codecpar;
+ 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 * st->time_base.num / st->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, par->width, par->height,
- buf, par->width * 3, AV_PIX_FMT_RGB24);
+ avio_write(pb, pkt->data, pkt->size);
+
+ av_packet_unref(gif->prev_pkt);
+ if (new)
+ av_packet_ref(gif->prev_pkt, new);
return 0;
}
static int gif_write_packet(AVFormatContext *s, AVPacket *pkt)
{
- AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar;
- if (par->codec_type == AVMEDIA_TYPE_AUDIO)
- return 0; /* just ignore audio */
- else
- return gif_write_video(s, s->streams[pkt->stream_index], pkt->data, pkt->size);
+ GIFContext *gif = s->priv_data;
+ AVStream *video_st = s->streams[0];
+
+ if (!gif->prev_pkt) {
+ gif->prev_pkt = av_packet_alloc();
+ if (!gif->prev_pkt)
+ return AVERROR(ENOMEM);
+
+ /* Write the first palette as global palette */
+ if (video_st->codecpar->format == 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_st, gif->loop, palette);
+ }
+
+ return av_packet_ref(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;
@@ -354,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 },
};
@@ -373,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 0000000..8993ca6
--- /dev/null
+++ b/libavformat/gifdec.c
@@ -0,0 +1,346 @@
+/*
+ * 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 max_delay;
+ int default_delay;
+
+ /**
+ * loop options
+ */
+ int total_iter;
+ int iter_count;
+ int ignore_loop;
+
+ int nb_frames;
+ int last_duration;
+} 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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_id = AV_CODEC_ID_GIF;
+ st->codecpar->width = width;
+ st->codecpar->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;
+ gdc->delay = FFMIN(gdc->delay, gdc->max_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;
+
+ gdc->nb_frames ++;
+ gdc->last_duration = pkt->duration;
+
+ /* 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) {
+ if (gdc->nb_frames == 1) {
+ s->streams[0]->r_frame_rate = (AVRational) {100, gdc->last_duration};
+ }
+ /* 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 },
+ { "max_gif_delay", "maximum valid delay between frames (in hundredths of seconds)", offsetof(GIFDemuxContext, max_delay) , AV_OPT_TYPE_INT, {.i64 = 65535} , 0, 65535 , 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_BOOL,{.i64 = 1} , 0, 1, AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+static const AVClass demuxer_class = {
+ .class_name = "GIF demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+ .category = AV_CLASS_CATEGORY_DEMUXER,
+};
+
+AVInputFormat ff_gif_demuxer = {
+ .name = "gif",
+ .long_name = NULL_IF_CONFIG_SMALL("CompuServe Graphics Interchange Format (GIF)"),
+ .priv_data_size = sizeof(GIFDemuxContext),
+ .read_probe = gif_probe,
+ .read_header = gif_read_header,
+ .read_packet = gif_read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+ .priv_class = &demuxer_class,
+};
diff --git a/libavformat/golomb_tab.c b/libavformat/golomb_tab.c
new file mode 100644
index 0000000..063fae3
--- /dev/null
+++ b/libavformat/golomb_tab.c
@@ -0,0 +1 @@
+#include "libavcodec/golomb.c"
diff --git a/libavformat/gopher.c b/libavformat/gopher.c
index 6d9fc38..3070b24 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
*/
@@ -93,8 +93,8 @@ static int gopher_open(URLContext *h, const char *uri, int flags)
ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL);
s->hd = NULL;
- err = ffurl_open(&s->hd, buf, AVIO_FLAG_READ_WRITE,
- &h->interrupt_callback, NULL, h->protocols, h);
+ err = ffurl_open_whitelist(&s->hd, buf, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist, h);
if (err < 0)
goto fail;
diff --git a/libavformat/gsmdec.c b/libavformat/gsmdec.c
index d13327d..1627106 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
*/
@@ -34,6 +34,23 @@ typedef struct GSMDemuxerContext {
int sample_rate;
} GSMDemuxerContext;
+static int gsm_probe(AVProbeData *p)
+{
+ int valid = 0, invalid = 0;
+ uint8_t *b = p->buf;
+ while (b < p->buf + p->buf_size - 32) {
+ if ((*b & 0xf0) == 0xd0) {
+ valid++;
+ } else {
+ invalid++;
+ }
+ b += 33;
+ }
+ if (valid >> 5 > invalid)
+ return AVPROBE_SCORE_EXTENSION + 1;
+ return 0;
+}
+
static int gsm_read_packet(AVFormatContext *s, AVPacket *pkt)
{
int ret, size;
@@ -48,7 +65,6 @@ static int gsm_read_packet(AVFormatContext *s, AVPacket *pkt)
av_packet_unref(pkt);
return ret < 0 ? ret : AVERROR(EIO);
}
- pkt->size = ret;
pkt->duration = 1;
pkt->pts = pkt->pos / GSM_BLOCK_SIZE;
@@ -81,7 +97,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,
@@ -92,10 +108,11 @@ AVInputFormat ff_gsm_demuxer = {
.name = "gsm",
.long_name = NULL_IF_CONFIG_SMALL("raw GSM"),
.priv_data_size = sizeof(GSMDemuxerContext),
+ .read_probe = gsm_probe,
.read_header = gsm_read_header,
.read_packet = gsm_read_packet,
.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 e6ebb27..399f745 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->codecpar->codec_id = AV_CODEC_ID_MJPEG;
break;
case 13:
- case 15:
- st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
- st->codecpar->codec_id = AV_CODEC_ID_DVVIDEO;
- break;
case 14:
+ case 15:
case 16:
+ case 25:
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->codec_id = AV_CODEC_ID_DVVIDEO;
break;
@@ -145,6 +165,12 @@ static int get_sindex(AVFormatContext *s, int id, int format) {
st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
st->codecpar->sample_rate = 48000;
break;
+ case 26: /* AVCi50 / AVCi100 (AVC Intra) */
+ case 29: /* AVCHD */
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->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->codecpar->codec_type = AVMEDIA_TYPE_DATA;
st->codecpar->codec_id = AV_CODEC_ID_NONE;
break;
+ case 30:
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_id = AV_CODEC_ID_DNXHD;
+ break;
default:
st->codecpar->codec_type = AVMEDIA_TYPE_UNKNOWN;
st->codecpar->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,11 +556,11 @@ 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) {
- int res = 0;
+ int64_t res = 0;
uint64_t pos;
uint64_t maxlen = 100 * 1024 * 1024;
AVStream *st = s->streams[0];
diff --git a/libavformat/gxf.h b/libavformat/gxf.h
index c1ac399..dcdcdef 100644
--- a/libavformat/gxf.h
+++ b/libavformat/gxf.h
@@ -2,20 +2,20 @@
* GXF demuxer
* copyright (c) 2006 Reimar Doeffinger
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/gxfenc.c b/libavformat/gxfenc.c
index 32714f6..0e0772b 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->codecpar->bit_rate, sc->p_per_gop, sc->b_per_i_or_p,
- st->codecpar->format == AV_PIX_FMT_YUV422P ? 2 : 1, sc->first_gop_closed == 1,
+ st->codecpar->format == AV_PIX_FMT_YUV422P ? 2 : 1, sc->first_gop_closed == 1,
starting_line, (st->codecpar->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->codecpar->format == 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);
@@ -397,26 +436,35 @@ static int gxf_write_umf_material_description(AVFormatContext *s)
AVIOContext *pb = s->pb;
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);
+ ff_parse_creation_time_metadata(s, &timestamp, 1);
- // 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 +539,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 +552,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->codecpar->format == 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 +617,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->codecpar->codec_id) {
@@ -574,7 +629,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 +680,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 +706,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 & AVIO_SEEKABLE_NORMAL)) {
- 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 +768,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 +821,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 +852,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 +860,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 +947,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->codecpar->codec_id == AV_CODEC_ID_MPEG2VIDEO && pkt->size % 4) /* MPEG-2 frames must be padded */
@@ -880,6 +966,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 +979,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 5420294..a1d6821 100644
--- a/libavformat/h261dec.c
+++ b/libavformat/h261dec.c
@@ -2,57 +2,53 @@
* 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
*/
-#include "libavcodec/bitstream.h"
-
+#include "libavcodec/get_bits.h"
#include "avformat.h"
#include "rawdec.h"
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;
- BitstreamContext bc;
-
- bitstream_init8(&bc, p->buf, p->buf_size);
- for(i=0; i<p->buf_size*8; i++){
- if ((code & 0x01ff0000) || !(code & 0xff00)) {
- code = (code << 8) + bitstream_read(&bc, 8);
- i += 7;
- } else
- code = (code << 1) + bitstream_read_bit(&bc);
- 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; 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++;
- if(src_fmt){ // CIF
- next_gn= (gn+1 )%13;
- }else{ //QCIF
- next_gn= (gn+1+!!gn)% 7;
+ if(src_fmt){ // CIF
+ static const int lut[16]={1,2,3,4,5,6,7,8,9,10,11,12,0,16,16,16};
+ next_gn = lut[gn];
+ }else{ //QCIF
+ static const int lut[16]={1,3,16,5,16,0,16,16,16,16,16,16,16,16,16,16};
+ next_gn = lut[gn];
+ }
}
}
}
diff --git a/libavformat/h263dec.c b/libavformat/h263dec.c
index 4d826d8..145fb85 100644
--- a/libavformat/h263dec.c
+++ b/libavformat/h263dec.c
@@ -2,20 +2,20 @@
* RAW H.263 video demuxer
* Copyright (c) 2009 Michael Niedermayer <michaelni@gmx.at>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -31,24 +31,37 @@ static int h263_probe(AVProbeData *p)
int res_change=0;
int src_fmt, last_src_fmt=-1;
int last_gn=0;
+ int tr, last_tr = -1;
for(i=0; i<p->buf_size; i++){
code = (code<<8) + p->buf[i];
- if ((code & 0xfffffc0000) == 0x800000) {
- src_fmt= (code>>2)&3;
+ if ((code & 0xfffffc000000) == 0x80000000) {
+ tr = (code >> 18) & 0xFF;
+ src_fmt= (code>>10)&7;
if( src_fmt != last_src_fmt
&& last_src_fmt>0 && last_src_fmt<6
&& src_fmt<6)
res_change++;
- if((code&0x300)==0x200 && src_fmt){
+ if (tr == last_tr) {
+ invalid_psc++;
+ continue;
+ }
+
+ if (src_fmt != 7 && !(code&(1<<9)) && (code&(1<<5))) {
+ invalid_psc++;
+ continue;
+ }
+
+ if((code&0x30000)==0x20000 && src_fmt){
valid_psc++;
last_gn=0;
}else
invalid_psc++;
last_src_fmt= src_fmt;
- } else if((code & 0xffff800000) == 0x800000) {
- int gn= (code>>(23-5)) & 0x1F;
+ last_tr = tr;
+ } else if((code & 0xffff80000000) == 0x80000000) {
+ int gn= (code>>(31-5)) & 0x1F;
if(gn<last_gn){
invalid_psc++;
}else
diff --git a/libavformat/h264dec.c b/libavformat/h264dec.c
index 6fd45c1..85d7163 100644
--- a/libavformat/h264dec.c
+++ b/libavformat/h264dec.c
@@ -2,33 +2,43 @@
* 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
*/
+#include "libavcodec/get_bits.h"
+#include "libavcodec/golomb.h"
#include "avformat.h"
#include "rawdec.h"
+#include "libavcodec/internal.h"
+
+#define MAX_SPS_COUNT 32
+#define MAX_PPS_COUNT 256
static int h264_probe(AVProbeData *p)
{
uint32_t code = -1;
int sps = 0, pps = 0, idr = 0, res = 0, sli = 0;
- int i;
+ int i, ret;
+ int pps_ids[MAX_PPS_COUNT+1] = {0};
+ int sps_ids[MAX_SPS_COUNT+1] = {0};
+ unsigned pps_id, sps_id;
+ GetBitContext gb;
- for (i = 0; i < p->buf_size; i++) {
+ for (i = 0; i + 2 < p->buf_size; i++) {
code = (code << 8) + p->buf[i];
if ((code & 0xffffff00) == 0x100) {
int ref_idc = (code >> 5) & 3;
@@ -47,27 +57,59 @@ static int h264_probe(AVProbeData *p)
return 0;
if (ref_zero[type] == -1 && !ref_idc)
return 0;
- if (ref_zero[type] == 2)
- res++;
+ if (ref_zero[type] == 2) {
+ if (!(code == 0x100 && !p->buf[i + 1] && !p->buf[i + 2]))
+ res++;
+ }
+
+ ret = init_get_bits8(&gb, p->buf + i + 1, p->buf_size - i - 1);
+ if (ret < 0)
+ return 0;
switch (type) {
case 1:
- sli++;
- break;
case 5:
- idr++;
+ get_ue_golomb_long(&gb);
+ if (get_ue_golomb_long(&gb) > 9U)
+ return 0;
+ pps_id = get_ue_golomb_long(&gb);
+ if (pps_id > MAX_PPS_COUNT)
+ return 0;
+ if (!pps_ids[pps_id])
+ break;
+
+ if (type == 1)
+ sli++;
+ else
+ idr++;
break;
case 7:
- if (p->buf[i + 2] & 0x03)
+ skip_bits(&gb, 14);
+ if (get_bits(&gb, 2))
+ return 0;
+ skip_bits(&gb, 8);
+ sps_id = get_ue_golomb_long(&gb);
+ if (sps_id > MAX_SPS_COUNT)
return 0;
+ sps_ids[sps_id] = 1;
sps++;
break;
case 8:
+ pps_id = get_ue_golomb_long(&gb);
+ if (pps_id > MAX_PPS_COUNT)
+ return 0;
+ sps_id = get_ue_golomb_long(&gb);
+ if (sps_id > MAX_SPS_COUNT)
+ return 0;
+ if (!sps_ids[sps_id])
+ break;
+ pps_ids[pps_id] = 1;
pps++;
break;
}
}
}
+ ff_tlog(NULL, "sps:%d pps:%d idr:%d sli:%d res:%d\n", sps, pps, idr, sli, res);
if (sps && pps && (idr || sli > 3) && res < (sps + pps + idr))
return AVPROBE_SCORE_EXTENSION + 1; // 1 more than .mpg
diff --git a/libavformat/hashenc.c b/libavformat/hashenc.c
new file mode 100644
index 0000000..a66db4a
--- /dev/null
+++ b/libavformat/hashenc.c
@@ -0,0 +1,262 @@
+/*
+ * Hash/MD5 encoder (for codec/format testing)
+ * Copyright (c) 2009 Reimar Döffinger, based on crcenc (c) 2002 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/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/hash.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "internal.h"
+
+struct HashContext {
+ const AVClass *avclass;
+ struct AVHashContext *hash;
+ char *hash_name;
+ int format_version;
+};
+
+#define OFFSET(x) offsetof(struct HashContext, x)
+#define ENC AV_OPT_FLAG_ENCODING_PARAM
+#if CONFIG_HASH_MUXER || CONFIG_FRAMEHASH_MUXER
+static const AVOption hash_options[] = {
+ { "hash", "set hash to use", OFFSET(hash_name), AV_OPT_TYPE_STRING, {.str = "sha256"}, 0, 0, ENC },
+ { "format_version", "file format version", OFFSET(format_version), AV_OPT_TYPE_INT, {.i64 = 2}, 1, 2, ENC },
+ { NULL },
+};
+#endif
+
+#if CONFIG_MD5_MUXER || CONFIG_FRAMEMD5_MUXER
+static const AVOption md5_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 = 2}, 1, 2, ENC },
+ { NULL },
+};
+#endif
+
+#if CONFIG_HASH_MUXER || CONFIG_MD5_MUXER
+static int hash_write_header(struct AVFormatContext *s)
+{
+ struct HashContext *c = s->priv_data;
+ int res = av_hash_alloc(&c->hash, c->hash_name);
+ if (res < 0)
+ return res;
+ av_hash_init(c->hash);
+ return 0;
+}
+
+static int hash_write_packet(struct AVFormatContext *s, AVPacket *pkt)
+{
+ struct HashContext *c = s->priv_data;
+ av_hash_update(c->hash, pkt->data, pkt->size);
+ return 0;
+}
+
+static int hash_write_trailer(struct AVFormatContext *s)
+{
+ struct HashContext *c = s->priv_data;
+ char buf[AV_HASH_MAX_SIZE*2+128];
+ snprintf(buf, sizeof(buf) - 200, "%s=", av_hash_get_name(c->hash));
+
+ av_hash_final_hex(c->hash, buf + strlen(buf), sizeof(buf) - strlen(buf));
+ av_strlcatf(buf, sizeof(buf), "\n");
+ avio_write(s->pb, buf, strlen(buf));
+ avio_flush(s->pb);
+
+ av_hash_freep(&c->hash);
+ return 0;
+}
+#endif
+
+#if CONFIG_HASH_MUXER
+static const AVClass hashenc_class = {
+ .class_name = "hash encoder class",
+ .item_name = av_default_item_name,
+ .option = hash_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVOutputFormat ff_hash_muxer = {
+ .name = "hash",
+ .long_name = NULL_IF_CONFIG_SMALL("Hash testing"),
+ .priv_data_size = sizeof(struct HashContext),
+ .audio_codec = AV_CODEC_ID_PCM_S16LE,
+ .video_codec = AV_CODEC_ID_RAWVIDEO,
+ .write_header = hash_write_header,
+ .write_packet = hash_write_packet,
+ .write_trailer = hash_write_trailer,
+ .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT |
+ AVFMT_TS_NEGATIVE,
+ .priv_class = &hashenc_class,
+};
+#endif
+
+#if CONFIG_MD5_MUXER
+static const AVClass md5enc_class = {
+ .class_name = "MD5 encoder class",
+ .item_name = av_default_item_name,
+ .option = md5_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVOutputFormat ff_md5_muxer = {
+ .name = "md5",
+ .long_name = NULL_IF_CONFIG_SMALL("MD5 testing"),
+ .priv_data_size = sizeof(struct HashContext),
+ .audio_codec = AV_CODEC_ID_PCM_S16LE,
+ .video_codec = AV_CODEC_ID_RAWVIDEO,
+ .write_header = hash_write_header,
+ .write_packet = hash_write_packet,
+ .write_trailer = hash_write_trailer,
+ .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT |
+ AVFMT_TS_NEGATIVE,
+ .priv_class = &md5enc_class,
+};
+#endif
+
+#if CONFIG_FRAMEHASH_MUXER || CONFIG_FRAMEMD5_MUXER
+static void framehash_print_extradata(struct AVFormatContext *s)
+{
+ int i;
+
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ AVCodecParameters *par = st->codecpar;
+ if (par->extradata) {
+ struct HashContext *c = s->priv_data;
+ char buf[AV_HASH_MAX_SIZE*2+1];
+
+ avio_printf(s->pb, "#extradata %d, %31d, ", i, par->extradata_size);
+ av_hash_init(c->hash);
+ av_hash_update(c->hash, par->extradata, par->extradata_size);
+ av_hash_final_hex(c->hash, buf, sizeof(buf));
+ avio_write(s->pb, buf, strlen(buf));
+ avio_printf(s->pb, "\n");
+ }
+ }
+}
+
+static int framehash_write_header(struct AVFormatContext *s)
+{
+ struct HashContext *c = s->priv_data;
+ 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));
+ framehash_print_extradata(s);
+ ff_framehash_write_header(s);
+ avio_printf(s->pb, "#stream#, dts, pts, duration, size, hash\n");
+ return 0;
+}
+
+static int framehash_write_packet(struct AVFormatContext *s, AVPacket *pkt)
+{
+ struct HashContext *c = s->priv_data;
+ char buf[AV_HASH_MAX_SIZE*2+128];
+ int len;
+ av_hash_init(c->hash);
+ av_hash_update(c->hash, pkt->data, pkt->size);
+
+ snprintf(buf, sizeof(buf) - (AV_HASH_MAX_SIZE * 2 + 1), "%d, %10"PRId64", %10"PRId64", %8"PRId64", %8d, ",
+ pkt->stream_index, pkt->dts, pkt->pts, pkt->duration, pkt->size);
+ len = strlen(buf);
+ av_hash_final_hex(c->hash, buf + len, sizeof(buf) - len);
+ avio_write(s->pb, buf, strlen(buf));
+
+ if (c->format_version > 1 && pkt->side_data_elems) {
+ int i, j;
+ avio_printf(s->pb, ", S=%d", pkt->side_data_elems);
+ for (i = 0; i < pkt->side_data_elems; i++) {
+ av_hash_init(c->hash);
+ if (HAVE_BIGENDIAN && pkt->side_data[i].type == AV_PKT_DATA_PALETTE) {
+ for (j = 0; j < pkt->side_data[i].size; j += sizeof(uint32_t)) {
+ uint32_t data = AV_RL32(pkt->side_data[i].data + j);
+ av_hash_update(c->hash, (uint8_t *)&data, sizeof(uint32_t));
+ }
+ } else
+ av_hash_update(c->hash, pkt->side_data[i].data, pkt->side_data[i].size);
+ snprintf(buf, sizeof(buf) - (AV_HASH_MAX_SIZE * 2 + 1), ", %8d, ", pkt->side_data[i].size);
+ len = strlen(buf);
+ av_hash_final_hex(c->hash, buf + len, sizeof(buf) - len);
+ avio_write(s->pb, buf, strlen(buf));
+ }
+ }
+
+ avio_printf(s->pb, "\n");
+ avio_flush(s->pb);
+ return 0;
+}
+
+static int framehash_write_trailer(struct AVFormatContext *s)
+{
+ struct HashContext *c = s->priv_data;
+ av_hash_freep(&c->hash);
+ return 0;
+}
+#endif
+
+#if CONFIG_FRAMEHASH_MUXER
+static const AVClass framehash_class = {
+ .class_name = "frame hash encoder class",
+ .item_name = av_default_item_name,
+ .option = hash_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVOutputFormat ff_framehash_muxer = {
+ .name = "framehash",
+ .long_name = NULL_IF_CONFIG_SMALL("Per-frame hash testing"),
+ .priv_data_size = sizeof(struct HashContext),
+ .audio_codec = AV_CODEC_ID_PCM_S16LE,
+ .video_codec = AV_CODEC_ID_RAWVIDEO,
+ .write_header = framehash_write_header,
+ .write_packet = framehash_write_packet,
+ .write_trailer = framehash_write_trailer,
+ .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT |
+ AVFMT_TS_NEGATIVE,
+ .priv_class = &framehash_class,
+};
+#endif
+
+#if CONFIG_FRAMEMD5_MUXER
+static const AVClass framemd5_class = {
+ .class_name = "frame hash encoder class",
+ .item_name = av_default_item_name,
+ .option = md5_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVOutputFormat ff_framemd5_muxer = {
+ .name = "framemd5",
+ .long_name = NULL_IF_CONFIG_SMALL("Per-frame MD5 testing"),
+ .priv_data_size = sizeof(struct HashContext),
+ .audio_codec = AV_CODEC_ID_PCM_S16LE,
+ .video_codec = AV_CODEC_ID_RAWVIDEO,
+ .write_header = framehash_write_header,
+ .write_packet = framehash_write_packet,
+ .write_trailer = framehash_write_trailer,
+ .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT |
+ AVFMT_TS_NEGATIVE,
+ .priv_class = &framemd5_class,
+};
+#endif
diff --git a/libavformat/hdsenc.c b/libavformat/hdsenc.c
index a608e7e..72829f7 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
*/
@@ -26,6 +26,7 @@
#endif
#include "avformat.h"
+#include "avio_internal.h"
#include "internal.h"
#include "os_support.h"
@@ -147,12 +148,12 @@ static void hds_free(AVFormatContext *s)
avio_context_free(&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);
}
@@ -163,7 +164,7 @@ static int write_manifest(AVFormatContext *s, int final)
AVIOContext *out;
char filename[1024], temp_filename[1024];
int ret, i;
- float duration = 0;
+ double duration = 0;
if (c->nb_streams > 0)
duration = c->streams[0].last_ts * av_q2d(s->streams[0]->time_base);
@@ -202,7 +203,7 @@ static int write_manifest(AVFormatContext *s, int final)
avio_printf(out, "</manifest>\n");
avio_flush(out);
ff_format_io_close(s, &out);
- return ff_rename(temp_filename, filename);
+ return ff_rename(temp_filename, filename, s);
}
static void update_size(AVIOContext *out, int64_t pos)
@@ -282,7 +283,7 @@ static int write_abst(AVFormatContext *s, OutputStream *os, int final)
update_size(out, afrt_pos);
update_size(out, 0);
ff_format_io_close(s, &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)
@@ -318,6 +319,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;
}
@@ -327,7 +329,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;
@@ -372,6 +374,7 @@ static int hds_write_header(AVFormatContext *s)
os->ctx = ctx;
ctx->oformat = oformat;
ctx->interrupt_callback = s->interrupt_callback;
+ ctx->flags = s->flags;
ctx->pb = avio_alloc_context(os->iobuf, sizeof(os->iobuf),
AVIO_FLAG_WRITE, os,
@@ -417,7 +420,6 @@ static int hds_write_header(AVFormatContext *s)
if (!os->has_video && c->min_frag_duration <= 0) {
av_log(s, AV_LOG_WARNING,
"No video stream in output stream %d and no min frag duration set\n", i);
- ret = AVERROR(EINVAL);
}
os->fragment_index = 1;
write_abst(s, os, 0);
@@ -475,7 +477,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);
@@ -493,7 +495,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,
@@ -511,7 +513,7 @@ static int hds_write_packet(AVFormatContext *s, AVPacket *pkt)
HDSContext *c = s->priv_data;
AVStream *st = s->streams[pkt->stream_index];
OutputStream *os = &c->streams[s->streams[pkt->stream_index]->id];
- int64_t end_dts = os->fragment_index * (int64_t) c->min_frag_duration;
+ int64_t end_dts = os->fragment_index * (int64_t)c->min_frag_duration;
int ret;
if (st->first_dts == AV_NOPTS_VALUE)
@@ -533,7 +535,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)
@@ -566,7 +568,7 @@ static const AVOption options[] = {
{ "window_size", "number of fragments kept in the manifest", OFFSET(window_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, E },
{ "extra_window_size", "number of fragments kept outside of the manifest before removing from disk", OFFSET(extra_window_size), AV_OPT_TYPE_INT, { .i64 = 5 }, 0, INT_MAX, E },
{ "min_frag_duration", "minimum fragment duration (in microseconds)", OFFSET(min_frag_duration), AV_OPT_TYPE_INT64, { .i64 = 10000000 }, 0, INT_MAX, E },
- { "remove_at_exit", "remove all fragments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, E },
+ { "remove_at_exit", "remove all fragments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
{ NULL },
};
diff --git a/libavformat/hevc.c b/libavformat/hevc.c
index 057f651..e45d2c0 100644
--- a/libavformat/hevc.c
+++ b/libavformat/hevc.c
@@ -1,26 +1,26 @@
/*
* 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
*/
#include "libavcodec/avcodec.h"
#include "libavcodec/get_bits.h"
-#include "libavcodec/golomb_legacy.h"
+#include "libavcodec/golomb.h"
#include "libavcodec/hevc.h"
#include "libavutil/intreadwrite.h"
#include "avc.h"
@@ -190,7 +190,7 @@ static void skip_sub_layer_hrd_parameters(GetBitContext *gb,
}
}
-static void skip_hrd_parameters(GetBitContext *gb, uint8_t cprms_present_flag,
+static int skip_hrd_parameters(GetBitContext *gb, uint8_t cprms_present_flag,
unsigned int max_sub_layers_minus1)
{
unsigned int i;
@@ -247,8 +247,11 @@ static void skip_hrd_parameters(GetBitContext *gb, uint8_t cprms_present_flag,
else
low_delay_hrd_flag = get_bits1(gb);
- if (!low_delay_hrd_flag)
+ if (!low_delay_hrd_flag) {
cpb_cnt_minus1 = get_ue_golomb_long(gb);
+ if (cpb_cnt_minus1 > 31)
+ return AVERROR_INVALIDDATA;
+ }
if (nal_hrd_parameters_present_flag)
skip_sub_layer_hrd_parameters(gb, cpb_cnt_minus1,
@@ -258,6 +261,8 @@ static void skip_hrd_parameters(GetBitContext *gb, uint8_t cprms_present_flag,
skip_sub_layer_hrd_parameters(gb, cpb_cnt_minus1,
sub_pic_hrd_params_present_flag);
}
+
+ return 0;
}
static void skip_timing_info(GetBitContext *gb)
@@ -412,7 +417,7 @@ static void skip_scaling_list_data(GetBitContext *gb)
static int parse_rps(GetBitContext *gb, unsigned int rps_idx,
unsigned int num_rps,
- unsigned int num_delta_pocs[HEVC_MAX_SHORT_TERM_RPS_COUNT])
+ unsigned int num_delta_pocs[HEVC_MAX_SHORT_TERM_REF_PIC_SETS])
{
unsigned int i;
@@ -445,7 +450,7 @@ static int parse_rps(GetBitContext *gb, unsigned int rps_idx,
*
* NumDeltaPocs[RefRpsIdx]: num_delta_pocs[rps_idx - 1]
*/
- for (i = 0; i < num_delta_pocs[rps_idx - 1]; i++) {
+ for (i = 0; i <= num_delta_pocs[rps_idx - 1]; i++) {
uint8_t use_delta_flag = 0;
uint8_t used_by_curr_pic_flag = get_bits1(gb);
if (!used_by_curr_pic_flag)
@@ -458,6 +463,9 @@ static int parse_rps(GetBitContext *gb, unsigned int rps_idx,
unsigned int num_negative_pics = get_ue_golomb_long(gb);
unsigned int num_positive_pics = get_ue_golomb_long(gb);
+ if ((num_positive_pics + (uint64_t)num_negative_pics) * 2 > get_bits_left(gb))
+ return AVERROR_INVALIDDATA;
+
num_delta_pocs[rps_idx] = num_negative_pics + num_positive_pics;
for (i = 0; i < num_negative_pics; i++) {
@@ -478,7 +486,7 @@ static int hvcc_parse_sps(GetBitContext *gb,
HEVCDecoderConfigurationRecord *hvcc)
{
unsigned int i, sps_max_sub_layers_minus1, log2_max_pic_order_cnt_lsb_minus4;
- unsigned int num_short_term_ref_pic_sets, num_delta_pocs[HEVC_MAX_SHORT_TERM_RPS_COUNT];
+ unsigned int num_short_term_ref_pic_sets, num_delta_pocs[HEVC_MAX_SHORT_TERM_REF_PIC_SETS];
skip_bits(gb, 4); // sps_video_parameter_set_id
@@ -548,7 +556,7 @@ static int hvcc_parse_sps(GetBitContext *gb,
}
num_short_term_ref_pic_sets = get_ue_golomb_long(gb);
- if (num_short_term_ref_pic_sets > HEVC_MAX_SHORT_TERM_RPS_COUNT)
+ if (num_short_term_ref_pic_sets > HEVC_MAX_SHORT_TERM_REF_PIC_SETS)
return AVERROR_INVALIDDATA;
for (i = 0; i < num_short_term_ref_pic_sets; i++) {
@@ -558,7 +566,10 @@ static int hvcc_parse_sps(GetBitContext *gb,
}
if (get_bits1(gb)) { // long_term_ref_pics_present_flag
- for (i = 0; i < get_ue_golomb_long(gb); i++) { // num_long_term_ref_pics_sps
+ unsigned num_long_term_ref_pics_sps = get_ue_golomb_long(gb);
+ if (num_long_term_ref_pics_sps > 31U)
+ return AVERROR_INVALIDDATA;
+ for (i = 0; i < num_long_term_ref_pics_sps; i++) { // num_long_term_ref_pics_sps
int len = FFMIN(log2_max_pic_order_cnt_lsb_minus4 + 4, 16);
skip_bits (gb, len); // lt_ref_pic_poc_lsb_sps[i]
skip_bits1(gb); // used_by_curr_pic_lt_sps_flag[i]
@@ -609,11 +620,12 @@ static int hvcc_parse_pps(GetBitContext *gb,
get_se_golomb_long(gb); // pps_cr_qp_offset
/*
+ * pps_slice_chroma_qp_offsets_present_flag u(1)
* weighted_pred_flag u(1)
* weighted_bipred_flag u(1)
* transquant_bypass_enabled_flag u(1)
*/
- skip_bits(gb, 3);
+ skip_bits(gb, 4);
tiles_enabled_flag = get_bits1(gb);
entropy_coding_sync_enabled_flag = get_bits1(gb);
diff --git a/libavformat/hevc.h b/libavformat/hevc.h
index 03c43bd..796eaf4 100644
--- a/libavformat/hevc.h
+++ b/libavformat/hevc.h
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2014 Tim Walker <tdskywalker@gmail.com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -89,7 +89,7 @@ int ff_hevc_annexb2mp4_buf(const uint8_t *buf_in, uint8_t **buf_out,
* @param size size (in bytes) of the data buffer
* @param ps_array_completeness whether all parameter sets are in the hvcC (1)
* or there may be additional parameter sets in the bitstream (0)
- * @return 0 in case of success, a negative value corresponding to an AVERROR
+ * @return >=0 in case of success, a negative value corresponding to an AVERROR
* code in case of failure
*/
int ff_isom_write_hvcc(AVIOContext *pb, const uint8_t *data,
diff --git a/libavformat/hevcdec.c b/libavformat/hevcdec.c
index c186b1a..aaab0ff 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 c9da4e3..786934a 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"
@@ -34,9 +36,16 @@
#include "avformat.h"
#include "internal.h"
#include "avio_internal.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
@@ -52,23 +61,35 @@
enum KeyType {
KEY_NONE,
KEY_AES_128,
+ KEY_SAMPLE_AES
};
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];
+ /* associated Media Initialization Section, treated as a segment */
+ struct segment *init_section;
+};
+
+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;
@@ -77,33 +98,114 @@ struct variant {
int index;
AVFormatContext *ctx;
AVPacket pkt;
- int stream_offset;
+ int has_noheader_flag;
+
+ /* main demuxer streams associated with this playlist
+ * indexed by the subdemuxer stream indexes */
+ AVStream **main_streams;
+ int n_main_streams;
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;
+ /* Currently active Media Initialization Section */
+ struct segment *cur_init_section;
+ uint8_t *init_sec_buf;
+ unsigned int init_sec_buf_size;
+ unsigned int init_sec_data_len;
+ unsigned int init_sec_buf_read_offset;
+
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;
+
+ /* Media Initialization Sections (EXT-X-MAP) associated with this
+ * playlist, if any. */
+ int n_init_sections;
+ struct segment **init_sections;
+};
+
+/*
+ * 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 {
+ AVClass *class;
AVFormatContext *ctx;
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 live_start_index;
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
+ char *http_proxy; ///< holds the address of the HTTP proxy server
AVDictionary *avio_opts;
+ int strict_std_compliance;
+ char *allowed_extensions;
+ int max_reload;
} HLSContext;
static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
@@ -114,13 +216,58 @@ 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_init_section_list(struct playlist *pls)
+{
+ int i;
+ for (i = 0; i < pls->n_init_sections; i++) {
+ av_freep(&pls->init_sections[i]->url);
+ av_freep(&pls->init_sections[i]);
+ }
+ av_freep(&pls->init_sections);
+ pls->n_init_sections = 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);
+ free_init_section_list(pls);
+ av_freep(&pls->main_streams);
+ 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_freep(&pls->init_sec_buf);
+ av_packet_unref(&pls->pkt);
+ av_freep(&pls->pb.buffer);
+ if (pls->input)
+ ff_format_io_close(c->ctx, &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);
+ av_freep(&c->headers);
+ av_freep(&c->http_proxy);
+ c->n_playlists = 0;
}
static void free_variant_list(HLSContext *c)
@@ -128,21 +275,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_packet_unref(&var->pkt);
- av_free(var->pb.buffer);
- if (var->input)
- ff_format_io_close(c->ctx, &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.
@@ -153,35 +301,78 @@ 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);
}
}
struct key_info {
char uri[MAX_URL_SIZE];
- char method[10];
+ char method[11];
char iv[35];
};
@@ -200,53 +391,321 @@ static void handle_key_args(struct key_info *info, const char *key,
}
}
-static int open_in(HLSContext *c, AVIOContext **in, const char *url)
+struct init_section_info {
+ char uri[MAX_URL_SIZE];
+ char byterange[32];
+};
+
+static struct segment *new_init_section(struct playlist *pls,
+ struct init_section_info *info,
+ const char *url_base)
{
- AVDictionary *tmp = NULL;
- int ret;
+ struct segment *sec;
+ char *ptr;
+ char tmp_str[MAX_URL_SIZE];
- av_dict_copy(&tmp, c->avio_opts, 0);
+ if (!info->uri[0])
+ return NULL;
- ret = c->ctx->io_open(c->ctx, in, url, AVIO_FLAG_READ, &tmp);
+ sec = av_mallocz(sizeof(*sec));
+ if (!sec)
+ return NULL;
- av_dict_free(&tmp);
- return ret;
+ ff_make_absolute_url(tmp_str, sizeof(tmp_str), url_base, info->uri);
+ sec->url = av_strdup(tmp_str);
+ if (!sec->url) {
+ av_free(sec);
+ return NULL;
+ }
+
+ if (info->byterange[0]) {
+ sec->size = strtoll(info->byterange, NULL, 10);
+ ptr = strchr(info->byterange, '@');
+ if (ptr)
+ sec->url_offset = strtoll(ptr+1, NULL, 10);
+ } else {
+ /* the entire file is the init section */
+ sec->size = -1;
+ }
+
+ dynarray_add(&pls->init_sections, &pls->n_init_sections, sec);
+
+ return sec;
+}
+
+static void handle_init_section_args(struct init_section_info *info, const char *key,
+ int key_len, char **dest, int *dest_len)
+{
+ if (!strncmp(key, "URI=", key_len)) {
+ *dest = info->uri;
+ *dest_len = sizeof(info->uri);
+ } else if (!strncmp(key, "BYTERANGE=", key_len)) {
+ *dest = info->byterange;
+ *dest_len = sizeof(info->byterange);
+ }
+}
+
+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 (c->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL)
+ 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)
+ * *pls == NULL => Master Playlist or parentless Media Playlist
+ * *pls != NULL => parented Media Playlist, playlist+variant allocated */
+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;
+}
+
+static void update_options(char **dest, const char *name, void *src)
+{
+ av_freep(dest);
+ av_opt_get(src, name, AV_OPT_SEARCH_CHILDREN, (uint8_t**)dest);
+ if (*dest && !strlen(*dest))
+ av_freep(dest);
}
static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
- const AVDictionary *opts)
+ AVDictionary *opts, AVDictionary *opts2, int *is_http)
{
+ HLSContext *c = s->priv_data;
AVDictionary *tmp = NULL;
+ const char *proto_name = NULL;
int ret;
av_dict_copy(&tmp, opts, 0);
+ av_dict_copy(&tmp, opts2, 0);
+
+ if (av_strstart(url, "crypto", NULL)) {
+ if (url[6] == '+' || url[6] == ':')
+ proto_name = avio_find_protocol_name(url + 7);
+ }
+
+ if (!proto_name)
+ proto_name = avio_find_protocol_name(url);
+
+ if (!proto_name)
+ return AVERROR_INVALIDDATA;
+
+ // only http(s) & file are allowed
+ if (av_strstart(proto_name, "file", NULL)) {
+ if (strcmp(c->allowed_extensions, "ALL") && !av_match_ext(url, c->allowed_extensions)) {
+ av_log(s, AV_LOG_ERROR,
+ "Filename extension of \'%s\' is not a common multimedia extension, blocked for security reasons.\n"
+ "If you wish to override this adjust allowed_extensions, you can set it to \'ALL\' to allow all\n",
+ url);
+ return AVERROR_INVALIDDATA;
+ }
+ } else if (av_strstart(proto_name, "http", NULL)) {
+ ;
+ } else
+ return AVERROR_INVALIDDATA;
+
+ if (!strncmp(proto_name, url, strlen(proto_name)) && url[strlen(proto_name)] == ':')
+ ;
+ else if (av_strstart(url, "crypto", NULL) && !strncmp(proto_name, url + 7, strlen(proto_name)) && url[7 + strlen(proto_name)] == ':')
+ ;
+ else if (strcmp(proto_name, "file") || !strncmp(url, "file,", 5))
+ return AVERROR_INVALIDDATA;
ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp);
+ if (ret >= 0) {
+ // update cookies on http response with setcookies.
+ char *new_cookies = NULL;
+
+ if (!(s->flags & AVFMT_FLAG_CUSTOM_IO))
+ av_opt_get(*pb, "cookies", AV_OPT_SEARCH_CHILDREN, (uint8_t**)&new_cookies);
+
+ if (new_cookies) {
+ av_free(c->cookies);
+ c->cookies = new_cookies;
+ }
+
+ av_dict_set(&opts, "cookies", c->cookies, 0);
+ }
av_dict_free(&tmp);
+ if (is_http)
+ *is_http = av_strstart(proto_name, "http", NULL);
+
return ret;
}
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];
+ struct segment *cur_init_section = NULL;
if (!in) {
+#if 1
+ AVDictionary *opts = NULL;
+ close_in = 1;
+ /* 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);
+ av_dict_set(&opts, "http_proxy", c->http_proxy, 0);
+
+ ret = c->ctx->io_open(c->ctx, &in, url, AVIO_FLAG_READ, &opts);
+ av_dict_free(&opts);
+ if (ret < 0)
+ return ret;
+#else
ret = open_in(c, &in, url);
if (ret < 0)
return ret;
close_in = 1;
+#endif
}
if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >= 0)
@@ -258,18 +717,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,
@@ -278,54 +737,73 @@ static int parse_playlist(HLSContext *c, const char *url,
has_iv = 0;
if (!strcmp(info.method, "AES-128"))
key_type = KEY_AES_128;
+ if (!strcmp(info.method, "SAMPLE-AES"))
+ key_type = KEY_SAMPLE_AES;
if (!strncmp(info.iv, "0x", 2) || !strncmp(info.iv, "0X", 2)) {
ff_hex_to_data(iv, info.iv + 2);
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 = strtoll(ptr, NULL, 10) * 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-MAP:", &ptr)) {
+ struct init_section_info info = {{0}};
+ ret = ensure_playlist(c, &pls, url);
+ if (ret < 0)
+ goto fail;
+ ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_init_section_args,
+ &info);
+ cur_init_section = new_init_section(pls, &info, url);
} 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 = strtoll(ptr, NULL, 10);
+ ptr = strchr(ptr, '@');
+ if (ptr)
+ seg_offset = strtoll(ptr+1, NULL, 10);
} 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) {
@@ -337,19 +815,51 @@ 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;
+ }
+
+ seg->init_section = cur_init_section;
}
}
}
- if (var)
- var->last_load_time = av_gettime_relative();
+ if (pls)
+ pls->last_load_time = av_gettime_relative();
fail:
av_free(new_url);
@@ -358,69 +868,444 @@ fail:
return ret;
}
-static int open_input(struct variant *var)
+static struct segment *current_segment(struct playlist *pls)
+{
+ return pls->segments[pls->cur_seq_no - pls->start_seq_no];
+}
+
+enum ReadFromURLMode {
+ READ_NORMAL,
+ READ_COMPLETE,
+};
+
+static int read_from_url(struct playlist *pls, struct segment *seg,
+ uint8_t *buf, int buf_size,
+ enum ReadFromURLMode mode)
+{
+ int ret;
+
+ /* 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 = avio_read(pls->input, buf, buf_size);
+ if (ret != buf_size)
+ av_log(NULL, AV_LOG_ERROR, "Could not read complete segment.\n");
+ } else
+ ret = avio_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 - AV_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);
+}
+
+static void intercept_id3(struct playlist *pls, uint8_t *buf,
+ int buf_size, int *len)
{
- HLSContext *c = var->parent->priv_data;
- struct segment *seg = var->segments[var->cur_seq_no - var->start_seq_no];
+ /* intercept id3 tags, we do not want to pass them to the raw
+ * demuxer on all segment switches */
+ int bytes;
+ int id3_buf_pos = 0;
+ int fill_buf = 0;
+ struct segment *seg = current_segment(pls);
+
+ /* 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, seg, 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)) {
+ 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, seg, 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, seg, 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)
+{
+ AVDictionary *opts = NULL;
+ int ret;
+ int is_http = 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);
+ av_dict_set(&opts, "http_proxy", c->http_proxy, 0);
+ av_dict_set(&opts, "seekable", "0", 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 open_url(var->parent, &var->input, seg->url, c->avio_opts);
+ ret = open_url(pls->parent, &pls->input, seg->url, c->avio_opts, opts, &is_http);
} else if (seg->key_type == KEY_AES_128) {
- AVDictionary *opts = NULL;
+ AVDictionary *opts2 = NULL;
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)) {
AVIOContext *pb;
- if (open_url(var->parent, &pb, seg->key, c->avio_opts) == 0) {
- ret = avio_read(pb, var->key, sizeof(var->key));
- if (ret != sizeof(var->key)) {
+ if (open_url(pls->parent, &pb, seg->key, c->avio_opts, opts, NULL) == 0) {
+ ret = avio_read(pb, pls->key, sizeof(pls->key));
+ if (ret != sizeof(pls->key)) {
av_log(NULL, AV_LOG_ERROR, "Unable to read key file %s\n",
seg->key);
}
- ff_format_io_close(var->parent, &pb);
+ ff_format_io_close(pls->parent, &pb);
} else {
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);
- av_dict_copy(&opts, c->avio_opts, 0);
- av_dict_set(&opts, "key", key, 0);
- av_dict_set(&opts, "iv", iv, 0);
+ av_dict_copy(&opts2, c->avio_opts, 0);
+ av_dict_set(&opts2, "key", key, 0);
+ av_dict_set(&opts2, "iv", iv, 0);
- ret = open_url(var->parent, &var->input, url, opts);
- av_dict_free(&opts);
+ ret = open_url(pls->parent, &pls->input, url, opts2, opts, &is_http);
+
+ av_dict_free(&opts2);
+
+ if (ret < 0) {
+ goto cleanup;
+ }
+ ret = 0;
+ } else if (seg->key_type == KEY_SAMPLE_AES) {
+ av_log(pls->parent, AV_LOG_ERROR,
+ "SAMPLE-AES encryption is not supported yet\n");
+ ret = AVERROR_PATCHWELCOME;
+ }
+ 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.
+ *
+ * This is not done for HTTP at all as avio_seek() does internal bookkeeping
+ * of file offset which is out-of-sync with the actual offset when "offset"
+ * AVOption is used with http protocol, causing the seek to not be a no-op
+ * as would be expected. Wrong offset received from the server will not be
+ * noticed without the call, though.
+ */
+ if (ret == 0 && !is_http && seg->key_type == KEY_NONE && seg->url_offset) {
+ int64_t seekret = avio_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;
+ ff_format_io_close(pls->parent, &pls->input);
+ }
+ }
+
+cleanup:
+ av_dict_free(&opts);
+ pls->cur_seg_offset = 0;
+ return ret;
+}
+
+static int update_init_section(struct playlist *pls, struct segment *seg)
+{
+ static const int max_init_section_size = 1024*1024;
+ HLSContext *c = pls->parent->priv_data;
+ int64_t sec_size;
+ int64_t urlsize;
+ int ret;
+
+ if (seg->init_section == pls->cur_init_section)
+ return 0;
+
+ pls->cur_init_section = NULL;
+
+ if (!seg->init_section)
+ return 0;
+
+ ret = open_input(c, pls, seg->init_section);
+ if (ret < 0) {
+ av_log(pls->parent, AV_LOG_WARNING,
+ "Failed to open an initialization section in playlist %d\n",
+ pls->index);
return ret;
}
- return AVERROR(ENOSYS);
+
+ if (seg->init_section->size >= 0)
+ sec_size = seg->init_section->size;
+ else if ((urlsize = avio_size(pls->input)) >= 0)
+ sec_size = urlsize;
+ else
+ sec_size = max_init_section_size;
+
+ av_log(pls->parent, AV_LOG_DEBUG,
+ "Downloading an initialization section of size %"PRId64"\n",
+ sec_size);
+
+ sec_size = FFMIN(sec_size, max_init_section_size);
+
+ av_fast_malloc(&pls->init_sec_buf, &pls->init_sec_buf_size, sec_size);
+
+ ret = read_from_url(pls, seg->init_section, pls->init_sec_buf,
+ pls->init_sec_buf_size, READ_COMPLETE);
+ ff_format_io_close(pls->parent, &pls->input);
+
+ if (ret < 0)
+ return ret;
+
+ pls->cur_init_section = seg->init_section;
+ pls->init_sec_data_len = ret;
+ pls->init_sec_buf_read_offset = 0;
+
+ /* spec says audio elementary streams do not have media initialization
+ * sections, so there should be no ID3 timestamps */
+ pls->is_id3_timestamped = 0;
+
+ return 0;
+}
+
+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;
+ int reload_count = 0;
restart:
+ if (!v->needed)
+ return AVERROR_EOF;
+
if (!v->input) {
+ int64_t reload_interval;
+ struct segment *seg;
+
+ /* Check that the playlist is still needed before opening a new
+ * segment. */
+ if (v->ctx && v->ctx->nb_streams) {
+ v->needed = 0;
+ for (i = 0; i < v->n_main_streams; i++) {
+ if (v->main_streams[i]->discard < AVDISCARD_ALL) {
+ v->needed = 1;
+ break;
+ }
+ }
+ }
+ 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:
+ reload_count++;
+ if (reload_count > c->max_reload)
+ return AVERROR_EOF;
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. */
@@ -444,46 +1329,183 @@ reload:
goto reload;
}
- ret = open_input(v);
- if (ret < 0)
+ seg = current_segment(v);
+
+ /* load/update Media Initialization Section, if any */
+ ret = update_init_section(v, seg);
+ if (ret)
return ret;
+
+ ret = open_input(c, v, seg);
+ if (ret < 0) {
+ if (ff_check_interrupt(c->interrupt_callback))
+ return AVERROR_EXIT;
+ av_log(v->parent, AV_LOG_WARNING, "Failed to open segment of playlist %d\n",
+ v->index);
+ v->cur_seq_no += 1;
+ goto reload;
+ }
+ just_opened = 1;
}
- ret = avio_read(v->input, buf, buf_size);
- if (ret > 0)
+
+ if (v->init_sec_buf_read_offset < v->init_sec_data_len) {
+ /* Push init section out first before first actual segment */
+ int copy_size = FFMIN(v->init_sec_data_len - v->init_sec_buf_read_offset, buf_size);
+ memcpy(buf, v->init_sec_buf, copy_size);
+ v->init_sec_buf_read_offset += copy_size;
+ return copy_size;
+ }
+
+ ret = read_from_url(v, current_segment(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;
- ff_format_io_close(c->ctx, &v->input);
+ }
+ ff_format_io_close(v->parent, &v->input);
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 void add_renditions_to_variant(HLSContext *c, struct variant *var,
+ enum AVMediaType type, const char *group_id)
+{
+ int i;
+
+ for (i = 0; i < c->n_renditions; i++) {
+ struct rendition *rend = c->renditions[i];
+
+ if (rend->type == type && !strcmp(rend->group_id, group_id)) {
+
+ if (rend->playlist)
+ /* rendition is an external playlist
+ * => add the playlist to the variant */
+ dynarray_add(&var->playlists, &var->n_playlists, rend->playlist);
+ else
+ /* rendition is part of the variant main Media Playlist
+ * => add the rendition to the main Media Playlist */
+ dynarray_add(&var->playlists[0]->renditions,
+ &var->playlists[0]->n_renditions,
+ rend);
}
}
- if (!v->needed) {
- av_log(v->parent, AV_LOG_INFO, "No longer receiving variant %d\n",
- v->index);
- return AVERROR_EOF;
+}
+
+static void add_metadata_from_renditions(AVFormatContext *s, struct playlist *pls,
+ enum AVMediaType type)
+{
+ int rend_idx = 0;
+ int i;
+
+ for (i = 0; i < pls->n_main_streams; i++) {
+ AVStream *st = pls->main_streams[i];
+
+ if (st->codecpar->codec_type != type)
+ continue;
+
+ for (; rend_idx < pls->n_renditions; rend_idx++) {
+ struct rendition *rend = pls->renditions[rend_idx];
+
+ if (rend->type != type)
+ continue;
+
+ if (rend->language[0])
+ av_dict_set(&st->metadata, "language", rend->language, 0);
+ if (rend->name[0])
+ av_dict_set(&st->metadata, "comment", rend->name, 0);
+
+ st->disposition |= rend->disposition;
+ }
+ if (rend_idx >=pls->n_renditions)
+ break;
}
- goto restart;
+}
+
+/* if timestamp was in valid range: returns 1 and sets seq_no
+ * if not: returns 0 and sets seq_no to closest segment */
+static int find_timestamp_in_playlist(HLSContext *c, struct playlist *pls,
+ int64_t timestamp, int *seq_no)
+{
+ int i;
+ int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ?
+ 0 : c->first_timestamp;
+
+ if (timestamp < pos) {
+ *seq_no = pls->start_seq_no;
+ return 0;
+ }
+
+ for (i = 0; i < pls->n_segments; i++) {
+ int64_t diff = pos + pls->segments[i]->duration - timestamp;
+ if (diff > 0) {
+ *seq_no = pls->start_seq_no + i;
+ return 1;
+ }
+ pos += pls->segments[i]->duration;
+ }
+
+ *seq_no = pls->start_seq_no + pls->n_segments - 1;
+
+ return 0;
+}
+
+static int select_cur_seq_no(HLSContext *c, struct playlist *pls)
+{
+ int seq_no;
+
+ if (!pls->finished && !c->first_packet &&
+ av_gettime_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, start live_start_index segments from the
+ * start or end */
+ if (c->live_start_index < 0)
+ return pls->start_seq_no + FFMAX(pls->n_segments + c->live_start_index, 0);
+ else
+ return pls->start_seq_no + FFMIN(c->live_start_index, pls->n_segments - 1);
+ }
+
+ /* Otherwise just start on the first segment. */
+ return pls->start_seq_no;
}
static int save_avio_options(AVFormatContext *s)
{
HLSContext *c = s->priv_data;
- static const char * const opts[] = { "headers", "user_agent", NULL };
- const char * const *opt = opts;
+ static const char * const opts[] = {
+ "headers", "http_proxy", "user_agent", "user-agent", "cookies", NULL };
+ const char * const * opt = opts;
uint8_t *buf;
int ret = 0;
while (*opt) {
- if (av_opt_get(s->pb, *opt, AV_OPT_SEARCH_CHILDREN, &buf) >= 0) {
+ if (av_opt_get(s->pb, *opt, AV_OPT_SEARCH_CHILDREN | AV_OPT_ALLOW_NULL, &buf) >= 0) {
ret = av_dict_set(&c->avio_opts, *opt, buf,
AV_DICT_DONT_STRDUP_VAL);
if (ret < 0)
@@ -505,13 +1527,138 @@ static int nested_io_open(AVFormatContext *s, AVIOContext **pb, const char *url,
return AVERROR(EPERM);
}
+static void add_stream_to_programs(AVFormatContext *s, struct playlist *pls, AVStream *stream)
+{
+ HLSContext *c = s->priv_data;
+ int i, j;
+ int bandwidth = -1;
+
+ for (i = 0; i < c->n_variants; i++) {
+ struct variant *v = c->variants[i];
+
+ for (j = 0; j < v->n_playlists; j++) {
+ if (v->playlists[j] != pls)
+ continue;
+
+ av_program_add_stream_index(s, i, stream->index);
+
+ if (bandwidth < 0)
+ bandwidth = v->bandwidth;
+ else if (bandwidth != v->bandwidth)
+ bandwidth = -1; /* stream in multiple variants with different bandwidths */
+ }
+ }
+
+ if (bandwidth >= 0)
+ av_dict_set_int(&stream->metadata, "variant_bitrate", bandwidth, 0);
+}
+
+static int set_stream_info_from_input_stream(AVStream *st, struct playlist *pls, AVStream *ist)
+{
+ int err;
+
+ err = avcodec_parameters_copy(st->codecpar, ist->codecpar);
+ if (err < 0)
+ return err;
+
+ 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);
+
+ st->internal->need_context_update = 1;
+
+ return 0;
+}
+
+/* add new subdemuxer streams to our context, if any */
+static int update_streams_from_subdemuxer(AVFormatContext *s, struct playlist *pls)
+{
+ int err;
+
+ while (pls->n_main_streams < pls->ctx->nb_streams) {
+ int ist_idx = pls->n_main_streams;
+ AVStream *st = avformat_new_stream(s, NULL);
+ AVStream *ist = pls->ctx->streams[ist_idx];
+
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->id = pls->index;
+ dynarray_add(&pls->main_streams, &pls->n_main_streams, st);
+
+ add_stream_to_programs(s, pls, st);
+
+ err = set_stream_info_from_input_stream(st, pls, ist);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void update_noheader_flag(AVFormatContext *s)
+{
+ HLSContext *c = s->priv_data;
+ int flag_needed = 0;
+ int i;
+
+ for (i = 0; i < c->n_playlists; i++) {
+ struct playlist *pls = c->playlists[i];
+
+ if (pls->has_noheader_flag) {
+ flag_needed = 1;
+ break;
+ }
+ }
+
+ if (flag_needed)
+ s->ctx_flags |= AVFMTCTX_NOHEADER;
+ else
+ s->ctx_flags &= ~AVFMTCTX_NOHEADER;
+}
+
+static int hls_close(AVFormatContext *s)
+{
+ HLSContext *c = s->priv_data;
+
+ free_playlist_list(c);
+ free_variant_list(c);
+ free_rendition_list(c);
+
+ av_dict_free(&c->avio_opts);
+
+ return 0;
+}
+
static int hls_read_header(AVFormatContext *s)
{
+ void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb;
HLSContext *c = s->priv_data;
- int ret = 0, i, j, stream_offset = 0;
+ int ret = 0, i;
+ int highest_cur_seq_no = 0;
c->ctx = s;
c->interrupt_callback = &s->interrupt_callback;
+ c->strict_std_compliance = s->strict_std_compliance;
+
+ c->first_packet = 1;
+ c->first_timestamp = AV_NOPTS_VALUE;
+ c->cur_timestamp = AV_NOPTS_VALUE;
+
+ if (u) {
+ // get the previous user agent & set back to null if string size is zero
+ update_options(&c->user_agent, "user_agent", u);
+
+ // get the previous cookies & set back to null if string size is zero
+ update_options(&c->cookies, "cookies", u);
+
+ // get the previous headers & set back to null if string size is zero
+ update_options(&c->headers, "headers", u);
+
+ // get the previous http proxt & set back to null if string size is zero
+ update_options(&c->http_proxy, "http_proxy", u);
+ }
if ((ret = parse_playlist(c, s->filename, NULL, s->pb)) < 0)
goto fail;
@@ -519,22 +1666,25 @@ static int hls_read_header(AVFormatContext *s)
if ((ret = save_avio_options(s)) < 0)
goto fail;
+ /* Some HLS servers don't like being sent the range header */
+ av_dict_set(&c->avio_opts, "seekable", "0", 0);
+
if (c->n_variants == 0) {
av_log(NULL, AV_LOG_WARNING, "Empty playlist\n");
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;
@@ -542,97 +1692,148 @@ 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 *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);
+ }
+
+ /* Create a program for each variant */
for (i = 0; i < c->n_variants; i++) {
struct variant *v = c->variants[i];
- AVInputFormat *in_fmt = NULL;
- char bitrate_str[20];
AVProgram *program;
- if (v->n_segments == 0)
+ program = av_new_program(s, i);
+ if (!program)
+ goto fail;
+ av_dict_set_int(&program->metadata, "variant_bitrate", v->bandwidth, 0);
+ }
+
+ /* Select the starting segments */
+ for (i = 0; i < c->n_playlists; i++) {
+ struct playlist *pls = c->playlists[i];
+
+ if (pls->n_segments == 0)
continue;
- if (!(v->ctx = avformat_alloc_context())) {
+ pls->cur_seq_no = select_cur_seq_no(c, pls);
+ highest_cur_seq_no = FFMAX(highest_cur_seq_no, pls->cur_seq_no);
+ }
+
+ /* Open the demuxer for each playlist */
+ for (i = 0; i < c->n_playlists; i++) {
+ struct playlist *pls = c->playlists[i];
+ AVInputFormat *in_fmt = NULL;
+
+ if (!(pls->ctx = avformat_alloc_context())) {
ret = AVERROR(ENOMEM);
goto fail;
}
- v->index = i;
- v->needed = 1;
- v->parent = s;
+ if (pls->n_segments == 0)
+ continue;
- /* 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;
+
+ /*
+ * If this is a live stream and this playlist looks like it is one segment
+ * behind, try to sync it up so that every substream starts at the same
+ * time position (so e.g. avformat_find_stream_info() will see packets from
+ * all active streams within the first few seconds). This is not very generic,
+ * though, as the sequence numbers are technically independent.
+ */
+ if (!pls->finished && pls->cur_seq_no == highest_cur_seq_no - 1 &&
+ highest_cur_seq_no < pls->start_seq_no + pls->n_segments) {
+ pls->cur_seq_no = highest_cur_seq_no;
+ }
- 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);
+ if (!pls->read_buffer){
+ ret = AVERROR(ENOMEM);
+ avformat_free_context(pls->ctx);
+ pls->ctx = NULL;
+ goto fail;
+ }
+ 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->ctx->io_open = nested_io_open;
- 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->ctx->io_open = nested_io_open;
+ pls->ctx->flags |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
+
+ if ((ret = ff_copy_whiteblacklists(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)
- 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++) {
- AVStream *st = avformat_new_stream(s, NULL);
- AVStream *ist = v->ctx->streams[j];
- if (!st) {
- ret = AVERROR(ENOMEM);
+ 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;
+ }
+
+ if (pls->is_id3_timestamped == -1)
+ av_log(s, AV_LOG_WARNING, "No expected HTTP requests have been made\n");
+
+ /*
+ * For ID3 timestamped raw audio streams we need to detect the packet
+ * durations to calculate timestamps in fill_timing_for_id3_timestamped_stream(),
+ * but for other streams we can rely on our user calling avformat_find_stream_info()
+ * on us if they want to.
+ */
+ if (pls->is_id3_timestamped) {
+ ret = avformat_find_stream_info(pls->ctx, NULL);
+ if (ret < 0)
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_parameters_copy(st->codecpar, v->ctx->streams[j]->codecpar);
- if (v->bandwidth)
- av_dict_set(&st->metadata, "variant_bitrate", bitrate_str,
- 0);
}
- stream_offset += v->ctx->nb_streams;
+
+ pls->has_noheader_flag = !!(pls->ctx->ctx_flags & AVFMTCTX_NOHEADER);
+
+ /* Create new AVStreams for each stream in this playlist */
+ ret = update_streams_from_subdemuxer(s, pls);
+ if (ret < 0)
+ goto fail;
+
+ 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);
}
- c->first_packet = 1;
- c->first_timestamp = AV_NOPTS_VALUE;
- c->seek_timestamp = AV_NOPTS_VALUE;
+ update_noheader_flag(s);
return 0;
fail:
- free_variant_list(c);
+ hls_close(s);
return ret;
}
@@ -642,196 +1843,293 @@ 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)
- ff_format_io_close(s, &v->input);
- 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)
+ ff_format_io_close(pls->parent, &pls->input);
+ 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);
+ c->first_packet = 0;
-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_packet_unref(&var->pkt);
- reset_packet(&var->pkt);
+ av_packet_unref(&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);
- return 0;
- }
- return AVERROR_EOF;
-}
+ if (minplaylist >= 0) {
+ struct playlist *pls = c->playlists[minplaylist];
+ AVStream *ist;
+ AVStream *st;
-static int hls_close(AVFormatContext *s)
-{
- HLSContext *c = s->priv_data;
+ ret = update_streams_from_subdemuxer(s, pls);
+ if (ret < 0) {
+ av_packet_unref(&pls->pkt);
+ reset_packet(&pls->pkt);
+ return ret;
+ }
- free_variant_list(c);
+ /* check if noheader flag has been cleared by the subdemuxer */
+ if (pls->has_noheader_flag && !(pls->ctx->ctx_flags & AVFMTCTX_NOHEADER)) {
+ pls->has_noheader_flag = 0;
+ update_noheader_flag(s);
+ }
- av_dict_free(&c->avio_opts);
+ if (pls->pkt.stream_index >= pls->n_main_streams) {
+ av_log(s, AV_LOG_ERROR, "stream index inconsistency: index %d, %d main streams, %d subdemuxer streams\n",
+ pls->pkt.stream_index, pls->n_main_streams, pls->ctx->nb_streams);
+ av_packet_unref(&pls->pkt);
+ reset_packet(&pls->pkt);
+ return AVERROR_BUG;
+ }
- return 0;
+ ist = pls->ctx->streams[pls->pkt.stream_index];
+ st = pls->main_streams[pls->pkt.stream_index];
+
+ *pkt = pls->pkt;
+ pkt->stream_index = st->index;
+ reset_packet(&c->playlists[minplaylist]->pkt);
+
+ if (pkt->dts != AV_NOPTS_VALUE)
+ c->cur_timestamp = av_rescale_q(pkt->dts,
+ ist->time_base,
+ AV_TIME_BASE_Q);
+
+ /* There may be more situations where this would be useful, but this at least
+ * handles newly probed codecs properly (i.e. request_probe by mpegts). */
+ if (ist->codecpar->codec_id != st->codecpar->codec_id) {
+ ret = set_stream_info_from_input_stream(st, pls, ist);
+ if (ret < 0) {
+ av_packet_unref(pkt);
+ return ret;
+ }
+ }
+
+ return 0;
+ }
+ return AVERROR_EOF;
}
static int hls_read_seek(AVFormatContext *s, int stream_index,
int64_t timestamp, int flags)
{
HLSContext *c = s->priv_data;
- int i, j, ret;
-
- if ((flags & AVSEEK_FLAG_BYTE) || !c->variants[0]->finished)
+ struct playlist *seek_pls = NULL;
+ int i, seq_no;
+ int j;
+ int stream_subdemuxer_index;
+ int64_t first_timestamp, seek_timestamp, duration;
+
+ 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];
+ for (j = 0; j < pls->n_main_streams; j++) {
+ if (pls->main_streams[j] == s->streams[stream_index]) {
+ seek_pls = pls;
+ stream_subdemuxer_index = j;
+ 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_subdemuxer_index;
+
+ 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)
- ff_format_io_close(s, &var->input);
- av_packet_unref(&var->pkt);
- reset_packet(&var->pkt);
- var->pb.eof_reached = 0;
+ struct playlist *pls = c->playlists[i];
+ if (pls->input)
+ ff_format_io_close(pls->parent, &pls->input);
+ av_packet_unref(&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)
@@ -840,6 +2138,7 @@ static int hls_probe(AVProbeData *p)
* somewhere for a proper match. */
if (strncmp(p->buf, "#EXTM3U", 7))
return 0;
+
if (strstr(p->buf, "#EXT-X-STREAM-INF:") ||
strstr(p->buf, "#EXT-X-TARGETDURATION:") ||
strstr(p->buf, "#EXT-X-MEDIA-SEQUENCE:"))
@@ -847,9 +2146,31 @@ static int hls_probe(AVProbeData *p)
return 0;
}
+#define OFFSET(x) offsetof(HLSContext, x)
+#define FLAGS AV_OPT_FLAG_DECODING_PARAM
+static const AVOption hls_options[] = {
+ {"live_start_index", "segment index to start live streams at (negative values are from the end)",
+ OFFSET(live_start_index), AV_OPT_TYPE_INT, {.i64 = -3}, INT_MIN, INT_MAX, FLAGS},
+ {"allowed_extensions", "List of file extensions that hls is allowed to access",
+ OFFSET(allowed_extensions), AV_OPT_TYPE_STRING,
+ {.str = "3gp,aac,avi,flac,mkv,m3u8,m4a,m4s,m4v,mpg,mov,mp2,mp3,mp4,mpeg,mpegts,ogg,ogv,oga,ts,vob,wav"},
+ INT_MIN, INT_MAX, FLAGS},
+ {"max_reload", "Maximum number of times a insufficient list is attempted to be reloaded",
+ OFFSET(max_reload), AV_OPT_TYPE_INT, {.i64 = 1000}, 0, INT_MAX, FLAGS},
+ {NULL}
+};
+
+static const AVClass hls_class = {
+ .class_name = "hls,applehttp",
+ .item_name = av_default_item_name,
+ .option = hls_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVInputFormat ff_hls_demuxer = {
.name = "hls,applehttp",
.long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"),
+ .priv_class = &hls_class,
.priv_data_size = sizeof(HLSContext),
.read_probe = hls_probe,
.read_header = hls_read_header,
diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
index 7aef02b..418f153 100644
--- a/libavformat/hlsenc.c
+++ b/libavformat/hlsenc.c
@@ -2,239 +2,977 @@
* 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 <config.h>
-
-#if CONFIG_OPENSSL
+#if CONFIG_GCRYPT
+#include <gcrypt.h>
+#elif CONFIG_OPENSSL
#include <openssl/rand.h>
#endif
+#include "libavutil/avassert.h"
#include "libavutil/mathematics.h"
#include "libavutil/parseutils.h"
#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
-#include "libavutil/opt.h"
#include "libavutil/random_seed.h"
+#include "libavutil/opt.h"
#include "libavutil/log.h"
+#include "libavutil/time_internal.h"
#include "avformat.h"
+#include "avio_internal.h"
#include "internal.h"
-
-typedef struct ListEntry {
- char name[1024];
- int64_t duration; // segment duration in AV_TIME_BASE units
- struct ListEntry *next;
-} ListEntry;
+#include "os_support.h"
+
+typedef enum {
+ HLS_START_SEQUENCE_AS_START_NUMBER = 0,
+ HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH = 1,
+ HLS_START_SEQUENCE_AS_FORMATTED_DATETIME = 2, // YYYYMMDDhhmmss
+} StartSequenceSourceType;
+
+#define KEYSIZE 16
+#define LINE_BUFFER_SIZE 1024
+#define HLS_MICROSECOND_UNIT 1000000
+
+typedef struct HLSSegment {
+ char filename[1024];
+ char sub_filename[1024];
+ double duration; /* in seconds */
+ int discont;
+ int64_t pos;
+ int64_t size;
+
+ char key_uri[LINE_BUFFER_SIZE + 1];
+ char iv_string[KEYSIZE*2 + 1];
+
+ 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),
+ HLS_ROUND_DURATIONS = (1 << 2),
+ HLS_DISCONT_START = (1 << 3),
+ HLS_OMIT_ENDLIST = (1 << 4),
+ HLS_SPLIT_BY_TIME = (1 << 5),
+ HLS_APPEND_LIST = (1 << 6),
+ HLS_PROGRAM_DATE_TIME = (1 << 7),
+ HLS_SECOND_LEVEL_SEGMENT_INDEX = (1 << 8), // include segment index in segment filenames when use_localtime e.g.: %%03d
+ HLS_SECOND_LEVEL_SEGMENT_DURATION = (1 << 9), // include segment duration (microsec) in segment filenames when use_localtime e.g.: %%09t
+ HLS_SECOND_LEVEL_SEGMENT_SIZE = (1 << 10), // include segment size (bytes) in segment filenames when use_localtime e.g.: %%014s
+ HLS_TEMP_FILE = (1 << 11),
+ HLS_PERIODIC_REKEY = (1 << 12),
+} HLSFlags;
+
+typedef enum {
+ SEGMENT_TYPE_MPEGTS,
+ SEGMENT_TYPE_FMP4,
+} SegmentType;
+
+typedef enum {
+ PLAYLIST_TYPE_NONE,
+ PLAYLIST_TYPE_EVENT,
+ PLAYLIST_TYPE_VOD,
+ PLAYLIST_TYPE_NB,
+} PlaylistType;
typedef struct HLSContext {
const AVClass *class; // Class for private options.
unsigned number;
int64_t sequence;
int64_t start_sequence;
+ uint32_t start_sequence_source_type; // enum StartSequenceSourceType
AVOutputFormat *oformat;
+ AVOutputFormat *vtt_oformat;
+
AVFormatContext *avf;
+ AVFormatContext *vtt_avf;
+
float time; // Set by a private option.
- int size; // Set by a private option.
+ float init_time; // Set by a private option.
+ int max_nb_segments; // Set by a private option.
+#if FF_API_HLS_WRAP
int wrap; // Set by a private option.
- int version; // Set by a private option.
- int allowcache;
+#endif
+ uint32_t flags; // enum HLSFlags
+ uint32_t pl_type; // enum PlaylistType
+ char *segment_filename;
+ char *fmp4_init_filename;
+ int segment_type;
+ int fmp4_init_mode;
+
+ int use_localtime; ///< flag to expand filename with localtime
+ int use_localtime_mkdir;///< flag to mkdir dirname in timebased filename
+ int allowcache;
int64_t recording_time;
int has_video;
- // The following timestamps are in AV_TIME_BASE units.
+ int has_subtitle;
+ int new_start;
+ double dpp; // duration per packet
int64_t start_pts;
int64_t end_pts;
- int64_t duration; // last segment duration computed so far.
+ double duration; // last segment duration computed so far, in seconds
+ int64_t start_pos; // last segment starting position
+ int64_t size; // last segment size
+ int64_t max_seg_size; // every segment file max size
int nb_entries;
- ListEntry *list;
- ListEntry *end_list;
+ int discontinuity_set;
+ int discontinuity;
+
+ HLSSegment *segments;
+ HLSSegment *last_segment;
+ HLSSegment *old_segments;
+
char *basename;
+ char *base_output_dirname;
+ char *vtt_basename;
+ char *vtt_m3u8_name;
char *baseurl;
+ char *format_options_str;
+ char *vtt_format_options_str;
+ char *subtitle_filename;
+ AVDictionary *format_options;
+
+ int encrypt;
+ char *key;
+ char *key_url;
+ char *iv;
+ char *key_basename;
- int encrypt; // Set by a private option.
- char *key; // Set by a private option.
- int key_len;
- char *key_url; // Set by a private option.
- char *iv; // Set by a private option.
- int iv_len;
+ char *key_info_file;
+ char key_file[LINE_BUFFER_SIZE + 1];
+ char key_uri[LINE_BUFFER_SIZE + 1];
+ char key_string[KEYSIZE*2 + 1];
+ char iv_string[KEYSIZE*2 + 1];
+ AVDictionary *vtt_format_options;
- char *key_basename;
+ char *method;
- AVDictionary *enc_opts;
+ double initial_prog_date_time;
+ char current_segment_final_filename_fmt[1024]; // when renaming segments
+ char *user_agent;
} HLSContext;
-
-static int randomize(uint8_t *buf, int len)
+static int get_int_from_double(double val)
{
-#if CONFIG_OPENSSL
- if (RAND_bytes(buf, len))
- return 0;
- return AVERROR(EIO);
-#else
- return AVERROR(ENOSYS);
-#endif
+ return (int)((val - (int)val) >= 0.001) ? (int)(val + 1) : (int)val;
+}
+
+static int mkdir_p(const char *path) {
+ int ret = 0;
+ char *temp = av_strdup(path);
+ char *pos = temp;
+ char tmp_ch = '\0';
+
+ if (!path || !temp) {
+ return -1;
+ }
+
+ if (!strncmp(temp, "/", 1) || !strncmp(temp, "\\", 1)) {
+ pos++;
+ } else if (!strncmp(temp, "./", 2) || !strncmp(temp, ".\\", 2)) {
+ pos += 2;
+ }
+
+ for ( ; *pos != '\0'; ++pos) {
+ if (*pos == '/' || *pos == '\\') {
+ tmp_ch = *pos;
+ *pos = '\0';
+ ret = mkdir(temp, 0755);
+ *pos = tmp_ch;
+ }
+ }
+
+ if ((*(pos - 1) != '/') || (*(pos - 1) != '\\')) {
+ ret = mkdir(temp, 0755);
+ }
+
+ av_free(temp);
+ return ret;
}
-static void free_encryption(AVFormatContext *s)
+static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSContext *c)
{
- HLSContext *hls = s->priv_data;
+ const char *proto = avio_find_protocol_name(s->filename);
+ int http_base_proto = proto ? (!av_strcasecmp(proto, "http") || !av_strcasecmp(proto, "https")) : 0;
+
+ if (c->method) {
+ av_dict_set(options, "method", c->method, 0);
+ } else if (http_base_proto) {
+ av_log(c, AV_LOG_WARNING, "No HTTP method set, hls muxer defaulting to method PUT.\n");
+ av_dict_set(options, "method", "PUT", 0);
+ }
+ if (c->user_agent)
+ av_dict_set(options, "user_agent", c->user_agent, 0);
- av_dict_free(&hls->enc_opts);
+}
- av_freep(&hls->key_basename);
+static int replace_int_data_in_filename(char *buf, int buf_size, const char *filename, char placeholder, int64_t number)
+{
+ const char *p;
+ char *q, buf1[20], c;
+ int nd, len, addchar_count;
+ int found_count = 0;
+
+ q = buf;
+ p = filename;
+ for (;;) {
+ c = *p;
+ if (c == '\0')
+ break;
+ if (c == '%' && *(p+1) == '%') // %%
+ addchar_count = 2;
+ else if (c == '%' && (av_isdigit(*(p+1)) || *(p+1) == placeholder)) {
+ nd = 0;
+ addchar_count = 1;
+ while (av_isdigit(*(p + addchar_count))) {
+ nd = nd * 10 + *(p + addchar_count) - '0';
+ addchar_count++;
+ }
+
+ if (*(p + addchar_count) == placeholder) {
+ len = snprintf(buf1, sizeof(buf1), "%0*"PRId64, (number < 0) ? nd : nd++, number);
+ if (len < 1) // returned error or empty buf1
+ goto fail;
+ if ((q - buf + len) > buf_size - 1)
+ goto fail;
+ memcpy(q, buf1, len);
+ q += len;
+ p += (addchar_count + 1);
+ addchar_count = 0;
+ found_count++;
+ }
+
+ } else
+ addchar_count = 1;
+
+ while (addchar_count--)
+ if ((q - buf) < buf_size - 1)
+ *q++ = *p++;
+ else
+ goto fail;
+ }
+ *q = '\0';
+ return found_count;
+fail:
+ *q = '\0';
+ return -1;
}
-static int dict_set_bin(AVDictionary **dict, const char *key,
- uint8_t *buf, size_t len)
+static void write_styp(AVIOContext *pb)
{
- char hex[33];
+ avio_wb32(pb, 24);
+ ffio_wfourcc(pb, "styp");
+ ffio_wfourcc(pb, "msdh");
+ avio_wb32(pb, 0); /* minor */
+ ffio_wfourcc(pb, "msdh");
+ ffio_wfourcc(pb, "msix");
+}
+
+static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls) {
- ff_data_to_hex(hex, buf, len, 0);
- hex[32] = '\0';
+ HLSSegment *segment, *previous_segment = NULL;
+ float playlist_duration = 0.0f;
+ int ret = 0, path_size, sub_path_size;
+ char *dirname = NULL, *p, *sub_path;
+ char *path = NULL;
+ AVDictionary *options = NULL;
+ AVIOContext *out = NULL;
+ const char *proto = NULL;
+
+ 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;
+ }
+ }
- return av_dict_set(dict, key, hex, 0);
+ if (segment && !hls->use_localtime_mkdir) {
+ 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 = (hls->use_localtime_mkdir ? 0 : strlen(dirname)) + strlen(segment->filename) + 1;
+ path = av_malloc(path_size);
+ if (!path) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ if (hls->use_localtime_mkdir)
+ av_strlcpy(path, segment->filename, path_size);
+ else { // segment->filename contains basename only
+ av_strlcpy(path, dirname, path_size);
+ av_strlcat(path, segment->filename, path_size);
+ }
+
+ proto = avio_find_protocol_name(s->filename);
+ if (hls->method || (proto && !av_strcasecmp(proto, "http"))) {
+ av_dict_set(&options, "method", "DELETE", 0);
+ if ((ret = hls->avf->io_open(hls->avf, &out, path, AVIO_FLAG_WRITE, &options)) < 0)
+ goto fail;
+ ff_format_io_close(hls->avf, &out);
+ } else if (unlink(path) < 0) {
+ av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n",
+ path, strerror(errno));
+ }
+
+ if ((segment->sub_filename[0] != '\0')) {
+ sub_path_size = strlen(segment->sub_filename) + 1 + (dirname ? strlen(dirname) : 0);
+ sub_path = av_malloc(sub_path_size);
+ if (!sub_path) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ av_strlcpy(sub_path, dirname, sub_path_size);
+ av_strlcat(sub_path, segment->sub_filename, sub_path_size);
+
+ if (hls->method || (proto && !av_strcasecmp(proto, "http"))) {
+ av_dict_set(&options, "method", "DELETE", 0);
+ if ((ret = hls->avf->io_open(hls->avf, &out, sub_path, AVIO_FLAG_WRITE, &options)) < 0) {
+ av_free(sub_path);
+ goto fail;
+ }
+ ff_format_io_close(hls->avf, &out);
+ } else if (unlink(sub_path) < 0) {
+ av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n",
+ sub_path, strerror(errno));
+ }
+ av_free(sub_path);
+ }
+ av_freep(&path);
+ previous_segment = segment;
+ segment = previous_segment->next;
+ av_free(previous_segment);
+ }
+
+fail:
+ av_free(path);
+ av_free(dirname);
+
+ return ret;
}
-static int setup_encryption(AVFormatContext *s)
+static int randomize(uint8_t *buf, int len)
+{
+#if CONFIG_GCRYPT
+ gcry_randomize(buf, len, GCRY_VERY_STRONG_RANDOM);
+ return 0;
+#elif CONFIG_OPENSSL
+ if (RAND_bytes(buf, len))
+ return 0;
+#else
+ return AVERROR(ENOSYS);
+#endif
+ return AVERROR(EINVAL);
+}
+
+static int do_encrypt(AVFormatContext *s)
{
HLSContext *hls = s->priv_data;
- AVIOContext *out = NULL;
- int len, ret;
- uint8_t buf[16];
- uint8_t *k;
+ int ret;
+ int len;
+ AVIOContext *pb;
+ uint8_t key[KEYSIZE];
len = strlen(hls->basename) + 4 + 1;
hls->key_basename = av_mallocz(len);
if (!hls->key_basename)
return AVERROR(ENOMEM);
- av_strlcpy(hls->key_basename, hls->basename + 7, len);
+ av_strlcpy(hls->key_basename, s->filename, len);
av_strlcat(hls->key_basename, ".key", len);
- if (hls->key) {
- if (hls->key_len != 16) {
- av_log(s, AV_LOG_ERROR,
- "Invalid key size %d, expected 16-bytes hex-coded key\n",
- hls->key_len);
- return AVERROR(EINVAL);
- }
-
- if ((ret = dict_set_bin(&hls->enc_opts, "key", hls->key, hls->key_len)) < 0)
- return ret;
- k = hls->key;
+ if (hls->key_url) {
+ av_strlcpy(hls->key_file, hls->key_url, sizeof(hls->key_file));
+ av_strlcpy(hls->key_uri, hls->key_url, sizeof(hls->key_uri));
} else {
- if ((ret = randomize(buf, sizeof(buf))) < 0) {
- av_log(s, AV_LOG_ERROR, "Cannot generate a strong random key\n");
- return ret;
+ av_strlcpy(hls->key_file, hls->key_basename, sizeof(hls->key_file));
+ av_strlcpy(hls->key_uri, hls->key_basename, sizeof(hls->key_uri));
+ }
+
+ if (!*hls->iv_string) {
+ uint8_t iv[16] = { 0 };
+ char buf[33];
+
+ if (!hls->iv) {
+ AV_WB64(iv + 8, hls->sequence);
+ } else {
+ memcpy(iv, hls->iv, sizeof(iv));
}
+ ff_data_to_hex(buf, iv, sizeof(iv), 0);
+ buf[32] = '\0';
+ memcpy(hls->iv_string, buf, sizeof(hls->iv_string));
+ }
- if ((ret = dict_set_bin(&hls->enc_opts, "key", buf, sizeof(buf))) < 0)
- return ret;
- k = buf;
+ if (!*hls->key_uri) {
+ av_log(hls, AV_LOG_ERROR, "no key URI specified in key info file\n");
+ return AVERROR(EINVAL);
}
- if (hls->iv) {
- if (hls->iv_len != 16) {
- av_log(s, AV_LOG_ERROR,
- "Invalid key size %d, expected 16-bytes hex-coded initialization vector\n",
- hls->iv_len);
- return AVERROR(EINVAL);
+ if (!*hls->key_file) {
+ av_log(hls, AV_LOG_ERROR, "no key file specified in key info file\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (!*hls->key_string) {
+ if (!hls->key) {
+ if ((ret = randomize(key, sizeof(key))) < 0) {
+ av_log(s, AV_LOG_ERROR, "Cannot generate a strong random key\n");
+ return ret;
+ }
+ } else {
+ memcpy(key, hls->key, sizeof(key));
}
- if ((ret = dict_set_bin(&hls->enc_opts, "iv", hls->iv, hls->iv_len)) < 0)
+ ff_data_to_hex(hls->key_string, key, sizeof(key), 0);
+ if ((ret = s->io_open(s, &pb, hls->key_file, AVIO_FLAG_WRITE, NULL)) < 0)
return ret;
+ avio_seek(pb, 0, SEEK_CUR);
+ avio_write(pb, key, KEYSIZE);
+ avio_close(pb);
}
+ return 0;
+}
+
+
+static int hls_encryption_start(AVFormatContext *s)
+{
+ HLSContext *hls = s->priv_data;
+ int ret;
+ AVIOContext *pb;
+ uint8_t key[KEYSIZE];
- if ((ret = s->io_open(s, &out, hls->key_basename, AVIO_FLAG_WRITE, NULL)) < 0)
+ if ((ret = s->io_open(s, &pb, hls->key_info_file, AVIO_FLAG_READ, NULL)) < 0) {
+ av_log(hls, AV_LOG_ERROR,
+ "error opening key info file %s\n", hls->key_info_file);
return ret;
+ }
+
+ ff_get_line(pb, hls->key_uri, sizeof(hls->key_uri));
+ hls->key_uri[strcspn(hls->key_uri, "\r\n")] = '\0';
- avio_write(out, k, 16);
+ ff_get_line(pb, hls->key_file, sizeof(hls->key_file));
+ hls->key_file[strcspn(hls->key_file, "\r\n")] = '\0';
- avio_close(out);
+ ff_get_line(pb, hls->iv_string, sizeof(hls->iv_string));
+ hls->iv_string[strcspn(hls->iv_string, "\r\n")] = '\0';
+
+ ff_format_io_close(s, &pb);
+
+ if (!*hls->key_uri) {
+ av_log(hls, AV_LOG_ERROR, "no key URI specified in key info file\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (!*hls->key_file) {
+ av_log(hls, AV_LOG_ERROR, "no key file specified in key info file\n");
+ return AVERROR(EINVAL);
+ }
+
+ if ((ret = s->io_open(s, &pb, hls->key_file, AVIO_FLAG_READ, NULL)) < 0) {
+ av_log(hls, AV_LOG_ERROR, "error opening key file %s\n", hls->key_file);
+ return ret;
+ }
+
+ ret = avio_read(pb, key, sizeof(key));
+ ff_format_io_close(s, &pb);
+ if (ret != sizeof(key)) {
+ av_log(hls, AV_LOG_ERROR, "error reading key file %s\n", hls->key_file);
+ if (ret >= 0 || ret == AVERROR_EOF)
+ ret = AVERROR(EINVAL);
+ return ret;
+ }
+ ff_data_to_hex(hls->key_string, key, sizeof(key), 0);
return 0;
}
+static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
+{
+ int len = ff_get_line(s, buf, maxlen);
+ while (len > 0 && av_isspace(buf[len - 1]))
+ buf[--len] = '\0';
+ return len;
+}
+
static int hls_mux_init(AVFormatContext *s)
{
+ AVDictionary *options = NULL;
HLSContext *hls = s->priv_data;
AVFormatContext *oc;
- int i;
+ AVFormatContext *vtt_oc = NULL;
+ int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);
+ 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->filename[0] = '\0';
oc->oformat = hls->oformat;
oc->interrupt_callback = s->interrupt_callback;
+ oc->max_delay = s->max_delay;
oc->opaque = s->opaque;
oc->io_open = s->io_open;
oc->io_close = s->io_close;
+ av_dict_copy(&oc->metadata, s->metadata, 0);
+
+ if(hls->vtt_oformat) {
+ ret = avformat_alloc_output_context2(&hls->vtt_avf, hls->vtt_oformat, NULL, NULL);
+ if (ret < 0)
+ return ret;
+ vtt_oc = hls->vtt_avf;
+ vtt_oc->oformat = hls->vtt_oformat;
+ av_dict_copy(&vtt_oc->metadata, s->metadata, 0);
+ }
for (i = 0; i < s->nb_streams; i++) {
AVStream *st;
- if (!(st = avformat_new_stream(oc, NULL)))
+ AVFormatContext *loc;
+ if (s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
+ loc = vtt_oc;
+ else
+ loc = oc;
+
+ if (!(st = avformat_new_stream(loc, NULL)))
return AVERROR(ENOMEM);
avcodec_parameters_copy(st->codecpar, s->streams[i]->codecpar);
+ if (!oc->oformat->codec_tag ||
+ av_codec_get_id (oc->oformat->codec_tag, s->streams[i]->codecpar->codec_tag) == st->codecpar->codec_id ||
+ av_codec_get_tag(oc->oformat->codec_tag, s->streams[i]->codecpar->codec_id) <= 0) {
+ st->codecpar->codec_tag = s->streams[i]->codecpar->codec_tag;
+ } else {
+ st->codecpar->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);
}
+ hls->start_pos = 0;
+ hls->new_start = 1;
+ hls->fmp4_init_mode = 0;
+
+ if (hls->segment_type == SEGMENT_TYPE_FMP4) {
+ if (hls->max_seg_size > 0) {
+ av_log(s, AV_LOG_WARNING, "Multi-file byterange mode is currently unsupported in the HLS muxer.\n");
+ return AVERROR_PATCHWELCOME;
+ }
+ hls->fmp4_init_mode = !byterange_mode;
+ set_http_options(s, &options, hls);
+ if ((ret = s->io_open(s, &oc->pb, hls->base_output_dirname, AVIO_FLAG_WRITE, &options)) < 0) {
+ av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", hls->fmp4_init_filename);
+ return ret;
+ }
+
+ 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);
+ return ret;
+ }
+ }
+ av_dict_copy(&options, hls->format_options, 0);
+ av_dict_set(&options, "fflags", "-autobsf", 0);
+ av_dict_set(&options, "movflags", "frag_custom+dash+delay_moov", 0);
+ ret = avformat_init_output(oc, &options);
+ if (ret < 0)
+ return ret;
+ if (av_dict_count(options)) {
+ av_log(s, AV_LOG_ERROR, "Some of the provided format options in '%s' are not recognized\n", hls->format_options_str);
+ av_dict_free(&options);
+ return AVERROR(EINVAL);
+ }
+ av_dict_free(&options);
+ }
return 0;
}
-static int append_entry(HLSContext *hls, int64_t duration)
+static HLSSegment *find_segment_by_filename(HLSSegment *segment, const char *filename)
+{
+ while (segment) {
+ if (!av_strcasecmp(segment->filename,filename))
+ return segment;
+ segment = segment->next;
+ }
+ return (HLSSegment *) NULL;
+}
+
+static int sls_flags_filename_process(struct AVFormatContext *s, HLSContext *hls, HLSSegment *en, double duration,
+ int64_t pos, int64_t size)
{
- ListEntry *en = av_malloc(sizeof(*en));
+ if ((hls->flags & (HLS_SECOND_LEVEL_SEGMENT_SIZE | HLS_SECOND_LEVEL_SEGMENT_DURATION)) &&
+ strlen(hls->current_segment_final_filename_fmt)) {
+ av_strlcpy(hls->avf->filename, hls->current_segment_final_filename_fmt, sizeof(hls->avf->filename));
+ if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_SIZE) {
+ char * filename = av_strdup(hls->avf->filename); // %%s will be %s after strftime
+ if (!filename) {
+ av_free(en);
+ return AVERROR(ENOMEM);
+ }
+ if (replace_int_data_in_filename(hls->avf->filename, sizeof(hls->avf->filename),
+ filename, 's', pos + size) < 1) {
+ av_log(hls, AV_LOG_ERROR,
+ "Invalid second level segment filename template '%s', "
+ "you can try to remove second_level_segment_size flag\n",
+ filename);
+ av_free(filename);
+ av_free(en);
+ return AVERROR(EINVAL);
+ }
+ av_free(filename);
+ }
+ if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_DURATION) {
+ char * filename = av_strdup(hls->avf->filename); // %%t will be %t after strftime
+ if (!filename) {
+ av_free(en);
+ return AVERROR(ENOMEM);
+ }
+ if (replace_int_data_in_filename(hls->avf->filename, sizeof(hls->avf->filename),
+ filename, 't', (int64_t)round(duration * HLS_MICROSECOND_UNIT)) < 1) {
+ av_log(hls, AV_LOG_ERROR,
+ "Invalid second level segment filename template '%s', "
+ "you can try to remove second_level_segment_time flag\n",
+ filename);
+ av_free(filename);
+ av_free(en);
+ return AVERROR(EINVAL);
+ }
+ av_free(filename);
+ }
+ }
+ return 0;
+}
+
+static int sls_flag_check_duration_size_index(HLSContext *hls)
+{
+ int ret = 0;
+
+ if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_DURATION) {
+ av_log(hls, AV_LOG_ERROR,
+ "second_level_segment_duration hls_flag requires use_localtime to be true\n");
+ ret = AVERROR(EINVAL);
+ }
+ if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_SIZE) {
+ av_log(hls, AV_LOG_ERROR,
+ "second_level_segment_size hls_flag requires use_localtime to be true\n");
+ ret = AVERROR(EINVAL);
+ }
+ if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_INDEX) {
+ av_log(hls, AV_LOG_ERROR,
+ "second_level_segment_index hls_flag requires use_localtime to be true\n");
+ ret = AVERROR(EINVAL);
+ }
+
+ return ret;
+}
+
+static int sls_flag_check_duration_size(HLSContext *hls)
+{
+ const char *proto = avio_find_protocol_name(hls->basename);
+ int segment_renaming_ok = proto && !strcmp(proto, "file");
+ int ret = 0;
+
+ if ((hls->flags & HLS_SECOND_LEVEL_SEGMENT_DURATION) && !segment_renaming_ok) {
+ av_log(hls, AV_LOG_ERROR,
+ "second_level_segment_duration hls_flag works only with file protocol segment names\n");
+ ret = AVERROR(EINVAL);
+ }
+ if ((hls->flags & HLS_SECOND_LEVEL_SEGMENT_SIZE) && !segment_renaming_ok) {
+ av_log(hls, AV_LOG_ERROR,
+ "second_level_segment_size hls_flag works only with file protocol segment names\n");
+ ret = AVERROR(EINVAL);
+ }
+
+ return ret;
+}
+
+static void sls_flag_file_rename(HLSContext *hls, char *old_filename) {
+ if ((hls->flags & (HLS_SECOND_LEVEL_SEGMENT_SIZE | HLS_SECOND_LEVEL_SEGMENT_DURATION)) &&
+ strlen(hls->current_segment_final_filename_fmt)) {
+ ff_rename(old_filename, hls->avf->filename, hls);
+ }
+}
+
+static int sls_flag_use_localtime_filename(AVFormatContext *oc, HLSContext *c)
+{
+ if (c->flags & HLS_SECOND_LEVEL_SEGMENT_INDEX) {
+ char * filename = av_strdup(oc->filename); // %%d will be %d after strftime
+ if (!filename)
+ return AVERROR(ENOMEM);
+ if (replace_int_data_in_filename(oc->filename, sizeof(oc->filename),
+#if FF_API_HLS_WRAP
+ filename, 'd', c->wrap ? c->sequence % c->wrap : c->sequence) < 1) {
+#else
+ filename, 'd', c->sequence) < 1) {
+#endif
+ av_log(c, AV_LOG_ERROR, "Invalid second level segment filename template '%s', "
+ "you can try to remove second_level_segment_index flag\n",
+ filename);
+ av_free(filename);
+ return AVERROR(EINVAL);
+ }
+ av_free(filename);
+ }
+ if (c->flags & (HLS_SECOND_LEVEL_SEGMENT_SIZE | HLS_SECOND_LEVEL_SEGMENT_DURATION)) {
+ av_strlcpy(c->current_segment_final_filename_fmt, oc->filename,
+ sizeof(c->current_segment_final_filename_fmt));
+ if (c->flags & HLS_SECOND_LEVEL_SEGMENT_SIZE) {
+ char * filename = av_strdup(oc->filename); // %%s will be %s after strftime
+ if (!filename)
+ return AVERROR(ENOMEM);
+ if (replace_int_data_in_filename(oc->filename, sizeof(oc->filename), filename, 's', 0) < 1) {
+ av_log(c, AV_LOG_ERROR, "Invalid second level segment filename template '%s', "
+ "you can try to remove second_level_segment_size flag\n",
+ filename);
+ av_free(filename);
+ return AVERROR(EINVAL);
+ }
+ av_free(filename);
+ }
+ if (c->flags & HLS_SECOND_LEVEL_SEGMENT_DURATION) {
+ char * filename = av_strdup(oc->filename); // %%t will be %t after strftime
+ if (!filename)
+ return AVERROR(ENOMEM);
+ if (replace_int_data_in_filename(oc->filename, sizeof(oc->filename), filename, 't', 0) < 1) {
+ av_log(c, AV_LOG_ERROR, "Invalid second level segment filename template '%s', "
+ "you can try to remove second_level_segment_time flag\n",
+ filename);
+ av_free(filename);
+ return AVERROR(EINVAL);
+ }
+ av_free(filename);
+ }
+ }
+ return 0;
+}
+
+/* Create a new segment and append it to the segment list */
+static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, double duration,
+ int64_t pos, int64_t size)
+{
+ HLSSegment *en = av_malloc(sizeof(*en));
+ const char *filename;
+ int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);
+ int ret;
if (!en)
return AVERROR(ENOMEM);
- av_strlcpy(en->name, av_basename(hls->avf->filename), sizeof(en->name));
+ ret = sls_flags_filename_process(s, hls, en, duration, pos, size);
+ if (ret < 0) {
+ return ret;
+ }
+
+ filename = av_basename(hls->avf->filename);
+
+ if (hls->use_localtime_mkdir) {
+ filename = hls->avf->filename;
+ }
+ if ((find_segment_by_filename(hls->segments, filename) || find_segment_by_filename(hls->old_segments, filename))
+ && !byterange_mode) {
+ av_log(hls, AV_LOG_WARNING, "Duplicated segment filename detected: %s\n", filename);
+ }
+ av_strlcpy(en->filename, filename, sizeof(en->filename));
+
+ if(hls->has_subtitle)
+ av_strlcpy(en->sub_filename, av_basename(hls->vtt_avf->filename), sizeof(en->sub_filename));
+ else
+ en->sub_filename[0] = '\0';
en->duration = duration;
+ en->pos = pos;
+ en->size = size;
en->next = NULL;
+ en->discont = 0;
+
+ if (hls->discontinuity) {
+ en->discont = 1;
+ hls->discontinuity = 0;
+ }
- if (!hls->list)
- hls->list = en;
+ if (hls->key_info_file || hls->encrypt) {
+ av_strlcpy(en->key_uri, hls->key_uri, sizeof(en->key_uri));
+ av_strlcpy(en->iv_string, hls->iv_string, sizeof(en->iv_string));
+ }
+
+ if (!hls->segments)
+ hls->segments = en;
else
- hls->end_list->next = en;
+ hls->last_segment->next = en;
- hls->end_list = en;
+ hls->last_segment = en;
- if (hls->nb_entries >= hls->size) {
- en = hls->list;
- hls->list = en->next;
- av_free(en);
+ // EVENT or VOD playlists imply sliding window cannot be used
+ if (hls->pl_type != PLAYLIST_TYPE_NONE)
+ hls->max_nb_segments = 0;
+
+ if (hls->max_nb_segments && hls->nb_entries >= hls->max_nb_segments) {
+ en = hls->segments;
+ hls->initial_prog_date_time += en->duration;
+ hls->segments = en->next;
+ if (en && hls->flags & HLS_DELETE_SEGMENTS &&
+#if FF_API_HLS_WRAP
+ !(hls->flags & HLS_SINGLE_FILE || hls->wrap)) {
+#else
+ !(hls->flags & HLS_SINGLE_FILE)) {
+#endif
+ en->next = hls->old_segments;
+ hls->old_segments = en;
+ if ((ret = hls_delete_old_segments(s, hls)) < 0)
+ return ret;
+ } else
+ av_free(en);
} else
hls->nb_entries++;
+ if (hls->max_seg_size > 0) {
+ return 0;
+ }
hls->sequence++;
return 0;
}
-static void free_entries(HLSContext *hls)
+static int parse_playlist(AVFormatContext *s, const char *url)
{
- ListEntry *p = hls->list, *en;
+ HLSContext *hls = s->priv_data;
+ AVIOContext *in;
+ int ret = 0, is_segment = 0;
+ int64_t new_start_pos;
+ char line[1024];
+ const char *ptr;
+ const char *end;
+
+ if ((ret = ffio_open_whitelist(&in, url, AVIO_FLAG_READ,
+ &s->interrupt_callback, NULL,
+ s->protocol_whitelist, s->protocol_blacklist)) < 0)
+ return ret;
+
+ read_chomp_line(in, line, sizeof(line));
+ if (strcmp(line, "#EXTM3U")) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ hls->discontinuity = 0;
+ while (!avio_feof(in)) {
+ read_chomp_line(in, line, sizeof(line));
+ if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
+ int64_t tmp_sequence = strtoll(ptr, NULL, 10);
+ if (tmp_sequence < hls->sequence)
+ av_log(hls, AV_LOG_VERBOSE,
+ "Found playlist sequence number was smaller """
+ "than specified start sequence number: %"PRId64" < %"PRId64", "
+ "omitting\n", tmp_sequence, hls->start_sequence);
+ else {
+ av_log(hls, AV_LOG_DEBUG, "Found playlist sequence number: %"PRId64"\n", tmp_sequence);
+ hls->sequence = tmp_sequence;
+ }
+ } else if (av_strstart(line, "#EXT-X-DISCONTINUITY", &ptr)) {
+ is_segment = 1;
+ hls->discontinuity = 1;
+ } else if (av_strstart(line, "#EXTINF:", &ptr)) {
+ is_segment = 1;
+ hls->duration = atof(ptr);
+ } else if (av_stristart(line, "#EXT-X-KEY:", &ptr)) {
+ ptr = av_stristr(line, "URI=\"");
+ if (ptr) {
+ ptr += strlen("URI=\"");
+ end = av_stristr(ptr, ",");
+ if (end) {
+ av_strlcpy(hls->key_uri, ptr, end - ptr);
+ } else {
+ av_strlcpy(hls->key_uri, ptr, sizeof(hls->key_uri));
+ }
+ }
+
+ ptr = av_stristr(line, "IV=0x");
+ if (ptr) {
+ ptr += strlen("IV=0x");
+ end = av_stristr(ptr, ",");
+ if (end) {
+ av_strlcpy(hls->iv_string, ptr, end - ptr);
+ } else {
+ av_strlcpy(hls->iv_string, ptr, sizeof(hls->iv_string));
+ }
+ }
+
+ } else if (av_strstart(line, "#", NULL)) {
+ continue;
+ } else if (line[0]) {
+ if (is_segment) {
+ is_segment = 0;
+ new_start_pos = avio_tell(hls->avf->pb);
+ hls->size = new_start_pos - hls->start_pos;
+ av_strlcpy(hls->avf->filename, line, sizeof(line));
+ ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size);
+ if (ret < 0)
+ goto fail;
+ hls->start_pos = new_start_pos;
+ }
+ }
+ }
+
+fail:
+ avio_close(in);
+ return ret;
+}
+
+static void hls_free_segments(HLSSegment *p)
+{
+ HLSSegment *en;
while(p) {
en = p;
@@ -243,75 +981,174 @@ static void free_entries(HLSContext *hls)
}
}
+static void write_m3u8_head_block(HLSContext *hls, AVIOContext *out, int version,
+ int target_duration, int64_t sequence)
+{
+ avio_printf(out, "#EXTM3U\n");
+ avio_printf(out, "#EXT-X-VERSION:%d\n", version);
+ if (hls->allowcache == 0 || hls->allowcache == 1) {
+ avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES");
+ }
+ avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration);
+ avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
+ av_log(hls, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
+}
+
+static void hls_rename_temp_file(AVFormatContext *s, AVFormatContext *oc)
+{
+ size_t len = strlen(oc->filename);
+ char final_filename[sizeof(oc->filename)];
+
+ av_strlcpy(final_filename, oc->filename, len);
+ final_filename[len-4] = '\0';
+ ff_rename(oc->filename, final_filename, s);
+ oc->filename[len-4] = '\0';
+}
+
static int hls_window(AVFormatContext *s, int last)
{
HLSContext *hls = s->priv_data;
- ListEntry *en;
- int64_t target_duration = 0;
+ HLSSegment *en;
+ int target_duration = 0;
int ret = 0;
AVIOContext *out = NULL;
+ AVIOContext *sub_out = NULL;
char temp_filename[1024];
- 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 = 3;
+ const char *proto = avio_find_protocol_name(s->filename);
+ int use_rename = proto && !strcmp(proto, "file");
+ static unsigned warned_non_file;
+ char *key_uri = NULL;
+ char *iv_string = NULL;
+ AVDictionary *options = NULL;
+ double prog_date_time = hls->initial_prog_date_time;
+ int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);
+
+ if (byterange_mode) {
+ version = 4;
+ sequence = 0;
+ }
- snprintf(temp_filename, sizeof(temp_filename), "%s.tmp", s->filename);
- if ((ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, NULL)) < 0)
+ if (hls->segment_type == SEGMENT_TYPE_FMP4) {
+ version = 7;
+ }
+
+ if (!use_rename && !warned_non_file++)
+ av_log(s, AV_LOG_ERROR, "Cannot use rename on non file protocol, this may lead to races and temporary partial files\n");
+
+ set_http_options(s, &options, hls);
+ snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", s->filename);
+ if ((ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, &options)) < 0)
goto fail;
- for (en = hls->list; en; en = en->next) {
- if (target_duration < en->duration)
- target_duration = en->duration;
+ for (en = hls->segments; en; en = en->next) {
+ if (target_duration <= en->duration)
+ target_duration = get_int_from_double(en->duration);
}
- avio_printf(out, "#EXTM3U\n");
- avio_printf(out, "#EXT-X-VERSION:%d\n", hls->version);
- if (hls->allowcache == 0 || hls->allowcache == 1) {
- avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES");
+ hls->discontinuity_set = 0;
+ write_m3u8_head_block(hls, out, version, target_duration, sequence);
+ if (hls->pl_type == PLAYLIST_TYPE_EVENT) {
+ avio_printf(out, "#EXT-X-PLAYLIST-TYPE:EVENT\n");
+ } else if (hls->pl_type == PLAYLIST_TYPE_VOD) {
+ avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n");
}
- avio_printf(out, "#EXT-X-TARGETDURATION:%"PRId64"\n",
- av_rescale_rnd(target_duration, 1, AV_TIME_BASE,
- AV_ROUND_UP));
- avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
- av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n",
- sequence);
+ if((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && hls->discontinuity_set==0 ){
+ avio_printf(out, "#EXT-X-DISCONTINUITY\n");
+ hls->discontinuity_set = 1;
+ }
+ for (en = hls->segments; en; en = en->next) {
+ if ((hls->encrypt || hls->key_info_file) && (!key_uri || strcmp(en->key_uri, key_uri) ||
+ av_strcasecmp(en->iv_string, iv_string))) {
+ avio_printf(out, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri);
+ if (*en->iv_string)
+ avio_printf(out, ",IV=0x%s", en->iv_string);
+ avio_printf(out, "\n");
+ key_uri = en->key_uri;
+ iv_string = en->iv_string;
+ }
- for (en = hls->list; en; en = en->next) {
- if (hls->encrypt) {
- char *key_url;
+ if (en->discont) {
+ avio_printf(out, "#EXT-X-DISCONTINUITY\n");
+ }
- if (hls->key_url)
- key_url = hls->key_url;
- else
- key_url = hls->baseurl;
-
- avio_printf(out, "#EXT-X-KEY:METHOD=AES-128");
- avio_printf(out, ",URI=\"");
- if (key_url)
- avio_printf(out, "%s", key_url);
- avio_printf(out, "%s\"", av_basename(hls->key_basename));
- if (hls->iv)
- avio_printf(out, ",IV=\"0x%s\"", hls->iv);
+ if ((hls->segment_type == SEGMENT_TYPE_FMP4) && (en == hls->segments)) {
+ avio_printf(out, "#EXT-X-MAP:URI=\"%s\"", hls->fmp4_init_filename);
+ if (hls->flags & HLS_SINGLE_FILE) {
+ avio_printf(out, ",BYTERANGE=\"%"PRId64"@%"PRId64"\"", en->size, en->pos);
+ }
avio_printf(out, "\n");
+ } else {
+ if (hls->flags & HLS_ROUND_DURATIONS)
+ avio_printf(out, "#EXTINF:%ld,\n", lrint(en->duration));
+ else
+ avio_printf(out, "#EXTINF:%f,\n", en->duration);
+ if (byterange_mode)
+ avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64"\n",
+ en->size, en->pos);
+ }
+ if (hls->flags & HLS_PROGRAM_DATE_TIME) {
+ time_t tt, wrongsecs;
+ int milli;
+ struct tm *tm, tmpbuf;
+ char buf0[128], buf1[128];
+ tt = (int64_t)prog_date_time;
+ milli = av_clip(lrint(1000*(prog_date_time - tt)), 0, 999);
+ tm = localtime_r(&tt, &tmpbuf);
+ strftime(buf0, sizeof(buf0), "%Y-%m-%dT%H:%M:%S", tm);
+ if (!strftime(buf1, sizeof(buf1), "%z", tm) || buf1[1]<'0' ||buf1[1]>'2') {
+ int tz_min, dst = tm->tm_isdst;
+ tm = gmtime_r(&tt, &tmpbuf);
+ tm->tm_isdst = dst;
+ wrongsecs = mktime(tm);
+ tz_min = (abs(wrongsecs - tt) + 30) / 60;
+ snprintf(buf1, sizeof(buf1),
+ "%c%02d%02d",
+ wrongsecs <= tt ? '+' : '-',
+ tz_min / 60,
+ tz_min % 60);
+ }
+ avio_printf(out, "#EXT-X-PROGRAM-DATE-TIME:%s.%03d%s\n", buf0, milli, buf1);
+ prog_date_time += en->duration;
+ }
+ if (!((hls->segment_type == SEGMENT_TYPE_FMP4) && (en == hls->segments))) {
+ if (hls->baseurl)
+ avio_printf(out, "%s", hls->baseurl);
+ avio_printf(out, "%s\n", en->filename);
}
-
- if (hls->version > 2)
- avio_printf(out, "#EXTINF:%f\n",
- (double)en->duration / AV_TIME_BASE);
- else
- avio_printf(out, "#EXTINF:%"PRId64",\n",
- av_rescale(en->duration, 1, AV_TIME_BASE));
- if (hls->baseurl)
- avio_printf(out, "%s", hls->baseurl);
- avio_printf(out, "%s\n", en->name);
}
- if (last)
+ if (last && (hls->flags & HLS_OMIT_ENDLIST)==0)
avio_printf(out, "#EXT-X-ENDLIST\n");
+ if( hls->vtt_m3u8_name ) {
+ if ((ret = s->io_open(s, &sub_out, hls->vtt_m3u8_name, AVIO_FLAG_WRITE, &options)) < 0)
+ goto fail;
+ write_m3u8_head_block(hls, sub_out, version, target_duration, sequence);
+
+ for (en = hls->segments; en; en = en->next) {
+ avio_printf(sub_out, "#EXTINF:%f,\n", en->duration);
+ if (byterange_mode)
+ avio_printf(sub_out, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
+ en->size, en->pos);
+ if (hls->baseurl)
+ avio_printf(sub_out, "%s", hls->baseurl);
+ avio_printf(sub_out, "%s\n", en->sub_filename);
+ }
+
+ if (last)
+ avio_printf(sub_out, "#EXT-X-ENDLIST\n");
+
+ }
+
fail:
+ av_dict_free(&options);
ff_format_io_close(s, &out);
- if (ret >= 0)
- ff_rename(temp_filename, s->filename);
+ ff_format_io_close(s, &sub_out);
+ if (ret >= 0 && use_rename)
+ ff_rename(temp_filename, s->filename, s);
return ret;
}
@@ -319,124 +1156,431 @@ static int hls_start(AVFormatContext *s)
{
HLSContext *c = s->priv_data;
AVFormatContext *oc = c->avf;
+ AVFormatContext *vtt_oc = c->vtt_avf;
+ AVDictionary *options = NULL;
+ char *filename, iv_string[KEYSIZE*2 + 1];
int err = 0;
- AVDictionary *opts = NULL;
-
- 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));
+ if (c->vtt_basename)
+ av_strlcpy(vtt_oc->filename, c->vtt_basename,
+ sizeof(vtt_oc->filename));
+ } else if (c->max_seg_size > 0) {
+ if (replace_int_data_in_filename(oc->filename, sizeof(oc->filename),
+#if FF_API_HLS_WRAP
+ c->basename, 'd', c->wrap ? c->sequence % c->wrap : c->sequence) < 1) {
+#else
+ c->basename, 'd', c->sequence) < 1) {
+#endif
+ av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s', you can try to use -use_localtime 1 with it\n", c->basename);
+ return AVERROR(EINVAL);
+ }
+ } else {
+ if (c->use_localtime) {
+ time_t now0;
+ struct tm *tm, tmpbuf;
+ time(&now0);
+ tm = localtime_r(&now0, &tmpbuf);
+ if (!strftime(oc->filename, sizeof(oc->filename), c->basename, tm)) {
+ av_log(oc, AV_LOG_ERROR, "Could not get segment filename with use_localtime\n");
+ return AVERROR(EINVAL);
+ }
+
+ err = sls_flag_use_localtime_filename(oc, c);
+ if (err < 0) {
+ return AVERROR(ENOMEM);
+ }
+
+ if (c->use_localtime_mkdir) {
+ const char *dir;
+ char *fn_copy = av_strdup(oc->filename);
+ if (!fn_copy) {
+ return AVERROR(ENOMEM);
+ }
+ dir = av_dirname(fn_copy);
+ if (mkdir_p(dir) == -1 && errno != EEXIST) {
+ av_log(oc, AV_LOG_ERROR, "Could not create directory %s with use_localtime_mkdir\n", dir);
+ av_free(fn_copy);
+ return AVERROR(errno);
+ }
+ av_free(fn_copy);
+ }
+ } else if (replace_int_data_in_filename(oc->filename, sizeof(oc->filename),
+#if FF_API_HLS_WRAP
+ c->basename, 'd', c->wrap ? c->sequence % c->wrap : c->sequence) < 1) {
+#else
+ c->basename, 'd', c->sequence) < 1) {
+#endif
+ av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s' you can try to use -use_localtime 1 with it\n", c->basename);
+ return AVERROR(EINVAL);
+ }
+ if( c->vtt_basename) {
+ if (replace_int_data_in_filename(vtt_oc->filename, sizeof(vtt_oc->filename),
+#if FF_API_HLS_WRAP
+ c->vtt_basename, 'd', c->wrap ? c->sequence % c->wrap : c->sequence) < 1) {
+#else
+ c->vtt_basename, 'd', c->sequence) < 1) {
+#endif
+ av_log(vtt_oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->vtt_basename);
+ return AVERROR(EINVAL);
+ }
+ }
+ }
c->number++;
- if (c->encrypt) {
- if ((err = av_dict_copy(&opts, c->enc_opts, 0)) < 0)
- return err;
- if (!c->iv) {
- uint8_t iv[16] = { 0 };
- char buf[33];
+ set_http_options(s, &options, c);
+
+ if (c->flags & HLS_TEMP_FILE) {
+ av_strlcat(oc->filename, ".tmp", sizeof(oc->filename));
+ }
- AV_WB64(iv + 8, c->sequence);
- ff_data_to_hex(buf, iv, sizeof(iv), 0);
- buf[32] = '\0';
+ if (c->key_info_file || c->encrypt) {
+ if (c->key_info_file && c->encrypt) {
+ av_log(s, AV_LOG_WARNING, "Cannot use both -hls_key_info_file and -hls_enc,"
+ " will use -hls_key_info_file priority\n");
+ }
- if ((err = av_dict_set(&opts, "iv", buf, 0)) < 0)
- goto fail;
+ if (c->number <= 1 || (c->flags & HLS_PERIODIC_REKEY)) {
+ if (c->key_info_file) {
+ if ((err = hls_encryption_start(s)) < 0)
+ goto fail;
+ } else {
+ if ((err = do_encrypt(s)) < 0)
+ goto fail;
+ }
}
+ if ((err = av_dict_set(&options, "encryption_key", c->key_string, 0))
+ < 0)
+ goto fail;
+ err = av_strlcpy(iv_string, c->iv_string, sizeof(iv_string));
+ if (!err)
+ snprintf(iv_string, sizeof(iv_string), "%032"PRIx64, c->sequence);
+ if ((err = av_dict_set(&options, "encryption_iv", iv_string, 0)) < 0)
+ goto fail;
+
+ filename = av_asprintf("crypto:%s", oc->filename);
+ if (!filename) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+ err = s->io_open(s, &oc->pb, filename, AVIO_FLAG_WRITE, &options);
+ av_free(filename);
+ av_dict_free(&options);
+ if (err < 0)
+ return err;
+ } else
+ if ((err = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, &options)) < 0)
+ goto fail;
+ if (c->vtt_basename) {
+ set_http_options(s, &options, c);
+ if ((err = s->io_open(s, &vtt_oc->pb, vtt_oc->filename, AVIO_FLAG_WRITE, &options)) < 0)
+ goto fail;
}
+ av_dict_free(&options);
- if ((err = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, &opts)) < 0)
- return err;
+ if (c->segment_type == SEGMENT_TYPE_FMP4 && !(c->flags & HLS_SINGLE_FILE)) {
+ write_styp(oc->pb);
+ } else {
+ /* We only require one PAT/PMT per segment. */
+ if (oc->oformat->priv_class && oc->priv_data) {
+ char period[21];
+
+ snprintf(period, sizeof(period), "%d", (INT_MAX / 2) - 1);
+
+ av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0);
+ av_opt_set(oc->priv_data, "sdt_period", period, 0);
+ av_opt_set(oc->priv_data, "pat_period", period, 0);
+ }
+ }
- if (oc->oformat->priv_class && oc->priv_data)
- av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0);
+ if (c->vtt_basename) {
+ err = avformat_write_header(vtt_oc,NULL);
+ if (err < 0)
+ return err;
+ }
+ return 0;
fail:
- av_dict_free(&opts);
+ av_dict_free(&options);
return err;
}
-static int hls_setup(AVFormatContext *s)
+static const char * get_default_pattern_localtime_fmt(AVFormatContext *s)
{
+ char b[21];
+ time_t t = time(NULL);
+ struct tm *p, tmbuf;
HLSContext *hls = s->priv_data;
- const char *pattern = "%d.ts";
- int basename_size = strlen(s->filename) + strlen(pattern) + 1;
- char *p;
-
- if (hls->encrypt)
- basename_size += 7;
-
- hls->basename = av_mallocz(basename_size);
- if (!hls->basename)
- return AVERROR(ENOMEM);
- // TODO: support protocol nesting?
- if (hls->encrypt)
- strcpy(hls->basename, "crypto:");
-
- av_strlcat(hls->basename, s->filename, basename_size);
-
- p = strrchr(hls->basename, '.');
-
- if (p)
- *p = '\0';
-
- if (hls->encrypt) {
- int ret = setup_encryption(s);
- if (ret < 0)
- return ret;
+ p = localtime_r(&t, &tmbuf);
+ // no %s support when strftime returned error or left format string unchanged
+ // also no %s support on MSVC, which invokes the invalid parameter handler on unsupported format strings, instead of returning an error
+ if (hls->segment_type == SEGMENT_TYPE_FMP4) {
+ return (HAVE_LIBC_MSVCRT || !strftime(b, sizeof(b), "%s", p) || !strcmp(b, "%s")) ? "-%Y%m%d%H%M%S.m4s" : "-%s.m4s";
}
-
- av_strlcat(hls->basename, pattern, basename_size);
-
- return 0;
+ return (HAVE_LIBC_MSVCRT || !strftime(b, sizeof(b), "%s", p) || !strcmp(b, "%s")) ? "-%Y%m%d%H%M%S.ts" : "-%s.ts";
}
static int hls_write_header(AVFormatContext *s)
{
HLSContext *hls = s->priv_data;
int ret, i;
+ char *p = NULL;
+ const char *pattern = "%d.ts";
+ const char *pattern_localtime_fmt = get_default_pattern_localtime_fmt(s);
+ const char *vtt_pattern = "%d.vtt";
+ AVDictionary *options = NULL;
+ int basename_size = 0;
+ int vtt_basename_size = 0;
+
+ if (hls->segment_type == SEGMENT_TYPE_FMP4) {
+ pattern = "%d.m4s";
+ }
+ if ((hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) ||
+ (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_FORMATTED_DATETIME)) {
+ time_t t = time(NULL); // we will need it in either case
+ if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) {
+ hls->start_sequence = (int64_t)t;
+ } else if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_FORMATTED_DATETIME) {
+ char b[15];
+ struct tm *p, tmbuf;
+ if (!(p = localtime_r(&t, &tmbuf)))
+ return AVERROR(ENOMEM);
+ if (!strftime(b, sizeof(b), "%Y%m%d%H%M%S", p))
+ return AVERROR(ENOMEM);
+ hls->start_sequence = strtoll(b, NULL, 10);
+ }
+ av_log(hls, AV_LOG_DEBUG, "start_number evaluated to %"PRId64"\n", hls->start_sequence);
+ }
hls->sequence = hls->start_sequence;
- hls->recording_time = hls->time * AV_TIME_BASE;
+ hls->recording_time = (hls->init_time ? hls->init_time : hls->time) * AV_TIME_BASE;
hls->start_pts = AV_NOPTS_VALUE;
+ hls->current_segment_final_filename_fmt[0] = '\0';
+
+ if (hls->flags & HLS_PROGRAM_DATE_TIME) {
+ time_t now0;
+ time(&now0);
+ hls->initial_prog_date_time = now0;
+ }
+
+ 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++)
+ for (i = 0; i < s->nb_streams; i++) {
hls->has_video +=
s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
+ hls->has_subtitle +=
+ s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE;
+ }
if (hls->has_video > 1)
av_log(s, AV_LOG_WARNING,
"More than a single video stream present, "
"expect issues decoding it.\n");
- hls->oformat = av_guess_format("mpegts", NULL, NULL);
+ if (hls->segment_type == SEGMENT_TYPE_FMP4) {
+ hls->oformat = av_guess_format("mp4", NULL, NULL);
+ } else {
+ hls->oformat = av_guess_format("mpegts", NULL, NULL);
+ }
if (!hls->oformat) {
ret = AVERROR_MUXER_NOT_FOUND;
goto fail;
}
- if ((ret = hls_setup(s)) < 0)
- goto fail;
+ if(hls->has_subtitle) {
+ hls->vtt_oformat = av_guess_format("webvtt", NULL, NULL);
+ if (!hls->oformat) {
+ ret = AVERROR_MUXER_NOT_FOUND;
+ 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) {
+ if (hls->segment_type == SEGMENT_TYPE_FMP4) {
+ pattern = ".m4s";
+ } else {
+ pattern = ".ts";
+ }
+ }
+
+ if (hls->use_localtime) {
+ basename_size = strlen(s->filename) + strlen(pattern_localtime_fmt) + 1;
+ } else {
+ 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';
+ if (hls->use_localtime) {
+ av_strlcat(hls->basename, pattern_localtime_fmt, basename_size);
+ } else {
+ av_strlcat(hls->basename, pattern, basename_size);
+ }
+ }
+
+ if (av_strcasecmp(hls->fmp4_init_filename, "init.mp4")) {
+ int fmp4_init_filename_len = strlen(hls->fmp4_init_filename) + 1;
+ hls->base_output_dirname = av_malloc(fmp4_init_filename_len);
+ if (!hls->base_output_dirname) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ av_strlcpy(hls->base_output_dirname, hls->fmp4_init_filename, fmp4_init_filename_len);
+ } else {
+ hls->base_output_dirname = av_malloc(basename_size);
+ if (!hls->base_output_dirname) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ av_strlcpy(hls->base_output_dirname, s->filename, basename_size);
+ p = strrchr(hls->base_output_dirname, '/');
+ if (p) {
+ *(p + 1) = '\0';
+ av_strlcat(hls->base_output_dirname, hls->fmp4_init_filename, basename_size);
+ } else {
+ av_strlcpy(hls->base_output_dirname, hls->fmp4_init_filename, basename_size);
+ }
+ }
+
+ if (!hls->use_localtime) {
+ ret = sls_flag_check_duration_size_index(hls);
+ if (ret < 0) {
+ goto fail;
+ }
+ } else {
+ ret = sls_flag_check_duration_size(hls);
+ if (ret < 0) {
+ goto fail;
+ }
+ }
+ if(hls->has_subtitle) {
+
+ if (hls->flags & HLS_SINGLE_FILE)
+ vtt_pattern = ".vtt";
+ vtt_basename_size = strlen(s->filename) + strlen(vtt_pattern) + 1;
+ hls->vtt_basename = av_malloc(vtt_basename_size);
+ if (!hls->vtt_basename) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ hls->vtt_m3u8_name = av_malloc(vtt_basename_size);
+ if (!hls->vtt_m3u8_name ) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ av_strlcpy(hls->vtt_basename, s->filename, vtt_basename_size);
+ p = strrchr(hls->vtt_basename, '.');
+ if (p)
+ *p = '\0';
+
+ if( hls->subtitle_filename ) {
+ strcpy(hls->vtt_m3u8_name, hls->subtitle_filename);
+ } else {
+ strcpy(hls->vtt_m3u8_name, hls->vtt_basename);
+ av_strlcat(hls->vtt_m3u8_name, "_vtt.m3u8", vtt_basename_size);
+ }
+ av_strlcat(hls->vtt_basename, vtt_pattern, vtt_basename_size);
+ }
+
+ if ((hls->flags & HLS_SINGLE_FILE) && (hls->segment_type == SEGMENT_TYPE_FMP4)) {
+ hls->fmp4_init_filename = av_strdup(hls->basename);
+ if (!hls->fmp4_init_filename) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ }
if ((ret = hls_mux_init(s)) < 0)
goto fail;
- if ((ret = hls_start(s)) < 0)
- goto fail;
+ if (hls->flags & HLS_APPEND_LIST) {
+ parse_playlist(s, s->filename);
+ hls->discontinuity = 1;
+ if (hls->init_time > 0) {
+ av_log(s, AV_LOG_WARNING, "append_list mode does not support hls_init_time,"
+ " hls_init_time value will have no effect\n");
+ hls->init_time = 0;
+ hls->recording_time = hls->time * AV_TIME_BASE;
+ }
+ }
- if ((ret = avformat_write_header(hls->avf, NULL)) < 0)
- return ret;
+ if (hls->segment_type != SEGMENT_TYPE_FMP4 || hls->flags & HLS_SINGLE_FILE) {
+ if ((ret = hls_start(s)) < 0)
+ goto fail;
+ }
+ 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;
+ AVStream *outer_st = s->streams[i];
+
+ if (hls->max_seg_size > 0) {
+ if ((outer_st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) &&
+ (outer_st->codecpar->bit_rate > hls->max_seg_size)) {
+ av_log(s, AV_LOG_WARNING, "Your video bitrate is bigger than hls_segment_size, "
+ "(%"PRId64 " > %"PRId64 "), the result maybe not be what you want.",
+ outer_st->codecpar->bit_rate, hls->max_seg_size);
+ }
+ }
+ if (outer_st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
+ inner_st = hls->avf->streams[i];
+ else if (hls->vtt_avf)
+ inner_st = hls->vtt_avf->streams[0];
+ else {
+ /* We have a subtitle stream, when the user does not want one */
+ inner_st = NULL;
+ continue;
+ }
+ 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->fmp4_init_filename);
+ av_freep(&hls->basename);
+ av_freep(&hls->vtt_basename);
+ av_freep(&hls->key_basename);
if (hls->avf)
avformat_free_context(hls->avf);
+ if (hls->vtt_avf)
+ avformat_free_context(hls->vtt_avf);
- free_encryption(s);
}
return ret;
}
@@ -444,49 +1588,132 @@ fail:
static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
{
HLSContext *hls = s->priv_data;
- AVFormatContext *oc = hls->avf;
+ AVFormatContext *oc = NULL;
AVStream *st = s->streams[pkt->stream_index];
int64_t end_pts = hls->recording_time * hls->number;
- int64_t pts = av_rescale_q(pkt->pts, st->time_base, AV_TIME_BASE_Q);
- int ret, can_split = 1;
+ int is_ref_pkt = 1;
+ int ret = 0, can_split = 1;
+ int stream_index = 0;
+
+ if (hls->sequence - hls->nb_entries > hls->start_sequence && hls->init_time > 0) {
+ /* reset end_pts, hls->recording_time at end of the init hls list */
+ int init_list_dur = hls->init_time * hls->nb_entries * AV_TIME_BASE;
+ int after_init_list_dur = (hls->sequence - hls->nb_entries ) * hls->time * AV_TIME_BASE;
+ hls->recording_time = hls->time * AV_TIME_BASE;
+ end_pts = init_list_dur + after_init_list_dur ;
+ }
+ if( st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE ) {
+ oc = hls->vtt_avf;
+ stream_index = 0;
+ } else {
+ oc = hls->avf;
+ stream_index = pkt->stream_index;
+ }
if (hls->start_pts == AV_NOPTS_VALUE) {
- hls->start_pts = pts;
- hls->end_pts = pts;
+ hls->start_pts = pkt->pts;
+ hls->end_pts = pkt->pts;
}
if (hls->has_video) {
can_split = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
- pkt->flags & AV_PKT_FLAG_KEY;
+ ((pkt->flags & AV_PKT_FLAG_KEY) || (hls->flags & HLS_SPLIT_BY_TIME));
+ is_ref_pkt = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
}
if (pkt->pts == AV_NOPTS_VALUE)
- can_split = 0;
- else
- hls->duration = pts - hls->end_pts;
+ is_ref_pkt = can_split = 0;
+
+ if (is_ref_pkt) {
+ if (hls->new_start) {
+ hls->new_start = 0;
+ hls->duration = (double)(pkt->pts - hls->end_pts)
+ * st->time_base.num / st->time_base.den;
+ hls->dpp = (double)(pkt->duration) * st->time_base.num / st->time_base.den;
+ } else {
+ if (pkt->duration) {
+ hls->duration += (double)(pkt->duration) * st->time_base.num / st->time_base.den;
+ } else {
+ av_log(s, AV_LOG_WARNING, "pkt->duration = 0, maybe the hls segment duration will not precise\n");
+ hls->duration = (double)(pkt->pts - hls->end_pts) * st->time_base.num / st->time_base.den;
+ }
+ }
- if (can_split && pts - hls->start_pts >= end_pts) {
- ret = append_entry(hls, hls->duration);
- if (ret)
- return ret;
+ }
+ if (hls->fmp4_init_mode || can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base,
+ end_pts, AV_TIME_BASE_Q) >= 0) {
+ int64_t new_start_pos;
+ char *old_filename = av_strdup(hls->avf->filename);
+ int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);
- hls->end_pts = pts;
- hls->duration = 0;
+ if (!old_filename) {
+ return AVERROR(ENOMEM);
+ }
- av_write_frame(oc, NULL); /* Flush any buffered data */
- ff_format_io_close(s, &oc->pb);
+ av_write_frame(hls->avf, NULL); /* Flush any buffered data */
+
+ new_start_pos = avio_tell(hls->avf->pb);
+ hls->size = new_start_pos - hls->start_pos;
+
+ if (!byterange_mode) {
+ ff_format_io_close(s, &oc->pb);
+ if (hls->vtt_avf) {
+ ff_format_io_close(s, &hls->vtt_avf->pb);
+ }
+ }
+ if ((hls->flags & HLS_TEMP_FILE) && oc->filename[0]) {
+ if (!(hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size <= 0))
+ if ((hls->avf->oformat->priv_class && hls->avf->priv_data) && hls->segment_type != SEGMENT_TYPE_FMP4)
+ av_opt_set(hls->avf->priv_data, "mpegts_flags", "resend_headers", 0);
+ hls_rename_temp_file(s, oc);
+ }
+
+ if (hls->fmp4_init_mode) {
+ hls->number--;
+ }
- ret = hls_start(s);
+ if (!hls->fmp4_init_mode || byterange_mode)
+ ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size);
- if (ret)
+ hls->start_pos = new_start_pos;
+ if (ret < 0) {
+ av_free(old_filename);
return ret;
+ }
- oc = hls->avf;
+ hls->end_pts = pkt->pts;
+ hls->duration = 0;
- if ((ret = hls_window(s, 0)) < 0)
+ hls->fmp4_init_mode = 0;
+ if (hls->flags & HLS_SINGLE_FILE) {
+ hls->number++;
+ } else if (hls->max_seg_size > 0) {
+ if (hls->start_pos >= hls->max_seg_size) {
+ hls->sequence++;
+ sls_flag_file_rename(hls, old_filename);
+ ret = hls_start(s);
+ hls->start_pos = 0;
+ /* When split segment by byte, the duration is short than hls_time,
+ * so it is not enough one segment duration as hls_time, */
+ hls->number--;
+ }
+ hls->number++;
+ } else {
+ sls_flag_file_rename(hls, old_filename);
+ ret = hls_start(s);
+ }
+ av_free(old_filename);
+
+ if (ret < 0) {
return ret;
+ }
+
+ if (!hls->fmp4_init_mode || byterange_mode)
+ if ((ret = hls_window(s, 0)) < 0) {
+ return ret;
+ }
}
- ret = ff_write_chained(oc, pkt->stream_index, pkt, s);
+ ret = ff_write_chained(oc, stream_index, pkt, s, 0);
return ret;
}
@@ -495,33 +1722,107 @@ static int hls_write_trailer(struct AVFormatContext *s)
{
HLSContext *hls = s->priv_data;
AVFormatContext *oc = hls->avf;
+ AVFormatContext *vtt_oc = hls->vtt_avf;
+ char *old_filename = av_strdup(hls->avf->filename);
+
+ if (!old_filename) {
+ return AVERROR(ENOMEM);
+ }
+
av_write_trailer(oc);
- ff_format_io_close(s, &oc->pb);
+ if (oc->pb) {
+ hls->size = avio_tell(hls->avf->pb) - hls->start_pos;
+ ff_format_io_close(s, &oc->pb);
+
+ if ((hls->flags & HLS_TEMP_FILE) && oc->filename[0]) {
+ hls_rename_temp_file(s, oc);
+ }
+
+ /* after av_write_trailer, then duration + 1 duration per packet */
+ hls_append_segment(s, hls, hls->duration + hls->dpp, hls->start_pos, hls->size);
+ }
+
+ sls_flag_file_rename(hls, old_filename);
+
+ if (vtt_oc) {
+ if (vtt_oc->pb)
+ av_write_trailer(vtt_oc);
+ hls->size = avio_tell(hls->vtt_avf->pb) - hls->start_pos;
+ ff_format_io_close(s, &vtt_oc->pb);
+ }
+ av_freep(&hls->basename);
+ av_freep(&hls->base_output_dirname);
+ av_freep(&hls->key_basename);
avformat_free_context(oc);
- av_free(hls->basename);
- append_entry(hls, hls->duration);
+
+ hls->avf = NULL;
hls_window(s, 1);
- free_entries(hls);
- free_encryption(s);
+ av_freep(&hls->fmp4_init_filename);
+ if (vtt_oc) {
+ av_freep(&hls->vtt_basename);
+ av_freep(&hls->vtt_m3u8_name);
+ avformat_free_context(vtt_oc);
+ }
+
+ hls_free_segments(hls->segments);
+ hls_free_segments(hls->old_segments);
+ av_free(old_filename);
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_init_time", "set segment length in seconds at init list", OFFSET(init_time), AV_OPT_TYPE_FLOAT, {.dbl = 0}, 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_vtt_options","set hls vtt list of options for the container format used for hls", OFFSET(vtt_format_options_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
+#if FF_API_HLS_WRAP
+ {"hls_wrap", "set number after which the index wraps (will be deprecated)", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E},
+#endif
{"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_version", "protocol version", OFFSET(version), AV_OPT_TYPE_INT, {.i64 = 3}, 2, 3, E},
- {"hls_enc", "AES128 encryption support", OFFSET(encrypt), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E},
- {"hls_enc_key", "use the specified hex-coded 16byte key to encrypt the segments", OFFSET(key), AV_OPT_TYPE_BINARY, .flags = E},
- {"hls_enc_key_url", "url to access the key to decrypt the segments", OFFSET(key_url), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
- {"hls_enc_iv", "use the specified hex-coded 16byte initialization vector", OFFSET(iv), AV_OPT_TYPE_BINARY, .flags = E},
+ {"hls_segment_filename", "filename template for segment files", OFFSET(segment_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
+ {"hls_segment_size", "maximum size per segment file, (in bytes)", OFFSET(max_seg_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E},
+ {"hls_key_info_file", "file with key URI and key file path", OFFSET(key_info_file), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
+ {"hls_enc", "enable AES128 encryption support", OFFSET(encrypt), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E},
+ {"hls_enc_key", "hex-coded 16 byte key to encrypt the segments", OFFSET(key), AV_OPT_TYPE_STRING, .flags = E},
+ {"hls_enc_key_url", "url to access the key to decrypt the segments", OFFSET(key_url), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
+ {"hls_enc_iv", "hex-coded 16 byte initialization vector", OFFSET(iv), AV_OPT_TYPE_STRING, .flags = E},
+ {"hls_subtitle_path", "set path of hls subtitles", OFFSET(subtitle_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
+ {"hls_segment_type", "set hls segment files type", OFFSET(segment_type), AV_OPT_TYPE_INT, {.i64 = SEGMENT_TYPE_MPEGTS }, 0, SEGMENT_TYPE_FMP4, E, "segment_type"},
+ {"mpegts", "make segment file to mpegts files in m3u8", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_MPEGTS }, 0, UINT_MAX, E, "segment_type"},
+ {"fmp4", "make segment file to fragment mp4 files in m3u8", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_FMP4 }, 0, UINT_MAX, E, "segment_type"},
+ {"hls_fmp4_init_filename", "set fragment mp4 file init filename", OFFSET(fmp4_init_filename), AV_OPT_TYPE_STRING, {.str = "init.mp4"}, 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"},
+ {"temp_file", "write segment to temporary file and rename when complete", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_TEMP_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"},
+ {"round_durations", "round durations in m3u8 to whole numbers", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_ROUND_DURATIONS }, 0, UINT_MAX, E, "flags"},
+ {"discont_start", "start the playlist with a discontinuity tag", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DISCONT_START }, 0, UINT_MAX, E, "flags"},
+ {"omit_endlist", "Do not append an endlist when ending stream", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_OMIT_ENDLIST }, 0, UINT_MAX, E, "flags"},
+ {"split_by_time", "split the hls segment by time which user set by hls_time", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SPLIT_BY_TIME }, 0, UINT_MAX, E, "flags"},
+ {"append_list", "append the new segments into old hls segment list", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_APPEND_LIST }, 0, UINT_MAX, E, "flags"},
+ {"program_date_time", "add EXT-X-PROGRAM-DATE-TIME", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PROGRAM_DATE_TIME }, 0, UINT_MAX, E, "flags"},
+ {"second_level_segment_index", "include segment index in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_INDEX }, 0, UINT_MAX, E, "flags"},
+ {"second_level_segment_duration", "include segment duration in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_DURATION }, 0, UINT_MAX, E, "flags"},
+ {"second_level_segment_size", "include segment size in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_SIZE }, 0, UINT_MAX, E, "flags"},
+ {"periodic_rekey", "reload keyinfo file periodically for re-keying", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PERIODIC_REKEY }, 0, UINT_MAX, E, "flags"},
+ {"use_localtime", "set filename expansion with strftime at segment creation", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
+ {"use_localtime_mkdir", "create last directory component in strftime-generated filename", OFFSET(use_localtime_mkdir), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
+ {"hls_playlist_type", "set the HLS playlist type", OFFSET(pl_type), AV_OPT_TYPE_INT, {.i64 = PLAYLIST_TYPE_NONE }, 0, PLAYLIST_TYPE_NB-1, E, "pl_type" },
+ {"event", "EVENT playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_EVENT }, INT_MIN, INT_MAX, E, "pl_type" },
+ {"vod", "VOD playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_VOD }, INT_MIN, INT_MAX, E, "pl_type" },
+ {"method", "set the HTTP method(default: PUT)", OFFSET(method), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
+ {"hls_start_number_source", "set source of first number in sequence", OFFSET(start_sequence_source_type), AV_OPT_TYPE_INT, {.i64 = HLS_START_SEQUENCE_AS_START_NUMBER }, 0, HLS_START_SEQUENCE_AS_FORMATTED_DATETIME, E, "start_sequence_source_type" },
+ {"generic", "start_number value (default)", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_START_NUMBER }, INT_MIN, INT_MAX, E, "start_sequence_source_type" },
+ {"epoch", "seconds since epoch", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH }, INT_MIN, INT_MAX, E, "start_sequence_source_type" },
+ {"datetime", "current datetime as YYYYMMDDhhmmss", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_FORMATTED_DATETIME }, INT_MIN, INT_MAX, E, "start_sequence_source_type" },
+ {"http_user_agent", "override User-Agent field in HTTP header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
{ NULL },
};
@@ -540,7 +1841,8 @@ AVOutputFormat ff_hls_muxer = {
.priv_data_size = sizeof(HLSContext),
.audio_codec = AV_CODEC_ID_AAC,
.video_codec = AV_CODEC_ID_H264,
- .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH,
+ .subtitle_codec = AV_CODEC_ID_WEBVTT,
+ .flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH,
.write_header = hls_write_header,
.write_packet = hls_write_packet,
.write_trailer = hls_write_trailer,
diff --git a/libavformat/hlsproto.c b/libavformat/hlsproto.c
index 4c3048a..2b19ed0 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
*/
@@ -28,6 +28,7 @@
#include "libavutil/avstring.h"
#include "libavutil/time.h"
#include "avformat.h"
+#include "avio_internal.h"
#include "internal.h"
#include "url.h"
#include "version.h"
@@ -80,7 +81,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 +90,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;
}
@@ -116,8 +117,9 @@ static int parse_playlist(URLContext *h, const char *url)
char line[1024];
const char *ptr;
- if ((ret = avio_open2(&in, url, AVIO_FLAG_READ,
- &h->interrupt_callback, NULL)) < 0)
+ if ((ret = ffio_open_whitelist(&in, url, AVIO_FLAG_READ,
+ &h->interrupt_callback, NULL,
+ h->protocol_whitelist, h->protocol_blacklist)) < 0)
return ret;
read_chomp_line(in, line, sizeof(line));
@@ -128,7 +130,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}};
@@ -303,8 +305,9 @@ retry:
}
url = s->segments[s->cur_seq_no - s->start_seq_no]->url,
av_log(h, AV_LOG_DEBUG, "opening %s\n", url);
- ret = ffurl_open(&s->seg_hd, url, AVIO_FLAG_READ,
- &h->interrupt_callback, NULL, h->protocols, h);
+ ret = ffurl_open_whitelist(&s->seg_hd, url, AVIO_FLAG_READ,
+ &h->interrupt_callback, NULL,
+ h->protocol_whitelist, h->protocol_blacklist, h);
if (ret < 0) {
if (ff_check_interrupt(&h->interrupt_callback))
return AVERROR_EXIT;
diff --git a/libavformat/hnm.c b/libavformat/hnm.c
index 8476e51..24d4e80 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 00cf295..bd9148f 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
*/
@@ -25,8 +25,11 @@
#include <zlib.h>
#endif /* CONFIG_ZLIB */
+#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/opt.h"
+#include "libavutil/time.h"
+#include "libavutil/parseutils.h"
#include "avformat.h"
#include "http.h"
@@ -36,7 +39,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
@@ -44,6 +47,16 @@
* path names). */
#define BUFFER_SIZE MAX_URL_SIZE
#define MAX_REDIRECTS 8
+#define HTTP_SINGLE 1
+#define HTTP_MUTLI 2
+#define MAX_EXPIRY 19
+#define WHITESPACES " \n\t\r"
+typedef enum {
+ LOWER_PROTO,
+ READ_HEADERS,
+ WRITE_REPLY_HEADERS,
+ FINISH
+}HandshakeState;
typedef struct HTTPContext {
const AVClass *class;
@@ -52,18 +65,23 @@ typedef struct HTTPContext {
int line_count;
int http_code;
/* Used if "Transfer-Encoding: chunked" otherwise -1. */
- int64_t chunksize;
- int64_t off, end_off, filesize;
+ uint64_t chunksize;
+ uint64_t off, end_off, filesize;
char *location;
HTTPAuthState auth_state;
HTTPAuthState proxy_auth_state;
+ char *http_proxy;
char *headers;
char *mime_type;
char *user_agent;
+#if FF_API_HTTP_USER_AGENT
+ char *user_agent_deprecated;
+#endif
char *content_type;
/* 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,11 +91,16 @@ 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)
+ /* A dictionary containing cookies keyed by cookie name */
+ AVDictionary *cookie_dict;
int icy;
/* how much data was read since the last ICY metadata packet */
- int icy_data_read;
+ uint64_t icy_data_read;
/* after how many bytes of read data a new metadata packet will be found */
- int icy_metaint;
+ uint64_t icy_metaint;
char *icy_metadata_headers;
char *icy_metadata_packet;
AVDictionary *metadata;
@@ -89,6 +112,17 @@ typedef struct HTTPContext {
AVDictionary *chained_options;
int send_expect_100;
char *method;
+ int reconnect;
+ int reconnect_at_eof;
+ int reconnect_streamed;
+ int reconnect_delay;
+ int reconnect_delay_max;
+ int listen;
+ char *resource;
+ int reply_code;
+ int is_multi_client;
+ HandshakeState handshake_step;
+ int is_connected_server;
} HTTPContext;
#define OFFSET(x) offsetof(HTTPContext, x)
@@ -97,32 +131,45 @@ typedef struct HTTPContext {
#define DEFAULT_USER_AGENT "Lavf/" AV_STRINGIFY(LIBAVFORMAT_VERSION)
static const AVOption options[] = {
- { "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 },
+ { "seekable", "control seekability of connection", OFFSET(seekable), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, D },
+ { "chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E },
+ { "http_proxy", "set HTTP proxy to tunnel through", OFFSET(http_proxy), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
+ { "headers", "set custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
+ { "content_type", "set a specific content type for the POST messages", OFFSET(content_type), AV_OPT_TYPE_STRING, { .str = NULL }, 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 },
- { "multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, D | E },
+#if FF_API_HTTP_USER_AGENT
+ { "user-agent", "override User-Agent header", OFFSET(user_agent_deprecated), AV_OPT_TYPE_STRING, { .str = DEFAULT_USER_AGENT }, 0, 0, D },
+#endif
+ { "multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_BOOL, { .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 },
- { "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 },
+ { "mime_type", "export the MIME type", OFFSET(mime_type), AV_OPT_TYPE_STRING, { .str = NULL }, 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, { .str = NULL }, 0, 0, D },
+ { "icy", "request ICY metadata", OFFSET(icy), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D },
+ { "icy_metadata_headers", "return ICY metadata headers", OFFSET(icy_metadata_headers), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT },
+ { "icy_metadata_packet", "return current ICY metadata packet", OFFSET(icy_metadata_packet), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT },
{ "metadata", "metadata read from the bitstream", OFFSET(metadata), AV_OPT_TYPE_DICT, {0}, 0, 0, AV_OPT_FLAG_EXPORT },
{ "auth_type", "HTTP authentication type", OFFSET(auth_state.auth_type), AV_OPT_TYPE_INT, { .i64 = HTTP_AUTH_NONE }, HTTP_AUTH_NONE, HTTP_AUTH_BASIC, D | E, "auth_type"},
{ "none", "No auth method set, autodetect", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_NONE }, 0, 0, D | E, "auth_type"},
{ "basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_BASIC }, 0, 0, D | E, "auth_type"},
- { "send_expect_100", "Force sending an Expect: 100-continue header for POST", OFFSET(send_expect_100), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, E },
- { "location", "The actual location of the data received", OFFSET(location), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D | E },
+ { "send_expect_100", "Force sending an Expect: 100-continue header for POST", OFFSET(send_expect_100), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
+ { "location", "The actual location of the data received", OFFSET(location), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
{ "offset", "initial byte offset", OFFSET(off), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D },
{ "end_offset", "try to limit the request to bytes preceding this offset", OFFSET(end_off), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D },
- { "method", "Override the HTTP method", OFFSET(method), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
+ { "method", "Override the HTTP method or set the expected HTTP method from a client", OFFSET(method), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
+ { "reconnect", "auto reconnect after disconnect before EOF", OFFSET(reconnect), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
+ { "reconnect_at_eof", "auto reconnect at EOF", OFFSET(reconnect_at_eof), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
+ { "reconnect_streamed", "auto reconnect streamed / non seekable streams", OFFSET(reconnect_streamed), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
+ { "reconnect_delay_max", "max reconnect delay in seconds after which to give up", OFFSET(reconnect_delay_max), AV_OPT_TYPE_INT, { .i64 = 120 }, 0, UINT_MAX/1000/1000, D },
+ { "listen", "listen on HTTP", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, D | E },
+ { "resource", "The resource requested by a client", OFFSET(resource), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
+ { "reply_code", "The http status code to return to a client", OFFSET(reply_code), AV_OPT_TYPE_INT, { .i64 = 200}, INT_MIN, 599, E},
{ NULL }
};
static int http_connect(URLContext *h, const char *path, const char *local_path,
const char *hoststr, const char *auth,
const char *proxyauth, int *new_location);
+static int http_read_header(URLContext *h, int *new_location);
void ff_http_init_auth_state(URLContext *dest, const URLContext *src)
{
@@ -149,7 +196,7 @@ static int http_open_cnx_internal(URLContext *h, AVDictionary **options)
path1, sizeof(path1), s->location);
ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
- proxy_path = getenv("http_proxy");
+ proxy_path = s->http_proxy ? s->http_proxy : getenv("http_proxy");
use_proxy = !ff_http_match_no_proxy(getenv("no_proxy"), hostname) &&
proxy_path && av_strstart(proxy_path, "http://", NULL);
@@ -180,8 +227,9 @@ static int http_open_cnx_internal(URLContext *h, AVDictionary **options)
ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL);
if (!s->hd) {
- err = ffurl_open(&s->hd, buf, AVIO_FLAG_READ_WRITE,
- &h->interrupt_callback, options, h->protocols, h);
+ err = ffurl_open_whitelist(&s->hd, buf, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, options,
+ h->protocol_whitelist, h->protocol_blacklist, h);
if (err < 0)
return err;
}
@@ -201,6 +249,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 +262,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 +270,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 +279,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 +293,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,21 +312,187 @@ 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_write_reply(URLContext* h, int status_code)
+{
+ int ret, body = 0, reply_code, message_len;
+ const char *reply_text, *content_type;
+ HTTPContext *s = h->priv_data;
+ char message[BUFFER_SIZE];
+ content_type = "text/plain";
+
+ if (status_code < 0)
+ body = 1;
+ switch (status_code) {
+ case AVERROR_HTTP_BAD_REQUEST:
+ case 400:
+ reply_code = 400;
+ reply_text = "Bad Request";
+ break;
+ case AVERROR_HTTP_FORBIDDEN:
+ case 403:
+ reply_code = 403;
+ reply_text = "Forbidden";
+ break;
+ case AVERROR_HTTP_NOT_FOUND:
+ case 404:
+ reply_code = 404;
+ reply_text = "Not Found";
+ break;
+ case 200:
+ reply_code = 200;
+ reply_text = "OK";
+ content_type = s->content_type ? s->content_type : "application/octet-stream";
+ break;
+ case AVERROR_HTTP_SERVER_ERROR:
+ case 500:
+ reply_code = 500;
+ reply_text = "Internal server error";
+ break;
+ default:
+ return AVERROR(EINVAL);
+ }
+ if (body) {
+ s->chunked_post = 0;
+ message_len = snprintf(message, sizeof(message),
+ "HTTP/1.1 %03d %s\r\n"
+ "Content-Type: %s\r\n"
+ "Content-Length: %"SIZE_SPECIFIER"\r\n"
+ "%s"
+ "\r\n"
+ "%03d %s\r\n",
+ reply_code,
+ reply_text,
+ content_type,
+ strlen(reply_text) + 6, // 3 digit status code + space + \r\n
+ s->headers ? s->headers : "",
+ reply_code,
+ reply_text);
+ } else {
+ s->chunked_post = 1;
+ message_len = snprintf(message, sizeof(message),
+ "HTTP/1.1 %03d %s\r\n"
+ "Content-Type: %s\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "%s"
+ "\r\n",
+ reply_code,
+ reply_text,
+ content_type,
+ s->headers ? s->headers : "");
+ }
+ av_log(h, AV_LOG_TRACE, "HTTP reply header: \n%s----\n", message);
+ if ((ret = ffurl_write(s->hd, message, message_len)) < 0)
+ return ret;
+ return 0;
+}
+
+static void handle_http_errors(URLContext *h, int error)
+{
+ av_assert0(error < 0);
+ http_write_reply(h, error);
+}
+
+static int http_handshake(URLContext *c)
+{
+ int ret, err, new_location;
+ HTTPContext *ch = c->priv_data;
+ URLContext *cl = ch->hd;
+ switch (ch->handshake_step) {
+ case LOWER_PROTO:
+ av_log(c, AV_LOG_TRACE, "Lower protocol\n");
+ if ((ret = ffurl_handshake(cl)) > 0)
+ return 2 + ret;
+ if (ret < 0)
+ return ret;
+ ch->handshake_step = READ_HEADERS;
+ ch->is_connected_server = 1;
+ return 2;
+ case READ_HEADERS:
+ av_log(c, AV_LOG_TRACE, "Read headers\n");
+ if ((err = http_read_header(c, &new_location)) < 0) {
+ handle_http_errors(c, err);
+ return err;
+ }
+ ch->handshake_step = WRITE_REPLY_HEADERS;
+ return 1;
+ case WRITE_REPLY_HEADERS:
+ av_log(c, AV_LOG_TRACE, "Reply code: %d\n", ch->reply_code);
+ if ((err = http_write_reply(c, ch->reply_code)) < 0)
+ return err;
+ ch->handshake_step = FINISH;
+ return 1;
+ case FINISH:
+ return 0;
+ }
+ // this should never be reached.
+ return AVERROR(EINVAL);
+}
+
+static int http_listen(URLContext *h, const char *uri, int flags,
+ AVDictionary **options) {
+ HTTPContext *s = h->priv_data;
+ int ret;
+ char hostname[1024], proto[10];
+ char lower_url[100];
+ const char *lower_proto = "tcp";
+ int port;
+ av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port,
+ NULL, 0, uri);
+ if (!strcmp(proto, "https"))
+ lower_proto = "tls";
+ ff_url_join(lower_url, sizeof(lower_url), lower_proto, NULL, hostname, port,
+ NULL);
+ if ((ret = av_dict_set_int(options, "listen", s->listen, 0)) < 0)
+ goto fail;
+ if ((ret = ffurl_open_whitelist(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, options,
+ h->protocol_whitelist, h->protocol_blacklist, h
+ )) < 0)
+ goto fail;
+ s->handshake_step = LOWER_PROTO;
+ if (s->listen == HTTP_SINGLE) { /* single client */
+ s->reply_code = 200;
+ while ((ret = http_handshake(h)) > 0);
+ }
+fail:
+ av_dict_free(&s->chained_options);
+ return ret;
+}
+
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->filesize = UINT64_MAX;
s->location = av_strdup(uri);
if (!s->location)
return AVERROR(ENOMEM);
@@ -299,12 +513,35 @@ static int http_open(URLContext *h, const char *uri, int flags,
}
}
+ if (s->listen) {
+ return http_listen(h, uri, flags, options);
+ }
ret = http_open_cnx(h, options);
if (ret < 0)
av_dict_free(&s->chained_options);
return ret;
}
+static int http_accept(URLContext *s, URLContext **c)
+{
+ int ret;
+ HTTPContext *sc = s->priv_data;
+ HTTPContext *cc;
+ URLContext *sl = sc->hd;
+ URLContext *cl = NULL;
+
+ av_assert0(sc->listen);
+ if ((ret = ffurl_alloc(c, s->filename, s->flags, &sl->interrupt_callback)) < 0)
+ goto fail;
+ cc = (*c)->priv_data;
+ if ((ret = ffurl_accept(sl, &cl)) < 0)
+ goto fail;
+ cc->hd = cl;
+ cc->is_multi_client = 1;
+fail:
+ return ret;
+}
+
static int http_getc(HTTPContext *s)
{
int len;
@@ -356,7 +593,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;
}
@@ -382,11 +619,12 @@ static void parse_content_range(URLContext *h, const char *p)
if (!strncmp(p, "bytes ", 6)) {
p += 6;
- s->off = strtoll(p, NULL, 10);
+ s->off = strtoull(p, NULL, 10);
if ((slash = strchr(p, '/')) && strlen(slash) > 0)
- s->filesize = strtoll(slash + 1, NULL, 10);
+ s->filesize = strtoull(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)
@@ -418,7 +656,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;
}
@@ -446,11 +683,151 @@ static int parse_icy(HTTPContext *s, const char *tag, const char *p)
return 0;
}
+static int parse_set_cookie_expiry_time(const char *exp_str, struct tm *buf)
+{
+ char exp_buf[MAX_EXPIRY];
+ int i, j, exp_buf_len = MAX_EXPIRY-1;
+ char *expiry;
+
+ // strip off any punctuation or whitespace
+ for (i = 0, j = 0; exp_str[i] != '\0' && j < exp_buf_len; i++) {
+ if ((exp_str[i] >= '0' && exp_str[i] <= '9') ||
+ (exp_str[i] >= 'A' && exp_str[i] <= 'Z') ||
+ (exp_str[i] >= 'a' && exp_str[i] <= 'z')) {
+ exp_buf[j] = exp_str[i];
+ j++;
+ }
+ }
+ exp_buf[j] = '\0';
+ expiry = exp_buf;
+
+ // move the string beyond the day of week
+ while ((*expiry < '0' || *expiry > '9') && *expiry != '\0')
+ expiry++;
+
+ return av_small_strptime(expiry, "%d%b%Y%H%M%S", buf) ? 0 : AVERROR(EINVAL);
+}
+
+static int parse_set_cookie(const char *set_cookie, AVDictionary **dict)
+{
+ char *param, *next_param, *cstr, *back;
+
+ if (!(cstr = av_strdup(set_cookie)))
+ return AVERROR(EINVAL);
+
+ // strip any trailing whitespace
+ back = &cstr[strlen(cstr)-1];
+ while (strchr(WHITESPACES, *back)) {
+ *back='\0';
+ back--;
+ }
+
+ next_param = cstr;
+ while ((param = av_strtok(next_param, ";", &next_param))) {
+ char *name, *value;
+ param += strspn(param, WHITESPACES);
+ if ((name = av_strtok(param, "=", &value))) {
+ if (av_dict_set(dict, name, value, 0) < 0) {
+ av_free(cstr);
+ return -1;
+ }
+ }
+ }
+
+ av_free(cstr);
+ return 0;
+}
+
+static int parse_cookie(HTTPContext *s, const char *p, AVDictionary **cookies)
+{
+ AVDictionary *new_params = NULL;
+ AVDictionaryEntry *e, *cookie_entry;
+ char *eql, *name;
+
+ // ensure the cookie is parsable
+ if (parse_set_cookie(p, &new_params))
+ return -1;
+
+ // if there is no cookie value there is nothing to parse
+ cookie_entry = av_dict_get(new_params, "", NULL, AV_DICT_IGNORE_SUFFIX);
+ if (!cookie_entry || !cookie_entry->value) {
+ av_dict_free(&new_params);
+ return -1;
+ }
+
+ // ensure the cookie is not expired or older than an existing value
+ if ((e = av_dict_get(new_params, "expires", NULL, 0)) && e->value) {
+ struct tm new_tm = {0};
+ if (!parse_set_cookie_expiry_time(e->value, &new_tm)) {
+ AVDictionaryEntry *e2;
+
+ // if the cookie has already expired ignore it
+ if (av_timegm(&new_tm) < av_gettime() / 1000000) {
+ av_dict_free(&new_params);
+ return -1;
+ }
+
+ // only replace an older cookie with the same name
+ e2 = av_dict_get(*cookies, cookie_entry->key, NULL, 0);
+ if (e2 && e2->value) {
+ AVDictionary *old_params = NULL;
+ if (!parse_set_cookie(p, &old_params)) {
+ e2 = av_dict_get(old_params, "expires", NULL, 0);
+ if (e2 && e2->value) {
+ struct tm old_tm = {0};
+ if (!parse_set_cookie_expiry_time(e->value, &old_tm)) {
+ if (av_timegm(&new_tm) < av_timegm(&old_tm)) {
+ av_dict_free(&new_params);
+ av_dict_free(&old_params);
+ return -1;
+ }
+ }
+ }
+ }
+ av_dict_free(&old_params);
+ }
+ }
+ }
+
+ // duplicate the cookie name (dict will dupe the value)
+ if (!(eql = strchr(p, '='))) return AVERROR(EINVAL);
+ if (!(name = av_strndup(p, eql - p))) return AVERROR(ENOMEM);
+
+ // add the cookie to the dictionary
+ av_dict_set(cookies, name, eql, AV_DICT_DONT_STRDUP_KEY);
+
+ return 0;
+}
+
+static int cookie_string(AVDictionary *dict, char **cookies)
+{
+ AVDictionaryEntry *e = NULL;
+ int len = 1;
+
+ // determine how much memory is needed for the cookies string
+ while (e = av_dict_get(dict, "", e, AV_DICT_IGNORE_SUFFIX))
+ len += strlen(e->key) + strlen(e->value) + 1;
+
+ // reallocate the cookies
+ e = NULL;
+ if (*cookies) av_free(*cookies);
+ *cookies = av_malloc(len);
+ if (!*cookies) return AVERROR(ENOMEM);
+ *cookies[0] = '\0';
+
+ // write out the cookies
+ while (e = av_dict_get(dict, "", e, AV_DICT_IGNORE_SUFFIX))
+ av_strlcatf(*cookies, len, "%s%s\n", e->key, e->value);
+
+ return 0;
+}
+
static int process_line(URLContext *h, char *line, int line_count,
int *new_location)
{
HTTPContext *s = h->priv_data;
- char *tag, *p, *end;
+ const char *auto_method = h->flags & AVIO_FLAG_READ ? "POST" : "GET";
+ char *tag, *p, *end, *method, *resource, *version;
int ret;
/* end of header */
@@ -461,16 +838,66 @@ static int process_line(URLContext *h, char *line, int line_count,
p = line;
if (line_count == 0) {
- while (!av_isspace(*p) && *p != '\0')
- p++;
- while (av_isspace(*p))
- p++;
- s->http_code = strtol(p, &end, 10);
+ if (s->is_connected_server) {
+ // HTTP method
+ method = p;
+ while (*p && !av_isspace(*p))
+ p++;
+ *(p++) = '\0';
+ av_log(h, AV_LOG_TRACE, "Received method: %s\n", method);
+ if (s->method) {
+ if (av_strcasecmp(s->method, method)) {
+ av_log(h, AV_LOG_ERROR, "Received and expected HTTP method do not match. (%s expected, %s received)\n",
+ s->method, method);
+ return ff_http_averror(400, AVERROR(EIO));
+ }
+ } else {
+ // use autodetected HTTP method to expect
+ av_log(h, AV_LOG_TRACE, "Autodetected %s HTTP method\n", auto_method);
+ if (av_strcasecmp(auto_method, method)) {
+ av_log(h, AV_LOG_ERROR, "Received and autodetected HTTP method did not match "
+ "(%s autodetected %s received)\n", auto_method, method);
+ return ff_http_averror(400, AVERROR(EIO));
+ }
+ if (!(s->method = av_strdup(method)))
+ return AVERROR(ENOMEM);
+ }
- av_log(NULL, AV_LOG_TRACE, "http_code=%d\n", s->http_code);
+ // HTTP resource
+ while (av_isspace(*p))
+ p++;
+ resource = p;
+ while (!av_isspace(*p))
+ p++;
+ *(p++) = '\0';
+ av_log(h, AV_LOG_TRACE, "Requested resource: %s\n", resource);
+ if (!(s->resource = av_strdup(resource)))
+ return AVERROR(ENOMEM);
+
+ // HTTP version
+ while (av_isspace(*p))
+ p++;
+ version = p;
+ while (*p && !av_isspace(*p))
+ p++;
+ *p = '\0';
+ if (av_strncasecmp(version, "HTTP/", 5)) {
+ av_log(h, AV_LOG_ERROR, "Malformed HTTP version string.\n");
+ return ff_http_averror(400, AVERROR(EIO));
+ }
+ av_log(h, AV_LOG_TRACE, "HTTP version string: %s\n", version);
+ } else {
+ while (!av_isspace(*p) && *p != '\0')
+ p++;
+ while (av_isspace(*p))
+ p++;
+ s->http_code = strtol(p, &end, 10);
- if ((ret = check_http_code(h, s->http_code, end)) < 0)
- return ret;
+ av_log(h, AV_LOG_TRACE, "http_code=%d\n", s->http_code);
+
+ if ((ret = check_http_code(h, s->http_code, end)) < 0)
+ return ret;
+ }
} else {
while (*p != '\0' && *p != ':')
p++;
@@ -486,16 +913,18 @@ static int process_line(URLContext *h, char *line, int line_count,
if ((ret = parse_location(s, p)) < 0)
return ret;
*new_location = 1;
- } else if (!av_strcasecmp(tag, "Content-Length") && s->filesize == -1) {
- s->filesize = strtoll(p, NULL, 10);
+ } else if (!av_strcasecmp(tag, "Content-Length") &&
+ s->filesize == UINT64_MAX) {
+ s->filesize = strtoull(p, NULL, 10);
} 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)) {
- s->filesize = -1;
+ s->filesize = UINT64_MAX;
s->chunksize = 0;
} else if (!av_strcasecmp(tag, "WWW-Authenticate")) {
ff_http_auth_handle_header(&s->auth_state, tag, p);
@@ -506,11 +935,20 @@ 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 (parse_cookie(s, p, &s->cookie_dict))
+ av_log(h, AV_LOG_WARNING, "Unable to parse '%s'\n", p);
} else if (!av_strcasecmp(tag, "Icy-MetaInt")) {
- s->icy_metaint = strtoll(p, NULL, 10);
+ s->icy_metaint = strtoull(p, NULL, 10);
} else if (!av_strncasecmp(tag, "Icy-", 4)) {
if ((ret = parse_icy(s, tag, p)) < 0)
return ret;
@@ -522,6 +960,104 @@ 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 *cookie, *set_cookies = av_strdup(s->cookies), *next = set_cookies;
+
+ if (!set_cookies) return AVERROR(EINVAL);
+
+ // destroy any cookies in the dictionary.
+ av_dict_free(&s->cookie_dict);
+
+ *cookies = NULL;
+ while ((cookie = av_strtok(next, "\n", &next))) {
+ AVDictionary *cookie_params = NULL;
+ AVDictionaryEntry *cookie_entry, *e;
+
+ // store the cookie in a dict in case it is updated in the response
+ if (parse_cookie(s, cookie, &s->cookie_dict))
+ av_log(s, AV_LOG_WARNING, "Unable to parse '%s'\n", cookie);
+
+ // continue on to the next cookie if this one cannot be parsed
+ if (parse_set_cookie(cookie, &cookie_params))
+ continue;
+
+ // if the cookie has no value, skip it
+ cookie_entry = av_dict_get(cookie_params, "", NULL, AV_DICT_IGNORE_SUFFIX);
+ if (!cookie_entry || !cookie_entry->value) {
+ av_dict_free(&cookie_params);
+ continue;
+ }
+
+ // if the cookie has expired, don't add it
+ if ((e = av_dict_get(cookie_params, "expires", NULL, 0)) && e->value) {
+ struct tm tm_buf = {0};
+ if (!parse_set_cookie_expiry_time(e->value, &tm_buf)) {
+ if (av_timegm(&tm_buf) < av_gettime() / 1000000) {
+ av_dict_free(&cookie_params);
+ continue;
+ }
+ }
+ }
+
+ // if no domain in the cookie assume it appied to this request
+ if ((e = av_dict_get(cookie_params, "domain", NULL, 0)) && e->value) {
+ // find the offset comparison is on the min domain (b.com, not a.b.com)
+ int domain_offset = strlen(domain) - strlen(e->value);
+ if (domain_offset < 0) {
+ av_dict_free(&cookie_params);
+ continue;
+ }
+
+ // match the cookie domain
+ if (av_strcasecmp(&domain[domain_offset], e->value)) {
+ av_dict_free(&cookie_params);
+ continue;
+ }
+ }
+
+ // ensure this cookie matches the path
+ e = av_dict_get(cookie_params, "path", NULL, 0);
+ if (!e || av_strncasecmp(path, e->value, strlen(e->value))) {
+ av_dict_free(&cookie_params);
+ continue;
+ }
+
+ // cookie parameters match, so copy the value
+ if (!*cookies) {
+ if (!(*cookies = av_asprintf("%s=%s", cookie_entry->key, cookie_entry->value))) {
+ ret = AVERROR(ENOMEM);
+ break;
+ }
+ } else {
+ char *tmp = *cookies;
+ size_t str_size = strlen(cookie_entry->key) + strlen(cookie_entry->value) + strlen(*cookies) + 4;
+ if (!(*cookies = av_malloc(str_size))) {
+ ret = AVERROR(ENOMEM);
+ av_free(tmp);
+ break;
+ }
+ snprintf(*cookies, str_size, "%s; %s=%s", tmp, cookie_entry->key, cookie_entry->value);
+ av_free(tmp);
+ }
+ }
+
+ av_free(set_cookies);
+
+ return ret;
+}
+
static inline int has_header(const char *str, const char *header)
{
/* header + 2 to skip over CRLF prefix. (make sure you have one!) */
@@ -536,13 +1072,13 @@ static int http_read_header(URLContext *h, int *new_location)
char line[MAX_URL_SIZE];
int err = 0;
- s->chunksize = -1;
+ s->chunksize = UINT64_MAX;
for (;;) {
if ((err = http_get_line(s, line, sizeof(line))) < 0)
return err;
- av_log(NULL, AV_LOG_TRACE, "header='%s'\n", line);
+ av_log(h, AV_LOG_TRACE, "header='%s'\n", line);
err = process_line(h, line, s->line_count, new_location);
if (err < 0)
@@ -552,6 +1088,13 @@ 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 */
+
+ // add any new cookies into the existing cookie string
+ cookie_string(s->cookie_dict, &s->cookies);
+ av_dict_free(&s->cookie_dict);
+
return err;
}
@@ -563,10 +1106,11 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
int post, err;
char headers[HTTP_HEADERS_SIZE] = "";
char *authstr = NULL, *proxyauthstr = NULL;
- int64_t off = s->off;
+ uint64_t off = s->off;
int len = 0;
const char *method;
int send_expect_100 = 0;
+ int ret;
/* send http header */
post = h->flags & AVIO_FLAG_WRITE;
@@ -599,6 +1143,12 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
send_expect_100 = 1;
}
+#if FF_API_HTTP_USER_AGENT
+ if (strcmp(s->user_agent_deprecated, DEFAULT_USER_AGENT)) {
+ av_log(s, AV_LOG_WARNING, "the user-agent option is deprecated, please use user_agent option\n");
+ s->user_agent = av_strdup(s->user_agent_deprecated);
+ }
+#endif
/* set default headers if needed */
if (!has_header(s->headers, "\r\nUser-Agent: "))
len += av_strlcatf(headers + len, sizeof(headers) - len,
@@ -609,9 +1159,9 @@ 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);
+ "Range: bytes=%"PRIu64"-", s->off);
if (s->end_off)
len += av_strlcatf(headers + len, sizeof(headers) - len,
"%"PRId64, s->end_off - 1);
@@ -641,6 +1191,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);
@@ -649,7 +1207,7 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
if (s->headers)
av_strlcpy(headers + len, s->headers, sizeof(headers) - len);
- snprintf(s->buffer, sizeof(s->buffer),
+ ret = snprintf(s->buffer, sizeof(s->buffer),
"%s %s HTTP/1.1\r\n"
"%s"
"%s"
@@ -663,14 +1221,22 @@ 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 (strlen(headers) + 1 == sizeof(headers) ||
+ ret >= sizeof(s->buffer)) {
+ av_log(h, AV_LOG_ERROR, "overlong headers\n");
+ err = AVERROR(EINVAL);
+ goto done;
+ }
+
+
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;
@@ -678,30 +1244,72 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
s->line_count = 0;
s->off = 0;
s->icy_data_read = 0;
- s->filesize = -1;
+ s->filesize = UINT64_MAX;
s->willclose = 0;
s->end_chunked_post = 0;
s->end_header = 0;
+#if CONFIG_ZLIB
+ s->compressed = 0;
+#endif
if (post && !s->post_data && !send_expect_100) {
/* Pretend that it did work. We didn't read any header yet, since
* 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;
+
+ if (*new_location)
+ s->off = off;
- 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)
{
HTTPContext *s = h->priv_data;
int len;
+
+ if (s->chunksize != UINT64_MAX) {
+ if (!s->chunksize) {
+ char line[32];
+ int err;
+
+ do {
+ if ((err = http_get_line(s, line, sizeof(line))) < 0)
+ return err;
+ } while (!*line); /* skip CR LF from last chunk */
+
+ s->chunksize = strtoull(line, NULL, 16);
+
+ av_log(h, AV_LOG_TRACE,
+ "Chunked encoding data size: %"PRIu64"'\n",
+ s->chunksize);
+
+ if (!s->chunksize) {
+ av_log(h, AV_LOG_DEBUG, "Last chunk received, closing conn\n");
+ ffurl_closep(&s->hd);
+ return 0;
+ }
+ else if (s->chunksize == UINT64_MAX) {
+ av_log(h, AV_LOG_ERROR, "Invalid chunk size %"PRIu64"\n",
+ s->chunksize);
+ return AVERROR(EINVAL);
+ }
+ }
+ size = FFMIN(size, s->chunksize);
+ }
+
/* read bytes from input buffer first */
len = s->buf_end - s->buf_ptr;
if (len > 0) {
@@ -710,15 +1318,24 @@ static int http_buf_read(URLContext *h, uint8_t *buf, int size)
memcpy(buf, s->buf_ptr, len);
s->buf_ptr += len;
} else {
- if ((!s->willclose || s->chunksize < 0) &&
- s->filesize >= 0 && s->off >= s->filesize)
+ uint64_t target_end = s->end_off ? s->end_off : s->filesize;
+ if ((!s->willclose || s->chunksize == UINT64_MAX) && s->off >= target_end)
return AVERROR_EOF;
len = ffurl_read(s->hd, buf, size);
+ if (!len && (!s->willclose || s->chunksize == UINT64_MAX) && s->off < target_end) {
+ av_log(h, AV_LOG_ERROR,
+ "Stream ends prematurely at %"PRIu64", should be %"PRIu64"\n",
+ s->off, target_end
+ );
+ return AVERROR(EIO);
+ }
}
if (len > 0) {
s->off += len;
- if (s->chunksize > 0)
+ if (s->chunksize > 0 && s->chunksize != UINT64_MAX) {
+ av_assert0(s->chunksize >= len);
s->chunksize -= len;
+ }
}
return len;
}
@@ -756,10 +1373,13 @@ static int http_buf_read_compressed(URLContext *h, uint8_t *buf, int size)
}
#endif /* CONFIG_ZLIB */
+static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int force_reconnect);
+
static int http_read_stream(URLContext *h, uint8_t *buf, int size)
{
HTTPContext *s = h->priv_data;
- int err, new_location;
+ int err, new_location, read_ret;
+ int64_t seek_ret;
if (!s->hd)
return AVERROR_EOF;
@@ -770,36 +1390,36 @@ static int http_read_stream(URLContext *h, uint8_t *buf, int size)
return err;
}
- if (s->chunksize >= 0) {
- if (!s->chunksize) {
- char line[32];
-
- for (;;) {
- do {
- if ((err = http_get_line(s, line, sizeof(line))) < 0)
- return err;
- } while (!*line); /* skip CR LF from last chunk */
-
- s->chunksize = strtoll(line, NULL, 16);
-
- av_log(NULL, AV_LOG_TRACE, "Chunked encoding data size: %"PRId64"'\n",
- s->chunksize);
- if (s->chunksize < 0)
- return AVERROR_INVALIDDATA;
- else if (!s->chunksize)
- return 0;
- break;
- }
- }
- size = FFMIN(size, s->chunksize);
- }
#if CONFIG_ZLIB
if (s->compressed)
return http_buf_read_compressed(h, buf, size);
#endif /* CONFIG_ZLIB */
- return http_buf_read(h, buf, size);
+ read_ret = http_buf_read(h, buf, size);
+ if ( (read_ret < 0 && s->reconnect && (!h->is_streamed || s->reconnect_streamed) && s->filesize > 0 && s->off < s->filesize)
+ || (read_ret == 0 && s->reconnect_at_eof && (!h->is_streamed || s->reconnect_streamed))) {
+ uint64_t target = h->is_streamed ? 0 : s->off;
+
+ if (s->reconnect_delay > s->reconnect_delay_max)
+ return AVERROR(EIO);
+
+ av_log(h, AV_LOG_INFO, "Will reconnect at %"PRIu64" error=%s.\n", s->off, av_err2str(read_ret));
+ av_usleep(1000U*1000*s->reconnect_delay);
+ s->reconnect_delay = 1 + 2*s->reconnect_delay;
+ seek_ret = http_seek_internal(h, target, SEEK_SET, 1);
+ if (seek_ret != target) {
+ av_log(h, AV_LOG_ERROR, "Failed to reconnect at %"PRIu64".\n", target);
+ return read_ret;
+ }
+
+ read_ret = http_buf_read(h, buf, size);
+ } else
+ s->reconnect_delay = 0;
+
+ return read_ret;
}
+// 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;
@@ -842,10 +1462,11 @@ static int store_icy(URLContext *h, int size)
{
HTTPContext *s = h->priv_data;
/* until next metadata packet */
- int remaining = s->icy_metaint - s->icy_data_read;
+ uint64_t remaining;
- if (remaining < 0)
+ if (s->icy_metaint < s->icy_data_read)
return AVERROR_INVALIDDATA;
+ remaining = s->icy_metaint - s->icy_data_read;
if (!remaining) {
/* The metadata packet is variable sized. It has a 1 byte header
@@ -925,7 +1546,8 @@ static int http_shutdown(URLContext *h, int flags)
HTTPContext *s = h->priv_data;
/* signal end of chunked encoding if used */
- if ((flags & AVIO_FLAG_WRITE) && s->chunked_post) {
+ if (((flags & AVIO_FLAG_WRITE) && s->chunked_post) ||
+ ((flags & AVIO_FLAG_READ) && s->chunked_post && s->listen)) {
ret = ffurl_write(s->hd, footer, sizeof(footer) - 1);
ret = ret > 0 ? 0 : ret;
s->end_chunked_post = 1;
@@ -949,40 +1571,48 @@ 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;
}
-static int64_t http_seek(URLContext *h, int64_t off, int whence)
+static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int force_reconnect)
{
HTTPContext *s = h->priv_data;
URLContext *old_hd = s->hd;
- int64_t old_off = s->off;
+ uint64_t old_off = s->off;
uint8_t old_buf[BUFFER_SIZE];
int old_buf_size, ret;
AVDictionary *options = NULL;
if (whence == AVSEEK_SIZE)
return s->filesize;
- else if ((whence == SEEK_CUR && off == 0) ||
- (whence == SEEK_SET && off == s->off))
+ else if (!force_reconnect &&
+ ((whence == SEEK_CUR && off == 0) ||
+ (whence == SEEK_SET && off == s->off)))
return s->off;
- else if ((s->filesize == -1 && whence == SEEK_END) || h->is_streamed)
+ else if ((s->filesize == UINT64_MAX && whence == SEEK_END))
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;
+ if (s->off && 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 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);
@@ -997,12 +1627,23 @@ static int64_t http_seek(URLContext *h, int64_t off, int whence)
return off;
}
+static int64_t http_seek(URLContext *h, int64_t off, int whence)
+{
+ return http_seek_internal(h, off, whence, 0);
+}
+
static int http_get_file_handle(URLContext *h)
{
HTTPContext *s = h->priv_data;
return ffurl_get_file_handle(s->hd);
}
+static int http_get_short_seek(URLContext *h)
+{
+ HTTPContext *s = h->priv_data;
+ return ffurl_get_short_seek(s->hd);
+}
+
#define HTTP_CLASS(flavor) \
static const AVClass flavor ## _context_class = { \
.class_name = # flavor, \
@@ -1017,15 +1658,19 @@ HTTP_CLASS(http);
const URLProtocol ff_http_protocol = {
.name = "http",
.url_open2 = http_open,
+ .url_accept = http_accept,
+ .url_handshake = http_handshake,
.url_read = http_read,
.url_write = http_write,
.url_seek = http_seek,
.url_close = http_close,
.url_get_file_handle = http_get_file_handle,
+ .url_get_short_seek = http_get_short_seek,
.url_shutdown = http_shutdown,
.priv_data_size = sizeof(HTTPContext),
.priv_data_class = &http_context_class,
.flags = URL_PROTOCOL_FLAG_NETWORK,
+ .default_whitelist = "http,https,tls,rtp,tcp,udp,crypto,httpproxy"
};
#endif /* CONFIG_HTTP_PROTOCOL */
@@ -1040,10 +1685,12 @@ const URLProtocol ff_https_protocol = {
.url_seek = http_seek,
.url_close = http_close,
.url_get_file_handle = http_get_file_handle,
+ .url_get_short_seek = http_get_short_seek,
.url_shutdown = http_shutdown,
.priv_data_size = sizeof(HTTPContext),
.priv_data_class = &https_context_class,
.flags = URL_PROTOCOL_FLAG_NETWORK,
+ .default_whitelist = "http,https,tls,rtp,tcp,udp,crypto,httpproxy"
};
#endif /* CONFIG_HTTPS_PROTOCOL */
@@ -1052,7 +1699,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;
}
@@ -1067,7 +1714,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);
@@ -1079,8 +1729,9 @@ static int http_proxy_open(URLContext *h, const char *uri, int flags)
ff_url_join(lower_url, sizeof(lower_url), "tcp", NULL, hostname, port,
NULL);
redo:
- ret = ffurl_open(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
- &h->interrupt_callback, NULL, h->protocols, h);
+ ret = ffurl_open_whitelist(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, NULL,
+ h->protocol_whitelist, h->protocol_blacklist, h);
if (ret < 0)
return ret;
@@ -1103,7 +1754,7 @@ redo:
s->buf_ptr = s->buffer;
s->buf_end = s->buffer;
s->line_count = 0;
- s->filesize = -1;
+ s->filesize = UINT64_MAX;
cur_auth_type = s->proxy_auth_state.auth_type;
/* Note: This uses buffering, potentially reading more than the
@@ -1123,14 +1774,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 b8fe33b..7d02713 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.
@@ -47,4 +47,6 @@ void ff_http_init_auth_state(URLContext *dest, const URLContext *src);
*/
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 b0d3c0c..2d42ab2 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
*/
@@ -90,7 +90,7 @@ static void choose_qop(char *qop, int size)
void ff_http_auth_handle_header(HTTPAuthState *state, const char *key,
const char *value)
{
- if (!strcmp(key, "WWW-Authenticate") || !strcmp(key, "Proxy-Authenticate")) {
+ if (!av_strcasecmp(key, "WWW-Authenticate") || !av_strcasecmp(key, "Proxy-Authenticate")) {
const char *p;
if (av_stristart(value, "Basic ", &p) &&
state->auth_type <= HTTP_AUTH_BASIC) {
@@ -112,7 +112,7 @@ void ff_http_auth_handle_header(HTTPAuthState *state, const char *key,
if (!av_strcasecmp(state->digest_params.stale, "true"))
state->stale = 1;
}
- } else if (!strcmp(key, "Authentication-Info")) {
+ } else if (!av_strcasecmp(key, "Authentication-Info")) {
ff_parse_key_value(value, (ff_parse_key_val_cb) handle_digest_update,
state);
}
@@ -225,8 +225,10 @@ static char *make_digest_auth(HTTPAuthState *state, const char *username,
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 9bfbc52..0e70859 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
*/
@@ -56,7 +56,7 @@ typedef struct HTTPAuthState {
/**
* The currently chosen auth type.
*/
- HTTPAuthType auth_type;
+ int auth_type;
/**
* Authentication realm
*/
diff --git a/libavformat/icecast.c b/libavformat/icecast.c
index 820681b..02e3e38 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"
@@ -52,40 +53,23 @@ typedef struct IcecastContext {
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
- { "ice_genre", "set stream genre", OFFSET(genre), AV_OPT_TYPE_STRING, { 0 }, 0, 0, E },
- { "ice_name", "set stream description", OFFSET(name), AV_OPT_TYPE_STRING, { 0 }, 0, 0, E },
- { "ice_description", "set stream description", OFFSET(description), AV_OPT_TYPE_STRING, { 0 }, 0, 0, E },
- { "ice_url", "set stream website", OFFSET(url), AV_OPT_TYPE_STRING, { 0 }, 0, 0, E },
- { "ice_public", "set if stream is public", OFFSET(public), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, E },
- { "user_agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, { 0 }, 0, 0, E },
- { "password", "set password", OFFSET(pass), AV_OPT_TYPE_STRING, { 0 }, 0, 0, E },
- { "content_type", "set content-type, MUST be set if not audio/mpeg", OFFSET(content_type), AV_OPT_TYPE_STRING, { 0 }, 0, 0, E },
- { "legacy_icecast", "use legacy SOURCE method, for Icecast < v2.4", OFFSET(legacy_icecast), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, E },
+ { "ice_genre", "set stream genre", OFFSET(genre), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
+ { "ice_name", "set stream description", OFFSET(name), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
+ { "ice_description", "set stream description", OFFSET(description), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
+ { "ice_url", "set stream website", OFFSET(url), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
+ { "ice_public", "set if stream is public", OFFSET(public), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
+ { "user_agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
+ { "password", "set password", OFFSET(pass), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
+ { "content_type", "set content-type, MUST be set if not audio/mpeg", OFFSET(content_type), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
+ { "legacy_icecast", "use legacy SOURCE method, for Icecast < v2.4", OFFSET(legacy_icecast), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
{ NULL }
};
-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++;
@@ -176,11 +164,10 @@ static int icecast_open(URLContext *h, const char *uri, int flags)
// Build new URI for passing to http protocol
ff_url_join(h_url, sizeof(h_url), "http", auth, host, port, "%s", path);
// Finally open http proto handler
- ret = ffurl_open(&s->hd, h_url, AVIO_FLAG_READ_WRITE, NULL, &opt_dict,
- h->protocols, h);
+ ret = ffurl_open_whitelist(&s->hd, h_url, AVIO_FLAG_READ_WRITE, NULL,
+ &opt_dict, h->protocol_whitelist, h->protocol_blacklist, h);
cleanup:
- // Free variables
av_freep(&user);
av_freep(&headers);
av_dict_free(&opt_dict);
diff --git a/libavformat/icodec.c b/libavformat/icodec.c
new file mode 100644
index 0000000..f33fa11
--- /dev/null
+++ b/libavformat/icodec.c
@@ -0,0 +1,222 @@
+/*
+ * 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 "libavcodec/png.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)
+{
+ unsigned i, frames, checked = 0;
+
+ if (p->buf_size < 22 || AV_RL16(p->buf) || AV_RL16(p->buf + 2) != 1)
+ return 0;
+ frames = AV_RL16(p->buf + 4);
+ if (!frames)
+ return 0;
+ for (i = 0; i < frames && i * 16 + 22 <= p->buf_size; i++) {
+ unsigned offset;
+ if (AV_RL16(p->buf + 10 + i * 16) & ~1)
+ return FFMIN(i, AVPROBE_SCORE_MAX / 4);
+ if (p->buf[13 + i * 16])
+ return FFMIN(i, AVPROBE_SCORE_MAX / 4);
+ if (AV_RL32(p->buf + 14 + i * 16) < 40)
+ return FFMIN(i, AVPROBE_SCORE_MAX / 4);
+ offset = AV_RL32(p->buf + 18 + i * 16);
+ if (offset < 22)
+ return FFMIN(i, AVPROBE_SCORE_MAX / 4);
+ if (offset > p->buf_size - 8)
+ continue;
+ if (p->buf[offset] != 40 && AV_RB64(p->buf + offset) != PNGSIG)
+ return FFMIN(i, AVPROBE_SCORE_MAX / 4);
+ checked++;
+ }
+
+ if (checked < frames)
+ return AVPROBE_SCORE_MAX / 4 + FFMIN(checked, 1);
+ return AVPROBE_SCORE_MAX / 2 + 1;
+}
+
+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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->width = avio_r8(pb);
+ st->codecpar->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);
+ if (ico->images[i].size <= 0) {
+ av_log(s, AV_LOG_ERROR, "Invalid image size %d\n", ico->images[i].size);
+ return AVERROR_INVALIDDATA;
+ }
+ 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->codecpar->codec_id = AV_CODEC_ID_PNG;
+ st->codecpar->width = 0;
+ st->codecpar->height = 0;
+ break;
+ case 40:
+ if (ico->images[i].size < 40)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->codec_id = AV_CODEC_ID_BMP;
+ tmp = avio_rl32(pb);
+ if (tmp)
+ st->codecpar->width = tmp;
+ tmp = avio_rl32(pb);
+ if (tmp)
+ st->codecpar->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_EOF;
+
+ image = &ico->images[ico->current_image];
+
+ if ((ret = avio_seek(pb, image->offset, SEEK_SET)) < 0)
+ return ret;
+
+ if (s->streams[ico->current_image]->codecpar->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)) != image->size) {
+ av_packet_unref(pkt);
+ return ret < 0 ? ret : AVERROR_INVALIDDATA;
+ }
+
+ st->codecpar->bits_per_coded_sample = AV_RL16(buf + 14);
+
+ if (AV_RL32(buf + 32))
+ image->nb_pal = AV_RL32(buf + 32);
+
+ if (st->codecpar->bits_per_coded_sample <= 8 && !image->nb_pal) {
+ image->nb_pal = 1 << st->codecpar->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;
+}
+
+static int ico_read_close(AVFormatContext * s)
+{
+ IcoDemuxContext *ico = s->priv_data;
+ av_freep(&ico->images);
+ 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,
+ .read_close = ico_read_close,
+ .flags = AVFMT_NOTIMESTAMPS,
+};
diff --git a/libavformat/icoenc.c b/libavformat/icoenc.c
new file mode 100644
index 0000000..e641f7b
--- /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 AVCodecParameters *p)
+{
+ if (p->codec_id == AV_CODEC_ID_BMP) {
+ if (p->format == 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 (p->format != AV_PIX_FMT_PAL8 &&
+ p->format != AV_PIX_FMT_RGB555LE &&
+ p->format != AV_PIX_FMT_BGR24 &&
+ p->format != 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 (p->codec_id == AV_CODEC_ID_PNG) {
+ if (p->format != 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(p->codec_id);
+ av_log(s, AV_LOG_ERROR, "Unsupported codec %s\n", codesc ? codesc->name : "");
+ return AVERROR(EINVAL);
+ }
+
+ if (p->width > 256 ||
+ p->height > 256) {
+ av_log(s, AV_LOG_ERROR, "Unsupported dimensions %dx%d (dimensions cannot exceed 256x256)\n", p->width, p->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 & AVIO_SEEKABLE_NORMAL)) {
+ 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]->codecpar))
+ 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;
+ AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar;
+ 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 = (par->width == 256) ? 0 : par->width;
+ image->height = (par->height == 256) ? 0 : par->height;
+
+ if (par->codec_id == AV_CODEC_ID_PNG) {
+ image->bits = par->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 + par->height * (par->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 < par->height * (par->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]->codecpar->codec_id == AV_CODEC_ID_BMP &&
+ s->streams[i]->codecpar->format == 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 9420d9e..19be421 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 7107073..d5dca35 100644
--- a/libavformat/id3v1.h
+++ b/libavformat/id3v1.h
@@ -2,20 +2,20 @@
* ID3v1 header parser
* Copyright (c) 2003 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c
index 643891c..6c216ba 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) < (int)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;
@@ -276,7 +334,7 @@ static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen,
}
if (!(strcmp(key, "TCON") && strcmp(key, "TCO")) &&
- (sscanf(dst, "(%u)", &genre) == 1 || sscanf(dst, "%u", &genre) == 1) &&
+ (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1) &&
genre <= ID3v1_GENRE_MAX) {
av_freep(&dst);
dst = av_strdup(ff_id3v1_genre_str[genre]);
@@ -293,7 +351,94 @@ 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);
+}
+
+/**
+ * Parse a comment tag.
+ */
+static void read_comment(AVFormatContext *s, AVIOContext *pb, int taglen,
+ AVDictionary **metadata)
+{
+ const char *key = "comment";
+ uint8_t *dst;
+ int encoding, dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL;
+ av_unused int language;
+
+ if (taglen < 4)
+ return;
+
+ encoding = avio_r8(pb);
+ language = avio_rl24(pb);
+ taglen -= 4;
+
+ if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
+ av_log(s, AV_LOG_ERROR, "Error reading comment frame, skipped\n");
+ return;
+ }
+
+ if (dst && !*dst)
+ av_freep(&dst);
+
+ if (dst) {
+ key = (const char *) dst;
+ dict_flags |= AV_DICT_DONT_STRDUP_KEY;
+ }
+
+ if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
+ av_log(s, AV_LOG_ERROR, "Error reading comment frame, skipped\n");
+ if (dict_flags & AV_DICT_DONT_STRDUP_KEY)
+ av_freep((void*)&key);
+ return;
+ }
+
+ if (dst)
+ av_dict_set(metadata, key, (const char *) dst, dict_flags);
}
/**
@@ -313,14 +458,14 @@ static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen,
geob_data = av_mallocz(sizeof(ID3v2ExtraMetaGEOB));
if (!geob_data) {
- av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n",
+ av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n",
sizeof(ID3v2ExtraMetaGEOB));
return;
}
new_extra = av_mallocz(sizeof(ID3v2ExtraMeta));
if (!new_extra) {
- av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n",
+ av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n",
sizeof(ID3v2ExtraMeta));
goto fail;
}
@@ -432,6 +577,13 @@ static void free_apic(void *obj)
av_freep(&apic);
}
+static void rstrip_spaces(char *buf)
+{
+ size_t len = strlen(buf);
+ while (len > 0 && buf[len - 1] == ' ')
+ buf[--len] = 0;
+}
+
static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen,
const char *tag, ID3v2ExtraMeta **extra_meta,
int isv34)
@@ -496,7 +648,7 @@ static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen,
}
apic->buf = av_buffer_alloc(taglen + AV_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, AV_INPUT_BUFFER_PADDING_SIZE);
@@ -505,6 +657,10 @@ static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen,
new_extra->next = *extra_meta;
*extra_meta = new_extra;
+ // The description must be unique, and some ID3v2 tag writers add spaces
+ // to write several APIC entries with the same description.
+ rstrip_spaces(apic->description);
+
return;
fail:
@@ -514,6 +670,115 @@ fail:
avio_seek(pb, end, SEEK_SET);
}
+static void free_chapter(void *obj)
+{
+ ID3v2ExtraMetaCHAP *chap = obj;
+ av_freep(&chap->element_id);
+ av_dict_free(&chap->meta);
+ av_freep(&chap);
+}
+
+static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, const char *ttag, ID3v2ExtraMeta **extra_meta, int isv34)
+{
+ int taglen;
+ char tag[5];
+ ID3v2ExtraMeta *new_extra = NULL;
+ ID3v2ExtraMetaCHAP *chap = NULL;
+
+ new_extra = av_mallocz(sizeof(*new_extra));
+ chap = av_mallocz(sizeof(*chap));
+
+ if (!new_extra || !chap)
+ goto fail;
+
+ if (decode_str(s, pb, 0, &chap->element_id, &len) < 0)
+ goto fail;
+
+ if (len < 16)
+ goto fail;
+
+ chap->start = avio_rb32(pb);
+ chap->end = avio_rb32(pb);
+ avio_skip(pb, 8);
+
+ len -= 16;
+ while (len > 10) {
+ if (avio_read(pb, tag, 4) < 4)
+ goto fail;
+ tag[4] = 0;
+ taglen = avio_rb32(pb);
+ avio_skip(pb, 2);
+ len -= 10;
+ if (taglen < 0 || taglen > len)
+ goto fail;
+ if (tag[0] == 'T')
+ read_ttag(s, pb, taglen, &chap->meta, tag);
+ else
+ avio_skip(pb, taglen);
+ len -= taglen;
+ }
+
+ ff_metadata_conv(&chap->meta, NULL, ff_id3v2_34_metadata_conv);
+ ff_metadata_conv(&chap->meta, NULL, ff_id3v2_4_metadata_conv);
+
+ new_extra->tag = "CHAP";
+ new_extra->data = chap;
+ new_extra->next = *extra_meta;
+ *extra_meta = new_extra;
+
+ return;
+
+fail:
+ if (chap)
+ free_chapter(chap);
+ av_freep(&new_extra);
+}
+
+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 +791,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, free_chapter },
+ { "PRIV","PRIV", read_priv, free_priv },
{ NULL }
};
@@ -538,7 +805,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 +815,26 @@ 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;
+ const char *comm_frame;
+
+ av_log(s, AV_LOG_DEBUG, "id3v2 ver:%d flags:%02X len:%d\n", version, flags, len);
switch (version) {
case 2:
@@ -570,12 +844,14 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
}
isv34 = 0;
taghdrlen = 6;
+ comm_frame = "COM";
break;
case 3:
case 4:
isv34 = 1;
taghdrlen = 10;
+ comm_frame = "COMM";
break;
default:
@@ -586,7 +862,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 +871,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 +937,113 @@ 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) ||
+ !strcmp(tag, comm_frame) ||
(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 if (!strcmp(tag, comm_frame))
+ read_comment(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 +1054,40 @@ 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 = ffio_ensure_seekback(pb, ID3v2_HEADER_SIZE);
+ if (ret >= 0)
+ 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 +1095,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 +1130,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)
@@ -764,6 +1153,9 @@ int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta)
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->codec_id = apic->id;
+ if (AV_RB64(apic->buf->data) == 0x89504e470d0a1a0a)
+ st->codecpar->codec_id = AV_CODEC_ID_PNG;
+
if (apic->description[0])
av_dict_set(&st->metadata, "title", apic->description, 0);
@@ -781,3 +1173,54 @@ int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta)
return 0;
}
+
+int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta **extra_meta)
+{
+ int ret = 0;
+ ID3v2ExtraMeta *cur;
+ AVRational time_base = {1, 1000};
+ ID3v2ExtraMetaCHAP **chapters = NULL;
+ int num_chapters = 0;
+ int i;
+
+ // since extra_meta is a linked list where elements are prepended,
+ // we need to reverse the order of chapters
+ for (cur = *extra_meta; cur; cur = cur->next) {
+ ID3v2ExtraMetaCHAP *chap;
+
+ if (strcmp(cur->tag, "CHAP"))
+ continue;
+ chap = cur->data;
+
+ if ((ret = av_dynarray_add_nofree(&chapters, &num_chapters, chap)) < 0)
+ goto end;
+ }
+
+ for (i = 0; i < (num_chapters / 2); i++) {
+ ID3v2ExtraMetaCHAP *right;
+ int right_index;
+
+ right_index = (num_chapters - 1) - i;
+ right = chapters[right_index];
+
+ chapters[right_index] = chapters[i];
+ chapters[i] = right;
+ }
+
+ for (i = 0; i < num_chapters; i++) {
+ ID3v2ExtraMetaCHAP *chap;
+ AVChapter *chapter;
+
+ chap = chapters[i];
+ chapter = avpriv_new_chapter(s, i, time_base, chap->start, chap->end, chap->element_id);
+ if (!chapter)
+ continue;
+
+ if ((ret = av_dict_copy(&chapter->metadata, chap->meta, 0)) < 0)
+ goto end;
+ }
+
+end:
+ av_freep(&chapters);
+ return ret;
+}
diff --git a/libavformat/id3v2.h b/libavformat/id3v2.h
index 2d65a4d..5e64ead 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,18 @@ typedef struct ID3v2ExtraMetaAPIC {
enum AVCodecID id;
} ID3v2ExtraMetaAPIC;
+typedef struct ID3v2ExtraMetaPRIV {
+ uint8_t *owner;
+ uint8_t *data;
+ uint32_t datasize;
+} ID3v2ExtraMetaPRIV;
+
+typedef struct ID3v2ExtraMetaCHAP {
+ uint8_t *element_id;
+ uint32_t start, end;
+ AVDictionary *meta;
+} ID3v2ExtraMetaCHAP;
+
/**
* Detect ID3v2 Header.
* @param buf must be ID3v2_HEADER_SIZE byte long
@@ -89,11 +101,25 @@ 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.
+ *
+ * @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(AVFormatContext *s, const char *magic, ID3v2ExtraMeta **extra_meta);
+void ff_id3v2_read_dict(AVIOContext *pb, AVDictionary **metadata, const char *magic, ID3v2ExtraMeta **extra_meta);
+
+/**
+ * Read an ID3v2 tag, including supported extra metadata.
+ *
+ * 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,
+ unsigned int max_search_size);
/**
* Initialize an ID3v2 tag.
@@ -114,7 +140,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.
@@ -136,6 +162,11 @@ void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta);
*/
int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta);
+/**
+ * Create chapters for all CHAP tags found in the ID3v2 header.
+ */
+int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta **extra_meta);
+
extern const AVMetadataConv ff_id3v2_34_metadata_conv[];
extern const AVMetadataConv ff_id3v2_4_metadata_conv[];
diff --git a/libavformat/id3v2enc.c b/libavformat/id3v2enc.c
index 3f9bd4e..14de76a 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,65 @@ 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;
+
+ ff_standardize_creation_time(s);
+ 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 +294,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 +323,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 +356,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 86e9f41..cf69102 100644
--- a/libavformat/idcin.c
+++ b/libavformat/idcin.c
@@ -2,20 +2,20 @@
* id Quake II CIN File Demuxer
* Copyright (c) 2003 The FFmpeg project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -93,7 +93,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->codecpar->height = height;
/* load up the Huffman tables into extradata */
- st->codecpar->extradata_size = HUFFMAN_TABLE_SIZE;
- st->codecpar->extradata = av_malloc(HUFFMAN_TABLE_SIZE);
- ret = avio_read(pb, st->codecpar->extradata, HUFFMAN_TABLE_SIZE);
- if (ret < 0) {
+ if ((ret = ff_get_extradata(s, st->codecpar, 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;
}
}
@@ -353,7 +357,7 @@ static int idcin_read_seek(AVFormatContext *s, int stream_index,
IdcinDemuxContext *idcin = s->priv_data;
if (idcin->first_pkt_pos > 0) {
- int ret = avio_seek(s->pb, idcin->first_pkt_pos, SEEK_SET);
+ int64_t ret = avio_seek(s->pb, idcin->first_pkt_pos, SEEK_SET);
if (ret < 0)
return ret;
ff_update_cur_dts(s, s->streams[idcin->video_stream_index], 0);
diff --git a/libavformat/idroqdec.c b/libavformat/idroqdec.c
index a408946..8fd67a6 100644
--- a/libavformat/idroqdec.c
+++ b/libavformat/idroqdec.c
@@ -2,20 +2,20 @@
* id RoQ (.roq) File Demuxer
* Copyright (c) 2003 The FFmpeg project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -31,6 +31,7 @@
#include "libavutil/intreadwrite.h"
#include "avformat.h"
#include "internal.h"
+#include "avio_internal.h"
#define RoQ_MAGIC_NUMBER 0x1084
#define RoQ_CHUNK_PREAMBLE_SIZE 8
@@ -105,7 +106,7 @@ static int roq_read_packet(AVFormatContext *s,
while (!packet_read) {
- if (s->pb->eof_reached)
+ if (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:
@@ -154,6 +157,9 @@ static int roq_read_packet(AVFormatContext *s,
chunk_size = AV_RL32(&preamble[2]) + RoQ_CHUNK_PREAMBLE_SIZE * 2 +
codebook_size;
+ if (chunk_size > INT_MAX)
+ return AVERROR_INVALIDDATA;
+
/* rewind */
avio_seek(pb, codebook_offset, SEEK_SET);
@@ -216,8 +222,10 @@ static int roq_read_packet(AVFormatContext *s,
pkt->pos= avio_tell(pb);
ret = avio_read(pb, pkt->data + RoQ_CHUNK_PREAMBLE_SIZE,
chunk_size);
- if (ret != chunk_size)
+ if (ret != chunk_size) {
+ av_packet_unref(pkt);
ret = AVERROR(EIO);
+ }
packet_read = 1;
break;
diff --git a/libavformat/idroqenc.c b/libavformat/idroqenc.c
index 2ce4d7d..8122efe 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,34 @@
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;
+
+// set the actual fps
+ for(n=0;n<s->nb_streams;n++) {
+ if (s->streams[n]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ unsigned int fps;
+
+ if (s->streams[n]->avg_frame_rate.den != 1) {
+ av_log(s, AV_LOG_ERROR, "Frame rate must be integer\n");
+ return AVERROR(EINVAL);
+ }
+
+ if ((fps=s->streams[n]->avg_frame_rate.num) > 255) {
+ av_log(s, AV_LOG_ERROR, "Frame rate may not exceed 255fps\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (fps != 30) {
+ av_log(s, 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 52c7e97..4cf17f6 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,25 @@
#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_DST MKTAG('D','S','T',' ')
+#define ID_DSTC MKTAG('D','S','T','C')
+#define ID_DSTF MKTAG('D','S','T','F')
+#define ID_FRTE MKTAG('F','R','T','E')
+#define ID_ANIM MKTAG('A','N','I','M')
+#define ID_ANHD MKTAG('A','N','H','D')
+#define ID_DLTA MKTAG('D','L','T','A')
+#define ID_DPAN MKTAG('D','P','A','N')
#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 +78,51 @@
#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
+ int64_t pts;
} IffDemuxContext;
-
/* Metadata string read */
static int get_metadata(AVFormatContext *s,
const char *const tag,
@@ -91,7 +133,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 +146,281 @@ 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_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_DST, ID_DST },
+ { 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 && !avio_feof(pb)) {
+ 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 && !avio_feof(pb)) {
+ 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->codecpar->channels = avio_rb16(pb);
+ if (size < 2 + st->codecpar->channels * 4)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->channel_layout = 0;
+ if (st->codecpar->channels > FF_ARRAY_ELEMS(dsd_layout)) {
+ avpriv_request_sample(s, "channel layout");
+ break;
+ }
+ for (i = 0; i < st->codecpar->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->codecpar->channels &&
+ !memcmp(d->dsd_layout, dsd_layout, st->codecpar->channels * sizeof(uint32_t))) {
+ st->codecpar->channel_layout = d->layout;
+ break;
+ }
+ }
+ break;
+
+ case MKTAG('C','M','P','R'):
+ if (size < 4)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->codec_tag = tag = avio_rl32(pb);
+ st->codecpar->codec_id = ff_codec_get_id(dsd_codec_tags, tag);
+ if (!st->codecpar->codec_id) {
+ av_log(s, AV_LOG_ERROR, "'%s' compression is not supported\n",
+ av_fourcc2str(tag));
+ return AVERROR_PATCHWELCOME;
+ }
+ break;
+
+ case MKTAG('F','S',' ',' '):
+ if (size < 4)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->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 ||
+ (ret = ff_id3v2_parse_chapters(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->codecpar->channel_layout = dsd_loudspeaker_config[config];
+ if (!st->codecpar->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 int read_dst_frame(AVFormatContext *s, AVPacket *pkt)
+{
+ IffDemuxContext *iff = s->priv_data;
+ AVIOContext *pb = s->pb;
+ uint32_t chunk_id;
+ uint64_t chunk_pos, data_pos, data_size;
+ int ret = AVERROR_EOF;
+
+ while (!avio_feof(pb)) {
+ chunk_pos = avio_tell(pb);
+ if (chunk_pos >= iff->body_end)
+ return AVERROR_EOF;
+
+ chunk_id = avio_rl32(pb);
+ data_size = iff->is_64bit ? avio_rb64(pb) : avio_rb32(pb);
+ data_pos = avio_tell(pb);
+
+ if (data_size < 1)
+ return AVERROR_INVALIDDATA;
+
+ switch (chunk_id) {
+ case ID_DSTF:
+ if (!pkt) {
+ iff->body_pos = avio_tell(pb) - (iff->is_64bit ? 12 : 8);
+ iff->body_size = iff->body_end - iff->body_pos;
+ return 0;
+ }
+ ret = av_get_packet(pb, pkt, data_size);
+ if (ret < 0)
+ return ret;
+ if (data_size & 1)
+ avio_skip(pb, 1);
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ pkt->stream_index = 0;
+ pkt->duration = 588 * s->streams[0]->codecpar->sample_rate / 44100;
+ pkt->pos = chunk_pos;
+
+ chunk_pos = avio_tell(pb);
+ if (chunk_pos >= iff->body_end)
+ return 0;
+
+ avio_seek(pb, chunk_pos, SEEK_SET);
+ return 0;
+
+ case ID_FRTE:
+ if (data_size < 4)
+ return AVERROR_INVALIDDATA;
+ s->streams[0]->duration = avio_rb32(pb) * 588LL * s->streams[0]->codecpar->sample_rate / 44100;
+ break;
+ }
+
+ avio_skip(pb, data_size - (avio_tell(pb) - data_pos) + (data_size & 1));
+ }
+
+ return ret;
+}
+
+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 +428,25 @@ static int iff_read_header(AVFormatContext *s)
st->codecpar->channels = 1;
st->codecpar->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->codecpar->codec_tag = avio_rl32(pb);
+ if (st->codecpar->codec_tag == ID_ANIM) {
+ avio_skip(pb, 12);
+ }
+ 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,13 +459,46 @@ static int iff_read_header(AVFormatContext *s)
st->codecpar->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->codecpar->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->codecpar->sample_rate = num / den;
+ st->codecpar->channels = avio_rb16(pb);
+ iff->maud_compression = avio_rb16(pb);
+ if (st->codecpar->channels == 1)
+ st->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
+ else if (st->codecpar->channels == 2)
+ st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
+ break;
+
+ case ID_ABIT:
case ID_BODY:
+ case ID_DBOD:
+ case ID_DSD:
+ case ID_DST:
+ case ID_MDAT:
iff->body_pos = avio_tell(pb);
+ iff->body_end = iff->body_pos + data_size;
iff->body_size = data_size;
+ if (chunk_id == ID_DST) {
+ int ret = read_dst_frame(s, NULL);
+ if (ret < 0)
+ return ret;
+ }
break;
case ID_CHAN:
@@ -167,17 +513,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->codecpar->extradata_size = data_size;
- st->codecpar->extradata = av_malloc(data_size);
+ st->codecpar->extradata_size = data_size + IFF_EXTRA_VIDEO_SIZE;
+ st->codecpar->extradata = av_malloc(data_size + IFF_EXTRA_VIDEO_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
if (!st->codecpar->extradata)
return AVERROR(ENOMEM);
- if (avio_read(pb, st->codecpar->extradata, data_size) < 0)
+ if (avio_read(pb, st->codecpar->extradata + IFF_EXTRA_VIDEO_SIZE, data_size) < 0)
return AVERROR(EIO);
break;
@@ -189,82 +541,249 @@ static int iff_read_header(AVFormatContext *s)
st->codecpar->height = avio_rb16(pb);
avio_skip(pb, 4); // x, y offset
st->codecpar->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); // padding, transparent
st->sample_aspect_ratio.num = avio_r8(pb);
st->sample_aspect_ratio.den = avio_r8(pb);
}
break;
+ case ID_ANHD:
+ break;
+
+ case ID_DPAN:
+ avio_skip(pb, 2);
+ st->duration = avio_rb16(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->codecpar->format = AV_PIX_FMT_RGB24;
+ else if (fmt_size == sizeof(deep_rgba) && !memcmp(fmt, deep_rgba, sizeof(deep_rgba)))
+ st->codecpar->format = AV_PIX_FMT_RGBA;
+ else if (fmt_size == sizeof(deep_bgra) && !memcmp(fmt, deep_bgra, sizeof(deep_bgra)))
+ st->codecpar->format = AV_PIX_FMT_BGRA;
+ else if (fmt_size == sizeof(deep_argb) && !memcmp(fmt, deep_argb, sizeof(deep_argb)))
+ st->codecpar->format = AV_PIX_FMT_ARGB;
+ else if (fmt_size == sizeof(deep_abgr) && !memcmp(fmt, deep_abgr, sizeof(deep_abgr)))
+ st->codecpar->format = AV_PIX_FMT_ABGR;
+ else {
+ avpriv_request_sample(s, "color format %.16s", fmt);
+ return AVERROR_PATCHWELCOME;
+ }
+ break;
+
+ case ID_DGBL:
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ if (data_size < 8)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->width = avio_rb16(pb);
+ st->codecpar->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->codecpar->bits_per_coded_sample = 24;
+ break;
+
+ case ID_DLOC:
+ if (data_size < 4)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->width = avio_rb16(pb);
+ st->codecpar->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->codecpar->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;
}
}
avio_skip(pb, data_size - (avio_tell(pb) - orig_pos) + (data_size & 1));
}
- avio_seek(pb, iff->body_pos, SEEK_SET);
+ if (st->codecpar->codec_tag == ID_ANIM)
+ avio_seek(pb, 12, SEEK_SET);
+ else
+ avio_seek(pb, iff->body_pos, SEEK_SET);
switch(st->codecpar->codec_type) {
case AVMEDIA_TYPE_AUDIO:
avpriv_set_pts_info(st, 32, 1, st->codecpar->sample_rate);
- switch(compression) {
- case COMP_NONE:
- st->codecpar->codec_id = AV_CODEC_ID_PCM_S8_PLANAR;
- break;
- case COMP_FIB:
- st->codecpar->codec_id = AV_CODEC_ID_8SVX_FIB;
- break;
- case COMP_EXP:
- st->codecpar->codec_id = AV_CODEC_ID_8SVX_EXP;
- break;
- default:
- av_log(s, AV_LOG_ERROR, "unknown compression method\n");
- return -1;
+ if (st->codecpar->codec_tag == ID_16SV)
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_S16BE_PLANAR;
+ else if (st->codecpar->codec_tag == ID_MAUD) {
+ if (iff->maud_bits == 8 && !iff->maud_compression) {
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_U8;
+ } else if (iff->maud_bits == 16 && !iff->maud_compression) {
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_S16BE;
+ } else if (iff->maud_bits == 8 && iff->maud_compression == 2) {
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_ALAW;
+ } else if (iff->maud_bits == 8 && iff->maud_compression == 3) {
+ st->codecpar->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->codecpar->codec_tag != ID_DSD &&
+ st->codecpar->codec_tag != ID_DST) {
+ switch (iff->svx8_compression) {
+ case COMP_NONE:
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_S8_PLANAR;
+ break;
+ case COMP_FIB:
+ st->codecpar->codec_id = AV_CODEC_ID_8SVX_FIB;
+ break;
+ case COMP_EXP:
+ st->codecpar->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->codecpar->bits_per_coded_sample = 8;
- st->codecpar->bit_rate = st->codecpar->channels * st->codecpar->sample_rate * st->codecpar->bits_per_coded_sample;
+ st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id);
+ st->codecpar->bit_rate = (int64_t)st->codecpar->channels * st->codecpar->sample_rate * st->codecpar->bits_per_coded_sample;
st->codecpar->block_align = st->codecpar->channels * st->codecpar->bits_per_coded_sample;
+ if (st->codecpar->codec_tag == ID_DSD && st->codecpar->block_align <= 0)
+ return AVERROR_INVALIDDATA;
break;
case AVMEDIA_TYPE_VIDEO:
- switch (compression) {
- case BITMAP_RAW:
- st->codecpar->codec_id = AV_CODEC_ID_IFF_ILBM;
- break;
- case BITMAP_BYTERUN1:
- st->codecpar->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->codecpar->bits_per_coded_sample;
+ if (st->codecpar->codec_tag == ID_ANIM)
+ avpriv_set_pts_info(st, 32, 1, 60);
+ if ((screenmode & 0x800 /* Hold And Modify */) && iff->bpp <= 8) {
+ iff->ham = iff->bpp > 6 ? 6 : 4;
+ st->codecpar->bits_per_coded_sample = 24;
+ }
+ iff->flags = (screenmode & 0x80 /* Extra HalfBrite */) && iff->bpp <= 8;
+ iff->masking = masking;
+ iff->transparency = transparency;
+
+ if (!st->codecpar->extradata) {
+ st->codecpar->extradata_size = IFF_EXTRA_VIDEO_SIZE;
+ st->codecpar->extradata = av_malloc(IFF_EXTRA_VIDEO_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!st->codecpar->extradata)
+ return AVERROR(ENOMEM);
}
+ av_assert0(st->codecpar->extradata_size >= IFF_EXTRA_VIDEO_SIZE);
+ buf = st->codecpar->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->codecpar->codec_id = AV_CODEC_ID_IFF_ILBM;
break;
default:
return -1;
@@ -273,24 +792,94 @@ static int iff_read_header(AVFormatContext *s)
return 0;
}
+static unsigned get_anim_duration(uint8_t *buf, int size)
+{
+ GetByteContext gb;
+
+ bytestream2_init(&gb, buf, size);
+ bytestream2_skip(&gb, 4);
+ while (bytestream2_get_bytes_left(&gb) > 8) {
+ unsigned chunk = bytestream2_get_le32(&gb);
+ unsigned size = bytestream2_get_be32(&gb);
+
+ if (chunk == ID_ANHD) {
+ if (size < 40)
+ break;
+ bytestream2_skip(&gb, 14);
+ return bytestream2_get_be32(&gb);
+ } else {
+ bytestream2_skip(&gb, size + size & 1);
+ }
+ }
+ return 10;
+}
+
static int iff_read_packet(AVFormatContext *s,
AVPacket *pkt)
{
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 (avio_feof(pb))
return AVERROR_EOF;
+ if (st->codecpar->codec_tag != ID_ANIM && pos >= iff->body_end)
+ return AVERROR_EOF;
+
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ if (st->codecpar->codec_tag == ID_DSD || st->codecpar->codec_tag == ID_MAUD) {
+ ret = av_get_packet(pb, pkt, FFMIN(iff->body_end - pos, 1024 * st->codecpar->block_align));
+ } else if (st->codecpar->codec_tag == ID_DST) {
+ return read_dst_frame(s, pkt);
+ } else {
+ if (iff->body_size > INT_MAX)
+ return AVERROR_INVALIDDATA;
+ ret = av_get_packet(pb, pkt, iff->body_size);
+ }
+ } else if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
+ st->codecpar->codec_tag == ID_ANIM) {
+ uint64_t data_size, orig_pos;
+ uint32_t chunk_id, chunk_id2;
+
+ while (!avio_feof(pb)) {
+ if (avio_feof(pb))
+ return AVERROR_EOF;
+
+ orig_pos = avio_tell(pb);
+ chunk_id = avio_rl32(pb);
+ data_size = avio_rb32(pb);
+ chunk_id2 = avio_rl32(pb);
+
+ if (chunk_id == ID_FORM &&
+ chunk_id2 == ID_ILBM) {
+ avio_skip(pb, -4);
+ break;
+ } else if (chunk_id == ID_FORM &&
+ chunk_id2 == ID_ANIM) {
+ continue;
+ } else {
+ avio_skip(pb, data_size);
+ }
+ }
+ ret = av_get_packet(pb, pkt, data_size);
+ pkt->pos = orig_pos;
+ pkt->duration = get_anim_duration(pkt->data, pkt->size);
+ if (pos == 12)
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ } else if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
+ st->codecpar->codec_tag != ID_ANIM) {
+ ret = av_get_packet(pb, pkt, iff->body_size);
+ pkt->pos = pos;
+ if (pos == iff->body_pos)
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ } else {
+ av_assert0(0);
+ }
- ret = av_get_packet(pb, pkt, iff->body_size);
if (ret < 0)
return ret;
-
- if(iff->sent_bytes == 0)
- pkt->flags |= AV_PKT_FLAG_KEY;
- iff->sent_bytes = iff->body_size;
-
pkt->stream_index = 0;
return ret;
}
@@ -302,4 +891,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 b8549f3..50e3c3c 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 3cfc08e..8432cc0 100644
--- a/libavformat/img2.c
+++ b/libavformat/img2.c
@@ -3,37 +3,34 @@
* 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/avstring.h"
#include "internal.h"
+#include "img2.h"
-typedef struct IdStrMap {
- enum AVCodecID id;
- const char *str;
-} IdStrMap;
-
-static const IdStrMap img_tags[] = {
+const IdStrMap ff_img_tags[] = {
{ AV_CODEC_ID_MJPEG, "jpeg" },
{ AV_CODEC_ID_MJPEG, "jpg" },
{ 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" },
@@ -48,16 +45,14 @@ static const IdStrMap img_tags[] = {
{ AV_CODEC_ID_MPEG1VIDEO, "mpg1-img" },
{ AV_CODEC_ID_MPEG2VIDEO, "mpg2-img" },
{ 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_QDRAW, "pic" },
{ AV_CODEC_ID_QDRAW, "pct" },
@@ -68,7 +63,10 @@ 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_SVG, "svg" },
+ { AV_CODEC_ID_SVG, "svgz" },
{ AV_CODEC_ID_JPEG2000, "j2c" },
{ AV_CODEC_ID_JPEG2000, "jp2" },
{ AV_CODEC_ID_JPEG2000, "jpc" },
@@ -76,8 +74,11 @@ 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_XPM, "xpm" },
+ { AV_CODEC_ID_XFACE, "xface" },
{ AV_CODEC_ID_XWD, "xwd" },
{ AV_CODEC_ID_NONE, NULL }
};
@@ -100,5 +101,5 @@ static enum AVCodecID str2id(const IdStrMap *tags, const char *str)
enum AVCodecID ff_guess_image2_codec(const char *filename)
{
- return str2id(img_tags, filename);
+ return str2id(ff_img_tags, filename);
}
diff --git a/libavformat/img2.h b/libavformat/img2.h
new file mode 100644
index 0000000..0e5b374
--- /dev/null
+++ b/libavformat/img2.h
@@ -0,0 +1,78 @@
+/*
+ * 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
+
+enum PatternType {
+ PT_GLOB_SEQUENCE,
+ PT_GLOB,
+ PT_SEQUENCE,
+ PT_NONE,
+ PT_DEFAULT
+};
+
+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;
+ int pattern_type; /**< PatternType */
+ 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;
+
+typedef struct IdStrMap {
+ enum AVCodecID id;
+ const char *str;
+} IdStrMap;
+
+extern const IdStrMap ff_img_tags[];
+
+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 0000000..c2650ad
--- /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 0000000..ae6b3dd
--- /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 3d23831..ecf64ea 100644
--- a/libavformat/img2dec.c
+++ b/libavformat/img2dec.c
@@ -3,45 +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
*/
+#define _DEFAULT_SOURCE
+#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"
+#include "libavcodec/mjpeg.h"
+#include "subtitles.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,26 +75,55 @@ static int infer_size(int *width_ptr, int *height_ptr, int size)
return -1;
}
-/* return -1 if no image found */
-static int find_image_range(int *pfirst_index, int *plast_index,
- const char *path, int max_start)
+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(AVIOContext *pb, int *pfirst_index, int *plast_index,
+ 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;
- if (avio_check(buf, AVIO_FLAG_READ) > 0)
+ if (pb || avio_check(buf, AVIO_FLAG_READ) > 0)
return 0;
return -1;
}
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 +163,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 = 1, last_index = 1;
AVStream *st;
enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE;
- AVRational framerate;
s1->ctx_flags |= AVFMTCTX_NOHEADER;
@@ -157,17 +197,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 +210,101 @@ 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->codecpar->width = width;
- st->codecpar->height = height;
+ if (s->width && s->height) {
+ st->codecpar->width = s->width;
+ st->codecpar->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_DEFAULT) {
+ if (s1->pb) {
+ s->pattern_type = PT_NONE;
+ } else
+ s->pattern_type = PT_GLOB_SEQUENCE;
+ }
+
+ 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(s1->pb, &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 && s->pattern_type != PT_NONE) {
+ 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 +313,55 @@ static int img_read_header(AVFormatContext *s1)
} else if (s1->audio_codec_id) {
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = s1->audio_codec_id;
+ } else if (s1->iformat->raw_codec_id) {
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_id = s1->iformat->raw_codec_id;
} else {
+ const char *str = strrchr(s->path, '.');
+ s->split_planes = str && !av_strcasecmp(str + 1, "y");
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
- st->codecpar->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->codecpar->codec_id = fmt->raw_codec_id;
+ break;
+ }
+ }
+ if (s1->flags & AVFMT_FLAG_CUSTOM_IO) {
+ avio_seek(s1->pb, 0, SEEK_SET);
+ } else
+ ffio_rewind_with_probe_data(s1->pb, &probe_buffer, probe_buffer_size);
+ }
+ if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
+ st->codecpar->codec_id = ff_guess_image2_codec(s->path);
+ if (st->codecpar->codec_id == AV_CODEC_ID_LJPEG)
+ st->codecpar->codec_id = AV_CODEC_ID_MJPEG;
+ if (st->codecpar->codec_id == AV_CODEC_ID_ALIAS_PIX) // we cannot distingiush this from BRENDER_PIX
+ st->codecpar->codec_id = AV_CODEC_ID_NONE;
}
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
pix_fmt != AV_PIX_FMT_NONE)
@@ -217,10 +370,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,12 +387,25 @@ 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->pattern_type == PT_NONE) {
+ av_strlcpy(filename_bytes, s->path, sizeof(filename_bytes));
+ } else 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 (s1->io_open(s1, &f[i], filename, AVIO_FLAG_READ, NULL) < 0) {
+ if (s1->pb &&
+ !strcmp(filename_bytes, s->path) &&
+ !s->loop &&
+ !s->split_planes) {
+ f[i] = s1->pb;
+ } else if (s1->io_open(s1, &f[i], filename, AVIO_FLAG_READ, NULL) < 0) {
if (i >= 1)
break;
av_log(s1, AV_LOG_ERROR, "Could not open file : %s\n",
@@ -247,31 +414,85 @@ static int img_read_packet(AVFormatContext *s1, AVPacket *pkt)
}
size[i] = avio_size(f[i]);
- if (par->codec_id != AV_CODEC_ID_RAWVIDEO)
+ if (!s->split_planes)
break;
filename[strlen(filename) - 1] = 'U' + i;
}
+ if (par->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)
+ par->codec_id = ifmt->raw_codec_id;
+ }
+
if (par->codec_id == AV_CODEC_ID_RAWVIDEO && !par->width)
infer_size(&par->width, &par->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]);
- if (res < 0)
- return res;
+ if (res < 0) {
+ goto fail;
+ }
pkt->stream_index = 0;
pkt->flags |= AV_PKT_FLAG_KEY;
+ if (s->ts_from_file) {
+ struct stat img_stat;
+ if (stat(filename, &img_stat)) {
+ res = AVERROR(EIO);
+ goto fail;
+ }
+ 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->is_pipe)
+ 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 && f[i] != s1->pb)
ff_format_io_close(s1, &f[i]);
if (ret[i] > 0)
pkt->size += ret[i];
@@ -280,22 +501,85 @@ static int img_read_packet(AVFormatContext *s1, AVPacket *pkt)
if (ret[0] <= 0 || ret[1] < 0 || ret[2] < 0) {
av_packet_unref(pkt);
- return AVERROR(EIO); /* signal EOF */
+ if (ret[0] < 0) {
+ res = ret[0];
+ } else if (ret[1] < 0) {
+ res = ret[1];
+ } else if (ret[2] < 0) {
+ res = ret[2];
+ } else {
+ res = AVERROR_EOF;
+ }
+ goto fail;
} else {
s->img_count++;
s->img_number++;
+ s->pts++;
+ return 0;
+ }
+
+fail:
+ if (!s->is_pipe) {
+ for (i = 0; i < 3; i++) {
+ if (f[i] != s1->pb)
+ ff_format_io_close(s1, &f[i]);
+ }
+ }
+ return res;
+}
+
+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, INT_MAX, DEC },
+ { "loop", "force loop over input file sequence", OFFSET(loop), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, DEC },
+
+ { "pattern_type", "set pattern type", OFFSET(pattern_type), AV_OPT_TYPE_INT, {.i64=PT_DEFAULT}, 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" },
+ { "none", "disable pattern matching", 0, AV_OPT_TYPE_CONST, {.i64=PT_NONE }, 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 },
};
@@ -303,7 +587,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 = {
@@ -311,8 +595,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,
};
@@ -321,15 +607,407 @@ 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 dds_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+
+ if ( AV_RB64(b) == 0x444453207c000000
+ && AV_RL32(b + 8)
+ && AV_RL32(b + 12))
+ return AVPROBE_SCORE_MAX - 1;
+ return 0;
+}
+
+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 = SOI;
+
+ if (AV_RB16(b) != 0xFFD8 ||
+ AV_RB32(b) == 0xFFD8FFF7)
+ return 0;
+
+ b += 2;
+ for (i = 0; i < p->buf_size - 3; i++) {
+ int c;
+ if (b[i] != 0xFF)
+ continue;
+ c = b[i + 1];
+ switch (c) {
+ case SOI:
+ return 0;
+ case SOF0:
+ case SOF1:
+ case SOF2:
+ case SOF3:
+ case SOF5:
+ case SOF6:
+ case SOF7:
+ i += AV_RB16(&b[i + 2]) + 1;
+ if (state != SOI)
+ return 0;
+ state = SOF0;
+ break;
+ case SOS:
+ i += AV_RB16(&b[i + 2]) + 1;
+ if (state != SOF0 && state != SOS)
+ return 0;
+ state = SOS;
+ break;
+ case EOI:
+ if (state != SOS)
+ return 0;
+ state = EOI;
+ break;
+ case DQT:
+ case APP0:
+ case APP1:
+ case APP2:
+ case APP3:
+ case APP4:
+ case APP5:
+ case APP6:
+ case APP7:
+ case APP8:
+ case APP9:
+ case APP10:
+ case APP11:
+ case APP12:
+ case APP13:
+ case APP14:
+ case APP15:
+ case COM:
+ i += AV_RB16(&b[i + 2]) + 1;
+ break;
+ default:
+ if ( (c > TEM && c < SOF0)
+ || c == JPG)
+ return 0;
+ }
+ }
+
+ if (state == EOI)
+ return AVPROBE_SCORE_EXTENSION + 1;
+ if (state == SOS)
+ return AVPROBE_SCORE_EXTENSION / 2;
+ 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 pcx_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+
+ if ( p->buf_size < 128
+ || b[0] != 10
+ || b[1] > 5
+ || b[2] > 1
+ || av_popcount(b[3]) != 1 || b[3] > 8
+ || AV_RL16(&b[4]) > AV_RL16(&b[8])
+ || AV_RL16(&b[6]) > AV_RL16(&b[10])
+ || b[64])
+ return 0;
+ b += 73;
+ while (++b < p->buf + 128)
+ if (*b)
+ return AVPROBE_SCORE_EXTENSION / 4;
+
+ return AVPROBE_SCORE_EXTENSION + 1;
+}
+
+static int qdraw_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+
+ if ( p->buf_size >= 528
+ && (AV_RB64(b + 520) & 0xFFFFFFFFFFFF) == 0x001102ff0c00
+ && AV_RB16(b + 520)
+ && AV_RB16(b + 518))
+ return AVPROBE_SCORE_MAX * 3 / 4;
+ if ( (AV_RB64(b + 8) & 0xFFFFFFFFFFFF) == 0x001102ff0c00
+ && AV_RB16(b + 8)
+ && AV_RB16(b + 6))
+ return AVPROBE_SCORE_EXTENSION / 4;
+ 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 psd_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+ int ret = 0;
+ uint16_t color_mode;
+
+ if (AV_RL32(b) == MKTAG('8','B','P','S')) {
+ ret += 1;
+ } else {
+ return 0;
+ }
+
+ if ((b[4] == 0) && (b[5] == 1)) {/* version 1 is PSD, version 2 is PSB */
+ ret += 1;
+ } else {
+ return 0;
+ }
+
+ if ((AV_RL32(b+6) == 0) && (AV_RL16(b+10) == 0))/* reserved must be 0 */
+ ret += 1;
+
+ color_mode = AV_RB16(b+24);
+ if ((color_mode <= 9) && (color_mode != 5) && (color_mode != 6))
+ ret += 1;
+
+ return AVPROBE_SCORE_EXTENSION + ret;
+}
+
+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 svg_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+ const uint8_t *end = p->buf + p->buf_size;
+ if (memcmp(p->buf, "<?xml", 5))
+ return 0;
+ while (b < end) {
+ b += ff_subtitles_next_line(b);
+ if (b >= end - 4)
+ return 0;
+ if (!memcmp(b, "<svg", 4))
+ 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;
+}
+
+static int pnm_magic_check(const AVProbeData *p, int magic)
+{
+ const uint8_t *b = p->buf;
+
+ return b[0] == 'P' && b[1] == magic + '0';
+}
+
+static inline int pnm_probe(const AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+
+ while (b[2] == '\r')
+ b++;
+ if (b[2] == '\n' && (b[3] == '#' || (b[3] >= '0' && b[3] <= '9')))
+ return AVPROBE_SCORE_EXTENSION + 2;
+ return 0;
+}
+
+static int pbm_probe(AVProbeData *p)
+{
+ return pnm_magic_check(p, 1) || pnm_magic_check(p, 4) ? pnm_probe(p) : 0;
+}
+
+static inline int pgmx_probe(AVProbeData *p)
+{
+ return pnm_magic_check(p, 2) || pnm_magic_check(p, 5) ? pnm_probe(p) : 0;
+}
+
+static int pgm_probe(AVProbeData *p)
+{
+ int ret = pgmx_probe(p);
+ return ret && !av_match_ext(p->filename, "pgmyuv") ? ret : 0;
+}
+
+static int pgmyuv_probe(AVProbeData *p) // custom FFmpeg format recognized by file extension
+{
+ int ret = pgmx_probe(p);
+ return ret && av_match_ext(p->filename, "pgmyuv") ? ret : 0;
+}
+
+static int ppm_probe(AVProbeData *p)
+{
+ return pnm_magic_check(p, 3) || pnm_magic_check(p, 6) ? pnm_probe(p) : 0;
+}
+
+static int pam_probe(AVProbeData *p)
+{
+ return pnm_magic_check(p, 7) ? pnm_probe(p) : 0;
+}
+
+static int xpm_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+
+ if (AV_RB64(b) == 0x2f2a2058504d202a && *(b+8) == '/')
+ 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(dds, AV_CODEC_ID_DDS)
+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(pam, AV_CODEC_ID_PAM)
+IMAGEAUTO_DEMUXER(pbm, AV_CODEC_ID_PBM)
+IMAGEAUTO_DEMUXER(pcx, AV_CODEC_ID_PCX)
+IMAGEAUTO_DEMUXER(pgm, AV_CODEC_ID_PGM)
+IMAGEAUTO_DEMUXER(pgmyuv, AV_CODEC_ID_PGMYUV)
+IMAGEAUTO_DEMUXER(pictor, AV_CODEC_ID_PICTOR)
+IMAGEAUTO_DEMUXER(png, AV_CODEC_ID_PNG)
+IMAGEAUTO_DEMUXER(ppm, AV_CODEC_ID_PPM)
+IMAGEAUTO_DEMUXER(psd, AV_CODEC_ID_PSD)
+IMAGEAUTO_DEMUXER(qdraw, AV_CODEC_ID_QDRAW)
+IMAGEAUTO_DEMUXER(sgi, AV_CODEC_ID_SGI)
+IMAGEAUTO_DEMUXER(sunrast, AV_CODEC_ID_SUNRAST)
+IMAGEAUTO_DEMUXER(svg, AV_CODEC_ID_SVG)
+IMAGEAUTO_DEMUXER(tiff, AV_CODEC_ID_TIFF)
+IMAGEAUTO_DEMUXER(webp, AV_CODEC_ID_WEBP)
+IMAGEAUTO_DEMUXER(xpm, AV_CODEC_ID_XPM)
diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c
index a365771..d793807 100644
--- a/libavformat/img2enc.c
+++ b/libavformat/img2enc.c
@@ -3,48 +3,54 @@
* 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 RenameIO {
- AVIOContext *pb;
- char filename[1024];
- char tmp[1024];
-} RenameIO;
+#include "img2.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];
+ char tmp[4][1024];
+ char target[4][1024];
int update;
+ int use_strftime;
+ const char *muxer;
+ int use_rename;
} 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->codecpar->format);
av_strlcpy(img->path, s->filename, sizeof(img->path));
@@ -54,105 +60,154 @@ static int write_header(AVFormatContext *s)
else
img->is_pipe = 1;
- return 0;
-}
-
-static int open_temporary(AVFormatContext *s, RenameIO *out, const char *filename)
-{
- snprintf(out->tmp, sizeof(out->tmp), "%s.tmp", filename);
- av_strlcpy(out->filename, filename, sizeof(out->filename));
- if (s->io_open(s, &out->pb, out->tmp, AVIO_FLAG_WRITE, NULL) < 0) {
- av_log(s, AV_LOG_ERROR, "Could not open file : %s\n", out->tmp);
- return AVERROR(EIO);
+ if (st->codecpar->codec_id == AV_CODEC_ID_GIF) {
+ img->muxer = "gif";
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_FITS) {
+ img->muxer = "fits";
+ } else if (st->codecpar->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 void close_and_rename(AVFormatContext *s, RenameIO *out)
-{
- ff_format_io_close(s, &out->pb);
- ff_rename(out->tmp, out->filename);
-}
-
static int write_packet(AVFormatContext *s, AVPacket *pkt)
{
VideoMuxData *img = s->priv_data;
- RenameIO out[3];
+ AVIOContext *pb[4];
char filename[1024];
AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar;
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(par->format);
int i;
+ int nb_renames = 0;
if (!img->is_pipe) {
if (img->update) {
av_strlcpy(filename, img->path, sizeof(filename));
- } else if (av_get_frame_filename(filename, sizeof(filename), img->path, img->img_number) < 0 &&
+ } 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_filename2(filename, sizeof(filename), img->path,
+ img->img_number,
+ AV_FRAME_FILENAME_FLAGS_MULTIPLE) < 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++) {
- int ret = open_temporary(s, &out[i], filename);
- if (ret < 0)
- return ret;
- if (par->codec_id != AV_CODEC_ID_RAWVIDEO)
+ for (i = 0; i < 4; i++) {
+ snprintf(img->tmp[i], sizeof(img->tmp[i]), "%s.tmp", filename);
+ av_strlcpy(img->target[i], filename, sizeof(img->target[i]));
+ if (s->io_open(s, &pb[i], img->use_rename ? img->tmp[i] : filename, AVIO_FLAG_WRITE, NULL) < 0) {
+ av_log(s, AV_LOG_ERROR, "Could not open file : %s\n", img->use_rename ? img->tmp[i] : filename);
+ return AVERROR(EIO);
+ }
+
+ if (!img->split_planes || i+1 >= desc->nb_components)
break;
- filename[strlen(filename) - 1] = 'U' + i;
+ filename[strlen(filename) - 1] = "UVAx"[i];
}
+ if (img->use_rename)
+ nb_renames = i + 1;
} else {
- out[0].pb = s->pb;
+ pb[0] = s->pb;
}
- if (par->codec_id == AV_CODEC_ID_RAWVIDEO) {
+ if (img->split_planes) {
int ysize = par->width * par->height;
- avio_write(out[0].pb, pkt->data, ysize);
- avio_write(out[1].pb, pkt->data + ysize, (pkt->size - ysize) / 2);
- avio_write(out[2].pb, pkt->data + ysize + (pkt->size - ysize) / 2, (pkt->size - ysize) / 2);
- close_and_rename(s, &out[1]);
- close_and_rename(s, &out[2]);
- } else {
- if (ff_guess_image2_codec(s->filename) == AV_CODEC_ID_JPEG2000) {
- AVStream *st = s->streams[0];
- if (st->codecpar->extradata_size > 8 &&
- AV_RL32(st->codecpar->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(out[0].pb, 12);
- ffio_wfourcc(out[0].pb, "jP ");
- avio_wb32(out[0].pb, 0x0D0A870A); // signature
- avio_wb32(out[0].pb, 20);
- ffio_wfourcc(out[0].pb, "ftyp");
- ffio_wfourcc(out[0].pb, "jp2 ");
- avio_wb32(out[0].pb, 0);
- ffio_wfourcc(out[0].pb, "jp2 ");
- avio_write(out[0].pb, st->codecpar->extradata, st->codecpar->extradata_size);
- } else if (pkt->size < 8 ||
- (!st->codecpar->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 = AV_CEIL_RSHIFT(par->width, desc->log2_chroma_w) * AV_CEIL_RSHIFT(par->height, desc->log2_chroma_h);
+ if (desc->comp[0].depth >= 9) {
+ 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);
+ ff_format_io_close(s, &pb[1]);
+ ff_format_io_close(s, &pb[2]);
+ if (desc->nb_components > 3) {
+ avio_write(pb[3], pkt->data + ysize + 2*usize, ysize);
+ ff_format_io_close(s, &pb[3]);
}
- avio_write(out[0].pb, pkt->data, pkt->size);
+ } 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_packet_ref(&pkt2, pkt)) < 0 ||
+ (ret = avcodec_parameters_copy(st->codecpar, s->streams[0]->codecpar)) < 0 ||
+ (ret = avformat_write_header(fmt, NULL)) < 0 ||
+ (ret = av_interleaved_write_frame(fmt, &pkt2)) < 0 ||
+ (ret = av_write_trailer(fmt)) < 0) {
+ av_packet_unref(&pkt2);
+ avformat_free_context(fmt);
+ return ret;
+ }
+ av_packet_unref(&pkt2);
+ avformat_free_context(fmt);
+ } else {
+ avio_write(pb[0], pkt->data, pkt->size);
}
- avio_flush(out[0].pb);
+ avio_flush(pb[0]);
if (!img->is_pipe) {
- close_and_rename(s, &out[0]);
+ ff_format_io_close(s, &pb[0]);
+ for (i = 0; i < nb_renames; i++) {
+ int ret = ff_rename(img->tmp[i], img->target[i], s);
+ if (ret < 0)
+ return ret;
+ }
}
img->img_number++;
return 0;
}
+static int query_codec(enum AVCodecID id, int std_compliance)
+{
+ int i;
+ for (i = 0; ff_img_tags[i].id != AV_CODEC_ID_NONE; i++)
+ if (ff_img_tags[i].id == id)
+ return 1;
+
+ // Anything really can be stored in img2
+ return std_compliance < FF_COMPLIANCE_NORMAL;
+}
+
#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 },
- { "update", "continuously overwrite one file", OFFSET(update), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, ENC },
+ { "updatefirst", "continuously overwrite one file", OFFSET(update), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, ENC },
+ { "update", "continuously overwrite one file", OFFSET(update), AV_OPT_TYPE_BOOL, { .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_BOOL, { .i64 = 0 }, 0, 1, ENC },
+ { "atomic_writing", "write files atomically (using temporary files and renames)", OFFSET(use_rename), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, ENC },
{ NULL },
};
@@ -167,13 +222,14 @@ 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,
.write_packet = write_packet,
+ .query_codec = query_codec,
.flags = AVFMT_NOTIMESTAMPS | AVFMT_NODIMENSIONS | AVFMT_NOFILE,
.priv_class = &img2mux_class,
};
@@ -186,6 +242,7 @@ AVOutputFormat ff_image2pipe_muxer = {
.video_codec = AV_CODEC_ID_MJPEG,
.write_header = write_header,
.write_packet = write_packet,
+ .query_codec = query_codec,
.flags = AVFMT_NOTIMESTAMPS | AVFMT_NODIMENSIONS
};
#endif
diff --git a/libavformat/ingenientdec.c b/libavformat/ingenientdec.c
index 2bb70e7..c0ba61e 100644
--- a/libavformat/ingenientdec.c
+++ b/libavformat/ingenientdec.c
@@ -2,27 +2,37 @@
* 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
*/
#include "avformat.h"
#include "rawdec.h"
+#include "libavutil/intreadwrite.h"
+
+// http://multimedia.cx/ingenient.txt
+static int ingenient_probe(AVProbeData *p)
+{
+ if ( AV_RN32(p->buf) != AV_RN32("MJPG")
+ || p->buf_size < 50
+ || AV_RB16(p->buf + 48) != 0xffd8)
+ return 0;
+ return AVPROBE_SCORE_MAX * 3 / 4;
+}
-// http://www.artificis.hu/files/texts/ingenient.txt
static int ingenient_read_packet(AVFormatContext *s, AVPacket *pkt)
{
int ret, size, w, h, unk1, unk2;
@@ -44,17 +54,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_packet_unref(pkt);
+ ret = av_get_packet(s->pb, pkt, size);
+ if (ret < 0)
return ret;
- }
- pkt->size = ret;
+ pkt->stream_index = 0;
return ret;
}
@@ -64,6 +67,7 @@ AVInputFormat ff_ingenient_demuxer = {
.name = "ingenient",
.long_name = NULL_IF_CONFIG_SMALL("raw Ingenient MJPEG"),
.priv_data_size = sizeof(FFRawVideoDemuxerContext),
+ .read_probe = ingenient_probe,
.read_header = ff_raw_video_read_header,
.read_packet = ingenient_read_packet,
.flags = AVFMT_GENERIC_INDEX,
diff --git a/libavformat/internal.h b/libavformat/internal.h
index 52cd29b..d136c79 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
*/
@@ -22,15 +22,23 @@
#define AVFORMAT_INTERNAL_H
#include <stdint.h>
+
+#include "libavutil/bprint.h"
#include "avformat.h"
#include "os_support.h"
#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)
+
+#define MAX_PROBE_PACKETS 2500
+
#ifdef DEBUG
# define hex_dump_debug(class, buf, size) av_hex_dump_log(class, AV_LOG_DEBUG, buf, size)
#else
-# define hex_dump_debug(class, buf, size)
+# define hex_dump_debug(class, buf, size) do { if (0) av_hex_dump_log(class, AV_LOG_DEBUG, buf, size); } while(0)
#endif
typedef struct AVCodecTag {
@@ -43,6 +51,18 @@ typedef struct CodecMime{
enum AVCodecID id;
} CodecMime;
+/*************************************************/
+/* fractional numbers for exact pts handling */
+
+/**
+ * The exact value of the fractional number is: 'val + num / den'.
+ * num is assumed to be 0 <= num < den.
+ */
+typedef struct FFFrac {
+ int64_t val, num, den;
+} FFFrac;
+
+
struct AVFormatInternal {
/**
* Number of streams relevant for interleaving.
@@ -83,6 +103,7 @@ struct AVFormatInternal {
/**
* Offset to remap timestamps to be non-negative.
* Expressed in timebase units.
+ * @see AVStream.mux_ts_offset
*/
int64_t offset;
@@ -94,6 +115,41 @@ struct AVFormatInternal {
#if FF_API_COMPUTE_PKT_FIELDS2
int missing_ts_warning;
#endif
+
+ int inject_global_side_data;
+
+ int avoid_negative_ts_use_pts;
+
+ /**
+ * Whether or not a header has already been written
+ */
+ int header_written;
+ int write_header_ret;
+
+ /**
+ * Timestamp of the end of the shortest stream.
+ */
+ int64_t shortest_end;
+
+ /**
+ * Whether or not avformat_init_output has already been called
+ */
+ int initialized;
+
+ /**
+ * Whether or not avformat_init_output fully initialized streams
+ */
+ int streams_initialized;
+
+ /**
+ * ID3v2 tag useful for MP3 demuxing
+ */
+ AVDictionary *id3v2_meta;
+
+ /*
+ * Prefer the codec framerate for avg_frame_rate computation.
+ */
+ int prefer_codec_framerate;
};
struct AVStreamInternal {
@@ -102,6 +158,20 @@ struct AVStreamInternal {
* from dts.
*/
int reorder;
+
+ /**
+ * bitstream filters to run on stream
+ * - encoding: Set by muxer using ff_stream_add_bitstream_filter
+ * - decoding: unused
+ */
+ AVBSFContext **bsfcs;
+ int nb_bsfcs;
+
+ /**
+ * Whether or not check_bitstream should still be run on each packet
+ */
+ int bitstream_checked;
+
/**
* The codec context used by avformat_find_stream_info, the parser, etc.
*/
@@ -122,27 +192,24 @@ struct AVStreamInternal {
int inited;
} extract_extradata;
-#if FF_API_LAVF_AVCTX
- // whether the deprecated stream codec context needs
- // to be filled from the codec parameters
- int need_codec_update;
-#endif
+ /**
+ * Whether the internal avctx needs to be updated from codecpar (after a late change to codecpar)
+ */
+ int need_context_update;
};
-void ff_dynarray_add(intptr_t **tab_ptr, int *nb_ptr, intptr_t elem);
-
#ifdef __GNUC__
#define dynarray_add(tab, nb_ptr, elem)\
do {\
__typeof__(tab) _tab = (tab);\
__typeof__(elem) _elem = (elem);\
(void)sizeof(**_tab == _elem); /* check that types are compatible */\
- ff_dynarray_add((intptr_t **)_tab, nb_ptr, (intptr_t)_elem);\
+ av_dynarray_add(_tab, nb_ptr, _elem);\
} while(0)
#else
#define dynarray_add(tab, nb_ptr, elem)\
do {\
- ff_dynarray_add((intptr_t **)(tab), nb_ptr, (intptr_t)(elem));\
+ av_dynarray_add((tab), nb_ptr, (elem));\
} while(0)
#endif
@@ -160,11 +227,10 @@ char *ff_data_to_hex(char *buf, const uint8_t *src, int size, int lowercase);
*/
int ff_hex_to_data(uint8_t *data, const char *p);
-void ff_program_add_stream_index(AVFormatContext *ac, int progid, unsigned int idx);
-
/**
* 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 *));
@@ -208,10 +274,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.
@@ -282,6 +349,8 @@ int ff_add_index_entry(AVIndexEntry **index_entries,
unsigned int *index_entries_allocated_size,
int64_t pos, int64_t timestamp, int size, int distance, int flags);
+void ff_configure_buffers_for_index(AVFormatContext *s, int64_t time_tolerance);
+
/**
* Add a new chapter.
*
@@ -306,11 +375,6 @@ void ff_reduce_index(AVFormatContext *s, int stream_index);
enum AVCodecID ff_guess_image2_codec(const char *filename);
/**
- * Convert a date string in ISO8601 format to Unix timestamp.
- */
-int64_t ff_iso8601_to_unix_time(const char *datestr);
-
-/**
* Perform a binary search using av_index_search_timestamp() and
* AVInputFormat.read_timestamp().
*
@@ -330,6 +394,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().
*
@@ -399,6 +466,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.
*/
@@ -425,32 +494,187 @@ 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);
+
+/**
+ * Chooses a timebase for muxing the specified stream.
+ */
+enum AVChromaLocation ff_choose_chroma_location(AVFormatContext *s, AVStream *st);
+
+/**
* Generate standard extradata for AVC-Intra based on width/height and field
* order.
*/
int ff_generate_avci_extradata(AVStream *st);
/**
+ * Add a bitstream filter to a stream.
+ *
+ * @param st output stream to add a filter to
+ * @param name the name of the filter to add
+ * @param args filter-specific argument string
+ * @return >0 on success;
+ * AVERROR code on failure
+ */
+int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args);
+
+/**
+ * Copy encoding parameters from source to destination stream
+ *
+ * @param dst pointer to destination AVStream
+ * @param src pointer to source AVStream
+ * @return >=0 on success, AVERROR code on error
+ */
+int ff_stream_encode_params_copy(AVStream *dst, const AVStream *src);
+
+/**
* Wrap errno on rename() error.
*
* @param oldpath source path
* @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;
}
/**
+ * Allocate extradata with additional AV_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(AVCodecParameters *par, int size);
+
+/**
+ * Allocate extradata with additional AV_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(AVFormatContext *s, AVCodecParameters *par, 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_whiteblacklists(AVFormatContext *dst, const AVFormatContext *src);
+
+int ffio_open2_wrapper(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags,
+ const AVIOInterruptCB *int_cb, AVDictionary **options);
+
+/**
+ * Returned by demuxers to indicate that data was consumed but discarded
+ * (ignored streams or junk data). The framework will re-call the demuxer.
+ */
+#define FFERROR_REDO FFERRTAG('R','E','D','O')
+
+/**
+ * Utility function to open IO stream of output format.
+ *
+ * @param s AVFormatContext
+ * @param url URL or file name to open for writing
+ * @options optional options which will be passed to io_open callback
+ * @return >=0 on success, negative AVERROR in case of failure
+ */
+int ff_format_output_open(AVFormatContext *s, const char *url, AVDictionary **options);
+
+/*
* A wrapper around AVFormatContext.io_close that should be used
* instead of calling the pointer directly.
*/
void ff_format_io_close(AVFormatContext *s, AVIOContext **pb);
/**
+ * Parse creation_time in AVFormatContext metadata if exists and warn if the
+ * parsing fails.
+ *
+ * @param s AVFormatContext
+ * @param timestamp parsed timestamp in microseconds, only set on successful parsing
+ * @param return_seconds set this to get the number of seconds in timestamp instead of microseconds
+ * @return 1 if OK, 0 if the metadata was not present, AVERROR(EINVAL) on parse error
+ */
+int ff_parse_creation_time_metadata(AVFormatContext *s, int64_t *timestamp, int return_seconds);
+
+/**
+ * Standardize creation_time metadata in AVFormatContext to an ISO-8601
+ * timestamp string.
+ *
+ * @param s AVFormatContext
+ * @return <0 on error
+ */
+int ff_standardize_creation_time(AVFormatContext *s);
+
+#define CONTAINS_PAL 2
+/**
+ * Reshuffles the lines to use the user specified stride.
+ *
+ * @param ppkt input and output packet
+ * @return negative error code or
+ * 0 if no new packet was allocated
+ * non-zero if a new packet was allocated and ppkt has to be freed
+ * CONTAINS_PAL if in addition to a new packet the old contained a palette
+ */
+int ff_reshuffle_raw_rgb(AVFormatContext *s, AVPacket **ppkt, AVCodecParameters *par, int expected_stride);
+
+/**
+ * Retrieves the palette from a packet, either from side data, or
+ * appended to the video data in the packet itself (raw video only).
+ * It is commonly used after a call to ff_reshuffle_raw_rgb().
+ *
+ * Use 0 for the ret parameter to check for side data only.
+ *
+ * @param pkt pointer to packet before calling ff_reshuffle_raw_rgb()
+ * @param ret return value from ff_reshuffle_raw_rgb(), or 0
+ * @param palette pointer to palette buffer
+ * @return negative error code or
+ * 1 if the packet has a palette, else 0
+ */
+int ff_get_packet_palette(AVFormatContext *s, AVPacket *pkt, int ret, uint32_t *palette);
+
+/**
+ * Finalize buf into extradata and set its size appropriately.
+ */
+int ff_bprint_to_codecpar_extradata(AVCodecParameters *par, struct AVBPrint *buf);
+
+/**
* Find the next packet in the interleaving queue for the given stream.
* The pkt parameter is filled in with the queued packet, including
* references to the data (which the caller is not allowed to keep or
diff --git a/libavformat/ipmovie.c b/libavformat/ipmovie.c
index 5d789ff..7f5a8c6 100644
--- a/libavformat/ipmovie.c
+++ b/libavformat/ipmovie.c
@@ -2,20 +2,20 @@
* Interplay MVE File Demuxer
* Copyright (c) 2003 The FFmpeg project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -58,7 +58,7 @@
#define OPCODE_INIT_AUDIO_BUFFERS 0x03
#define OPCODE_START_STOP_AUDIO 0x04
#define OPCODE_INIT_VIDEO_BUFFERS 0x05
-#define OPCODE_UNKNOWN_06 0x06
+#define OPCODE_VIDEO_DATA_06 0x06
#define OPCODE_SEND_BUFFER 0x07
#define OPCODE_AUDIO_FRAME 0x08
#define OPCODE_SILENCE_FRAME 0x09
@@ -66,10 +66,10 @@
#define OPCODE_CREATE_GRADIENT 0x0B
#define OPCODE_SET_PALETTE 0x0C
#define OPCODE_SET_PALETTE_COMPRESSED 0x0D
-#define OPCODE_UNKNOWN_0E 0x0E
+#define OPCODE_SET_SKIP_MAP 0x0E
#define OPCODE_SET_DECODING_MAP 0x0F
-#define OPCODE_UNKNOWN_10 0x10
-#define OPCODE_VIDEO_DATA 0x11
+#define OPCODE_VIDEO_DATA_10 0x10
+#define OPCODE_VIDEO_DATA_11 0x11
#define OPCODE_UNKNOWN_12 0x12
#define OPCODE_UNKNOWN_13 0x13
#define OPCODE_UNKNOWN_14 0x14
@@ -78,7 +78,7 @@
#define PALETTE_COUNT 256
typedef struct IPMVEContext {
-
+ AVFormatContext *avf;
unsigned char *buf;
int buf_size;
@@ -91,6 +91,8 @@ typedef struct IPMVEContext {
uint32_t palette[256];
int has_palette;
int changed;
+ uint8_t send_buffer;
+ uint8_t frame_format;
unsigned int audio_bits;
unsigned int audio_channels;
@@ -105,6 +107,8 @@ typedef struct IPMVEContext {
int audio_chunk_size;
int64_t video_chunk_offset;
int video_chunk_size;
+ int64_t skip_map_chunk_offset;
+ int skip_map_chunk_size;
int64_t decode_map_chunk_offset;
int decode_map_chunk_size;
@@ -117,9 +121,9 @@ 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"
+ av_log(s->avf, AV_LOG_ERROR, "Can not read audio packet before"
"audio codec is known\n");
return CHUNK_BAD;
}
@@ -147,16 +151,16 @@ static int load_ipmovie_packet(IPMVEContext *s, AVIOContext *pb,
s->audio_frame_count +=
(s->audio_chunk_size - 6 - s->audio_channels) / s->audio_channels;
- av_log(NULL, AV_LOG_TRACE, "sending audio frame with pts %"PRId64" (%d audio frames)\n",
+ av_log(s->avf, AV_LOG_TRACE, "sending audio frame with pts %"PRId64" (%d audio frames)\n",
pkt->pts, s->audio_frame_count);
chunk_type = CHUNK_VIDEO;
- } else if (s->decode_map_chunk_offset) {
+ } else if (s->frame_format) {
- /* send both the decode map and the video data together */
+ /* send the frame format, decode map, the video data, skip map, and the send_buffer flag together */
- if (av_new_packet(pkt, s->decode_map_chunk_size + s->video_chunk_size))
+ if (av_new_packet(pkt, 8 + s->decode_map_chunk_size + s->video_chunk_size + s->skip_map_chunk_size))
return CHUNK_NOMEM;
if (s->has_palette) {
@@ -174,29 +178,58 @@ static int load_ipmovie_packet(IPMVEContext *s, AVIOContext *pb,
ff_add_param_change(pkt, 0, 0, 0, s->video_width, s->video_height);
s->changed = 0;
}
- pkt->pos= s->decode_map_chunk_offset;
- avio_seek(pb, s->decode_map_chunk_offset, SEEK_SET);
- s->decode_map_chunk_offset = 0;
- if (avio_read(pb, pkt->data, s->decode_map_chunk_size) !=
- s->decode_map_chunk_size) {
- av_packet_unref(pkt);
- return CHUNK_EOF;
- }
+ AV_WL8(pkt->data, s->frame_format);
+ AV_WL8(pkt->data + 1, s->send_buffer);
+ AV_WL16(pkt->data + 2, s->video_chunk_size);
+ AV_WL16(pkt->data + 4, s->decode_map_chunk_size);
+ AV_WL16(pkt->data + 6, s->skip_map_chunk_size);
+
+ s->frame_format = 0;
+ s->send_buffer = 0;
+ pkt->pos = s->video_chunk_offset;
avio_seek(pb, s->video_chunk_offset, SEEK_SET);
s->video_chunk_offset = 0;
- if (avio_read(pb, pkt->data + s->decode_map_chunk_size,
- s->video_chunk_size) != s->video_chunk_size) {
+ if (avio_read(pb, pkt->data + 8, s->video_chunk_size) !=
+ s->video_chunk_size) {
av_packet_unref(pkt);
return CHUNK_EOF;
}
+ if (s->decode_map_chunk_size) {
+ pkt->pos = s->decode_map_chunk_offset;
+ avio_seek(pb, s->decode_map_chunk_offset, SEEK_SET);
+ s->decode_map_chunk_offset = 0;
+
+ if (avio_read(pb, pkt->data + 8 + s->video_chunk_size,
+ s->decode_map_chunk_size) != s->decode_map_chunk_size) {
+ av_packet_unref(pkt);
+ return CHUNK_EOF;
+ }
+ }
+
+ if (s->skip_map_chunk_size) {
+ pkt->pos = s->skip_map_chunk_offset;
+ avio_seek(pb, s->skip_map_chunk_offset, SEEK_SET);
+ s->skip_map_chunk_offset = 0;
+
+ if (avio_read(pb, pkt->data + 8 + s->video_chunk_size + s->decode_map_chunk_size,
+ s->skip_map_chunk_size) != s->skip_map_chunk_size) {
+ av_packet_unref(pkt);
+ return CHUNK_EOF;
+ }
+ }
+
+ s->video_chunk_size = 0;
+ s->decode_map_chunk_size = 0;
+ s->skip_map_chunk_size = 0;
+
pkt->stream_index = s->video_stream_index;
pkt->pts = s->video_pts;
- av_log(NULL, AV_LOG_TRACE, "sending video frame with pts %"PRId64"\n", pkt->pts);
+ av_log(s->avf, AV_LOG_TRACE, "sending video frame with pts %"PRId64"\n", pkt->pts);
s->video_pts += s->frame_pts_inc;
@@ -212,6 +245,31 @@ static int load_ipmovie_packet(IPMVEContext *s, AVIOContext *pb,
return chunk_type;
}
+static int init_audio(AVFormatContext *s)
+{
+ IPMVEContext *ipmovie = s->priv_data;
+ AVStream *st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 32, 1, ipmovie->audio_sample_rate);
+ ipmovie->audio_stream_index = st->index;
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = ipmovie->audio_type;
+ st->codecpar->codec_tag = 0; /* no tag */
+ st->codecpar->channels = ipmovie->audio_channels;
+ st->codecpar->channel_layout = st->codecpar->channels == 1 ? AV_CH_LAYOUT_MONO :
+ AV_CH_LAYOUT_STEREO;
+ st->codecpar->sample_rate = ipmovie->audio_sample_rate;
+ st->codecpar->bits_per_coded_sample = ipmovie->audio_bits;
+ st->codecpar->bit_rate = st->codecpar->channels * st->codecpar->sample_rate *
+ st->codecpar->bits_per_coded_sample;
+ if (st->codecpar->codec_id == AV_CODEC_ID_INTERPLAY_DPCM)
+ st->codecpar->bit_rate /= 2;
+ st->codecpar->block_align = st->codecpar->channels * st->codecpar->bits_per_coded_sample;
+
+ return 0;
+}
+
/* This function loads and processes a single chunk in an IP movie file.
* It returns the type of chunk that was processed. */
static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
@@ -237,7 +295,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)
@@ -245,36 +303,36 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
chunk_size = AV_RL16(&chunk_preamble[0]);
chunk_type = AV_RL16(&chunk_preamble[2]);
- av_log(NULL, AV_LOG_TRACE, "chunk type 0x%04X, 0x%04X bytes: ", chunk_type, chunk_size);
+ av_log(s->avf, AV_LOG_TRACE, "chunk type 0x%04X, 0x%04X bytes: ", chunk_type, chunk_size);
switch (chunk_type) {
case CHUNK_INIT_AUDIO:
- av_log(NULL, AV_LOG_TRACE, "initialize audio\n");
+ av_log(s->avf, AV_LOG_TRACE, "initialize audio\n");
break;
case CHUNK_AUDIO_ONLY:
- av_log(NULL, AV_LOG_TRACE, "audio only\n");
+ av_log(s->avf, AV_LOG_TRACE, "audio only\n");
break;
case CHUNK_INIT_VIDEO:
- av_log(NULL, AV_LOG_TRACE, "initialize video\n");
+ av_log(s->avf, AV_LOG_TRACE, "initialize video\n");
break;
case CHUNK_VIDEO:
- av_log(NULL, AV_LOG_TRACE, "video (and audio)\n");
+ av_log(s->avf, AV_LOG_TRACE, "video (and audio)\n");
break;
case CHUNK_SHUTDOWN:
- av_log(NULL, AV_LOG_TRACE, "shutdown\n");
+ av_log(s->avf, AV_LOG_TRACE, "shutdown\n");
break;
case CHUNK_END:
- av_log(NULL, AV_LOG_TRACE, "end\n");
+ av_log(s->avf, AV_LOG_TRACE, "end\n");
break;
default:
- av_log(NULL, AV_LOG_TRACE, "invalid chunk\n");
+ av_log(s->avf, AV_LOG_TRACE, "invalid chunk\n");
chunk_type = CHUNK_BAD;
break;
@@ -283,7 +341,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;
}
@@ -300,29 +358,29 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
chunk_size -= OPCODE_PREAMBLE_SIZE;
chunk_size -= opcode_size;
if (chunk_size < 0) {
- av_log(NULL, AV_LOG_TRACE, "chunk_size countdown just went negative\n");
+ av_log(s->avf, AV_LOG_TRACE, "chunk_size countdown just went negative\n");
chunk_type = CHUNK_BAD;
break;
}
- av_log(NULL, AV_LOG_TRACE, " opcode type %02X, version %d, 0x%04X bytes: ",
+ av_log(s->avf, AV_LOG_TRACE, " opcode type %02X, version %d, 0x%04X bytes: ",
opcode_type, opcode_version, opcode_size);
switch (opcode_type) {
case OPCODE_END_OF_STREAM:
- av_log(NULL, AV_LOG_TRACE, "end of stream\n");
+ av_log(s->avf, AV_LOG_TRACE, "end of stream\n");
avio_skip(pb, opcode_size);
break;
case OPCODE_END_OF_CHUNK:
- av_log(NULL, AV_LOG_TRACE, "end of chunk\n");
+ av_log(s->avf, AV_LOG_TRACE, "end of chunk\n");
avio_skip(pb, opcode_size);
break;
case OPCODE_CREATE_TIMER:
- av_log(NULL, AV_LOG_TRACE, "create timer\n");
- if ((opcode_version > 0) || (opcode_size > 6)) {
- av_log(NULL, AV_LOG_TRACE, "bad create_timer opcode\n");
+ av_log(s->avf, AV_LOG_TRACE, "create timer\n");
+ if ((opcode_version > 0) || (opcode_size != 6)) {
+ av_log(s->avf, AV_LOG_TRACE, "bad create_timer opcode\n");
chunk_type = CHUNK_BAD;
break;
}
@@ -335,9 +393,9 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
break;
case OPCODE_INIT_AUDIO_BUFFERS:
- av_log(NULL, AV_LOG_TRACE, "initialize audio buffers\n");
- if ((opcode_version > 1) || (opcode_size > 10)) {
- av_log(NULL, AV_LOG_TRACE, "bad init_audio_buffers opcode\n");
+ av_log(s->avf, AV_LOG_TRACE, "initialize audio buffers\n");
+ if (opcode_version > 1 || opcode_size > 10 || opcode_size < 6) {
+ av_log(s->avf, AV_LOG_TRACE, "bad init_audio_buffers opcode\n");
chunk_type = CHUNK_BAD;
break;
}
@@ -359,7 +417,7 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
s->audio_type = AV_CODEC_ID_PCM_S16LE;
else
s->audio_type = AV_CODEC_ID_PCM_U8;
- av_log(NULL, AV_LOG_TRACE, "audio: %d bits, %d Hz, %s, %s format\n",
+ av_log(s->avf, AV_LOG_TRACE, "audio: %d bits, %d Hz, %s, %s format\n",
s->audio_bits, s->audio_sample_rate,
(s->audio_channels == 2) ? "stereo" : "mono",
(s->audio_type == AV_CODEC_ID_INTERPLAY_DPCM) ?
@@ -367,14 +425,16 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
break;
case OPCODE_START_STOP_AUDIO:
- av_log(NULL, AV_LOG_TRACE, "start/stop audio\n");
+ av_log(s->avf, AV_LOG_TRACE, "start/stop audio\n");
avio_skip(pb, opcode_size);
break;
case OPCODE_INIT_VIDEO_BUFFERS:
- av_log(NULL, AV_LOG_TRACE, "initialize video buffers\n");
- if ((opcode_version > 2) || (opcode_size > 8)) {
- av_log(NULL, AV_LOG_TRACE, "bad init_video_buffers opcode\n");
+ av_log(s->avf, AV_LOG_TRACE, "initialize video buffers\n");
+ if ((opcode_version > 2) || (opcode_size > 8) || opcode_size < 4
+ || opcode_version == 2 && opcode_size < 8
+ ) {
+ av_log(s->avf, AV_LOG_TRACE, "bad init_video_buffers opcode\n");
chunk_type = CHUNK_BAD;
break;
}
@@ -398,28 +458,26 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
} else {
s->video_bpp = 16;
}
- av_log(NULL, AV_LOG_TRACE, "video resolution: %d x %d\n",
+ av_log(s->avf, AV_LOG_TRACE, "video resolution: %d x %d\n",
s->video_width, s->video_height);
break;
- case OPCODE_UNKNOWN_06:
- case OPCODE_UNKNOWN_0E:
- case OPCODE_UNKNOWN_10:
case OPCODE_UNKNOWN_12:
case OPCODE_UNKNOWN_13:
case OPCODE_UNKNOWN_14:
case OPCODE_UNKNOWN_15:
- av_log(NULL, AV_LOG_TRACE, "unknown (but documented) opcode %02X\n", opcode_type);
+ av_log(s->avf, AV_LOG_TRACE, "unknown (but documented) opcode %02X\n", opcode_type);
avio_skip(pb, opcode_size);
break;
case OPCODE_SEND_BUFFER:
- av_log(NULL, AV_LOG_TRACE, "send buffer\n");
+ av_log(s->avf, AV_LOG_TRACE, "send buffer\n");
avio_skip(pb, opcode_size);
+ s->send_buffer = 1;
break;
case OPCODE_AUDIO_FRAME:
- av_log(NULL, AV_LOG_TRACE, "audio frame\n");
+ av_log(s->avf, AV_LOG_TRACE, "audio frame\n");
/* log position and move on for now */
s->audio_chunk_offset = avio_tell(pb);
@@ -428,26 +486,26 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
break;
case OPCODE_SILENCE_FRAME:
- av_log(NULL, AV_LOG_TRACE, "silence frame\n");
+ av_log(s->avf, AV_LOG_TRACE, "silence frame\n");
avio_skip(pb, opcode_size);
break;
case OPCODE_INIT_VIDEO_MODE:
- av_log(NULL, AV_LOG_TRACE, "initialize video mode\n");
+ av_log(s->avf, AV_LOG_TRACE, "initialize video mode\n");
avio_skip(pb, opcode_size);
break;
case OPCODE_CREATE_GRADIENT:
- av_log(NULL, AV_LOG_TRACE, "create gradient\n");
+ av_log(s->avf, AV_LOG_TRACE, "create gradient\n");
avio_skip(pb, opcode_size);
break;
case OPCODE_SET_PALETTE:
- av_log(NULL, AV_LOG_TRACE, "set palette\n");
+ av_log(s->avf, AV_LOG_TRACE, "set palette\n");
/* check for the logical maximum palette size
* (3 * 256 + 4 bytes) */
- if (opcode_size > 0x304) {
- av_log(NULL, AV_LOG_TRACE, "demux_ipmovie: set_palette opcode too large\n");
+ if (opcode_size > 0x304 || opcode_size < 4) {
+ av_log(s->avf, AV_LOG_TRACE, "demux_ipmovie: set_palette opcode with invalid size\n");
chunk_type = CHUNK_BAD;
break;
}
@@ -460,8 +518,9 @@ 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)) {
- av_log(NULL, AV_LOG_TRACE, "demux_ipmovie: set_palette indexes out of range (%d -> %d)\n",
+ if ( (first_color > 0xFF) || (last_color > 0xFF)
+ || (last_color - first_color + 1)*3 + 4 > opcode_size) {
+ av_log(s->avf, AV_LOG_TRACE, "demux_ipmovie: set_palette indexes out of range (%d -> %d)\n",
first_color, last_color);
chunk_type = CHUNK_BAD;
break;
@@ -473,18 +532,28 @@ 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;
case OPCODE_SET_PALETTE_COMPRESSED:
- av_log(NULL, AV_LOG_TRACE, "set palette compressed\n");
+ av_log(s->avf, AV_LOG_TRACE, "set palette compressed\n");
+ avio_skip(pb, opcode_size);
+ break;
+
+ case OPCODE_SET_SKIP_MAP:
+ av_log(s->avf, AV_LOG_TRACE, "set skip map\n");
+
+ /* log position and move on for now */
+ s->skip_map_chunk_offset = avio_tell(pb);
+ s->skip_map_chunk_size = opcode_size;
avio_skip(pb, opcode_size);
break;
case OPCODE_SET_DECODING_MAP:
- av_log(NULL, AV_LOG_TRACE, "set decoding map\n");
+ av_log(s->avf, AV_LOG_TRACE, "set decoding map\n");
/* log position and move on for now */
s->decode_map_chunk_offset = avio_tell(pb);
@@ -492,8 +561,29 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
avio_skip(pb, opcode_size);
break;
- case OPCODE_VIDEO_DATA:
- av_log(NULL, AV_LOG_TRACE, "set video data\n");
+ case OPCODE_VIDEO_DATA_06:
+ av_log(s->avf, AV_LOG_TRACE, "set video data format 0x06\n");
+ s->frame_format = 0x06;
+
+ /* log position and move on for now */
+ s->video_chunk_offset = avio_tell(pb);
+ s->video_chunk_size = opcode_size;
+ avio_skip(pb, opcode_size);
+ break;
+
+ case OPCODE_VIDEO_DATA_10:
+ av_log(s->avf, AV_LOG_TRACE, "set video data format 0x10\n");
+ s->frame_format = 0x10;
+
+ /* log position and move on for now */
+ s->video_chunk_offset = avio_tell(pb);
+ s->video_chunk_size = opcode_size;
+ avio_skip(pb, opcode_size);
+ break;
+
+ case OPCODE_VIDEO_DATA_11:
+ av_log(s->avf, AV_LOG_TRACE, "set video data format 0x11\n");
+ s->frame_format = 0x11;
/* log position and move on for now */
s->video_chunk_offset = avio_tell(pb);
@@ -502,13 +592,16 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
break;
default:
- av_log(NULL, AV_LOG_TRACE, "*** unknown opcode type\n");
+ av_log(s->avf, AV_LOG_TRACE, "*** unknown opcode type\n");
chunk_type = CHUNK_BAD;
break;
}
}
+ if (s->avf->nb_streams == 1 && s->audio_type)
+ init_audio(s->avf);
+
/* make a note of where the stream is sitting */
s->next_chunk_offset = avio_tell(pb);
@@ -523,11 +616,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;
@@ -540,24 +634,32 @@ 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)];
+ ipmovie->avf = s;
+
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 */
ipmovie->video_pts = ipmovie->audio_frame_count = 0;
ipmovie->audio_chunk_offset = ipmovie->video_chunk_offset =
- ipmovie->decode_map_chunk_offset = 0;
+ ipmovie->decode_map_chunk_offset = ipmovie->skip_map_chunk_offset = 0;
+ ipmovie->decode_map_chunk_size = ipmovie->video_chunk_size =
+ ipmovie->skip_map_chunk_size = 0;
+ ipmovie->send_buffer = ipmovie->frame_format = 0;
/* 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;
@@ -589,25 +691,9 @@ static int ipmovie_read_header(AVFormatContext *s)
st->codecpar->bits_per_coded_sample = ipmovie->video_bpp;
if (ipmovie->audio_type) {
- st = avformat_new_stream(s, NULL);
- if (!st)
- return AVERROR(ENOMEM);
- avpriv_set_pts_info(st, 32, 1, ipmovie->audio_sample_rate);
- ipmovie->audio_stream_index = st->index;
- st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
- st->codecpar->codec_id = ipmovie->audio_type;
- st->codecpar->codec_tag = 0; /* no tag */
- st->codecpar->channels = ipmovie->audio_channels;
- st->codecpar->channel_layout = st->codecpar->channels == 1 ? AV_CH_LAYOUT_MONO :
- AV_CH_LAYOUT_STEREO;
- st->codecpar->sample_rate = ipmovie->audio_sample_rate;
- st->codecpar->bits_per_coded_sample = ipmovie->audio_bits;
- st->codecpar->bit_rate = st->codecpar->channels * st->codecpar->sample_rate *
- st->codecpar->bits_per_coded_sample;
- if (st->codecpar->codec_id == AV_CODEC_ID_INTERPLAY_DPCM)
- st->codecpar->bit_rate /= 2;
- st->codecpar->block_align = st->codecpar->channels * st->codecpar->bits_per_coded_sample;
- }
+ return init_audio(s);
+ } else
+ s->ctx_flags |= AVFMTCTX_NOHEADER;
return 0;
}
@@ -619,6 +705,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;
@@ -626,12 +713,17 @@ static int ipmovie_read_packet(AVFormatContext *s,
ret = AVERROR(EIO);
else if (ret == CHUNK_NOMEM)
ret = AVERROR(ENOMEM);
+ else if (ret == CHUNK_END || ret == CHUNK_SHUTDOWN)
+ ret = AVERROR_EOF;
else if (ret == CHUNK_VIDEO)
ret = 0;
+ else if (ret == CHUNK_INIT_VIDEO || ret == CHUNK_INIT_AUDIO)
+ continue;
else
- ret = -1;
+ continue;
return ret;
+ }
}
AVInputFormat ff_ipmovie_demuxer = {
diff --git a/libavformat/ircam.c b/libavformat/ircam.c
new file mode 100644
index 0000000..a267c18
--- /dev/null
+++ b/libavformat/ircam.c
@@ -0,0 +1,47 @@
+/*
+ * IRCAM common code
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "internal.h"
+
+const AVCodecTag ff_codec_ircam_le_tags[] = {
+ { AV_CODEC_ID_PCM_ALAW, 0x10001 },
+ { AV_CODEC_ID_PCM_F32LE, 0x00004 },
+ { AV_CODEC_ID_PCM_F64LE, 0x00008 },
+ { AV_CODEC_ID_PCM_MULAW, 0x20001 },
+ { AV_CODEC_ID_PCM_S16LE, 0x00002 },
+ { AV_CODEC_ID_PCM_S24LE, 0x00003 },
+ { AV_CODEC_ID_PCM_S32LE, 0x40004 },
+ { AV_CODEC_ID_PCM_S8, 0x00001 },
+ { AV_CODEC_ID_NONE, 0 },
+};
+
+const AVCodecTag ff_codec_ircam_be_tags[] = {
+ { AV_CODEC_ID_PCM_ALAW, 0x10001 },
+ { AV_CODEC_ID_PCM_F32BE, 0x00004 },
+ { AV_CODEC_ID_PCM_F64BE, 0x00008 },
+ { AV_CODEC_ID_PCM_MULAW, 0x20001 },
+ { AV_CODEC_ID_PCM_S16BE, 0x00002 },
+ { AV_CODEC_ID_PCM_S24BE, 0x00003 },
+ { AV_CODEC_ID_PCM_S32BE, 0x40004 },
+ { AV_CODEC_ID_PCM_S8, 0x00001 },
+ { AV_CODEC_ID_NONE, 0 },
+};
diff --git a/libavformat/ircam.h b/libavformat/ircam.h
new file mode 100644
index 0000000..f7f9c84
--- /dev/null
+++ b/libavformat/ircam.h
@@ -0,0 +1,30 @@
+/*
+ * IRCAM common code
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_IRCAM_H
+#define AVFORMAT_IRCAM_H
+
+#include "internal.h"
+
+extern const AVCodecTag ff_codec_ircam_be_tags[];
+extern const AVCodecTag ff_codec_ircam_le_tags[];
+
+#endif /* AVFORMAT_IRCAM_H */
diff --git a/libavformat/ircamdec.c b/libavformat/ircamdec.c
new file mode 100644
index 0000000..d376ffe
--- /dev/null
+++ b/libavformat/ircamdec.c
@@ -0,0 +1,118 @@
+/*
+ * 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 "libavcodec/internal.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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->channels = channels;
+ if (st->codecpar->channels > FF_SANE_NB_CHANNELS)
+ return AVERROR(ENOSYS);
+ st->codecpar->sample_rate = sample_rate;
+
+ st->codecpar->codec_id = ff_codec_get_id(tags, tag);
+ if (st->codecpar->codec_id == AV_CODEC_ID_NONE) {
+ av_log(s, AV_LOG_ERROR, "unknown tag %"PRIx32"\n", tag);
+ return AVERROR_INVALIDDATA;
+ }
+
+ st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id);
+ st->codecpar->block_align = st->codecpar->bits_per_coded_sample * st->codecpar->channels / 8;
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+ avio_skip(s->pb, 1008);
+
+ return 0;
+}
+
+AVInputFormat ff_ircam_demuxer = {
+ .name = "ircam",
+ .long_name = NULL_IF_CONFIG_SMALL("Berkeley/IRCAM/CARL Sound Format"),
+ .read_probe = ircam_probe,
+ .read_header = ircam_read_header,
+ .read_packet = ff_pcm_read_packet,
+ .read_seek = ff_pcm_read_seek,
+ .extensions = "sf,ircam",
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/ircamenc.c b/libavformat/ircamenc.c
new file mode 100644
index 0000000..323ecb3
--- /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)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ 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, par->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_q2intfloat((AVRational){par->sample_rate, 1}));
+ avio_wl32(s->pb, par->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 931c438..77983c5 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,8 +57,13 @@ 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_OPUS , 0xAD }, /* mp4ra.org */
+ { AV_CODEC_ID_VP9 , 0xB1 }, /* mp4ra.org */
+ { AV_CODEC_ID_FLAC , 0xC1 }, /* nonstandard, update when there is a standard value */
{ AV_CODEC_ID_TSCC2 , 0xD0 }, /* nonstandard, camtasia uses it */
+ { AV_CODEC_ID_EVRC , 0xD1 }, /* nonstandard, pvAuthor uses it */
{ AV_CODEC_ID_VORBIS , 0xDD }, /* nonstandard, gpac uses it */
{ AV_CODEC_ID_DVD_SUBTITLE, 0xE0 }, /* nonstandard, see unsupported-embedded-subs-2.mp4 */
{ AV_CODEC_ID_QCELP , 0xE1 },
@@ -72,7 +77,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 8-bit 4:2:2 */
{ AV_CODEC_ID_RAWVIDEO, MKTAG('y', 'u', 'v', 's') }, /* same as 2VUY but byte-swapped */
@@ -85,6 +89,7 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
{ AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'B', 'G', 'R') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('b', '1', '6', 'g') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('b', '4', '8', 'r') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('b', '6', '4', 'a') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('b', 'x', 'b', 'g') }, /* BOXX */
{ AV_CODEC_ID_RAWVIDEO, MKTAG('b', 'x', 'r', 'g') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('b', 'x', 'y', 'v') },
@@ -96,13 +101,21 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
{ AV_CODEC_ID_R10K, MKTAG('R', '1', '0', 'k') }, /* uncompressed 10-bit RGB */
{ AV_CODEC_ID_R10K, MKTAG('R', '1', '0', 'g') }, /* uncompressed 10-bit RGB */
{ AV_CODEC_ID_R210, MKTAG('r', '2', '1', '0') }, /* uncompressed 10-bit 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 10-bit 4:2:2 */
{ AV_CODEC_ID_V210, MKTAG('b', 'x', 'y', '2') }, /* BOXX 10-bit 4:2:2 */
+ { AV_CODEC_ID_V308, MKTAG('v', '3', '0', '8') }, /* uncompressed 8-bit 4:4:4 */
+ { AV_CODEC_ID_V408, MKTAG('v', '4', '0', '8') }, /* uncompressed 8-bit 4:4:4:4 */
{ AV_CODEC_ID_V410, MKTAG('v', '4', '1', '0') }, /* uncompressed 10-bit 4:4:4 */
+ { AV_CODEC_ID_Y41P, MKTAG('Y', '4', '1', 'P') }, /* uncompressed 12-bit 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) */
- { AV_CODEC_ID_MJPEG, MKTAG('A', 'V', 'D', 'J') }, /* MJPEG with alpha-channel (AVID JFIF meridien compressed) */
+ { AV_CODEC_ID_AVRN , MKTAG('A', 'V', 'D', 'J') }, /* MJPEG with alpha-channel (AVID JFIF meridien compressed) */
/* { AV_CODEC_ID_MJPEG, MKTAG('A', 'V', 'R', 'n') }, *//* MJPEG with alpha-channel (AVID ABVB/Truevision NuVista) */
{ AV_CODEC_ID_MJPEG, MKTAG('d', 'm', 'b', '1') }, /* Motion JPEG OpenDML */
{ AV_CODEC_ID_MJPEGB, MKTAG('m', 'j', 'p', 'b') }, /* Motion-JPEG (format B) */
@@ -152,6 +165,9 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
{ AV_CODEC_ID_HEVC, MKTAG('h', 'v', 'c', '1') }, /* HEVC/H.265 which indicates parameter sets shall not be in ES */
{ AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') }, /* AVC-1/H.264 */
+ { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '2') },
+ { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '3') },
+ { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '4') },
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', 'p') }, /* AVC-Intra 50M 720p24/30/60 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', 'q') }, /* AVC-Intra 50M 720p25/50 */
{ AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '2') }, /* AVC-Intra 50M 1080p25/50 */
@@ -167,6 +183,10 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
{ 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 10-bit 4:2:2 */
{ AV_CODEC_ID_H264, MKTAG('r', 'v', '6', '4') }, /* X-Com Radvision */
+ { AV_CODEC_ID_H264, MKTAG('x', 'a', 'l', 'g') }, /* XAVC-L HD422 produced by FCP */
+ { AV_CODEC_ID_H264, MKTAG('a', 'v', 'l', 'g') }, /* Panasonic P2 AVC-LongG */
+
+ { AV_CODEC_ID_VP9, MKTAG('v', 'p', '0', '9') }, /* VP9 */
{ AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', '1', 'v', ' ') },
{ AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', '1', 'v', '1') }, /* Apple MPEG-1 Camcorder */
@@ -232,6 +252,7 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
{ AV_CODEC_ID_DIRAC, MKTAG('d', 'r', 'a', 'c') },
{ AV_CODEC_ID_DNXHD, MKTAG('A', 'V', 'd', 'n') }, /* AVID DNxHD */
+ { AV_CODEC_ID_DNXHD, MKTAG('A', 'V', 'd', 'h') }, /* AVID DNxHR */
{ AV_CODEC_ID_H263, MKTAG('H', '2', '6', '3') },
{ AV_CODEC_ID_MSMPEG4V3, MKTAG('3', 'I', 'V', 'D') }, /* 3ivx DivX Doctor */
{ AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'V', '1', 'x') }, /* AVID 1:1x */
@@ -253,10 +274,16 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
{ AV_CODEC_ID_HAP, MKTAG('H', 'a', 'p', '1') },
{ AV_CODEC_ID_HAP, MKTAG('H', 'a', 'p', '5') },
{ AV_CODEC_ID_HAP, MKTAG('H', 'a', 'p', 'Y') },
+ { AV_CODEC_ID_HAP, MKTAG('H', 'a', 'p', 'A') },
+ { AV_CODEC_ID_HAP, MKTAG('H', 'a', 'p', 'M') },
{ AV_CODEC_ID_DXV, MKTAG('D', 'X', 'D', '3') },
{ AV_CODEC_ID_DXV, MKTAG('D', 'X', 'D', 'I') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '0', 'R', '0') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '0', 'R', 'A') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '0', 'R', 'G') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '0', 'Y', '2') },
{ AV_CODEC_ID_MAGICYUV, MKTAG('M', '8', 'R', 'G') },
{ AV_CODEC_ID_MAGICYUV, MKTAG('M', '8', 'R', 'A') },
{ AV_CODEC_ID_MAGICYUV, MKTAG('M', '8', 'G', '0') },
@@ -264,6 +291,17 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
{ AV_CODEC_ID_MAGICYUV, MKTAG('M', '8', 'Y', '2') },
{ AV_CODEC_ID_MAGICYUV, MKTAG('M', '8', 'Y', '4') },
{ AV_CODEC_ID_MAGICYUV, MKTAG('M', '8', 'Y', 'A') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '2', 'R', 'A') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '2', 'R', 'G') },
+
+ { AV_CODEC_ID_SHEERVIDEO, MKTAG('S', 'h', 'r', '0') },
+ { AV_CODEC_ID_SHEERVIDEO, MKTAG('S', 'h', 'r', '1') },
+ { AV_CODEC_ID_SHEERVIDEO, MKTAG('S', 'h', 'r', '2') },
+ { AV_CODEC_ID_SHEERVIDEO, MKTAG('S', 'h', 'r', '3') },
+ { AV_CODEC_ID_SHEERVIDEO, MKTAG('S', 'h', 'r', '4') },
+ { AV_CODEC_ID_SHEERVIDEO, MKTAG('S', 'h', 'r', '5') },
+ { AV_CODEC_ID_SHEERVIDEO, MKTAG('S', 'h', 'r', '6') },
+ { AV_CODEC_ID_SHEERVIDEO, MKTAG('S', 'h', 'r', '7') },
{ AV_CODEC_ID_PIXLET, MKTAG('p', 'x', 'l', 't') },
@@ -281,6 +319,7 @@ const AVCodecTag ff_codec_movaudio_tags[] = {
{ AV_CODEC_ID_DTS, MKTAG('d', 't', 's', 'c') }, /* DTS formats prior to DTS-HD */
{ AV_CODEC_ID_DTS, MKTAG('d', 't', 's', 'h') }, /* DTS-HD audio formats */
{ AV_CODEC_ID_DTS, MKTAG('d', 't', 's', 'l') }, /* DTS-HD Lossless formats */
+ { AV_CODEC_ID_DTS, MKTAG('d', 't', 's', 'e') }, /* DTS Express */
{ AV_CODEC_ID_DTS, MKTAG('D', 'T', 'S', ' ') }, /* non-standard */
{ AV_CODEC_ID_EAC3, MKTAG('e', 'c', '-', '3') }, /* ETSI TS 102 366 Annex F (only valid in ISOBMFF) */
{ AV_CODEC_ID_DVAUDIO, MKTAG('v', 'd', 'v', 'a') },
@@ -294,6 +333,7 @@ const AVCodecTag ff_codec_movaudio_tags[] = {
{ AV_CODEC_ID_MP3, MKTAG('.', 'm', 'p', '3') },
{ AV_CODEC_ID_MP3, 0x6D730055 },
{ AV_CODEC_ID_NELLYMOSER, MKTAG('n', 'm', 'o', 's') }, /* Flash Media Server */
+ { AV_CODEC_ID_NELLYMOSER, MKTAG('N', 'E', 'L', 'L') }, /* Perian */
{ AV_CODEC_ID_PCM_ALAW, MKTAG('a', 'l', 'a', 'w') },
{ AV_CODEC_ID_PCM_F32BE, MKTAG('f', 'l', '3', '2') },
{ AV_CODEC_ID_PCM_F32LE, MKTAG('f', 'l', '3', '2') },
@@ -318,13 +358,17 @@ const AVCodecTag ff_codec_movaudio_tags[] = {
{ AV_CODEC_ID_QDMC, MKTAG('Q', 'D', 'M', 'C') },
{ AV_CODEC_ID_SPEEX, MKTAG('s', 'p', 'e', 'x') }, /* Flash Media Server */
{ AV_CODEC_ID_SPEEX, MKTAG('S', 'P', 'X', 'N') }, /* 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_FLAC, MKTAG('f', 'L', 'a', 'C') }, /* nonstandard */
+ { AV_CODEC_ID_OPUS, MKTAG('O', 'p', 'u', 's') }, /* mp4ra.org */
{ 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 },
};
@@ -452,14 +496,29 @@ static const AVCodecTag mp4_audio_types[] = {
int ff_mp4_read_dec_config_descr(AVFormatContext *fc, AVStream *st, AVIOContext *pb)
{
+ enum AVCodecID codec_id;
+ unsigned v;
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->codecpar->codec_id = ff_codec_get_id(ff_mp4_obj_type, object_type_id);
+ v = avio_rb32(pb);
+
+ // TODO: fix this with codecpar
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ if (v < INT32_MAX)
+ st->codec->rc_max_rate = v;
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+ st->codecpar->bit_rate = avio_rb32(pb); /* avg bitrate */
+
+ codec_id= ff_codec_get_id(ff_mp4_obj_type, object_type_id);
+ if (codec_id)
+ st->codecpar->codec_id = codec_id;
av_log(fc, AV_LOG_TRACE, "esds object type id 0x%02x\n", object_type_id);
len = ff_mp4_read_descr(fc, pb, &tag);
if (tag == MP4DecSpecificDescrTag) {
@@ -467,15 +526,14 @@ int ff_mp4_read_dec_config_descr(AVFormatContext *fc, AVStream *st, AVIOContext
if (!len || (uint64_t)len > (1<<30))
return -1;
av_free(st->codecpar->extradata);
- st->codecpar->extradata = av_mallocz(len + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
- return AVERROR(ENOMEM);
- avio_read(pb, st->codecpar->extradata, len);
- st->codecpar->extradata_size = len;
+ if ((ret = ff_get_extradata(fc, st->codecpar, pb, len)) < 0)
+ return ret;
if (st->codecpar->codec_id == AV_CODEC_ID_AAC) {
- MPEG4AudioConfig cfg;
- avpriv_mpeg4audio_get_config(&cfg, st->codecpar->extradata,
- st->codecpar->extradata_size * 8, 1);
+ MPEG4AudioConfig cfg = {0};
+ ret = avpriv_mpeg4audio_get_config(&cfg, st->codecpar->extradata,
+ st->codecpar->extradata_size * 8, 1);
+ if (ret < 0)
+ return ret;
st->codecpar->channels = cfg.channels;
if (cfg.object_type == 29 && cfg.sampling_index < 3) // old mp3on4
st->codecpar->sample_rate = avpriv_mpa_freq_tab[cfg.sampling_index];
@@ -494,3 +552,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 8cc5ab7..b9380e9 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
*/
@@ -27,6 +27,7 @@
#include <stddef.h>
#include <stdint.h>
+#include "libavutil/mastering_display_metadata.h"
#include "libavutil/spherical.h"
#include "libavutil/stereo3d.h"
@@ -43,13 +44,15 @@ extern const AVCodecTag ff_codec_movsubtitle_tags[];
int ff_mov_iso639_to_lang(const char lang[4], int mp4);
int ff_mov_lang_to_iso639(unsigned code, char to[4]);
+struct AVAESCTR;
+
/* the QuickTime file format is quite convoluted...
* it has lots of index tables, each indexing something in another one...
* Here we just use what is needed to read the chunks
*/
typedef struct MOVStts {
- int count;
+ unsigned int count;
int duration;
} MOVStts;
@@ -59,6 +62,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;
@@ -84,6 +93,7 @@ typedef struct MOVFragment {
unsigned duration;
unsigned size;
unsigned flags;
+ int64_t time;
} MOVFragment;
typedef struct MOVTrackExt {
@@ -99,8 +109,27 @@ typedef struct MOVSbgp {
unsigned int index;
} MOVSbgp;
+typedef struct MOVFragmentIndexItem {
+ int64_t moof_offset;
+ int64_t time;
+ int headers_read;
+} MOVFragmentIndexItem;
+
+typedef struct MOVFragmentIndex {
+ unsigned track_id;
+ unsigned item_count;
+ unsigned current_item;
+ MOVFragmentIndexItem *items;
+} MOVFragmentIndex;
+
+typedef struct MOVIndexRange {
+ int64_t start;
+ int64_t end;
+} MOVIndexRange;
+
typedef struct MOVStreamContext {
AVIOContext *pb;
+ int pb_is_copied;
int ffindex; ///< AVStream index
int next_chunk;
unsigned int chunk_count;
@@ -108,6 +137,7 @@ typedef struct MOVStreamContext {
unsigned int stts_count;
MOVStts *stts_data;
unsigned int ctts_count;
+ unsigned int ctts_allocated_size;
MOVStts *ctts_data;
unsigned int stsc_count;
MOVStsc *stsc_data;
@@ -115,17 +145,23 @@ typedef struct MOVStreamContext {
int stsc_sample;
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;
+ int64_t current_index;
+ MOVIndexRange* index_ranges;
+ MOVIndexRange* current_index_range;
unsigned int bytes_per_frame;
unsigned int samples_per_frame;
int dv_audio_container;
@@ -134,16 +170,22 @@ typedef struct MOVStreamContext {
unsigned drefs_count;
MOVDref *drefs;
int dref_id;
+ int timecode_track;
int width; ///< tkhd width
int height; ///< tkhd height
int dts_shift; ///< dts shift when ctts is negative
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;
+
/** extradata array (and size) for multiple stsd */
uint8_t **extradata;
int *extradata_size;
@@ -154,6 +196,24 @@ typedef struct MOVStreamContext {
AVStereo3D *stereo3d;
AVSphericalMapping *spherical;
size_t spherical_size;
+ AVMasteringDisplayMetadata *mastering;
+ AVContentLightMetadata *coll;
+ size_t coll_size;
+
+ uint32_t format;
+
+ int has_sidx; // If there is an sidx entry for this stream.
+ struct {
+ int use_subsamples;
+ uint8_t* auxiliary_info;
+ uint8_t* auxiliary_info_end;
+ uint8_t* auxiliary_info_pos;
+ uint8_t auxiliary_info_default_size;
+ uint8_t* auxiliary_info_sizes;
+ size_t auxiliary_info_sizes_count;
+ int64_t auxiliary_info_index;
+ struct AVAESCTR* aes_ctr;
+ } cenc;
} MOVStreamContext;
typedef struct MOVContext {
@@ -163,6 +223,10 @@ typedef struct MOVContext {
int64_t duration; ///< duration of the longest track
int found_moov; ///< 'moov' atom has been found
int found_mdat; ///< 'mdat' atom has been found
+ int found_hdlr_mdta; ///< 'hdlr' atom with type 'mdta' has been found
+ int trak_index; ///< Index of the current 'trak'
+ char **meta_keys;
+ unsigned meta_keys_count;
DVDemuxContext *dv_demux;
AVFormatContext *dv_fctx;
int isom; ///< 1 if file is ISO Media (mp4/3gp)
@@ -170,13 +234,37 @@ typedef struct MOVContext {
MOVTrackExt *trex_data;
unsigned trex_count;
int itunes_metadata; ///< metadata are itunes style
- int chapter_track;
+ int handbrake_version;
+ int *chapter_tracks;
+ unsigned int nb_chapter_tracks;
+ int use_absolute_path;
+ int ignore_editlist;
+ int advanced_editlist;
+ int ignore_chapters;
int seek_individually;
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 fragment_index_complete;
+ int atom_depth;
+ unsigned int aax_mode; ///< 'aax' file has been detected
+ uint8_t file_key[20];
+ uint8_t file_iv[20];
+ void *activation_bytes;
+ int activation_bytes_size;
+ void *audible_fixed_key;
+ int audible_fixed_key_size;
+ struct AVAES *aes_decrypt;
+ uint8_t *decryption_key;
+ int decryption_key_len;
int enable_drefs;
-
int32_t movie_display_matrix[3][3]; ///< display matrix from mvhd
} MOVContext;
@@ -235,6 +323,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'))
@@ -242,5 +331,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 1653b27..95b35dc 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 1fa88c4..077d905 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 a602f7c..197c099 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 1e57106..fdc0ee0 100644
--- a/libavformat/ivfenc.c
+++ b/libavformat/ivfenc.c
@@ -1,25 +1,31 @@
/*
* 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"
+#include "internal.h"
#include "libavutil/intreadwrite.h"
+typedef struct IVFEncContext {
+ unsigned frame_cnt;
+ uint64_t last_pts, sum_delta_pts;
+} IVFEncContext;
+
static int ivf_write_header(AVFormatContext *s)
{
AVCodecParameters *par;
@@ -30,19 +36,20 @@ static int ivf_write_header(AVFormatContext *s)
return AVERROR(EINVAL);
}
par = s->streams[0]->codecpar;
- if (par->codec_type != AVMEDIA_TYPE_VIDEO || par->codec_id != AV_CODEC_ID_VP8) {
- av_log(s, AV_LOG_ERROR, "Currently only VP8 is supported!\n");
+ if (par->codec_type != AVMEDIA_TYPE_VIDEO ||
+ !(par->codec_id == AV_CODEC_ID_VP8 || par->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);
avio_wl16(pb, 0); // version
avio_wl16(pb, 32); // header length
- avio_wl32(pb, par->codec_tag ? par->codec_tag : AV_RL32("VP80"));
+ avio_wl32(pb, par->codec_tag ? par->codec_tag : par->codec_id == AV_CODEC_ID_VP9 ? AV_RL32("VP90") : AV_RL32("VP80"));
avio_wl16(pb, par->width);
avio_wl16(pb, par->height);
avio_wl32(pb, s->streams[0]->time_base.den);
avio_wl32(pb, s->streams[0]->time_base.num);
- avio_wl64(pb, s->streams[0]->duration); // TODO: duration or number of frames?!?
+ avio_wl64(pb, 0xFFFFFFFFFFFFFFFFULL);
return 0;
}
@@ -50,14 +57,54 @@ static int ivf_write_header(AVFormatContext *s)
static int ivf_write_packet(AVFormatContext *s, AVPacket *pkt)
{
AVIOContext *pb = s->pb;
+ IVFEncContext *ctx = s->priv_data;
+
avio_wl32(pb, pkt->size);
avio_wl64(pb, pkt->pts);
avio_write(pb, pkt->data, pkt->size);
+ if (ctx->frame_cnt)
+ ctx->sum_delta_pts += pkt->pts - ctx->last_pts;
+ ctx->frame_cnt++;
+ ctx->last_pts = pkt->pts;
+
+ return 0;
+}
+
+static int ivf_write_trailer(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ IVFEncContext *ctx = s->priv_data;
+
+ if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && ctx->frame_cnt > 1) {
+ size_t end = avio_tell(pb);
+
+ avio_seek(pb, 24, SEEK_SET);
+ avio_wl64(pb, ctx->frame_cnt * ctx->sum_delta_pts / (ctx->frame_cnt - 1));
+ avio_seek(pb, end, SEEK_SET);
+ }
return 0;
}
+static int ivf_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt)
+{
+ int ret = 1;
+ AVStream *st = s->streams[pkt->stream_index];
+
+ if (st->codecpar->codec_id == AV_CODEC_ID_VP9)
+ ret = ff_stream_add_bitstream_filter(st, "vp9_superframe", NULL);
+
+ return ret;
+}
+
+static const AVCodecTag codec_ivf_tags[] = {
+ { AV_CODEC_ID_VP8, MKTAG('V', 'P', '8', '0') },
+ { AV_CODEC_ID_VP9, MKTAG('V', 'P', '9', '0') },
+ { AV_CODEC_ID_NONE, 0 }
+};
+
AVOutputFormat ff_ivf_muxer = {
+ .priv_data_size = sizeof(IVFEncContext),
.name = "ivf",
.long_name = NULL_IF_CONFIG_SMALL("On2 IVF"),
.extensions = "ivf",
@@ -65,4 +112,7 @@ AVOutputFormat ff_ivf_muxer = {
.video_codec = AV_CODEC_ID_VP8,
.write_header = ivf_write_header,
.write_packet = ivf_write_packet,
+ .write_trailer = ivf_write_trailer,
+ .check_bitstream = ivf_check_bitstream,
+ .codec_tag = (const AVCodecTag* const []){ codec_ivf_tags, 0 },
};
diff --git a/libavformat/jacosubdec.c b/libavformat/jacosubdec.c
new file mode 100644
index 0000000..520c435
--- /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, int64_t *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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->codec_id = AV_CODEC_ID_JACOSUB;
+
+ jacosub->timeres = 30;
+
+ av_bprint_init(&header, 1024+AV_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 = ff_bprint_to_codecpar_extradata(st->codecpar, &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(s, &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 0000000..0954f5f
--- /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 AVCodecParameters *par = s->streams[0]->codecpar;
+
+ if (par->extradata_size) {
+ avio_write(s->pb, par->extradata, par->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 7fc3131..b2c067f 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) + 4 <= pd->buf_size &&
+ !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 a98a6ff..c919976 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,10 @@
#include "libavcodec/mpeg4audio.h"
#include "libavutil/opt.h"
#include "avformat.h"
+#include "internal.h"
+#include "rawenc.h"
+
+#define MAX_EXTRADATA_SIZE 1024
typedef struct LATMContext {
AVClass *av_class;
@@ -33,6 +37,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 +55,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 +87,9 @@ static int latm_write_header(AVFormatContext *s)
LATMContext *ctx = s->priv_data;
AVCodecParameters *par = s->streams[0]->codecpar;
+ if (par->codec_id == AV_CODEC_ID_AAC_LATM)
+ return 0;
+
if (par->extradata_size > 0 &&
latm_decode_extradata(ctx, par->extradata, par->extradata_size) < 0)
return AVERROR_INVALIDDATA;
@@ -83,19 +97,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;
AVCodecParameters *par = s->streams[0]->codecpar;
- GetBitContext gb;
int header_size;
/* AudioMuxElement */
put_bits(bs, 1, !!ctx->counter);
if (!ctx->counter) {
- init_get_bits(&gb, par->extradata, par->extradata_size * 8);
-
/* StreamMuxConfig */
put_bits(bs, 1, 0); /* audioMuxVersion */
put_bits(bs, 1, 1); /* allStreamsSameTimeFraming */
@@ -105,12 +116,18 @@ static int latm_write_frame_header(AVFormatContext *s, PutBitContext *bs)
/* AudioSpecificConfig */
if (ctx->object_type == AOT_ALS) {
- header_size = par->extradata_size-(ctx->off + 7) >> 3;
- avpriv_copy_bits(bs, &par->extradata[ctx->off], header_size);
+ header_size = par->extradata_size-(ctx->off >> 3);
+ avpriv_copy_bits(bs, &par->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, par->extradata, ctx->off + 3);
if (!ctx->channel_conf) {
+ GetBitContext gb;
+ int ret = init_get_bits8(&gb, par->extradata, par->extradata_size);
+ av_assert0(ret >= 0); // extradata size has been checked already, so this should not fail
+ skip_bits_long(&gb, ctx->off + 3);
avpriv_copy_pce_data(bs, &gb);
}
}
@@ -124,28 +141,45 @@ 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;
+ AVCodecParameters *par = s->streams[0]->codecpar;
AVIOContext *pb = s->pb;
PutBitContext bs;
int i, len;
uint8_t loas_header[] = "\x56\xe0\x00";
- uint8_t *buf;
- if (pkt->size > 2 && pkt->data[0] == 0xff && (pkt->data[1] >> 4) == 0xf) {
- av_log(s, AV_LOG_ERROR, "ADTS header detected - ADTS will not be incorrectly muxed into LATM\n");
- return AVERROR_INVALIDDATA;
+ if (par->codec_id == AV_CODEC_ID_AAC_LATM)
+ return ff_raw_write_packet(s, pkt);
+
+ if (!par->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 {
+ uint8_t *side_data;
+ int side_data_size = 0, ret;
+
+ side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
+ &side_data_size);
+ if (side_data_size) {
+ if (latm_decode_extradata(ctx, side_data, side_data_size) < 0)
+ return AVERROR_INVALIDDATA;
+ ret = ff_alloc_extradata(par, side_data_size);
+ if (ret < 0)
+ return ret;
+ memcpy(par->extradata, side_data, side_data_size);
+ }
+ }
}
- buf = av_malloc(pkt->size+1024);
- if (!buf)
- return AVERROR(ENOMEM);
+ 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,35 +192,65 @@ 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;
+}
+
+static int latm_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt)
+{
+ int ret = 1;
+ AVStream *st = s->streams[pkt->stream_index];
+
+ if (st->codecpar->codec_id == AV_CODEC_ID_AAC) {
+ if (pkt->size > 2 && (AV_RB16(pkt->data) & 0xfff0) == 0xfff0)
+ ret = ff_stream_add_bitstream_filter(st, "aac_adtstoasc", NULL);
+ }
+
+ return ret;
}
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,
.write_header = latm_write_header,
.write_packet = latm_write_packet,
.priv_class = &latm_muxer_class,
+ .check_bitstream= latm_check_bitstream,
.flags = AVFMT_NOTIMESTAMPS,
};
diff --git a/libavformat/libavformat.v b/libavformat/libavformat.v
index 47d5ddc..c961cd8 100644
--- a/libavformat/libavformat.v
+++ b/libavformat/libavformat.v
@@ -1,6 +1,19 @@
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_write;
+ #those are deprecated, remove on next bump
+ url_feof;
local:
*;
};
diff --git a/libavformat/libgme.c b/libavformat/libgme.c
new file mode 100644
index 0000000..228273d
--- /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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE);
+ st->codecpar->channels = 2;
+ st->codecpar->sample_rate = gme->sample_rate;
+
+ return 0;
+}
+
+static int read_packet_gme(AVFormatContext *s, AVPacket *pkt)
+{
+ GMEContext *gme = s->priv_data;
+ int n_samples = AUDIO_PKT_SIZE / 2;
+ int ret;
+
+ if (gme_track_ended(gme->music_emu))
+ return AVERROR_EOF;
+
+ if ((ret = av_new_packet(pkt, AUDIO_PKT_SIZE)) < 0)
+ return ret;
+
+ if (gme_play(gme->music_emu, n_samples, (short *)pkt->data))
+ return AVERROR_EXTERNAL;
+ pkt->size = AUDIO_PKT_SIZE;
+
+ return 0;
+}
+
+static int read_close_gme(AVFormatContext *s)
+{
+ GMEContext *gme = s->priv_data;
+ gme_free_info(gme->info);
+ gme_delete(gme->music_emu);
+ return 0;
+}
+
+static int read_seek_gme(AVFormatContext *s, int stream_idx, int64_t ts, int flags)
+{
+ GMEContext *gme = s->priv_data;
+ if (!gme_seek(gme->music_emu, (int)ts))
+ return AVERROR_EXTERNAL;
+ return 0;
+}
+
+static int probe_gme(AVProbeData *p)
+{
+ // Reads 4 bytes - returns "" if unknown format.
+ if (gme_identify_header(p->buf)[0]) {
+ if (p->buf_size < 16384)
+ return AVPROBE_SCORE_MAX / 4 ;
+ else
+ return AVPROBE_SCORE_MAX / 2;
+ }
+ return 0;
+}
+
+static const AVClass class_gme = {
+ .class_name = "Game Music Emu demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_libgme_demuxer = {
+ .name = "libgme",
+ .long_name = NULL_IF_CONFIG_SMALL("Game Music Emu demuxer"),
+ .priv_data_size = sizeof(GMEContext),
+ .read_probe = probe_gme,
+ .read_header = read_header_gme,
+ .read_packet = read_packet_gme,
+ .read_close = read_close_gme,
+ .read_seek = read_seek_gme,
+ .priv_class = &class_gme,
+};
diff --git a/libavformat/libmodplug.c b/libavformat/libmodplug.c
new file mode 100644
index 0000000..f18c610
--- /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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE;
+ st->codecpar->channels = settings.mChannels;
+ st->codecpar->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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ vst->codecpar->codec_id = AV_CODEC_ID_XBIN;
+ vst->codecpar->width = modplug->w << 3;
+ vst->codecpar->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_packet_unref(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/libopenmpt.c b/libavformat/libopenmpt.c
new file mode 100644
index 0000000..af6eb1a
--- /dev/null
+++ b/libavformat/libopenmpt.c
@@ -0,0 +1,212 @@
+/*
+ * Tracked MOD demuxer (libopenmpt)
+ * Copyright (c) 2016 Josh de Kock
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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 <libopenmpt/libopenmpt.h>
+#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
+
+#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "internal.h"
+
+typedef struct OpenMPTContext {
+ const AVClass *class;
+ openmpt_module *module;
+
+ int channels;
+ double duration;
+ /* options */
+ int sample_rate;
+ int64_t layout;
+ int subsong;
+} OpenMPTContext;
+
+#define OFFSET(x) offsetof(OpenMPTContext, x)
+#define A AV_OPT_FLAG_AUDIO_PARAM
+#define D AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+ { "sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, { .i64 = 48000 }, 1000, INT_MAX, A | D },
+ { "layout", "set channel layout", OFFSET(layout), AV_OPT_TYPE_CHANNEL_LAYOUT, { .i64 = AV_CH_LAYOUT_STEREO }, 0, INT64_MAX, A | D },
+ { "subsong", "set subsong", OFFSET(subsong), AV_OPT_TYPE_INT, { .i64 = -2 }, -2, INT_MAX, A | D, "subsong"},
+ { "all", "all", 0, AV_OPT_TYPE_CONST, { .i64 = -1}, 0, 0, A | D, "subsong" },
+ { "auto", "auto", 0, AV_OPT_TYPE_CONST, { .i64 = -2}, 0, 0, A | D, "subsong" },
+ { NULL }
+};
+
+static void openmpt_logfunc(const char *message, void *userdata)
+{
+ int level = AV_LOG_INFO;
+ if (strstr(message, "ERROR") != NULL) {
+ level = AV_LOG_ERROR;
+ }
+ av_log(userdata, level, "%s\n", message);
+}
+
+#define add_meta(s, name, meta) \
+do { \
+ const char *value = meta; \
+ if (value && value[0]) \
+ av_dict_set(&s->metadata, name, value, 0); \
+ openmpt_free_string(value); \
+} while(0)
+
+static int read_header_openmpt(AVFormatContext *s)
+{
+ AVStream *st;
+ OpenMPTContext *openmpt = s->priv_data;
+ int64_t size = avio_size(s->pb);
+ if (size <= 0)
+ return AVERROR_INVALIDDATA;
+ char *buf = av_malloc(size);
+ int ret;
+
+
+ if (!buf)
+ return AVERROR(ENOMEM);
+ size = avio_read(s->pb, buf, size);
+ if (size < 0) {
+ av_log(s, AV_LOG_ERROR, "Reading input buffer failed.\n");
+ av_freep(&buf);
+ return size;
+ }
+
+ openmpt->module = openmpt_module_create_from_memory(buf, size, openmpt_logfunc, s, NULL);
+ av_freep(&buf);
+ if (!openmpt->module)
+ return AVERROR_INVALIDDATA;
+
+ openmpt->channels = av_get_channel_layout_nb_channels(openmpt->layout);
+
+ if (openmpt->subsong >= openmpt_module_get_num_subsongs(openmpt->module)) {
+ openmpt_module_destroy(openmpt->module);
+ av_log(s, AV_LOG_ERROR, "Invalid subsong index: %d\n", openmpt->subsong);
+ return AVERROR(EINVAL);
+ }
+
+ if (openmpt->subsong != -2) {
+ if (openmpt->subsong >= 0) {
+ av_dict_set_int(&s->metadata, "track", openmpt->subsong + 1, 0);
+ }
+ ret = openmpt_module_select_subsong(openmpt->module, openmpt->subsong);
+ if (!ret){
+ openmpt_module_destroy(openmpt->module);
+ av_log(s, AV_LOG_ERROR, "Could not select requested subsong: %d", openmpt->subsong);
+ return AVERROR(EINVAL);
+ }
+ }
+
+ openmpt->duration = openmpt_module_get_duration_seconds(openmpt->module);
+
+ add_meta(s, "artist", openmpt_module_get_metadata(openmpt->module, "artist"));
+ add_meta(s, "title", openmpt_module_get_metadata(openmpt->module, "title"));
+ add_meta(s, "encoder", openmpt_module_get_metadata(openmpt->module, "tracker"));
+ add_meta(s, "comment", openmpt_module_get_metadata(openmpt->module, "message"));
+ add_meta(s, "date", openmpt_module_get_metadata(openmpt->module, "date"));
+
+ st = avformat_new_stream(s, NULL);
+ if (!st) {
+ openmpt_module_destroy(openmpt->module);
+ openmpt->module = NULL;
+ return AVERROR(ENOMEM);
+ }
+ avpriv_set_pts_info(st, 64, 1, AV_TIME_BASE);
+ st->duration = llrint(openmpt->duration*AV_TIME_BASE);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_NE(AV_CODEC_ID_PCM_F32BE, AV_CODEC_ID_PCM_F32LE);
+ st->codecpar->channels = openmpt->channels;
+ st->codecpar->sample_rate = openmpt->sample_rate;
+
+ return 0;
+}
+
+#define AUDIO_PKT_SIZE 2048
+
+static int read_packet_openmpt(AVFormatContext *s, AVPacket *pkt)
+{
+ OpenMPTContext *openmpt = s->priv_data;
+ int n_samples = AUDIO_PKT_SIZE / (openmpt->channels ? openmpt->channels*4 : 4);
+ int ret;
+
+ if ((ret = av_new_packet(pkt, AUDIO_PKT_SIZE)) < 0)
+ return ret;
+
+ switch (openmpt->channels) {
+ case 1:
+ ret = openmpt_module_read_float_mono(openmpt->module, openmpt->sample_rate,
+ n_samples, (float *)pkt->data);
+ break;
+ case 2:
+ ret = openmpt_module_read_interleaved_float_stereo(openmpt->module, openmpt->sample_rate,
+ n_samples, (float *)pkt->data);
+ break;
+ case 4:
+ ret = openmpt_module_read_interleaved_float_quad(openmpt->module, openmpt->sample_rate,
+ n_samples, (float *)pkt->data);
+ break;
+ default:
+ av_log(s, AV_LOG_ERROR, "Unsupported number of channels: %d", openmpt->channels);
+ return AVERROR(EINVAL);
+ }
+
+ if (ret < 1) {
+ pkt->size = 0;
+ return AVERROR_EOF;
+ }
+
+ pkt->size = ret * (openmpt->channels * 4);
+
+ return 0;
+}
+
+static int read_close_openmpt(AVFormatContext *s)
+{
+ OpenMPTContext *openmpt = s->priv_data;
+ openmpt_module_destroy(openmpt->module);
+ openmpt->module = NULL;
+ return 0;
+}
+
+static int read_seek_openmpt(AVFormatContext *s, int stream_idx, int64_t ts, int flags)
+{
+ OpenMPTContext *openmpt = s->priv_data;
+ openmpt_module_set_position_seconds(openmpt->module, (double)ts/AV_TIME_BASE);
+ return 0;
+}
+
+static const AVClass class_openmpt = {
+ .class_name = "libopenmpt",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_libopenmpt_demuxer = {
+ .name = "libopenmpt",
+ .long_name = NULL_IF_CONFIG_SMALL("Tracker formats (libopenmpt)"),
+ .priv_data_size = sizeof(OpenMPTContext),
+ .read_header = read_header_openmpt,
+ .read_packet = read_packet_openmpt,
+ .read_close = read_close_openmpt,
+ .read_seek = read_seek_openmpt,
+ .priv_class = &class_openmpt,
+ .extensions = "669,amf,ams,dbm,digi,dmf,dsm,far,gdm,imf,it,j2b,m15,mdl,med,mmcmp,mms,mo3,mod,mptm,mt2,mtm,nst,okt,plm,ppm,psm,pt36,ptm,s3m,sfx,sfx2,stk,stm,ult,umx,wow,xm,xpk",
+};
diff --git a/libavformat/librtmp.c b/libavformat/librtmp.c
index 97c8ad6..f3cfa9a 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)
@@ -232,10 +236,23 @@ 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;
+ if (setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp))) {
+ rc = AVERROR_EXTERNAL;
+ goto fail;
+ }
+ }
+#endif
+
s->is_streamed = 1;
return 0;
fail:
av_freep(&ctx->temp_filename);
+ if (rc)
+ RTMP_Close(r);
+
return rc;
}
@@ -310,6 +327,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 0000000..b68cd8b
--- /dev/null
+++ b/libavformat/libsmbclient.c
@@ -0,0 +1,383 @@
+/*
+ * 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 dh;
+ 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;
+}
+
+static int libsmbc_open_dir(URLContext *h)
+{
+ LIBSMBContext *libsmbc = h->priv_data;
+ int ret;
+
+ if ((ret = libsmbc_connect(h)) < 0)
+ goto fail;
+
+ if ((libsmbc->dh = smbc_opendir(h->filename)) < 0) {
+ ret = AVERROR(errno);
+ av_log(h, AV_LOG_ERROR, "Error opening dir: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ return 0;
+
+ fail:
+ libsmbc_close(h);
+ return ret;
+}
+
+static int libsmbc_read_dir(URLContext *h, AVIODirEntry **next)
+{
+ LIBSMBContext *libsmbc = h->priv_data;
+ AVIODirEntry *entry;
+ struct smbc_dirent *dirent = NULL;
+ char *url = NULL;
+ int skip_entry;
+
+ *next = entry = ff_alloc_dir_entry();
+ if (!entry)
+ return AVERROR(ENOMEM);
+
+ do {
+ skip_entry = 0;
+ dirent = smbc_readdir(libsmbc->dh);
+ if (!dirent) {
+ av_freep(next);
+ return 0;
+ }
+ switch (dirent->smbc_type) {
+ case SMBC_DIR:
+ entry->type = AVIO_ENTRY_DIRECTORY;
+ break;
+ case SMBC_FILE:
+ entry->type = AVIO_ENTRY_FILE;
+ break;
+ case SMBC_FILE_SHARE:
+ entry->type = AVIO_ENTRY_SHARE;
+ break;
+ case SMBC_SERVER:
+ entry->type = AVIO_ENTRY_SERVER;
+ break;
+ case SMBC_WORKGROUP:
+ entry->type = AVIO_ENTRY_WORKGROUP;
+ break;
+ case SMBC_COMMS_SHARE:
+ case SMBC_IPC_SHARE:
+ case SMBC_PRINTER_SHARE:
+ skip_entry = 1;
+ break;
+ case SMBC_LINK:
+ default:
+ entry->type = AVIO_ENTRY_UNKNOWN;
+ break;
+ }
+ } while (skip_entry || !strcmp(dirent->name, ".") ||
+ !strcmp(dirent->name, ".."));
+
+ entry->name = av_strdup(dirent->name);
+ if (!entry->name) {
+ av_freep(next);
+ return AVERROR(ENOMEM);
+ }
+
+ url = av_append_path_component(h->filename, dirent->name);
+ if (url) {
+ struct stat st;
+ if (!smbc_stat(url, &st)) {
+ entry->group_id = st.st_gid;
+ entry->user_id = st.st_uid;
+ entry->size = st.st_size;
+ entry->filemode = st.st_mode & 0777;
+ entry->modification_timestamp = INT64_C(1000000) * st.st_mtime;
+ entry->access_timestamp = INT64_C(1000000) * st.st_atime;
+ entry->status_change_timestamp = INT64_C(1000000) * st.st_ctime;
+ }
+ av_free(url);
+ }
+
+ return 0;
+}
+
+static int libsmbc_close_dir(URLContext *h)
+{
+ LIBSMBContext *libsmbc = h->priv_data;
+ if (libsmbc->dh >= 0) {
+ smbc_closedir(libsmbc->dh);
+ libsmbc->dh = -1;
+ }
+ libsmbc_close(h);
+ return 0;
+}
+
+static int libsmbc_delete(URLContext *h)
+{
+ LIBSMBContext *libsmbc = h->priv_data;
+ int ret;
+ struct stat st;
+
+ if ((ret = libsmbc_connect(h)) < 0)
+ goto cleanup;
+
+ if ((libsmbc->fd = smbc_open(h->filename, O_WRONLY, 0666)) < 0) {
+ ret = AVERROR(errno);
+ goto cleanup;
+ }
+
+ if (smbc_fstat(libsmbc->fd, &st) < 0) {
+ ret = AVERROR(errno);
+ goto cleanup;
+ }
+
+ smbc_close(libsmbc->fd);
+ libsmbc->fd = -1;
+
+ if (S_ISDIR(st.st_mode)) {
+ if (smbc_rmdir(h->filename) < 0) {
+ ret = AVERROR(errno);
+ goto cleanup;
+ }
+ } else {
+ if (smbc_unlink(h->filename) < 0) {
+ ret = AVERROR(errno);
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+
+cleanup:
+ libsmbc_close(h);
+ return ret;
+}
+
+static int libsmbc_move(URLContext *h_src, URLContext *h_dst)
+{
+ LIBSMBContext *libsmbc = h_src->priv_data;
+ int ret;
+
+ if ((ret = libsmbc_connect(h_src)) < 0)
+ goto cleanup;
+
+ if ((libsmbc->dh = smbc_rename(h_src->filename, h_dst->filename)) < 0) {
+ ret = AVERROR(errno);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ libsmbc_close(h_src);
+ return ret;
+}
+
+#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,
+};
+
+const 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,
+ .url_delete = libsmbc_delete,
+ .url_move = libsmbc_move,
+ .url_open_dir = libsmbc_open_dir,
+ .url_read_dir = libsmbc_read_dir,
+ .url_close_dir = libsmbc_close_dir,
+ .priv_data_size = sizeof(LIBSMBContext),
+ .priv_data_class = &libsmbclient_context_class,
+ .flags = URL_PROTOCOL_FLAG_NETWORK,
+};
diff --git a/libavformat/libssh.c b/libavformat/libssh.c
new file mode 100644
index 0000000..9e3d4da
--- /dev/null
+++ b/libavformat/libssh.c
@@ -0,0 +1,507 @@
+/*
+ * 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 "libavformat/avio.h"
+#include "avformat.h"
+#include "internal.h"
+#include "url.h"
+
+typedef struct {
+ const AVClass *class;
+ ssh_session session;
+ sftp_session sftp;
+ sftp_file file;
+ sftp_dir dir;
+ 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_options_parse_config(libssh->session, NULL) < 0) {
+ av_log(libssh, AV_LOG_WARNING, "Could not parse the config file.\n");
+ }
+
+ 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 && password && (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_connect(URLContext *h, const char *url, char *path, size_t path_size)
+{
+ LIBSSHContext *libssh = h->priv_data;
+ char proto[10], 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, path_size,
+ url);
+
+ if (!(*path))
+ av_strlcpy(path, "/", path_size);
+
+ // a port of 0 will use a port from ~/.ssh/config or the default value 22
+ if (port < 0 || port > 65535)
+ port = 0;
+
+ if ((ret = libssh_create_ssh_session(libssh, hostname, port)) < 0)
+ return ret;
+
+ user = av_strtok(credencials, ":", &end);
+ pass = av_strtok(end, ":", &end);
+
+ if ((ret = libssh_authentication(libssh, user, pass)) < 0)
+ return ret;
+
+ if ((ret = libssh_create_sftp_session(libssh)) < 0)
+ return ret;
+
+ return 0;
+}
+
+static av_cold int libssh_open(URLContext *h, const char *url, int flags)
+{
+ int ret;
+ LIBSSHContext *libssh = h->priv_data;
+ char path[MAX_URL_SIZE];
+
+ if ((ret = libssh_connect(h, url, path, sizeof(path))) < 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;
+}
+
+static int libssh_open_dir(URLContext *h)
+{
+ LIBSSHContext *libssh = h->priv_data;
+ int ret;
+ char path[MAX_URL_SIZE];
+
+ if ((ret = libssh_connect(h, h->filename, path, sizeof(path))) < 0)
+ goto fail;
+
+ if (!(libssh->dir = sftp_opendir(libssh->sftp, path))) {
+ av_log(libssh, AV_LOG_ERROR, "Error opening sftp dir: %s\n", ssh_get_error(libssh->session));
+ ret = AVERROR(EIO);
+ goto fail;
+ }
+
+ return 0;
+
+ fail:
+ libssh_close(h);
+ return ret;
+}
+
+static int libssh_read_dir(URLContext *h, AVIODirEntry **next)
+{
+ LIBSSHContext *libssh = h->priv_data;
+ sftp_attributes attr = NULL;
+ AVIODirEntry *entry;
+
+ *next = entry = ff_alloc_dir_entry();
+ if (!entry)
+ return AVERROR(ENOMEM);
+
+ do {
+ if (attr)
+ sftp_attributes_free(attr);
+ attr = sftp_readdir(libssh->sftp, libssh->dir);
+ if (!attr) {
+ av_freep(next);
+ if (sftp_dir_eof(libssh->dir))
+ return 0;
+ return AVERROR(EIO);
+ }
+ } while (!strcmp(attr->name, ".") || !strcmp(attr->name, ".."));
+
+ entry->name = av_strdup(attr->name);
+ entry->group_id = attr->gid;
+ entry->user_id = attr->uid;
+ entry->size = attr->size;
+ entry->access_timestamp = INT64_C(1000000) * attr->atime;
+ entry->modification_timestamp = INT64_C(1000000) * attr->mtime;
+ entry->filemode = attr->permissions & 0777;
+ switch(attr->type) {
+ case SSH_FILEXFER_TYPE_REGULAR:
+ entry->type = AVIO_ENTRY_FILE;
+ break;
+ case SSH_FILEXFER_TYPE_DIRECTORY:
+ entry->type = AVIO_ENTRY_DIRECTORY;
+ break;
+ case SSH_FILEXFER_TYPE_SYMLINK:
+ entry->type = AVIO_ENTRY_SYMBOLIC_LINK;
+ break;
+ case SSH_FILEXFER_TYPE_SPECIAL:
+ /* Special type includes: sockets, char devices, block devices and pipes.
+ It is probably better to return unknown type, to not confuse anybody. */
+ case SSH_FILEXFER_TYPE_UNKNOWN:
+ default:
+ entry->type = AVIO_ENTRY_UNKNOWN;
+ }
+ sftp_attributes_free(attr);
+ return 0;
+}
+
+static int libssh_close_dir(URLContext *h)
+{
+ LIBSSHContext *libssh = h->priv_data;
+ if (libssh->dir)
+ sftp_closedir(libssh->dir);
+ libssh->dir = NULL;
+ libssh_close(h);
+ return 0;
+}
+
+static int libssh_delete(URLContext *h)
+{
+ int ret;
+ LIBSSHContext *libssh = h->priv_data;
+ sftp_attributes attr = NULL;
+ char path[MAX_URL_SIZE];
+
+ if ((ret = libssh_connect(h, h->filename, path, sizeof(path))) < 0)
+ goto cleanup;
+
+ if (!(attr = sftp_stat(libssh->sftp, path))) {
+ ret = AVERROR(sftp_get_error(libssh->sftp));
+ goto cleanup;
+ }
+
+ if (attr->type == SSH_FILEXFER_TYPE_DIRECTORY) {
+ if (sftp_rmdir(libssh->sftp, path) < 0) {
+ ret = AVERROR(sftp_get_error(libssh->sftp));
+ goto cleanup;
+ }
+ } else {
+ if (sftp_unlink(libssh->sftp, path) < 0) {
+ ret = AVERROR(sftp_get_error(libssh->sftp));
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+
+cleanup:
+ if (attr)
+ sftp_attributes_free(attr);
+ libssh_close(h);
+ return ret;
+}
+
+static int libssh_move(URLContext *h_src, URLContext *h_dst)
+{
+ int ret;
+ LIBSSHContext *libssh = h_src->priv_data;
+ char path_src[MAX_URL_SIZE], path_dst[MAX_URL_SIZE];
+ char hostname_src[1024], hostname_dst[1024];
+ char credentials_src[1024], credentials_dst[1024];
+ int port_src = 22, port_dst = 22;
+
+ av_url_split(NULL, 0,
+ credentials_src, sizeof(credentials_src),
+ hostname_src, sizeof(hostname_src),
+ &port_src,
+ path_src, sizeof(path_src),
+ h_src->filename);
+
+ av_url_split(NULL, 0,
+ credentials_dst, sizeof(credentials_dst),
+ hostname_dst, sizeof(hostname_dst),
+ &port_dst,
+ path_dst, sizeof(path_dst),
+ h_dst->filename);
+
+ if (strcmp(credentials_src, credentials_dst) ||
+ strcmp(hostname_src, hostname_dst) ||
+ port_src != port_dst) {
+ return AVERROR(EINVAL);
+ }
+
+ if ((ret = libssh_connect(h_src, h_src->filename, path_src, sizeof(path_src))) < 0)
+ goto cleanup;
+
+ if (sftp_rename(libssh->sftp, path_src, path_dst) < 0) {
+ ret = AVERROR(sftp_get_error(libssh->sftp));
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ libssh_close(h_src);
+ return ret;
+}
+
+#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,
+};
+
+const 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,
+ .url_delete = libssh_delete,
+ .url_move = libssh_move,
+ .url_open_dir = libssh_open_dir,
+ .url_read_dir = libssh_read_dir,
+ .url_close_dir = libssh_close_dir,
+ .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 447b3d3..d0cf8fe 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 0000000..7044055
--- /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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = s->iformat->raw_codec_id;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
+
+ //LCM of all possible AAC sample rates
+ avpriv_set_pts_info(st, 64, 1, 28224000);
+
+ return 0;
+}
+
+AVInputFormat ff_loas_demuxer = {
+ .name = "loas",
+ .long_name = NULL_IF_CONFIG_SMALL("LOAS AudioSyncStream"),
+ .read_probe = loas_probe,
+ .read_header = loas_read_header,
+ .read_packet = ff_raw_read_partial_packet,
+ .flags= AVFMT_GENERIC_INDEX,
+ .raw_codec_id = AV_CODEC_ID_AAC_LATM,
+};
diff --git a/libavformat/lrc.c b/libavformat/lrc.c
new file mode 100644
index 0000000..139c650
--- /dev/null
+++ b/libavformat/lrc.c
@@ -0,0 +1,34 @@
+/*
+ * LRC lyrics file format decoder
+ * Copyright (c) 2014 StarBrilliant <m13253@hotmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "metadata.h"
+#include "lrc.h"
+
+const AVMetadataConv ff_lrc_metadata_conv[] = {
+ {"ti", "title"},
+ {"al", "album"},
+ {"ar", "artist"},
+ {"au", "author"},
+ {"by", "creator"},
+ {"re", "encoder"},
+ {"ve", "encoder_version"},
+ {0, 0}
+};
diff --git a/libavformat/lrc.h b/libavformat/lrc.h
new file mode 100644
index 0000000..0297bc1
--- /dev/null
+++ b/libavformat/lrc.h
@@ -0,0 +1,29 @@
+/*
+ * LRC lyrics file format decoder
+ * Copyright (c) 2014 StarBrilliant <m13253@hotmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_LRC_H
+#define AVFORMAT_LRC_H
+
+#include "metadata.h"
+
+extern const AVMetadataConv ff_lrc_metadata_conv[];
+
+#endif
diff --git a/libavformat/lrcdec.c b/libavformat/lrcdec.c
new file mode 100644
index 0000000..12f74b2
--- /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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->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(s, &lrc->q);
+ ff_metadata_conv_ctx(s, NULL, ff_lrc_metadata_conv);
+ return 0;
+}
+
+static int lrc_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ LRCContext *lrc = s->priv_data;
+ return ff_subtitles_queue_read_packet(&lrc->q, pkt);
+}
+
+static int lrc_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ LRCContext *lrc = s->priv_data;
+ return ff_subtitles_queue_seek(&lrc->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int lrc_read_close(AVFormatContext *s)
+{
+ LRCContext *lrc = s->priv_data;
+ ff_subtitles_queue_clean(&lrc->q);
+ return 0;
+}
+
+AVInputFormat ff_lrc_demuxer = {
+ .name = "lrc",
+ .long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"),
+ .priv_data_size = sizeof (LRCContext),
+ .read_probe = lrc_probe,
+ .read_header = lrc_read_header,
+ .read_packet = lrc_read_packet,
+ .read_close = lrc_read_close,
+ .read_seek2 = lrc_read_seek
+};
diff --git a/libavformat/lrcenc.c b/libavformat/lrcenc.c
new file mode 100644
index 0000000..c5fda64
--- /dev/null
+++ b/libavformat/lrcenc.c
@@ -0,0 +1,153 @@
+/*
+ * 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]->codecpar->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]->codecpar->codec_id != AV_CODEC_ID_SUBRIP &&
+ s->streams[0]->codecpar->codec_id != AV_CODEC_ID_TEXT) {
+ av_log(s, AV_LOG_ERROR, "Unsupported subtitle codec: %s\n",
+ avcodec_get_name(s->streams[0]->codecpar->codec_id));
+ return AVERROR(EINVAL);
+ }
+ avpriv_set_pts_info(s->streams[0], 64, 1, 100);
+
+ ff_standardize_creation_time(s);
+ ff_metadata_conv_ctx(s, ff_lrc_metadata_conv, NULL);
+ if(!(s->flags & AVFMT_FLAG_BITEXACT)) { // avoid breaking regression tests
+ /* LRC provides a metadata slot for specifying encoder version
+ * in addition to encoder name. We will store LIBAVFORMAT_VERSION
+ * to it.
+ */
+ av_dict_set(&s->metadata, "ve", AV_STRINGIFY(LIBAVFORMAT_VERSION), 0);
+ } else {
+ av_dict_set(&s->metadata, "ve", NULL, 0);
+ }
+ for(metadata_item = NULL;
+ (metadata_item = av_dict_get(s->metadata, "", metadata_item,
+ AV_DICT_IGNORE_SUFFIX));) {
+ char *delim;
+ if(!metadata_item->value[0]) {
+ continue;
+ }
+ while((delim = strchr(metadata_item->value, '\n'))) {
+ *delim = ' ';
+ }
+ while((delim = strchr(metadata_item->value, '\r'))) {
+ *delim = ' ';
+ }
+ avio_printf(s->pb, "[%s:%s]\n",
+ metadata_item->key, metadata_item->value);
+ }
+ avio_printf(s->pb, "\n");
+ return 0;
+}
+
+static int lrc_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ if(pkt->pts != AV_NOPTS_VALUE) {
+ char *data = av_malloc(pkt->size + 1);
+ char *line;
+ char *delim;
+
+ if(!data) {
+ return AVERROR(ENOMEM);
+ }
+ memcpy(data, pkt->data, pkt->size);
+ data[pkt->size] = '\0';
+
+ for(delim = data + pkt->size - 1;
+ delim >= data && (delim[0] == '\n' || delim[0] == '\r'); delim--) {
+ delim[0] = '\0'; // Strip last empty lines
+ }
+ line = data;
+ while(line[0] == '\n' || line[0] == '\r') {
+ line++; // Skip first empty lines
+ }
+
+ while(line) {
+ delim = strchr(line, '\n');
+ if(delim) {
+ if(delim > line && delim[-1] == '\r') {
+ delim[-1] = '\0';
+ }
+ delim[0] = '\0';
+ delim++;
+ }
+ if(line[0] == '[') {
+ av_log(s, AV_LOG_WARNING,
+ "Subtitle starts with '[', may cause problems with LRC format.\n");
+ }
+
+ if(pkt->pts >= 0) {
+ avio_printf(s->pb, "[%02"PRId64":%02"PRId64".%02"PRId64"]",
+ (pkt->pts / 6000),
+ ((pkt->pts / 100) % 60),
+ (pkt->pts % 100));
+ } else {
+ /* Offset feature of LRC can easily make pts negative,
+ * we just output it directly and let the player drop it. */
+ avio_printf(s->pb, "[-%02"PRId64":%02"PRId64".%02"PRId64"]",
+ (-pkt->pts) / 6000,
+ ((-pkt->pts) / 100) % 60,
+ (-pkt->pts) % 100);
+ }
+ avio_printf(s->pb, "%s\n", line);
+ line = delim;
+ }
+ av_free(data);
+ }
+ return 0;
+}
+
+AVOutputFormat ff_lrc_muxer = {
+ .name = "lrc",
+ .long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"),
+ .extensions = "lrc",
+ .priv_data_size = 0,
+ .write_header = lrc_write_header,
+ .write_packet = lrc_write_packet,
+ .flags = AVFMT_VARIABLE_FPS | AVFMT_GLOBALHEADER |
+ AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT,
+ .subtitle_codec = AV_CODEC_ID_SUBRIP
+};
diff --git a/libavformat/lvfdec.c b/libavformat/lvfdec.c
new file mode 100644
index 0000000..b8af256
--- /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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ avio_skip(s->pb, 4);
+ st->codecpar->width = avio_rl32(s->pb);
+ st->codecpar->height = avio_rl32(s->pb);
+ avio_skip(s->pb, 4);
+ st->codecpar->codec_tag = avio_rl32(s->pb);
+ st->codecpar->codec_id = ff_codec_get_id(ff_codec_bmp_tags,
+ st->codecpar->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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_tag = avio_rl16(s->pb);
+ st->codecpar->channels = avio_rl16(s->pb);
+ st->codecpar->sample_rate = avio_rl16(s->pb);
+ avio_skip(s->pb, 8);
+ st->codecpar->bits_per_coded_sample = avio_r8(s->pb);
+ st->codecpar->codec_id = ff_codec_get_id(ff_codec_wav_tags,
+ st->codecpar->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 285539c..9b3eb6a 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))
@@ -259,6 +260,7 @@ static int lxf_read_header(AVFormatContext *s)
st->codecpar->bit_rate = 1000000 * ((video_params >> 14) & 0xFF);
st->codecpar->codec_tag = video_params & 0xF;
st->codecpar->codec_id = ff_codec_get_id(lxf_tags, st->codecpar->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,
@@ -302,7 +304,7 @@ static int lxf_read_packet(AVFormatContext *s, AVPacket *pkt)
if (stream > 1) {
av_log(s, AV_LOG_WARNING,
"got packet with illegal stream index %"PRIu32"\n", stream);
- return AVERROR(EAGAIN);
+ return FFERROR_REDO;
}
if (stream == 1 && s->nb_streams < 2) {
diff --git a/libavformat/m4vdec.c b/libavformat/m4vdec.c
index 9d69dcc..34d434f 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
*/
@@ -52,9 +52,12 @@ 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;
+
+ if (VOP >= VISO && VOP >= VOL && VO >= VOL && VOL > 0 && VOP+VO > 4)
+ return AVPROBE_SCORE_EXTENSION/10;
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 a8f5e98..94ccbec 100644
--- a/libavformat/matroska.c
+++ b/libavformat/matroska.c
@@ -2,20 +2,20 @@
* Matroska common data
* Copyright (c) 2003-2004 The FFmpeg project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -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},
@@ -32,9 +35,10 @@ const CodecTags ff_mkv_codec_tags[]={
{"A_FLAC" , AV_CODEC_ID_FLAC},
{"A_MLP" , AV_CODEC_ID_MLP},
{"A_MPEG/L2" , AV_CODEC_ID_MP2},
- {"A_MPEG/L1" , AV_CODEC_ID_MP2},
+ {"A_MPEG/L1" , AV_CODEC_ID_MP1},
{"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},
@@ -44,6 +48,7 @@ const CodecTags ff_mkv_codec_tags[]={
{"A_PCM/INT/LIT" , AV_CODEC_ID_PCM_S24LE},
{"A_PCM/INT/LIT" , AV_CODEC_ID_PCM_S32LE},
{"A_PCM/INT/LIT" , AV_CODEC_ID_PCM_U8},
+ {"A_QUICKTIME/QDMC" , AV_CODEC_ID_QDMC},
{"A_QUICKTIME/QDM2" , AV_CODEC_ID_QDM2},
{"A_REAL/14_4" , AV_CODEC_ID_RA_144},
{"A_REAL/28_8" , AV_CODEC_ID_RA_288},
@@ -55,19 +60,26 @@ const CodecTags ff_mkv_codec_tags[]={
{"A_VORBIS" , AV_CODEC_ID_VORBIS},
{"A_WAVPACK4" , AV_CODEC_ID_WAVPACK},
- {"S_TEXT/UTF8" , AV_CODEC_ID_SRT},
+ {"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/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},
+ {"S_HDMV/TEXTST" , AV_CODEC_ID_HDMV_TEXT_SUBTITLE},
{"V_AV1" , AV_CODEC_ID_AV1},
{"V_DIRAC" , AV_CODEC_ID_DIRAC},
+ {"V_FFV1" , AV_CODEC_ID_FFV1},
{"V_MJPEG" , AV_CODEC_ID_MJPEG},
{"V_MPEG1" , AV_CODEC_ID_MPEG1VIDEO},
{"V_MPEG2" , AV_CODEC_ID_MPEG2VIDEO},
@@ -82,6 +94,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},
@@ -103,6 +116,8 @@ const CodecMime ff_mkv_mime_tags[] = {
{"text/plain" , AV_CODEC_ID_TEXT},
{"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}
};
@@ -113,6 +128,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)
{
AVStereo3D *stereo;
diff --git a/libavformat/matroska.h b/libavformat/matroska.h
index 4e9f96e..83c8246 100644
--- a/libavformat/matroska.h
+++ b/libavformat/matroska.h
@@ -2,20 +2,20 @@
* Matroska constants
* Copyright (c) 2003-2004 The FFmpeg project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -78,8 +78,13 @@
#define MATROSKA_ID_TRACKNUMBER 0xD7
#define MATROSKA_ID_TRACKUID 0x73C5
#define MATROSKA_ID_TRACKTYPE 0x83
-#define MATROSKA_ID_TRACKAUDIO 0xE1
-#define MATROSKA_ID_TRACKVIDEO 0xE0
+#define MATROSKA_ID_TRACKVIDEO 0xE0
+#define MATROSKA_ID_TRACKAUDIO 0xE1
+#define MATROSKA_ID_TRACKOPERATION 0xE2
+#define MATROSKA_ID_TRACKCOMBINEPLANES 0xE3
+#define MATROSKA_ID_TRACKPLANE 0xE4
+#define MATROSKA_ID_TRACKPLANEUID 0xE5
+#define MATROSKA_ID_TRACKPLANETYPE 0xE6
#define MATROSKA_ID_CODECID 0x86
#define MATROSKA_ID_CODECPRIVATE 0x63A2
#define MATROSKA_ID_CODECNAME 0x258688
@@ -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
@@ -115,8 +121,37 @@
#define MATROSKA_ID_VIDEOFLAGINTERLACED 0x9A
#define MATROSKA_ID_VIDEOFIELDORDER 0x9D
#define MATROSKA_ID_VIDEOSTEREOMODE 0x53B8
+#define MATROSKA_ID_VIDEOALPHAMODE 0x53C0
#define MATROSKA_ID_VIDEOASPECTRATIO 0x54B3
#define MATROSKA_ID_VIDEOCOLORSPACE 0x2EB524
+#define MATROSKA_ID_VIDEOCOLOR 0x55B0
+
+#define MATROSKA_ID_VIDEOCOLORMATRIXCOEFF 0x55B1
+#define MATROSKA_ID_VIDEOCOLORBITSPERCHANNEL 0x55B2
+#define MATROSKA_ID_VIDEOCOLORCHROMASUBHORZ 0x55B3
+#define MATROSKA_ID_VIDEOCOLORCHROMASUBVERT 0x55B4
+#define MATROSKA_ID_VIDEOCOLORCBSUBHORZ 0x55B5
+#define MATROSKA_ID_VIDEOCOLORCBSUBVERT 0x55B6
+#define MATROSKA_ID_VIDEOCOLORCHROMASITINGHORZ 0x55B7
+#define MATROSKA_ID_VIDEOCOLORCHROMASITINGVERT 0x55B8
+#define MATROSKA_ID_VIDEOCOLORRANGE 0x55B9
+#define MATROSKA_ID_VIDEOCOLORTRANSFERCHARACTERISTICS 0x55BA
+
+#define MATROSKA_ID_VIDEOCOLORPRIMARIES 0x55BB
+#define MATROSKA_ID_VIDEOCOLORMAXCLL 0x55BC
+#define MATROSKA_ID_VIDEOCOLORMAXFALL 0x55BD
+
+#define MATROSKA_ID_VIDEOCOLORMASTERINGMETA 0x55D0
+#define MATROSKA_ID_VIDEOCOLOR_RX 0x55D1
+#define MATROSKA_ID_VIDEOCOLOR_RY 0x55D2
+#define MATROSKA_ID_VIDEOCOLOR_GX 0x55D3
+#define MATROSKA_ID_VIDEOCOLOR_GY 0x55D4
+#define MATROSKA_ID_VIDEOCOLOR_BX 0x55D5
+#define MATROSKA_ID_VIDEOCOLOR_BY 0x55D6
+#define MATROSKA_ID_VIDEOCOLOR_WHITEX 0x55D7
+#define MATROSKA_ID_VIDEOCOLOR_WHITEY 0x55D8
+#define MATROSKA_ID_VIDEOCOLOR_LUMINANCEMAX 0x55D9
+#define MATROSKA_ID_VIDEOCOLOR_LUMINANCEMIN 0x55DA
#define MATROSKA_ID_VIDEOPROJECTION 0x7670
#define MATROSKA_ID_VIDEOPROJECTIONTYPE 0x7671
@@ -140,6 +175,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
@@ -150,6 +194,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 */
@@ -179,6 +225,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 */
@@ -186,6 +236,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
@@ -203,6 +254,7 @@
#define MATROSKA_ID_CHAPTERDISPLAY 0x80
#define MATROSKA_ID_CHAPSTRING 0x85
#define MATROSKA_ID_CHAPLANG 0x437C
+#define MATROSKA_ID_CHAPCOUNTRY 0x437E
#define MATROSKA_ID_EDITIONUID 0x45BC
#define MATROSKA_ID_EDITIONFLAGHIDDEN 0x45BD
#define MATROSKA_ID_EDITIONFLAGDEFAULT 0x45DB
@@ -220,6 +272,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 {
@@ -264,6 +317,28 @@ typedef enum {
} MatroskaVideoStereoModeType;
typedef enum {
+ MATROSKA_VIDEO_DISPLAYUNIT_PIXELS = 0,
+ MATROSKA_VIDEO_DISPLAYUNIT_CENTIMETERS = 1,
+ MATROSKA_VIDEO_DISPLAYUNIT_INCHES = 2,
+ MATROSKA_VIDEO_DISPLAYUNIT_DAR = 3,
+ MATROSKA_VIDEO_DISPLAYUNIT_UNKNOWN = 4,
+} MatroskaVideoDisplayUnit;
+
+typedef enum {
+ MATROSKA_COLOUR_CHROMASITINGHORZ_UNDETERMINED = 0,
+ MATROSKA_COLOUR_CHROMASITINGHORZ_LEFT = 1,
+ MATROSKA_COLOUR_CHROMASITINGHORZ_HALF = 2,
+ MATROSKA_COLOUR_CHROMASITINGHORZ_NB
+} MatroskaColourChromaSitingHorz;
+
+typedef enum {
+ MATROSKA_COLOUR_CHROMASITINGVERT_UNDETERMINED = 0,
+ MATROSKA_COLOUR_CHROMASITINGVERT_TOP = 1,
+ MATROSKA_COLOUR_CHROMASITINGVERT_HALF = 2,
+ MATROSKA_COLOUR_CHROMASITINGVERT_NB
+} MatroskaColourChromaSitingVert;
+
+typedef enum {
MATROSKA_VIDEO_PROJECTION_TYPE_RECTANGULAR = 0,
MATROSKA_VIDEO_PROJECTION_TYPE_EQUIRECTANGULAR = 1,
MATROSKA_VIDEO_PROJECTION_TYPE_CUBEMAP = 2,
@@ -275,17 +350,33 @@ 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 CodecMime ff_mkv_image_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 4fbf4b9..cdb2e20 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,17 @@
#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/mastering_display_metadata.h"
#include "libavutil/mathematics.h"
+#include "libavutil/opt.h"
+#include "libavutil/time_internal.h"
#include "libavutil/spherical.h"
#include "libavcodec/bytestream.h"
@@ -61,6 +59,15 @@
#include "riff.h"
#include "rmsipr.h"
+#if CONFIG_BZLIB
+#include <bzlib.h>
+#endif
+#if CONFIG_ZLIB
+#include <zlib.h>
+#endif
+
+#include "qtpalette.h"
+
typedef enum {
EBML_NONE,
EBML_UINT,
@@ -69,8 +76,10 @@ typedef enum {
EBML_UTF8,
EBML_BIN,
EBML_NEST,
+ EBML_LEVEL1,
EBML_PASS,
EBML_STOP,
+ EBML_SINT,
EBML_TYPE_COUNT
} EbmlType;
@@ -80,6 +89,7 @@ typedef const struct EbmlSyntax {
int list_elem_size;
int data_offset;
union {
+ int64_t i;
uint64_t u;
double f;
const char *s;
@@ -111,12 +121,48 @@ 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 MatroskaMasteringMeta {
+ double r_x;
+ double r_y;
+ double g_x;
+ double g_y;
+ double b_x;
+ double b_y;
+ double white_x;
+ double white_y;
+ double max_luminance;
+ double min_luminance;
+} MatroskaMasteringMeta;
+
+typedef struct MatroskaTrackVideoColor {
+ uint64_t matrix_coefficients;
+ uint64_t bits_per_channel;
+ uint64_t chroma_sub_horz;
+ uint64_t chroma_sub_vert;
+ uint64_t cb_sub_horz;
+ uint64_t cb_sub_vert;
+ uint64_t chroma_siting_horz;
+ uint64_t chroma_siting_vert;
+ uint64_t range;
+ uint64_t transfer_characteristics;
+ uint64_t primaries;
+ uint64_t max_cll;
+ uint64_t max_fall;
+ MatroskaMasteringMeta mastering_meta;
+} MatroskaTrackVideoColor;
+
typedef struct MatroskaTrackVideoProjection {
uint64_t type;
EbmlBin private;
@@ -131,10 +177,13 @@ typedef struct MatroskaTrackVideo {
uint64_t display_height;
uint64_t pixel_width;
uint64_t pixel_height;
- uint64_t fourcc;
+ EbmlBin color_space;
+ uint64_t display_unit;
uint64_t interlaced;
uint64_t field_order;
uint64_t stereo_mode;
+ uint64_t alpha_mode;
+ EbmlList color;
MatroskaTrackVideoProjection projection;
} MatroskaTrackVideo;
@@ -155,6 +204,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;
@@ -167,14 +225,21 @@ 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;
+ uint64_t codec_delay_in_track_tb;
AVStream *stream;
int64_t end_timecode;
int ms_compat;
+ uint64_t max_block_additional_id;
+
+ uint32_t palette[AVPALETTE_COUNT];
+ int has_palette;
} MatroskaTrack;
typedef struct MatroskaAttachment {
@@ -241,7 +306,14 @@ typedef struct MatroskaCluster {
EbmlList blocks;
} MatroskaCluster;
+typedef struct MatroskaLevel1Element {
+ uint64_t id;
+ uint64_t pos;
+ int parsed;
+} MatroskaLevel1Element;
+
typedef struct MatroskaDemuxContext {
+ const AVClass *class;
AVFormatContext *ctx;
/* EBML stuff */
@@ -253,6 +325,8 @@ typedef struct MatroskaDemuxContext {
uint64_t time_scale;
double duration;
char *title;
+ char *muxingapp;
+ EbmlBin date_utc;
EbmlList tracks;
EbmlList attachments;
EbmlList chapters;
@@ -277,12 +351,22 @@ 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;
/* File has SSA subtitles which prevent incremental cluster parsing. */
int contains_ssa;
+
+ /* WebM DASH Manifest live flag */
+ int is_live;
+
+ /* Bandwidth value for WebM DASH Manifest */
+ int bandwidth;
} MatroskaDemuxContext;
typedef struct MatroskaBlock {
@@ -290,9 +374,12 @@ 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[] = {
+static const EbmlSyntax ebml_header[] = {
{ EBML_ID_EBMLREADVERSION, EBML_UINT, 0, offsetof(Ebml, version), { .u = EBML_VERSION } },
{ EBML_ID_EBMLMAXSIZELENGTH, EBML_UINT, 0, offsetof(Ebml, max_size), { .u = 8 } },
{ EBML_ID_EBMLMAXIDLENGTH, EBML_UINT, 0, offsetof(Ebml, id_length), { .u = 4 } },
@@ -303,44 +390,78 @@ static EbmlSyntax ebml_header[] = {
{ 0 }
};
-static EbmlSyntax ebml_syntax[] = {
+static const EbmlSyntax ebml_syntax[] = {
{ EBML_ID_HEADER, EBML_NEST, 0, 0, { .n = ebml_header } },
{ 0 }
};
-static EbmlSyntax matroska_info[] = {
+static const EbmlSyntax matroska_info[] = {
{ MATROSKA_ID_TIMECODESCALE, EBML_UINT, 0, offsetof(MatroskaDemuxContext, time_scale), { .u = 1000000 } },
{ 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 const EbmlSyntax matroska_mastering_meta[] = {
+ { MATROSKA_ID_VIDEOCOLOR_RX, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, r_x), { .f=-1 } },
+ { MATROSKA_ID_VIDEOCOLOR_RY, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, r_y), { .f=-1 } },
+ { MATROSKA_ID_VIDEOCOLOR_GX, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, g_x), { .f=-1 } },
+ { MATROSKA_ID_VIDEOCOLOR_GY, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, g_y), { .f=-1 } },
+ { MATROSKA_ID_VIDEOCOLOR_BX, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, b_x), { .f=-1 } },
+ { MATROSKA_ID_VIDEOCOLOR_BY, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, b_y), { .f=-1 } },
+ { MATROSKA_ID_VIDEOCOLOR_WHITEX, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, white_x), { .f=-1 } },
+ { MATROSKA_ID_VIDEOCOLOR_WHITEY, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, white_y), { .f=-1 } },
+ { MATROSKA_ID_VIDEOCOLOR_LUMINANCEMIN, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, min_luminance), { .f=-1 } },
+ { MATROSKA_ID_VIDEOCOLOR_LUMINANCEMAX, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, max_luminance), { .f=-1 } },
+ { 0 }
+};
+
+static const EbmlSyntax matroska_track_video_color[] = {
+ { MATROSKA_ID_VIDEOCOLORMATRIXCOEFF, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, matrix_coefficients), { .u = AVCOL_SPC_UNSPECIFIED } },
+ { MATROSKA_ID_VIDEOCOLORBITSPERCHANNEL, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, bits_per_channel), { .u=0 } },
+ { MATROSKA_ID_VIDEOCOLORCHROMASUBHORZ, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, chroma_sub_horz), { .u=0 } },
+ { MATROSKA_ID_VIDEOCOLORCHROMASUBVERT, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, chroma_sub_vert), { .u=0 } },
+ { MATROSKA_ID_VIDEOCOLORCBSUBHORZ, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, cb_sub_horz), { .u=0 } },
+ { MATROSKA_ID_VIDEOCOLORCBSUBVERT, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, cb_sub_vert), { .u=0 } },
+ { MATROSKA_ID_VIDEOCOLORCHROMASITINGHORZ, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, chroma_siting_horz), { .u = MATROSKA_COLOUR_CHROMASITINGHORZ_UNDETERMINED } },
+ { MATROSKA_ID_VIDEOCOLORCHROMASITINGVERT, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, chroma_siting_vert), { .u = MATROSKA_COLOUR_CHROMASITINGVERT_UNDETERMINED } },
+ { MATROSKA_ID_VIDEOCOLORRANGE, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, range), { .u = AVCOL_RANGE_UNSPECIFIED } },
+ { MATROSKA_ID_VIDEOCOLORTRANSFERCHARACTERISTICS, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, transfer_characteristics), { .u = AVCOL_TRC_UNSPECIFIED } },
+ { MATROSKA_ID_VIDEOCOLORPRIMARIES, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, primaries), { .u = AVCOL_PRI_UNSPECIFIED } },
+ { MATROSKA_ID_VIDEOCOLORMAXCLL, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, max_cll), { .u=0 } },
+ { MATROSKA_ID_VIDEOCOLORMAXFALL, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, max_fall), { .u=0 } },
+ { MATROSKA_ID_VIDEOCOLORMASTERINGMETA, EBML_NEST, 0, offsetof(MatroskaTrackVideoColor, mastering_meta), { .n = matroska_mastering_meta } },
+ { 0 }
+};
+
static const EbmlSyntax matroska_track_video_projection[] = {
- { MATROSKA_ID_VIDEOPROJECTIONTYPE, EBML_UINT, 0, offsetof(MatroskaTrackVideoProjection, type), { .u = MATROSKA_VIDEO_PROJECTION_TYPE_RECTANGULAR } },
- { MATROSKA_ID_VIDEOPROJECTIONPRIVATE, EBML_BIN, 0, offsetof(MatroskaTrackVideoProjection, private) },
- { MATROSKA_ID_VIDEOPROJECTIONPOSEYAW, EBML_FLOAT, 0, offsetof(MatroskaTrackVideoProjection, yaw), { .f=0.0 } },
- { MATROSKA_ID_VIDEOPROJECTIONPOSEPITCH, EBML_FLOAT, 0, offsetof(MatroskaTrackVideoProjection, pitch), { .f=0.0 } },
- { MATROSKA_ID_VIDEOPROJECTIONPOSEROLL, EBML_FLOAT, 0, offsetof(MatroskaTrackVideoProjection, roll), { .f=0.0 } },
+ { MATROSKA_ID_VIDEOPROJECTIONTYPE, EBML_UINT, 0, offsetof(MatroskaTrackVideoProjection, type), { .u = MATROSKA_VIDEO_PROJECTION_TYPE_RECTANGULAR } },
+ { MATROSKA_ID_VIDEOPROJECTIONPRIVATE, EBML_BIN, 0, offsetof(MatroskaTrackVideoProjection, private) },
+ { MATROSKA_ID_VIDEOPROJECTIONPOSEYAW, EBML_FLOAT, 0, offsetof(MatroskaTrackVideoProjection, yaw), { .f=0.0 } },
+ { MATROSKA_ID_VIDEOPROJECTIONPOSEPITCH, EBML_FLOAT, 0, offsetof(MatroskaTrackVideoProjection, pitch), { .f=0.0 } },
+ { MATROSKA_ID_VIDEOPROJECTIONPOSEROLL, EBML_FLOAT, 0, offsetof(MatroskaTrackVideoProjection, roll), { .f=0.0 } },
{ 0 }
};
-static EbmlSyntax matroska_track_video[] = {
+static const 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_VIDEOCOLOR, EBML_NEST, sizeof(MatroskaTrackVideoColor), offsetof(MatroskaTrackVideo, color), { .n = matroska_track_video_color } },
{ MATROSKA_ID_VIDEOPROJECTION, EBML_NEST, 0, offsetof(MatroskaTrackVideo, projection), { .n = matroska_track_video_projection } },
{ MATROSKA_ID_VIDEOPIXELCROPB, EBML_NONE },
{ MATROSKA_ID_VIDEOPIXELCROPT, EBML_NONE },
{ MATROSKA_ID_VIDEOPIXELCROPL, EBML_NONE },
{ MATROSKA_ID_VIDEOPIXELCROPR, EBML_NONE },
- { MATROSKA_ID_VIDEODISPLAYUNIT, EBML_NONE },
+ { MATROSKA_ID_VIDEODISPLAYUNIT, EBML_UINT, 0, offsetof(MatroskaTrackVideo, display_unit), { .u= MATROSKA_VIDEO_DISPLAYUNIT_PIXELS } },
{ MATROSKA_ID_VIDEOFLAGINTERLACED, EBML_UINT, 0, offsetof(MatroskaTrackVideo, interlaced), { .u = MATROSKA_VIDEO_INTERLACE_FLAG_UNDETERMINED } },
{ MATROSKA_ID_VIDEOFIELDORDER, EBML_UINT, 0, offsetof(MatroskaTrackVideo, field_order), { .u = MATROSKA_VIDEO_FIELDORDER_UNDETERMINED } },
{ MATROSKA_ID_VIDEOSTEREOMODE, EBML_UINT, 0, offsetof(MatroskaTrackVideo, stereo_mode), { .u = MATROSKA_VIDEO_STEREOMODE_TYPE_NB } },
@@ -348,7 +469,7 @@ static EbmlSyntax matroska_track_video[] = {
{ 0 }
};
-static EbmlSyntax matroska_track_audio[] = {
+static const EbmlSyntax matroska_track_audio[] = {
{ MATROSKA_ID_AUDIOSAMPLINGFREQ, EBML_FLOAT, 0, offsetof(MatroskaTrackAudio, samplerate), { .f = 8000.0 } },
{ MATROSKA_ID_AUDIOOUTSAMPLINGFREQ, EBML_FLOAT, 0, offsetof(MatroskaTrackAudio, out_samplerate) },
{ MATROSKA_ID_AUDIOBITDEPTH, EBML_UINT, 0, offsetof(MatroskaTrackAudio, bitdepth) },
@@ -356,26 +477,53 @@ static EbmlSyntax matroska_track_audio[] = {
{ 0 }
};
-static EbmlSyntax matroska_track_encoding_compression[] = {
+static const EbmlSyntax matroska_track_encoding_compression[] = {
{ MATROSKA_ID_ENCODINGCOMPALGO, EBML_UINT, 0, offsetof(MatroskaTrackCompression, algo), { .u = 0 } },
{ MATROSKA_ID_ENCODINGCOMPSETTINGS, EBML_BIN, 0, offsetof(MatroskaTrackCompression, settings) },
{ 0 }
};
-static EbmlSyntax matroska_track_encoding[] = {
+static const 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 const 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 }
};
-static EbmlSyntax matroska_track_encodings[] = {
+static const EbmlSyntax matroska_track_encodings[] = {
{ MATROSKA_ID_TRACKCONTENTENCODING, EBML_NEST, sizeof(MatroskaTrackEncoding), offsetof(MatroskaTrack, encodings), { .n = matroska_track_encoding } },
{ 0 }
};
-static EbmlSyntax matroska_track[] = {
+static const EbmlSyntax matroska_track_plane[] = {
+ { MATROSKA_ID_TRACKPLANEUID, EBML_UINT, 0, offsetof(MatroskaTrackPlane,uid) },
+ { MATROSKA_ID_TRACKPLANETYPE, EBML_UINT, 0, offsetof(MatroskaTrackPlane,type) },
+ { 0 }
+};
+
+static const EbmlSyntax matroska_track_combine_planes[] = {
+ { MATROSKA_ID_TRACKPLANE, EBML_NEST, sizeof(MatroskaTrackPlane), offsetof(MatroskaTrackOperation,combine_planes), {.n = matroska_track_plane} },
+ { 0 }
+};
+
+static const EbmlSyntax matroska_track_operation[] = {
+ { MATROSKA_ID_TRACKCOMBINEPLANES, EBML_NEST, 0, 0, {.n = matroska_track_combine_planes} },
+ { 0 }
+};
+
+static const EbmlSyntax matroska_track[] = {
{ MATROSKA_ID_TRACKNUMBER, EBML_UINT, 0, offsetof(MatroskaTrack, num) },
{ MATROSKA_ID_TRACKNAME, EBML_UTF8, 0, offsetof(MatroskaTrack, name) },
{ MATROSKA_ID_TRACKUID, EBML_UINT, 0, offsetof(MatroskaTrack, uid) },
@@ -383,14 +531,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 },
@@ -399,16 +550,15 @@ 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 }
};
-static EbmlSyntax matroska_tracks[] = {
+static const EbmlSyntax matroska_tracks[] = {
{ MATROSKA_ID_TRACKENTRY, EBML_NEST, sizeof(MatroskaTrack), offsetof(MatroskaDemuxContext, tracks), { .n = matroska_track } },
{ 0 }
};
-static EbmlSyntax matroska_attachment[] = {
+static const EbmlSyntax matroska_attachment[] = {
{ MATROSKA_ID_FILEUID, EBML_UINT, 0, offsetof(MatroskaAttachment, uid) },
{ MATROSKA_ID_FILENAME, EBML_UTF8, 0, offsetof(MatroskaAttachment, filename) },
{ MATROSKA_ID_FILEMIMETYPE, EBML_STR, 0, offsetof(MatroskaAttachment, mime) },
@@ -417,18 +567,19 @@ static EbmlSyntax matroska_attachment[] = {
{ 0 }
};
-static EbmlSyntax matroska_attachments[] = {
+static const EbmlSyntax matroska_attachments[] = {
{ MATROSKA_ID_ATTACHEDFILE, EBML_NEST, sizeof(MatroskaAttachment), offsetof(MatroskaDemuxContext, attachments), { .n = matroska_attachment } },
{ 0 }
};
-static EbmlSyntax matroska_chapter_display[] = {
- { MATROSKA_ID_CHAPSTRING, EBML_UTF8, 0, offsetof(MatroskaChapter, title) },
- { MATROSKA_ID_CHAPLANG, EBML_NONE },
+static const EbmlSyntax matroska_chapter_display[] = {
+ { MATROSKA_ID_CHAPSTRING, EBML_UTF8, 0, offsetof(MatroskaChapter, title) },
+ { MATROSKA_ID_CHAPLANG, EBML_NONE },
+ { MATROSKA_ID_CHAPCOUNTRY, EBML_NONE },
{ 0 }
};
-static EbmlSyntax matroska_chapter_entry[] = {
+static const EbmlSyntax matroska_chapter_entry[] = {
{ MATROSKA_ID_CHAPTERTIMESTART, EBML_UINT, 0, offsetof(MatroskaChapter, start), { .u = AV_NOPTS_VALUE } },
{ MATROSKA_ID_CHAPTERTIMEEND, EBML_UINT, 0, offsetof(MatroskaChapter, end), { .u = AV_NOPTS_VALUE } },
{ MATROSKA_ID_CHAPTERUID, EBML_UINT, 0, offsetof(MatroskaChapter, uid) },
@@ -440,7 +591,7 @@ static EbmlSyntax matroska_chapter_entry[] = {
{ 0 }
};
-static EbmlSyntax matroska_chapter[] = {
+static const EbmlSyntax matroska_chapter[] = {
{ MATROSKA_ID_CHAPTERATOM, EBML_NEST, sizeof(MatroskaChapter), offsetof(MatroskaDemuxContext, chapters), { .n = matroska_chapter_entry } },
{ MATROSKA_ID_EDITIONUID, EBML_NONE },
{ MATROSKA_ID_EDITIONFLAGHIDDEN, EBML_NONE },
@@ -449,30 +600,32 @@ static EbmlSyntax matroska_chapter[] = {
{ 0 }
};
-static EbmlSyntax matroska_chapters[] = {
+static const EbmlSyntax matroska_chapters[] = {
{ MATROSKA_ID_EDITIONENTRY, EBML_NEST, 0, 0, { .n = matroska_chapter } },
{ 0 }
};
-static EbmlSyntax matroska_index_pos[] = {
+static const 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 }
};
-static EbmlSyntax matroska_index_entry[] = {
+static const EbmlSyntax matroska_index_entry[] = {
{ MATROSKA_ID_CUETIME, EBML_UINT, 0, offsetof(MatroskaIndex, time) },
{ MATROSKA_ID_CUETRACKPOSITION, EBML_NEST, sizeof(MatroskaIndexPos), offsetof(MatroskaIndex, pos), { .n = matroska_index_pos } },
{ 0 }
};
-static EbmlSyntax matroska_index[] = {
+static const EbmlSyntax matroska_index[] = {
{ MATROSKA_ID_POINTENTRY, EBML_NEST, sizeof(MatroskaIndex), offsetof(MatroskaDemuxContext, index), { .n = matroska_index_entry } },
{ 0 }
};
-static EbmlSyntax matroska_simpletag[] = {
+static const EbmlSyntax matroska_simpletag[] = {
{ MATROSKA_ID_TAGNAME, EBML_UTF8, 0, offsetof(MatroskaTag, name) },
{ MATROSKA_ID_TAGSTRING, EBML_UTF8, 0, offsetof(MatroskaTag, string) },
{ MATROSKA_ID_TAGLANG, EBML_STR, 0, offsetof(MatroskaTag, lang), { .s = "und" } },
@@ -482,7 +635,7 @@ static EbmlSyntax matroska_simpletag[] = {
{ 0 }
};
-static EbmlSyntax matroska_tagtargets[] = {
+static const EbmlSyntax matroska_tagtargets[] = {
{ MATROSKA_ID_TAGTARGETS_TYPE, EBML_STR, 0, offsetof(MatroskaTagTarget, type) },
{ MATROSKA_ID_TAGTARGETS_TYPEVALUE, EBML_UINT, 0, offsetof(MatroskaTagTarget, typevalue), { .u = 50 } },
{ MATROSKA_ID_TAGTARGETS_TRACKUID, EBML_UINT, 0, offsetof(MatroskaTagTarget, trackuid) },
@@ -491,56 +644,69 @@ static EbmlSyntax matroska_tagtargets[] = {
{ 0 }
};
-static EbmlSyntax matroska_tag[] = {
+static const EbmlSyntax matroska_tag[] = {
{ MATROSKA_ID_SIMPLETAG, EBML_NEST, sizeof(MatroskaTag), offsetof(MatroskaTags, tag), { .n = matroska_simpletag } },
{ MATROSKA_ID_TAGTARGETS, EBML_NEST, 0, offsetof(MatroskaTags, target), { .n = matroska_tagtargets } },
{ 0 }
};
-static EbmlSyntax matroska_tags[] = {
+static const EbmlSyntax matroska_tags[] = {
{ MATROSKA_ID_TAG, EBML_NEST, sizeof(MatroskaTags), offsetof(MatroskaDemuxContext, tags), { .n = matroska_tag } },
{ 0 }
};
-static EbmlSyntax matroska_seekhead_entry[] = {
+static const EbmlSyntax matroska_seekhead_entry[] = {
{ MATROSKA_ID_SEEKID, EBML_UINT, 0, offsetof(MatroskaSeekhead, id) },
{ MATROSKA_ID_SEEKPOSITION, EBML_UINT, 0, offsetof(MatroskaSeekhead, pos), { .u = -1 } },
{ 0 }
};
-static EbmlSyntax matroska_seekhead[] = {
+static const EbmlSyntax matroska_seekhead[] = {
{ MATROSKA_ID_SEEKENTRY, EBML_NEST, sizeof(MatroskaSeekhead), offsetof(MatroskaDemuxContext, seekhead), { .n = matroska_seekhead_entry } },
{ 0 }
};
-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 } },
+static const EbmlSyntax matroska_segment[] = {
+ { 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 }
};
-static EbmlSyntax matroska_segments[] = {
+static const EbmlSyntax matroska_segments[] = {
{ MATROSKA_ID_SEGMENT, EBML_NEST, 0, 0, { .n = matroska_segment } },
{ 0 }
};
-static EbmlSyntax matroska_blockgroup[] = {
+static const EbmlSyntax matroska_blockmore[] = {
+ { MATROSKA_ID_BLOCKADDID, EBML_UINT, 0, offsetof(MatroskaBlock,additional_id) },
+ { MATROSKA_ID_BLOCKADDITIONAL, EBML_BIN, 0, offsetof(MatroskaBlock,additional) },
+ { 0 }
+};
+
+static const EbmlSyntax matroska_blockadditions[] = {
+ { MATROSKA_ID_BLOCKMORE, EBML_NEST, 0, 0, {.n = matroska_blockmore} },
+ { 0 }
+};
+
+static const 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), { .i = INT64_MIN } },
{ MATROSKA_ID_CODECSTATE, EBML_NONE },
{ 1, EBML_UINT, 0, offsetof(MatroskaBlock, non_simple), { .u = 1 } },
{ 0 }
};
-static EbmlSyntax matroska_cluster[] = {
+static const EbmlSyntax matroska_cluster[] = {
{ MATROSKA_ID_CLUSTERTIMECODE, EBML_UINT, 0, offsetof(MatroskaCluster, timecode) },
{ MATROSKA_ID_BLOCKGROUP, EBML_NEST, sizeof(MatroskaBlock), offsetof(MatroskaCluster, blocks), { .n = matroska_blockgroup } },
{ MATROSKA_ID_SIMPLEBLOCK, EBML_PASS, sizeof(MatroskaBlock), offsetof(MatroskaCluster, blocks), { .n = matroska_blockgroup } },
@@ -549,7 +715,7 @@ static EbmlSyntax matroska_cluster[] = {
{ 0 }
};
-static EbmlSyntax matroska_clusters[] = {
+static const EbmlSyntax matroska_clusters[] = {
{ MATROSKA_ID_CLUSTER, EBML_NEST, 0, 0, { .n = matroska_cluster } },
{ MATROSKA_ID_INFO, EBML_NONE },
{ MATROSKA_ID_CUES, EBML_NONE },
@@ -558,7 +724,7 @@ static EbmlSyntax matroska_clusters[] = {
{ 0 }
};
-static EbmlSyntax matroska_cluster_incremental_parsing[] = {
+static const EbmlSyntax matroska_cluster_incremental_parsing[] = {
{ MATROSKA_ID_CLUSTERTIMECODE, EBML_UINT, 0, offsetof(MatroskaCluster, timecode) },
{ MATROSKA_ID_BLOCKGROUP, EBML_NEST, sizeof(MatroskaBlock), offsetof(MatroskaCluster, blocks), { .n = matroska_blockgroup } },
{ MATROSKA_ID_SIMPLEBLOCK, EBML_PASS, sizeof(MatroskaBlock), offsetof(MatroskaCluster, blocks), { .n = matroska_blockgroup } },
@@ -572,7 +738,7 @@ static EbmlSyntax matroska_cluster_incremental_parsing[] = {
{ 0 }
};
-static EbmlSyntax matroska_cluster_incremental[] = {
+static const EbmlSyntax matroska_cluster_incremental[] = {
{ MATROSKA_ID_CLUSTERTIMECODE, EBML_UINT, 0, offsetof(MatroskaCluster, timecode) },
{ MATROSKA_ID_BLOCKGROUP, EBML_STOP },
{ MATROSKA_ID_SIMPLEBLOCK, EBML_STOP },
@@ -581,7 +747,7 @@ static EbmlSyntax matroska_cluster_incremental[] = {
{ 0 }
};
-static EbmlSyntax matroska_clusters_incremental[] = {
+static const EbmlSyntax matroska_clusters_incremental[] = {
{ MATROSKA_ID_CLUSTER, EBML_NEST, 0, 0, { .n = matroska_cluster_incremental } },
{ MATROSKA_ID_INFO, EBML_NONE },
{ MATROSKA_ID_CUES, EBML_NONE },
@@ -592,21 +758,26 @@ static EbmlSyntax matroska_clusters_incremental[] = {
static const char *const matroska_doctypes[] = { "matroska", "webm" };
+static int matroska_read_close(AVFormatContext *s);
+
static int matroska_resync(MatroskaDemuxContext *matroska, int64_t last_pos)
{
AVIOContext *pb = matroska->ctx->pb;
+ int64_t ret;
uint32_t id;
matroska->current_id = 0;
matroska->num_levels = 0;
/* seek to next position to resync from */
- if (avio_seek(pb, last_pos + 1, SEEK_SET) < 0)
- goto eof;
+ if ((ret = avio_seek(pb, last_pos + 1, SEEK_SET)) < 0) {
+ matroska->done = 1;
+ return ret;
+ }
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 ||
@@ -617,7 +788,6 @@ static int matroska_resync(MatroskaDemuxContext *matroska, int64_t last_pos)
id = (id << 8) | avio_r8(pb);
}
-eof:
matroska->done = 1;
return AVERROR_EOF;
}
@@ -637,7 +807,7 @@ static int ebml_level_end(MatroskaDemuxContext *matroska)
return 1;
}
}
- return 0;
+ return (matroska->is_live && matroska->ctx->pb->eof_reached) ? 1 : 0;
}
/*
@@ -659,7 +829,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",
@@ -723,6 +893,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 = ((uint64_t)*num << 8) | avio_r8(pb);
+ }
+
+ return 0;
+}
+
+/*
* Read the next element as a float.
* 0 is success, < 0 is failure.
*/
@@ -769,20 +963,18 @@ 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);
- bin->size = 0;
-
- if (!(bin->data = av_mallocz(length + AV_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);
}
- bin->size = length;
-
return 0;
}
@@ -855,9 +1047,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);
}
@@ -868,8 +1058,11 @@ static int ebml_parse(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
if (!matroska->current_id) {
uint64_t id;
int res = ebml_read_num(matroska, matroska->ctx->pb, 4, &id);
- if (res < 0)
- return res;
+ if (res < 0) {
+ // in live mode, finish parsing if EOF is reached.
+ return (matroska->is_live && matroska->ctx->pb->eof_reached &&
+ res == AVERROR_EOF) ? 1 : res;
+ }
matroska->current_id = id | 1 << 7 * res;
}
return ebml_parse_id(matroska, syntax, matroska->current_id, data);
@@ -882,6 +1075,9 @@ static int ebml_parse_nest(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
for (i = 0; syntax[i].id; i++)
switch (syntax[i].type) {
+ case EBML_SINT:
+ *(int64_t *) ((char *) data + syntax[i].data_offset) = syntax[i].def.i;
+ break;
case EBML_UINT:
*(uint64_t *) ((char *) data + syntax[i].data_offset) = syntax[i].def.u;
break;
@@ -906,6 +1102,54 @@ static int ebml_parse_nest(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
return res;
}
+static int is_ebml_id_valid(uint32_t id)
+{
+ // Due to endian nonsense in Matroska, the highest byte with any bits set
+ // will contain the leading length bit. This bit in turn identifies the
+ // total byte length of the element by its position within the byte.
+ unsigned int bits = av_log2(id);
+ return id && (bits + 7) / 8 == (8 - bits % 8);
+}
+
+/*
+ * 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;
+
+ if (!is_ebml_id_valid(id))
+ return NULL;
+
+ // 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)
{
@@ -923,16 +1167,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++;
@@ -954,6 +1198,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;
@@ -964,17 +1211,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)
@@ -997,6 +1255,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;
@@ -1004,7 +1263,8 @@ 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);
+ list->nb_elem = 0;
} else
ebml_free(syntax[i].def.n, data_off);
default:
@@ -1046,7 +1306,7 @@ static int matroska_probe(AVProbeData *p)
* availability of that array of characters inside the header.
* Not fully fool-proof, but good enough. */
for (i = 0; i < FF_ARRAY_ELEMS(matroska_doctypes); i++) {
- int probelen = strlen(matroska_doctypes[i]);
+ size_t probelen = strlen(matroska_doctypes[i]);
if (total < probelen)
continue;
for (n = 4 + size; n <= 4 + size + total - probelen; n++)
@@ -1084,7 +1344,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) {
@@ -1093,6 +1353,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;
@@ -1137,12 +1402,13 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size,
newpktdata = av_realloc(pkt_data, pkt_size);
if (!newpktdata) {
inflateEnd(&zstream);
+ result = AVERROR(ENOMEM);
goto failed;
}
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);
+ result = inflate(&zstream, Z_NO_FLUSH);
} while (result == Z_OK && pkt_size < 10000000);
pkt_size = zstream.total_out;
inflateEnd(&zstream);
@@ -1169,12 +1435,13 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size,
newpktdata = av_realloc(pkt_data, pkt_size);
if (!newpktdata) {
BZ2_bzDecompressEnd(&bzstream);
+ result = AVERROR(ENOMEM);
goto failed;
}
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);
+ result = BZ2_bzDecompress(&bzstream);
} while (result == BZ_OK && pkt_size < 10000000);
pkt_size = bzstream.total_out_lo32;
BZ2_bzDecompressEnd(&bzstream);
@@ -1201,63 +1468,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 + AV_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_packet_unref(in);
- av_free(in);
- return 0;
-}
-
static void matroska_convert_tag(AVFormatContext *s, EbmlList *list,
AVDictionary **metadata, char *prefix)
{
@@ -1359,24 +1569,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. */
@@ -1413,52 +1616,54 @@ 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 & AVIO_SEEKABLE_NORMAL) ||
- (matroska->ctx->flags & AVFMT_FLAG_IGNIDX))
+ if (!(matroska->ctx->pb->seekable & AVIO_SEEKABLE_NORMAL))
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;
+ uint64_t 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;
- if (index_list->nb_elem &&
- index[0].time > 1E14 / matroska->time_scale) {
- av_log(matroska->ctx, AV_LOG_WARNING, "Working around broken index.\n");
- index_scale = matroska->time_scale;
+ if (index_list->nb_elem < 2)
+ return;
+ if (index[1].time > 1E14 / matroska->time_scale) {
+ av_log(matroska->ctx, AV_LOG_WARNING, "Dropping apparently-broken index.\n");
+ return;
}
for (i = 0; i < index_list->nb_elem; i++) {
EbmlList *pos_list = &index[i].pos;
@@ -1475,6 +1680,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" };
@@ -1496,6 +1720,12 @@ static int matroska_aac_sri(int samplerate)
return sri;
}
+static void matroska_metadata_creation_time(AVDictionary **metadata, int64_t date_utc)
+{
+ /* Convert to seconds and adjust by number of seconds between 2001-01-01 and Epoch */
+ avpriv_dict_set_timestamp(metadata, "creation_time", date_utc / 1000 + 978307200000000LL);
+}
+
static int matroska_parse_flac(AVFormatContext *s,
MatroskaTrack *track,
int *offset)
@@ -1551,8 +1781,15 @@ static int matroska_parse_flac(AVFormatContext *s,
return 0;
}
-static int mkv_field_order(int64_t field_order)
+static int mkv_field_order(MatroskaDemuxContext *matroska, int64_t field_order)
{
+ int major, minor, micro, bttb = 0;
+
+ /* workaround a bug in our Matroska muxer, introduced in version 57.36 alongside
+ * this function, and fixed in 57.52 */
+ if (matroska->muxingapp && sscanf(matroska->muxingapp, "Lavf%d.%d.%d", &major, &minor, &micro) == 3)
+ bttb = (major == 57 && minor >= 36 && minor <= 51 && micro >= 100);
+
switch (field_order) {
case MATROSKA_VIDEO_FIELDORDER_PROGRESSIVE:
return AV_FIELD_PROGRESSIVE;
@@ -1563,9 +1800,9 @@ static int mkv_field_order(int64_t field_order)
case MATROSKA_VIDEO_FIELDORDER_BB:
return AV_FIELD_BB;
case MATROSKA_VIDEO_FIELDORDER_BT:
- return AV_FIELD_BT;
+ return bttb ? AV_FIELD_TB : AV_FIELD_BT;
case MATROSKA_VIDEO_FIELDORDER_TB:
- return AV_FIELD_TB;
+ return bttb ? AV_FIELD_BT : AV_FIELD_TB;
default:
return AV_FIELD_UNKNOWN;
}
@@ -1575,34 +1812,127 @@ static void mkv_stereo_mode_display_mul(int stereo_mode,
int *h_width, int *h_height)
{
switch (stereo_mode) {
- case MATROSKA_VIDEO_STEREOMODE_TYPE_MONO:
- case MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_RL:
- case MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_LR:
- case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_RL:
- case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_LR:
- break;
- case MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT:
- case MATROSKA_VIDEO_STEREOMODE_TYPE_LEFT_RIGHT:
- case MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_RL:
- case MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_LR:
- *h_width = 2;
- break;
- case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTTOM_TOP:
- case MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM:
- case MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_RL:
- case MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_LR:
- *h_height = 2;
- break;
+ case MATROSKA_VIDEO_STEREOMODE_TYPE_MONO:
+ case MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_RL:
+ case MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_LR:
+ case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_RL:
+ case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_LR:
+ break;
+ case MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT:
+ case MATROSKA_VIDEO_STEREOMODE_TYPE_LEFT_RIGHT:
+ case MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_RL:
+ case MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_LR:
+ *h_width = 2;
+ break;
+ case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTTOM_TOP:
+ case MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM:
+ case MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_RL:
+ case MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_LR:
+ *h_height = 2;
+ break;
}
}
-static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track)
-{
+static int mkv_parse_video_color(AVStream *st, const MatroskaTrack *track) {
+ const MatroskaTrackVideoColor *color = track->video.color.elem;
+ const MatroskaMasteringMeta *mastering_meta;
+ int has_mastering_primaries, has_mastering_luminance;
+
+ if (!track->video.color.nb_elem)
+ return 0;
+
+ mastering_meta = &color->mastering_meta;
+ // Mastering primaries are CIE 1931 coords, and must be > 0.
+ has_mastering_primaries =
+ mastering_meta->r_x > 0 && mastering_meta->r_y > 0 &&
+ mastering_meta->g_x > 0 && mastering_meta->g_y > 0 &&
+ mastering_meta->b_x > 0 && mastering_meta->b_y > 0 &&
+ mastering_meta->white_x > 0 && mastering_meta->white_y > 0;
+ has_mastering_luminance = mastering_meta->max_luminance > 0;
+
+ if (color->matrix_coefficients != AVCOL_SPC_RESERVED)
+ st->codecpar->color_space = color->matrix_coefficients;
+ if (color->primaries != AVCOL_PRI_RESERVED &&
+ color->primaries != AVCOL_PRI_RESERVED0)
+ st->codecpar->color_primaries = color->primaries;
+ if (color->transfer_characteristics != AVCOL_TRC_RESERVED &&
+ color->transfer_characteristics != AVCOL_TRC_RESERVED0)
+ st->codecpar->color_trc = color->transfer_characteristics;
+ if (color->range != AVCOL_RANGE_UNSPECIFIED &&
+ color->range <= AVCOL_RANGE_JPEG)
+ st->codecpar->color_range = color->range;
+ if (color->chroma_siting_horz != MATROSKA_COLOUR_CHROMASITINGHORZ_UNDETERMINED &&
+ color->chroma_siting_vert != MATROSKA_COLOUR_CHROMASITINGVERT_UNDETERMINED &&
+ color->chroma_siting_horz < MATROSKA_COLOUR_CHROMASITINGHORZ_NB &&
+ color->chroma_siting_vert < MATROSKA_COLOUR_CHROMASITINGVERT_NB) {
+ st->codecpar->chroma_location =
+ avcodec_chroma_pos_to_enum((color->chroma_siting_horz - 1) << 7,
+ (color->chroma_siting_vert - 1) << 7);
+ }
+ if (color->max_cll && color->max_fall) {
+ size_t size = 0;
+ int ret;
+ AVContentLightMetadata *metadata = av_content_light_metadata_alloc(&size);
+ if (!metadata)
+ return AVERROR(ENOMEM);
+ ret = av_stream_add_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
+ (uint8_t *)metadata, size);
+ if (ret < 0) {
+ av_freep(&metadata);
+ return ret;
+ }
+ metadata->MaxCLL = color->max_cll;
+ metadata->MaxFALL = color->max_fall;
+ }
+
+ if (has_mastering_primaries || has_mastering_luminance) {
+ // Use similar rationals as other standards.
+ const int chroma_den = 50000;
+ const int luma_den = 10000;
+ AVMasteringDisplayMetadata *metadata =
+ (AVMasteringDisplayMetadata*) av_stream_new_side_data(
+ st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
+ sizeof(AVMasteringDisplayMetadata));
+ if (!metadata) {
+ return AVERROR(ENOMEM);
+ }
+ memset(metadata, 0, sizeof(AVMasteringDisplayMetadata));
+ if (has_mastering_primaries) {
+ metadata->display_primaries[0][0] = av_make_q(
+ round(mastering_meta->r_x * chroma_den), chroma_den);
+ metadata->display_primaries[0][1] = av_make_q(
+ round(mastering_meta->r_y * chroma_den), chroma_den);
+ metadata->display_primaries[1][0] = av_make_q(
+ round(mastering_meta->g_x * chroma_den), chroma_den);
+ metadata->display_primaries[1][1] = av_make_q(
+ round(mastering_meta->g_y * chroma_den), chroma_den);
+ metadata->display_primaries[2][0] = av_make_q(
+ round(mastering_meta->b_x * chroma_den), chroma_den);
+ metadata->display_primaries[2][1] = av_make_q(
+ round(mastering_meta->b_y * chroma_den), chroma_den);
+ metadata->white_point[0] = av_make_q(
+ round(mastering_meta->white_x * chroma_den), chroma_den);
+ metadata->white_point[1] = av_make_q(
+ round(mastering_meta->white_y * chroma_den), chroma_den);
+ metadata->has_primaries = 1;
+ }
+ if (has_mastering_luminance) {
+ metadata->max_luminance = av_make_q(
+ round(mastering_meta->max_luminance * luma_den), luma_den);
+ metadata->min_luminance = av_make_q(
+ round(mastering_meta->min_luminance * luma_den), luma_den);
+ metadata->has_luminance = 1;
+ }
+ }
+ return 0;
+}
+
+static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track) {
AVSphericalMapping *spherical;
enum AVSphericalProjection projection;
size_t spherical_size;
- size_t l = 0, t = 0, r = 0, b = 0;
- size_t padding = 0;
+ uint32_t l = 0, t = 0, r = 0, b = 0;
+ uint32_t padding = 0;
int ret;
GetByteContext gb;
@@ -1627,7 +1957,8 @@ static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track)
if (b >= UINT_MAX - t || r >= UINT_MAX - l) {
av_log(NULL, AV_LOG_ERROR,
"Invalid bounding rectangle coordinates "
- "%zu,%zu,%zu,%zu\n", l, t, r, b);
+ "%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"\n",
+ l, t, r, b);
return AVERROR_INVALIDDATA;
}
} else if (track->video.projection.private.size != 0) {
@@ -1659,9 +1990,6 @@ static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track)
}
break;
default:
- av_log(NULL, AV_LOG_WARNING,
- "Unknown spherical metadata type %"PRIu64"\n",
- track->video.projection.type);
return 0;
}
@@ -1685,19 +2013,47 @@ static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track)
ret = av_stream_add_side_data(st, AV_PKT_DATA_SPHERICAL, (uint8_t *)spherical,
spherical_size);
if (ret < 0) {
- av_free(spherical);
+ av_freep(&spherical);
return ret;
}
return 0;
}
+static int get_qt_codec(MatroskaTrack *track, uint32_t *fourcc, enum AVCodecID *codec_id)
+{
+ const AVCodecTag *codec_tags;
+
+ codec_tags = track->type == MATROSKA_TRACK_TYPE_VIDEO ?
+ ff_codec_movvideo_tags : ff_codec_movaudio_tags;
+
+ /* Normalize noncompliant private data that starts with the fourcc
+ * by expanding/shifting the data by 4 bytes and storing the data
+ * size at the start. */
+ if (ff_codec_get_id(codec_tags, AV_RL32(track->codec_priv.data))) {
+ uint8_t *p = av_realloc(track->codec_priv.data,
+ track->codec_priv.size + 4);
+ if (!p)
+ return AVERROR(ENOMEM);
+ memmove(p + 4, p, track->codec_priv.size);
+ track->codec_priv.data = p;
+ track->codec_priv.size += 4;
+ AV_WB32(track->codec_priv.data, track->codec_priv.size);
+ }
+
+ *fourcc = AV_RL32(track->codec_priv.data + 4);
+ *codec_id = ff_codec_get_id(codec_tags, *fourcc);
+
+ return 0;
+}
+
static int matroska_parse_tracks(AVFormatContext *s)
{
MatroskaDemuxContext *matroska = s->priv_data;
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];
@@ -1707,12 +2063,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);
@@ -1721,13 +2081,23 @@ static int matroska_parse_tracks(AVFormatContext *s)
if (!track->codec_id)
continue;
+ if (track->audio.samplerate < 0 || track->audio.samplerate > INT_MAX ||
+ isnan(track->audio.samplerate)) {
+ av_log(matroska->ctx, AV_LOG_WARNING,
+ "Invalid sample rate %f, defaulting to 8000 instead.\n",
+ track->audio.samplerate);
+ track->audio.samplerate = 8000;
+ }
+
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;
@@ -1736,8 +2106,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
@@ -1747,7 +2133,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");
@@ -1777,16 +2163,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 &&
@@ -1795,29 +2193,56 @@ 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(s, &b, st->codecpar, track->codec_priv.size);
+ ret = ff_get_wav_header(s, &b, st->codecpar, track->codec_priv.size, 0);
if (ret < 0)
return ret;
codec_id = st->codecpar->codec_id;
+ fourcc = st->codecpar->codec_tag;
extradata_offset = FFMIN(track->codec_priv.size, 18);
+ } else if (!strcmp(track->codec_id, "A_QUICKTIME")
+ /* Normally 36, but allow noncompliant private data */
+ && (track->codec_priv.size >= 32)
+ && (track->codec_priv.data)) {
+ uint16_t sample_size;
+ int ret = get_qt_codec(track, &fourcc, &codec_id);
+ if (ret < 0)
+ return ret;
+ sample_size = AV_RB16(track->codec_priv.data + 26);
+ if (fourcc == 0) {
+ if (sample_size == 8) {
+ fourcc = MKTAG('r','a','w',' ');
+ codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc);
+ } else if (sample_size == 16) {
+ fourcc = MKTAG('t','w','o','s');
+ codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc);
+ }
+ }
+ if ((fourcc == MKTAG('t','w','o','s') ||
+ fourcc == MKTAG('s','o','w','t')) &&
+ sample_size == 8)
+ codec_id = AV_CODEC_ID_PCM_S8;
} else if (!strcmp(track->codec_id, "V_QUICKTIME") &&
- (track->codec_priv.size >= 86) &&
+ (track->codec_priv.size >= 21) &&
(track->codec_priv.data)) {
- if (track->codec_priv.size == AV_RB32(track->codec_priv.data)) {
- track->video.fourcc = AV_RL32(track->codec_priv.data + 4);
- codec_id = ff_codec_get_id(ff_codec_movvideo_tags,
- track->video.fourcc);
- }
- if (codec_id == AV_CODEC_ID_NONE) {
- track->video.fourcc = AV_RL32(track->codec_priv.data);
- codec_id = ff_codec_get_id(ff_codec_movvideo_tags,
- track->video.fourcc);
+ int ret = get_qt_codec(track, &fourcc, &codec_id);
+ if (ret < 0)
+ return ret;
+ if (codec_id == AV_CODEC_ID_NONE && AV_RL32(track->codec_priv.data+4) == AV_RL32("SMI ")) {
+ fourcc = MKTAG('S','V','Q','3');
+ codec_id = ff_codec_get_id(ff_codec_movvideo_tags, fourcc);
}
- if (codec_id == AV_CODEC_ID_NONE) {
- char buf[32];
- av_get_codec_tag_string(buf, sizeof(buf), track->video.fourcc);
+ if (codec_id == AV_CODEC_ID_NONE)
av_log(matroska->ctx, AV_LOG_ERROR,
- "mov FourCC not found %s.\n", buf);
+ "mov FourCC not found %s.\n", av_fourcc2str(fourcc));
+ if (track->codec_priv.size >= 86) {
+ bit_depth = AV_RB16(track->codec_priv.data + 82);
+ ffio_init_context(&b, track->codec_priv.data,
+ track->codec_priv.size,
+ 0, NULL, NULL, NULL, NULL);
+ if (ff_get_qtpalette(codec_id, &b, track->palette)) {
+ bit_depth &= 0x1F;
+ track->has_palette = 1;
+ }
}
} else if (codec_id == AV_CODEC_ID_PCM_S16BE) {
switch (track->audio.bitdepth) {
@@ -1862,7 +2287,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 - AV_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. */
@@ -1878,18 +2303,33 @@ 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 + AV_INPUT_BUFFER_PADDING_SIZE);
if (!extradata)
return AVERROR(ENOMEM);
ffio_init_context(&b, extradata, extradata_size, 1,
NULL, NULL, NULL, NULL);
avio_write(&b, "TTA1", 4);
avio_wl16(&b, 1);
+ if (track->audio.channels > UINT16_MAX ||
+ track->audio.bitdepth > UINT16_MAX) {
+ av_log(matroska->ctx, AV_LOG_WARNING,
+ "Too large audio channel number %"PRIu64
+ " or bitdepth %"PRIu64". Skipping track.\n",
+ track->audio.channels, track->audio.bitdepth);
+ av_freep(&extradata);
+ if (matroska->ctx->error_recognition & AV_EF_EXPLODE)
+ return AVERROR_INVALIDDATA;
+ else
+ continue;
+ }
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 ||
@@ -1898,11 +2338,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);
@@ -1913,14 +2355,14 @@ 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)
+ track->audio.sub_packet_size <= 0 && codec_id != AV_CODEC_ID_SIPR)
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) {
@@ -1939,6 +2381,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;
@@ -1951,20 +2395,13 @@ static int matroska_parse_tracks(AVFormatContext *s)
avpriv_set_pts_info(st, 64, matroska->time_scale * track->time_scale,
1000 * 1000 * 1000); /* 64 bit pts in ns */
- if (track->type == MATROSKA_TRACK_TYPE_AUDIO &&
- track->audio.out_samplerate) {
- st->codecpar->initial_padding = av_rescale_q(track->codec_delay,
- (AVRational){ 1, 1000000000 },
- (AVRational){ 1, track->audio.out_samplerate });
- }
-
/* convert the delay from ns to the track timebase */
- track->codec_delay = av_rescale_q(track->codec_delay,
+ track->codec_delay_in_track_tb = av_rescale_q(track->codec_delay,
(AVRational){ 1, 1000000000 },
st->time_base);
st->codecpar->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);
@@ -1979,11 +2416,8 @@ static int matroska_parse_tracks(AVFormatContext *s)
st->codecpar->extradata = extradata;
st->codecpar->extradata_size = extradata_size;
} else if (track->codec_priv.data && track->codec_priv.size > 0) {
- st->codecpar->extradata = av_mallocz(track->codec_priv.size +
- AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
+ if (ff_alloc_extradata(st->codecpar, track->codec_priv.size))
return AVERROR(ENOMEM);
- st->codecpar->extradata_size = track->codec_priv.size;
memcpy(st->codecpar->extradata,
track->codec_priv.data + extradata_offset,
track->codec_priv.size);
@@ -1991,31 +2425,66 @@ static int matroska_parse_tracks(AVFormatContext *s)
}
if (track->type == MATROSKA_TRACK_TYPE_VIDEO) {
+ MatroskaTrackPlane *planes = track->operation.combine_planes.elem;
int display_width_mul = 1;
int display_height_mul = 1;
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
- st->codecpar->codec_tag = track->video.fourcc;
+ st->codecpar->codec_tag = fourcc;
+ if (bit_depth >= 0)
+ st->codecpar->bits_per_coded_sample = bit_depth;
st->codecpar->width = track->video.pixel_width;
st->codecpar->height = track->video.pixel_height;
if (track->video.interlaced == MATROSKA_VIDEO_INTERLACE_FLAG_INTERLACED)
- st->codecpar->field_order = mkv_field_order(track->video.field_order);
+ st->codecpar->field_order = mkv_field_order(matroska, track->video.field_order);
+ else if (track->video.interlaced == MATROSKA_VIDEO_INTERLACE_FLAG_PROGRESSIVE)
+ st->codecpar->field_order = AV_FIELD_PROGRESSIVE;
if (track->video.stereo_mode && track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB)
mkv_stereo_mode_display_mul(track->video.stereo_mode, &display_width_mul, &display_height_mul);
- av_reduce(&st->sample_aspect_ratio.num,
- &st->sample_aspect_ratio.den,
- st->codecpar->height * track->video.display_width * display_width_mul,
- st->codecpar->width * track->video.display_height * display_height_mul,
- 255);
- if (st->codecpar->codec_id != AV_CODEC_ID_H264 &&
- st->codecpar->codec_id != AV_CODEC_ID_HEVC)
+ if (track->video.display_unit < MATROSKA_VIDEO_DISPLAYUNIT_UNKNOWN) {
+ av_reduce(&st->sample_aspect_ratio.num,
+ &st->sample_aspect_ratio.den,
+ st->codecpar->height * track->video.display_width * display_width_mul,
+ st->codecpar->width * track->video.display_height * display_height_mul,
+ 255);
+ }
+ if (st->codecpar->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 && tracks[k].stream) {
+ av_dict_set(&tracks[k].stream->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 &&
@@ -2025,20 +2494,47 @@ static int matroska_parse_tracks(AVFormatContext *s)
return ret;
}
+ ret = mkv_parse_video_color(st, track);
+ if (ret < 0)
+ return ret;
ret = mkv_parse_video_projection(st, track);
if (ret < 0)
return ret;
} else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) {
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_tag = fourcc;
st->codecpar->sample_rate = track->audio.out_samplerate;
st->codecpar->channels = track->audio.channels;
- if (st->codecpar->codec_id != AV_CODEC_ID_AAC)
- st->need_parsing = AVSTREAM_PARSE_HEADERS;
+ if (!st->codecpar->bits_per_coded_sample)
+ st->codecpar->bits_per_coded_sample = track->audio.bitdepth;
if (st->codecpar->codec_id == AV_CODEC_ID_MP3)
st->need_parsing = AVSTREAM_PARSE_FULL;
+ else if (st->codecpar->codec_id != AV_CODEC_ID_AAC)
+ st->need_parsing = AVSTREAM_PARSE_HEADERS;
+ if (track->codec_delay > 0) {
+ st->codecpar->initial_padding = av_rescale_q(track->codec_delay,
+ (AVRational){1, 1000000000},
+ (AVRational){1, st->codecpar->codec_id == AV_CODEC_ID_OPUS ?
+ 48000 : st->codecpar->sample_rate});
+ }
+ if (track->seek_preroll > 0) {
+ st->codecpar->seek_preroll = av_rescale_q(track->seek_preroll,
+ (AVRational){1, 1000000000},
+ (AVRational){1, st->codecpar->sample_rate});
+ }
+ } else if (codec_id == AV_CODEC_ID_WEBVTT) {
+ st->codecpar->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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
- if (st->codecpar->codec_id == AV_CODEC_ID_SSA)
+ if (st->codecpar->codec_id == AV_CODEC_ID_ASS)
matroska->contains_ssa = 1;
}
}
@@ -2059,6 +2555,7 @@ 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.doctype) {
@@ -2075,6 +2572,11 @@ static int matroska_read_header(AVFormatContext *s)
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]))
@@ -2095,7 +2597,7 @@ static int matroska_read_header(AVFormatContext *s)
while (res != 1) {
res = matroska_resync(matroska, pos);
if (res < 0)
- return res;
+ goto fail;
pos = avio_tell(matroska->ctx->pb);
res = ebml_parse(matroska, matroska_segment, matroska);
}
@@ -2107,10 +2609,14 @@ 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)
- return res;
+ goto fail;
attachments = attachments_list->elem;
for (j = 0; j < attachments_list->nb_elem; j++) {
@@ -2147,11 +2653,8 @@ static int matroska_read_header(AVFormatContext *s)
st->attached_pic.flags |= AV_PKT_FLAG_KEY;
} else {
st->codecpar->codec_type = AVMEDIA_TYPE_ATTACHMENT;
- st->codecpar->extradata = av_malloc(attachments[j].bin.size);
- if (!st->codecpar->extradata)
+ if (ff_alloc_extradata(st->codecpar, attachments[j].bin.size))
break;
-
- st->codecpar->extradata_size = attachments[j].bin.size;
memcpy(st->codecpar->extradata, attachments[j].bin.data,
attachments[j].bin.size);
@@ -2175,14 +2678,21 @@ 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;
+fail:
+ matroska_read_close(s);
+ return res;
}
/*
@@ -2193,8 +2703,20 @@ static int matroska_deliver_packet(MatroskaDemuxContext *matroska,
AVPacket *pkt)
{
if (matroska->num_packets > 0) {
+ MatroskaTrack *tracks = matroska->tracks.elem;
+ MatroskaTrack *track;
memcpy(pkt, matroska->packets[0], sizeof(AVPacket));
- av_free(matroska->packets[0]);
+ av_freep(&matroska->packets[0]);
+ track = &tracks[pkt->stream_index];
+ if (track->has_palette) {
+ uint8_t *pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
+ if (!pal) {
+ av_log(matroska->ctx, AV_LOG_ERROR, "Cannot append palette to packet\n");
+ } else {
+ memcpy(pal, track->palette, AVPALETTE_SIZE);
+ }
+ track->has_palette = 0;
+ }
if (matroska->num_packets > 1) {
void *newpackets;
memmove(&matroska->packets[0], &matroska->packets[1],
@@ -2225,7 +2747,7 @@ static void matroska_clear_queue(MatroskaDemuxContext *matroska)
int n;
for (n = 0; n < matroska->num_packets; n++) {
av_packet_unref(matroska->packets[n]);
- av_free(matroska->packets[n]);
+ av_freep(&matroska->packets[n]);
}
av_freep(&matroska->packets);
matroska->num_packets = 0;
@@ -2250,7 +2772,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;
@@ -2265,18 +2787,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;
@@ -2301,10 +2823,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;
@@ -2314,10 +2836,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;
@@ -2344,7 +2866,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->codecpar->block_align;
int sps = track->audio.sub_packet_size;
@@ -2374,7 +2896,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;
@@ -2498,18 +3020,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;
@@ -2528,22 +3168,24 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska,
pkt_data = wv_data;
}
- if (st->codecpar->codec_id == AV_CODEC_ID_PRORES)
+ if (st->codecpar->codec_id == AV_CODEC_ID_PRORES &&
+ AV_RB32(&data[4]) != MKBETAG('i', 'c', 'p', 'f'))
offset = 8;
pkt = av_mallocz(sizeof(AVPacket));
if (!pkt) {
- av_freep(&pkt_data);
+ if (pkt_data != data)
+ av_freep(&pkt_data);
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->codecpar->codec_id == AV_CODEC_ID_PRORES) {
+ if (st->codecpar->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'));
@@ -2552,38 +3194,60 @@ 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_packet_unref(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_packet_unref(pkt);
+ av_free(pkt);
+ return AVERROR(ENOMEM);
+ }
+ discard_padding = av_rescale_q(discard_padding,
+ (AVRational){1, 1000000000},
+ (AVRational){1, st->codecpar->sample_rate});
+ if (discard_padding > 0) {
+ AV_WL32(side_data + 4, discard_padding);
+ } else {
+ AV_WL32(side_data, -discard_padding);
+ }
+ }
+
if (track->ms_compat)
pkt->dts = timecode;
else
pkt->pts = timecode;
pkt->pos = pos;
- if (track->type != MATROSKA_TRACK_TYPE_SUBTITLE || st->codecpar->codec_id == AV_CODEC_ID_SRT)
- pkt->duration = duration;
+ pkt->duration = lace_duration;
+
#if FF_API_CONVERGENCE_DURATION
FF_DISABLE_DEPRECATION_WARNINGS
- if (st->codecpar->codec_id == AV_CODEC_ID_SRT)
- pkt->convergence_duration = duration;
+ if (st->codecpar->codec_id == AV_CODEC_ID_SUBRIP) {
+ pkt->convergence_duration = lace_duration;
+ }
FF_ENABLE_DEPRECATION_WARNINGS
#endif
- if (st->codecpar->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->codecpar->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;
- }
+ dynarray_add(&matroska->packets, &matroska->num_packets, pkt);
+ matroska->prev_pkt = pkt;
return 0;
@@ -2596,7 +3260,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;
@@ -2605,7 +3270,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");
@@ -2624,8 +3290,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;
@@ -2634,20 +3301,30 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data,
if (cluster_time != (uint64_t) -1 &&
(block_time >= 0 || cluster_time >= -block_time)) {
- timecode = cluster_time + block_time - track->codec_delay;
+ timecode = cluster_time + block_time - track->codec_delay_in_track_tb;
if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE &&
timecode < track->end_timecode)
is_keyframe = 0; /* overlapping subtitles are not key frame */
- if (is_keyframe)
+ if (is_keyframe) {
+ ff_reduce_index(matroska->ctx, st->index);
av_add_index_entry(st, cluster_pos, timecode, 0, 0,
AVINDEX_KEYFRAME);
+ }
}
if (matroska->skip_to_keyframe &&
track->type != MATROSKA_TRACK_TYPE_SUBTITLE) {
- if (!is_keyframe || timecode < matroska->skip_to_timecode)
+ // Compare signed timecodes. Timecode may be negative due to codec delay
+ // offset. We don't support timestamps greater than int64_t anyway - see
+ // AVPacket's pts.
+ if ((int64_t)timecode < (int64_t)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,
@@ -2656,22 +3333,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->codecpar->codec_id == AV_CODEC_ID_AC3) {
+ if (track->audio.samplerate != st->codecpar->sample_rate || !st->codecpar->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->codecpar->codec_id == AV_CODEC_ID_RA_288 ||
st->codecpar->codec_id == AV_CODEC_ID_COOK ||
st->codecpar->codec_id == AV_CODEC_ID_SIPR ||
@@ -2679,20 +3363,31 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data,
st->codecpar->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->codecpar->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:
@@ -2739,19 +3434,22 @@ static int matroska_parse_cluster_incremental(MatroskaDemuxContext *matroska)
matroska->current_cluster_num_blocks = blocks_list->nb_elem;
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;
+ int is_keyframe = blocks[i].non_simple ? blocks[i].reference == INT64_MIN : -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;
}
@@ -2772,15 +3470,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;
+ int is_keyframe = blocks[i].non_simple ? blocks[i].reference == INT64_MIN : -1;
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;
@@ -2791,19 +3488,14 @@ 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;
+ return (ret < 0) ? ret : AVERROR_EOF;
if (matroska_parse_cluster(matroska) < 0)
ret = matroska_resync(matroska, pos);
}
- if (ret == AVERROR_INVALIDDATA && pkt->data) {
- pkt->flags |= AV_PKT_FLAG_CORRUPT;
- return 0;
- }
-
return ret;
}
@@ -2813,23 +3505,23 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index,
MatroskaDemuxContext *matroska = s->priv_data;
MatroskaTrack *tracks = NULL;
AVStream *st = s->streams[stream_index];
- int i, index, index_sub, index_min;
+ int i, index, 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;
@@ -2837,8 +3529,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;
@@ -2847,26 +3539,32 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index,
tracks[i].audio.sub_packet_cnt = 0;
tracks[i].audio.buf_timecode = AV_NOPTS_VALUE;
tracks[i].end_timecode = 0;
- if (tracks[i].type == MATROSKA_TRACK_TYPE_SUBTITLE &&
- tracks[i].stream->discard != AVDISCARD_ALL) {
- 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;
- }
}
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)
@@ -2879,13 +3577,411 @@ 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 to
+ * 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, int64_t init_range)
+{
+ 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;
+ int end = 0;
+
+ // 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);
+
+ // if the file has cues at the start, fix up the init range so tht
+ // it does not include it
+ if (cues_start <= init_range)
+ av_dict_set_int(&s->streams[0]->metadata, INITIALIZATION_RANGE, cues_start - 1, 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++) {
+ int ret = snprintf(buf + end, 20 * sizeof(char),
+ "%" PRId64, s->streams[0]->index_entries[i].timestamp);
+ if (ret <= 0 || (ret == 20 && i == s->streams[0]->nb_index_entries - 1)) {
+ av_log(s, AV_LOG_ERROR, "timestamp too long.\n");
+ av_free(buf);
+ return AVERROR_INVALIDDATA;
+ }
+ end += ret;
+ if (i != s->streams[0]->nb_index_entries - 1) {
+ strncat(buf, ",", sizeof(char));
+ end++;
+ }
+ }
+ 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);
+ int64_t init_range;
+ MatroskaTrack *tracks;
+ MatroskaDemuxContext *matroska = s->priv_data;
+ if (ret) {
+ av_log(s, AV_LOG_ERROR, "Failed to read file headers\n");
+ return -1;
+ }
+ if (!s->nb_streams) {
+ matroska_read_close(s);
+ av_log(s, AV_LOG_ERROR, "No streams found\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (!matroska->is_live) {
+ buf = av_asprintf("%g", matroska->duration);
+ if (!buf) return AVERROR(ENOMEM);
+ av_dict_set(&s->streams[0]->metadata, DURATION, buf, 0);
+ av_free(buf);
+
+ // initialization range
+ // 5 is the offset of Cluster ID.
+ init_range = avio_tell(s->pb) - 5;
+ av_dict_set_int(&s->streams[0]->metadata, INITIALIZATION_RANGE, init_range, 0);
+ }
+
+ // basename of the file
+ buf = strrchr(s->filename, '/');
+ av_dict_set(&s->streams[0]->metadata, FILENAME, buf ? ++buf : s->filename, 0);
+
+ // 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
+ if (!matroska->is_live) {
+ ret = webm_dash_manifest_cues(s, init_range);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "Error parsing Cues\n");
+ return ret;
+ }
+ }
+
+ // use the bandwidth from the command line if it was provided
+ if (matroska->bandwidth > 0) {
+ av_dict_set_int(&s->streams[0]->metadata, BANDWIDTH,
+ matroska->bandwidth, 0);
+ }
+ return 0;
+}
+
+static int webm_dash_manifest_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ return AVERROR_EOF;
+}
+
+#define OFFSET(x) offsetof(MatroskaDemuxContext, x)
+static const AVOption options[] = {
+ { "live", "flag indicating that the input is a live file that only has the headers.", OFFSET(is_live), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
+ { "bandwidth", "bandwidth of this stream to be specified in the DASH manifest.", OFFSET(bandwidth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+static const AVClass webm_dash_class = {
+ .class_name = "WebM DASH Manifest demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVInputFormat ff_matroska_demuxer = {
.name = "matroska,webm",
.long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"),
@@ -2898,3 +3994,13 @@ 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,
+ .priv_class = &webm_dash_class,
+};
diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 34d9833..dad6d6c 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
*/
@@ -31,24 +31,30 @@
#include "isom.h"
#include "matroska.h"
#include "riff.h"
+#include "subtitles.h"
#include "vorbiscomment.h"
#include "wv.h"
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
+#include "libavutil/crc.h"
#include "libavutil/dict.h"
#include "libavutil/intfloat.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/lfg.h"
+#include "libavutil/mastering_display_metadata.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
#include "libavutil/random_seed.h"
+#include "libavutil/rational.h"
#include "libavutil/samplefmt.h"
+#include "libavutil/sha.h"
#include "libavutil/stereo3d.h"
-#include "libavutil/spherical.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
@@ -71,8 +77,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 {
@@ -83,19 +92,40 @@ typedef struct mkv_cues {
typedef struct mkv_track {
int write_dts;
+ int has_cue;
int sample_rate;
int64_t sample_rate_offset;
int64_t codecpriv_offset;
int64_t ts_offset;
} mkv_track;
+typedef struct mkv_attachment {
+ int stream_idx;
+ uint32_t fileuid;
+} mkv_attachment;
+
+typedef struct mkv_attachments {
+ mkv_attachment *entries;
+ int num_entries;
+} mkv_attachments;
+
#define MODE_MATROSKAv2 0x01
#define MODE_WEBM 0x02
+/** Maximum number of tracks allowed in a Matroska file (with track numbers in
+ * range 1 to 126 (inclusive) */
+#define MAX_TRACKS 126
+
typedef struct MatroskaMuxContext {
const AVClass *class;
int mode;
AVIOContext *dyn_bc;
+ AVIOContext *tags_bc;
+ ebml_master tags;
+ AVIOContext *info_bc;
+ ebml_master info;
+ AVIOContext *tracks_bc;
+ ebml_master tracks_master;
ebml_master segment;
int64_t segment_offset;
ebml_master cluster;
@@ -106,6 +136,7 @@ typedef struct MatroskaMuxContext {
mkv_seekhead *main_seekhead;
mkv_cues *cues;
mkv_track *tracks;
+ mkv_attachments *attachments;
AVPacket cur_audio_pkt;
@@ -116,7 +147,20 @@ typedef struct MatroskaMuxContext {
int cluster_size_limit;
int64_t cues_pos;
int64_t cluster_time_limit;
+ int is_dash;
+ int dash_track_number;
+ int is_live;
+ int write_crc;
+
+ uint32_t chapter_id_offset;
int wrote_chapters;
+
+ int64_t last_track_timestamp[MAX_TRACKS];
+
+ int64_t* stream_durations;
+ int64_t* stream_duration_offsets;
+
+ int allow_raw_vfw;
} MatroskaMuxContext;
@@ -124,13 +168,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;
@@ -140,7 +187,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)));
}
/**
@@ -150,10 +197,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);
}
/**
@@ -178,18 +224,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)
@@ -202,7 +248,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)
@@ -236,18 +295,17 @@ 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
// size we need to reserve so 2 cases, we use 8 bytes to store the
// size if possible, 1 byte otherwise
if (size < 10)
- put_ebml_num(pb, size - 1, 0);
+ put_ebml_num(pb, size - 2, 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,
@@ -269,15 +327,109 @@ static void end_ebml_master(AVIOContext *pb, ebml_master master)
avio_seek(pb, pos, SEEK_SET);
}
+static int start_ebml_master_crc32(AVIOContext *pb, AVIOContext **dyn_cp, MatroskaMuxContext *mkv,
+ ebml_master *master, unsigned int elementid, uint64_t expectedsize)
+{
+ int ret;
+
+ if ((ret = avio_open_dyn_buf(dyn_cp)) < 0)
+ return ret;
+
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ *master = start_ebml_master(pb, elementid, expectedsize);
+ if (mkv->write_crc && mkv->mode != MODE_WEBM)
+ put_ebml_void(*dyn_cp, 6); /* Reserve space for CRC32 so position/size calculations using avio_tell() take it into account */
+ } else
+ *master = start_ebml_master(*dyn_cp, elementid, expectedsize);
+
+ return 0;
+}
+
+static void end_ebml_master_crc32(AVIOContext *pb, AVIOContext **dyn_cp, MatroskaMuxContext *mkv,
+ ebml_master master)
+{
+ uint8_t *buf, crc[4];
+ int size, skip = 0;
+
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ size = avio_close_dyn_buf(*dyn_cp, &buf);
+ if (mkv->write_crc && mkv->mode != MODE_WEBM) {
+ skip = 6; /* Skip reserved 6-byte long void element from the dynamic buffer. */
+ AV_WL32(crc, av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE), UINT32_MAX, buf + skip, size - skip) ^ UINT32_MAX);
+ put_ebml_binary(pb, EBML_ID_CRC32, crc, sizeof(crc));
+ }
+ avio_write(pb, buf + skip, size - skip);
+ end_ebml_master(pb, master);
+ } else {
+ end_ebml_master(*dyn_cp, master);
+ size = avio_close_dyn_buf(*dyn_cp, &buf);
+ avio_write(pb, buf, size);
+ }
+ av_free(buf);
+ *dyn_cp = NULL;
+}
+
+/**
+* Complete ebml master whithout destroying the buffer, allowing for later updates
+*/
+static void end_ebml_master_crc32_preliminary(AVIOContext *pb, AVIOContext **dyn_cp, MatroskaMuxContext *mkv,
+ ebml_master master)
+{
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
+
+ uint8_t *buf;
+ int size = avio_get_dyn_buf(*dyn_cp, &buf);
+
+ avio_write(pb, buf, size);
+ end_ebml_master(pb, 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);
}
/**
+ * Free the members allocated in the mux context.
+ */
+static void mkv_free(MatroskaMuxContext *mkv) {
+ uint8_t* buf;
+ if (mkv->dyn_bc) {
+ avio_close_dyn_buf(mkv->dyn_bc, &buf);
+ av_free(buf);
+ }
+ if (mkv->info_bc) {
+ avio_close_dyn_buf(mkv->info_bc, &buf);
+ av_free(buf);
+ }
+ if (mkv->tracks_bc) {
+ avio_close_dyn_buf(mkv->tracks_bc, &buf);
+ av_free(buf);
+ }
+ if (mkv->tags_bc) {
+ avio_close_dyn_buf(mkv->tags_bc, &buf);
+ av_free(buf);
+ }
+ if (mkv->main_seekhead) {
+ av_freep(&mkv->main_seekhead->entries);
+ av_freep(&mkv->main_seekhead);
+ }
+ if (mkv->cues) {
+ av_freep(&mkv->cues->entries);
+ av_freep(&mkv->cues);
+ }
+ if (mkv->attachments) {
+ av_freep(&mkv->attachments->entries);
+ av_freep(&mkv->attachments);
+ }
+ av_freep(&mkv->tracks);
+ av_freep(&mkv->stream_durations);
+ av_freep(&mkv->stream_duration_offsets);
+}
+
+/**
* Initialize a mkv_seekhead element to be ready to index level 1 Matroska
* elements. If a maximum number of elements is specified, enough space
* will be reserved at the current file location to write a seek head of
@@ -300,9 +452,9 @@ static mkv_seekhead *mkv_start_seekhead(AVIOContext *pb, int64_t segment_offset,
if (numelements > 0) {
new_seekhead->filepos = avio_tell(pb);
// 21 bytes max for a seek entry, 10 bytes max for the SeekHead ID
- // and size, and 3 bytes to guarantee that an EBML void element
- // will fit afterwards
- new_seekhead->reserved_size = numelements * MAX_SEEKENTRY_SIZE + 13;
+ // and size, 6 bytes for a CRC32 element, and 3 bytes to guarantee
+ // that an EBML void element will fit afterwards
+ new_seekhead->reserved_size = numelements * MAX_SEEKENTRY_SIZE + 19;
new_seekhead->max_entries = numelements;
put_ebml_void(pb, new_seekhead->reserved_size);
}
@@ -311,17 +463,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;
@@ -338,8 +489,10 @@ static int mkv_add_seekhead_entry(mkv_seekhead *seekhead, unsigned int elementid
* @return The file offset where the seekhead was written,
* -1 if an error occurred.
*/
-static int64_t mkv_write_seekhead(AVIOContext *pb, mkv_seekhead *seekhead)
+static int64_t mkv_write_seekhead(AVIOContext *pb, MatroskaMuxContext *mkv)
{
+ AVIOContext *dyn_cp;
+ mkv_seekhead *seekhead = mkv->main_seekhead;
ebml_master metaseek, seekentry;
int64_t currentpos;
int i;
@@ -353,20 +506,25 @@ static int64_t mkv_write_seekhead(AVIOContext *pb, mkv_seekhead *seekhead)
}
}
- metaseek = start_ebml_master(pb, MATROSKA_ID_SEEKHEAD, seekhead->reserved_size);
+ if (start_ebml_master_crc32(pb, &dyn_cp, mkv, &metaseek, MATROSKA_ID_SEEKHEAD,
+ seekhead->reserved_size) < 0) {
+ currentpos = -1;
+ goto fail;
+ }
+
for (i = 0; i < seekhead->num_entries; i++) {
mkv_seekhead_entry *entry = &seekhead->entries[i];
- seekentry = start_ebml_master(pb, MATROSKA_ID_SEEKENTRY, MAX_SEEKENTRY_SIZE);
+ seekentry = start_ebml_master(dyn_cp, MATROSKA_ID_SEEKENTRY, MAX_SEEKENTRY_SIZE);
- put_ebml_id(pb, MATROSKA_ID_SEEKID);
- put_ebml_num(pb, ebml_id_size(entry->elementid), 0);
- put_ebml_id(pb, entry->elementid);
+ put_ebml_id(dyn_cp, MATROSKA_ID_SEEKID);
+ put_ebml_num(dyn_cp, ebml_id_size(entry->elementid), 0);
+ put_ebml_id(dyn_cp, entry->elementid);
- put_ebml_uint(pb, MATROSKA_ID_SEEKPOSITION, entry->segmentpos);
- end_ebml_master(pb, seekentry);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_SEEKPOSITION, entry->segmentpos);
+ end_ebml_master(dyn_cp, seekentry);
}
- end_ebml_master(pb, metaseek);
+ end_ebml_master_crc32(pb, &dyn_cp, mkv, metaseek);
if (seekhead->reserved_size > 0) {
uint64_t remaining = seekhead->filepos + seekhead->reserved_size - avio_tell(pb);
@@ -376,8 +534,8 @@ static int64_t mkv_write_seekhead(AVIOContext *pb, mkv_seekhead *seekhead)
currentpos = seekhead->filepos;
}
fail:
- av_free(seekhead->entries);
- av_free(seekhead);
+ av_freep(&mkv->main_seekhead->entries);
+ av_freep(&mkv->main_seekhead);
return currentpos;
}
@@ -392,62 +550,92 @@ 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)
{
+ MatroskaMuxContext *mkv = s->priv_data;
+ AVIOContext *dyn_cp, *pb = s->pb;
ebml_master cues_element;
int64_t currentpos;
- int i, j;
+ int i, j, ret;
currentpos = avio_tell(pb);
- cues_element = start_ebml_master(pb, MATROSKA_ID_CUES, 0);
+ ret = start_ebml_master_crc32(pb, &dyn_cp, mkv, &cues_element, MATROSKA_ID_CUES, 0);
+ if (ret < 0)
+ return ret;
for (i = 0; i < cues->num_entries; i++) {
ebml_master cuepoint, track_positions;
mkv_cuepoint *entry = &cues->entries[i];
uint64_t pts = entry->pts;
+ int ctp_nb = 0;
- cuepoint = start_ebml_master(pb, MATROSKA_ID_POINTENTRY, MAX_CUEPOINT_SIZE(num_tracks));
- put_ebml_uint(pb, MATROSKA_ID_CUETIME, pts);
+ // 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]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
+ continue;
+ tracks[tracknum].has_cue = 1;
+ ctp_nb ++;
+ }
+
+ cuepoint = start_ebml_master(dyn_cp, MATROSKA_ID_POINTENTRY, MAX_CUEPOINT_SIZE(ctp_nb));
+ put_ebml_uint(dyn_cp, 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++) {
- 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);
- end_ebml_master(pb, track_positions);
+ int tracknum = entry[j].stream_idx;
+ av_assert0(tracknum>=0 && tracknum<num_tracks);
+ if (tracks[tracknum].has_cue && s->streams[tracknum]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
+ continue;
+ tracks[tracknum].has_cue = 1;
+ track_positions = start_ebml_master(dyn_cp, MATROSKA_ID_CUETRACKPOSITION, MAX_CUETRACKPOS_SIZE);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_CUETRACK , entry[j].tracknum );
+ put_ebml_uint(dyn_cp, MATROSKA_ID_CUECLUSTERPOSITION , entry[j].cluster_pos);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_CUERELATIVEPOSITION, entry[j].relative_pos);
+ if (entry[j].duration != -1)
+ put_ebml_uint(dyn_cp, MATROSKA_ID_CUEDURATION , entry[j].duration);
+ end_ebml_master(dyn_cp, track_positions);
}
i += j - 1;
- end_ebml_master(pb, cuepoint);
+ end_ebml_master(dyn_cp, cuepoint);
}
- end_ebml_master(pb, cues_element);
+ end_ebml_master_crc32(pb, &dyn_cp, mkv, cues_element);
return currentpos;
}
static int put_xiph_codecpriv(AVFormatContext *s, AVIOContext *pb, AVCodecParameters *par)
{
- uint8_t *header_start[3];
+ const uint8_t *header_start[3];
int header_len[3];
int first_header_size;
int j;
@@ -496,15 +684,18 @@ 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;
+ int64_t len;
snprintf(buf, sizeof(buf), "0x%"PRIx64, par->channel_layout);
av_dict_set(&dict, "WAVEFORMATEXTENSIBLE_CHANNEL_MASK", buf, 0);
len = ff_vorbiscomment_length(dict, vendor);
+ if (len >= ((1<<24) - 4))
+ return AVERROR(EINVAL);
+
data = av_malloc(len + 4);
if (!data) {
av_dict_free(&dict);
@@ -559,7 +750,7 @@ static int get_aac_sample_rates(AVFormatContext *s, uint8_t *extradata, int extr
return 0;
}
-static int mkv_write_native_codecprivate(AVFormatContext *s,
+static int mkv_write_native_codecprivate(AVFormatContext *s, AVIOContext *pb,
AVCodecParameters *par,
AVIOContext *dyn_cp)
{
@@ -575,8 +766,9 @@ static int mkv_write_native_codecprivate(AVFormatContext *s,
return ff_isom_write_avcc(dyn_cp, par->extradata,
par->extradata_size);
case AV_CODEC_ID_HEVC:
- return ff_isom_write_hvcc(dyn_cp, par->extradata,
- par->extradata_size, 0);
+ ff_isom_write_hvcc(dyn_cp, par->extradata,
+ par->extradata_size, 0);
+ return 0;
case AV_CODEC_ID_ALAC:
if (par->extradata_size < 36) {
av_log(s, AV_LOG_ERROR,
@@ -587,9 +779,18 @@ static int mkv_write_native_codecprivate(AVFormatContext *s,
avio_write(dyn_cp, par->extradata + 12,
par->extradata_size - 12);
break;
- default:
+ case AV_CODEC_ID_AAC:
if (par->extradata_size)
- avio_write(dyn_cp, par->extradata, par->extradata_size);
+ avio_write(dyn_cp, par->extradata, par->extradata_size);
+ else
+ put_ebml_void(pb, MAX_PCE_SIZE + 2 + 4);
+ break;
+ default:
+ if (par->codec_id == AV_CODEC_ID_PRORES &&
+ ff_codec_get_id(ff_codec_movvideo_tags, par->codec_tag) == AV_CODEC_ID_PRORES) {
+ avio_wl32(dyn_cp, par->codec_tag);
+ } else if (par->extradata_size && par->codec_id != AV_CODEC_ID_TTA)
+ avio_write(dyn_cp, par->extradata, par->extradata_size);
}
return 0;
@@ -608,36 +809,50 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb,
return ret;
if (native_id) {
- ret = mkv_write_native_codecprivate(s, par, dyn_cp);
+ ret = mkv_write_native_codecprivate(s, pb, par, dyn_cp);
} else if (par->codec_type == AVMEDIA_TYPE_VIDEO) {
if (qt_id) {
if (!par->codec_tag)
par->codec_tag = ff_codec_get_tag(ff_codec_movvideo_tags,
- par->codec_id);
- if (par->extradata_size)
- avio_write(dyn_cp, par->extradata, par->extradata_size);
+ par->codec_id);
+ if ( ff_codec_get_id(ff_codec_movvideo_tags, par->codec_tag) == par->codec_id
+ && (!par->extradata_size || ff_codec_get_id(ff_codec_movvideo_tags, AV_RL32(par->extradata + 4)) != par->codec_id)
+ ) {
+ int i;
+ avio_wb32(dyn_cp, 0x5a + par->extradata_size);
+ avio_wl32(dyn_cp, par->codec_tag);
+ for(i = 0; i < 0x5a - 8; i++)
+ avio_w8(dyn_cp, 0);
+ }
+ avio_write(dyn_cp, par->extradata, par->extradata_size);
} else {
+ if (!ff_codec_get_tag(ff_codec_bmp_tags, par->codec_id))
+ av_log(s, AV_LOG_WARNING, "codec %s is not supported by this format\n",
+ avcodec_get_name(par->codec_id));
+
if (!par->codec_tag)
par->codec_tag = ff_codec_get_tag(ff_codec_bmp_tags,
par->codec_id);
- if (!par->codec_tag) {
- av_log(s, AV_LOG_ERROR, "No bmp codec ID found.\n");
- ret = -1;
+ if (!par->codec_tag && par->codec_id != AV_CODEC_ID_RAWVIDEO) {
+ av_log(s, AV_LOG_ERROR, "No bmp codec tag found for codec %s\n",
+ avcodec_get_name(par->codec_id));
+ ret = AVERROR(EINVAL);
}
- ff_put_bmp_header(dyn_cp, par, ff_codec_bmp_tags, 0);
+ ff_put_bmp_header(dyn_cp, par, 0, 0);
}
} else if (par->codec_type == AVMEDIA_TYPE_AUDIO) {
unsigned int tag;
tag = ff_codec_get_tag(ff_codec_wav_tags, par->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(par->codec_id));
+ ret = AVERROR(EINVAL);
}
if (!par->codec_tag)
par->codec_tag = tag;
- ff_put_wav_header(s, dyn_cp, par);
+ ff_put_wav_header(s, dyn_cp, par, FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX);
}
codecpriv_size = avio_close_dyn_buf(dyn_cp, &codecpriv);
@@ -648,6 +863,96 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb,
return ret;
}
+static int mkv_write_video_color(AVIOContext *pb, AVCodecParameters *par, AVStream *st) {
+ AVIOContext *dyn_cp;
+ uint8_t *colorinfo_ptr;
+ int side_data_size = 0;
+ int ret, colorinfo_size;
+ const uint8_t *side_data;
+
+ ret = avio_open_dyn_buf(&dyn_cp);
+ if (ret < 0)
+ return ret;
+
+ if (par->color_trc != AVCOL_TRC_UNSPECIFIED &&
+ par->color_trc < AVCOL_TRC_NB) {
+ put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORTRANSFERCHARACTERISTICS,
+ par->color_trc);
+ }
+ if (par->color_space != AVCOL_SPC_UNSPECIFIED &&
+ par->color_space < AVCOL_SPC_NB) {
+ put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORMATRIXCOEFF, par->color_space);
+ }
+ if (par->color_primaries != AVCOL_PRI_UNSPECIFIED &&
+ par->color_primaries < AVCOL_PRI_NB) {
+ put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORPRIMARIES, par->color_primaries);
+ }
+ if (par->color_range != AVCOL_RANGE_UNSPECIFIED &&
+ par->color_range < AVCOL_RANGE_NB) {
+ put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORRANGE, par->color_range);
+ }
+ if (par->chroma_location != AVCHROMA_LOC_UNSPECIFIED &&
+ par->chroma_location <= AVCHROMA_LOC_TOP) {
+ int xpos, ypos;
+
+ avcodec_enum_to_chroma_pos(&xpos, &ypos, par->chroma_location);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORCHROMASITINGHORZ, (xpos >> 7) + 1);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORCHROMASITINGVERT, (ypos >> 7) + 1);
+ }
+
+ side_data = av_stream_get_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
+ &side_data_size);
+ if (side_data_size) {
+ const AVContentLightMetadata *metadata =
+ (const AVContentLightMetadata*)side_data;
+ put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORMAXCLL, metadata->MaxCLL);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORMAXFALL, metadata->MaxFALL);
+ }
+
+ side_data = av_stream_get_side_data(st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
+ &side_data_size);
+ if (side_data_size == sizeof(AVMasteringDisplayMetadata)) {
+ ebml_master meta_element = start_ebml_master(
+ dyn_cp, MATROSKA_ID_VIDEOCOLORMASTERINGMETA, 0);
+ const AVMasteringDisplayMetadata *metadata =
+ (const AVMasteringDisplayMetadata*)side_data;
+ if (metadata->has_primaries) {
+ put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_RX,
+ av_q2d(metadata->display_primaries[0][0]));
+ put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_RY,
+ av_q2d(metadata->display_primaries[0][1]));
+ put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_GX,
+ av_q2d(metadata->display_primaries[1][0]));
+ put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_GY,
+ av_q2d(metadata->display_primaries[1][1]));
+ put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_BX,
+ av_q2d(metadata->display_primaries[2][0]));
+ put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_BY,
+ av_q2d(metadata->display_primaries[2][1]));
+ put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_WHITEX,
+ av_q2d(metadata->white_point[0]));
+ put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_WHITEY,
+ av_q2d(metadata->white_point[1]));
+ }
+ if (metadata->has_luminance) {
+ put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_LUMINANCEMAX,
+ av_q2d(metadata->max_luminance));
+ put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_LUMINANCEMIN,
+ av_q2d(metadata->min_luminance));
+ }
+ end_ebml_master(dyn_cp, meta_element);
+ }
+
+ colorinfo_size = avio_close_dyn_buf(dyn_cp, &colorinfo_ptr);
+ if (colorinfo_size) {
+ ebml_master colorinfo = start_ebml_master(pb, MATROSKA_ID_VIDEOCOLOR, colorinfo_size);
+ avio_write(pb, colorinfo_ptr, colorinfo_size);
+ end_ebml_master(pb, colorinfo);
+ }
+ av_free(colorinfo_ptr);
+ return 0;
+}
+
static int mkv_write_video_projection(AVFormatContext *s, AVIOContext *pb,
AVStream *st)
{
@@ -662,10 +967,8 @@ static int mkv_write_video_projection(AVFormatContext *s, AVIOContext *pb,
(const AVSphericalMapping *)av_stream_get_side_data(st, AV_PKT_DATA_SPHERICAL,
&side_data_size);
- if (!side_data_size) {
- av_log(NULL, AV_LOG_WARNING, "Unknown spherical metadata\n");
+ if (!side_data_size)
return 0;
- }
ret = avio_open_dyn_buf(&dyn_cp);
if (ret < 0)
@@ -686,7 +989,7 @@ static int mkv_write_video_projection(AVFormatContext *s, AVIOContext *pb,
avio_wb32(&b, spherical->bound_left);
avio_wb32(&b, spherical->bound_right);
put_ebml_binary(dyn_cp, MATROSKA_ID_VIDEOPROJECTIONPRIVATE,
- private, sizeof(private));
+ private, avio_tell(&b));
break;
case AV_SPHERICAL_CUBEMAP:
ffio_init_context(&b, private, 12, 1, NULL, NULL, NULL, NULL);
@@ -696,7 +999,7 @@ static int mkv_write_video_projection(AVFormatContext *s, AVIOContext *pb,
avio_wb32(&b, 0); // layout
avio_wb32(&b, spherical->padding);
put_ebml_binary(dyn_cp, MATROSKA_ID_VIDEOPROJECTIONPRIVATE,
- private, sizeof(private));
+ private, avio_tell(&b));
break;
default:
av_log(s, AV_LOG_WARNING, "Unknown projection type\n");
@@ -727,13 +1030,11 @@ end:
return 0;
}
-static void mkv_write_field_order(AVIOContext *pb,
+static void mkv_write_field_order(AVIOContext *pb, int mode,
enum AVFieldOrder field_order)
{
switch (field_order) {
case AV_FIELD_UNKNOWN:
- put_ebml_uint(pb, MATROSKA_ID_VIDEOFLAGINTERLACED,
- MATROSKA_VIDEO_INTERLACE_FLAG_UNDETERMINED);
break;
case AV_FIELD_PROGRESSIVE:
put_ebml_uint(pb, MATROSKA_ID_VIDEOFLAGINTERLACED,
@@ -745,40 +1046,50 @@ static void mkv_write_field_order(AVIOContext *pb,
case AV_FIELD_BT:
put_ebml_uint(pb, MATROSKA_ID_VIDEOFLAGINTERLACED,
MATROSKA_VIDEO_INTERLACE_FLAG_INTERLACED);
- switch (field_order) {
- case AV_FIELD_TT:
- put_ebml_uint(pb, MATROSKA_ID_VIDEOFIELDORDER,
- MATROSKA_VIDEO_FIELDORDER_TT);
- break;
- case AV_FIELD_BB:
- put_ebml_uint(pb, MATROSKA_ID_VIDEOFIELDORDER,
- MATROSKA_VIDEO_FIELDORDER_BB);
- break;
- case AV_FIELD_TB:
- put_ebml_uint(pb, MATROSKA_ID_VIDEOFIELDORDER,
- MATROSKA_VIDEO_FIELDORDER_TB);
- break;
- case AV_FIELD_BT:
- put_ebml_uint(pb, MATROSKA_ID_VIDEOFIELDORDER,
- MATROSKA_VIDEO_FIELDORDER_BT);
- break;
+ if (mode != MODE_WEBM) {
+ switch (field_order) {
+ case AV_FIELD_TT:
+ put_ebml_uint(pb, MATROSKA_ID_VIDEOFIELDORDER,
+ MATROSKA_VIDEO_FIELDORDER_TT);
+ break;
+ case AV_FIELD_BB:
+ put_ebml_uint(pb, MATROSKA_ID_VIDEOFIELDORDER,
+ MATROSKA_VIDEO_FIELDORDER_BB);
+ break;
+ case AV_FIELD_TB:
+ put_ebml_uint(pb, MATROSKA_ID_VIDEOFIELDORDER,
+ MATROSKA_VIDEO_FIELDORDER_TB);
+ break;
+ case AV_FIELD_BT:
+ put_ebml_uint(pb, MATROSKA_ID_VIDEOFIELDORDER,
+ MATROSKA_VIDEO_FIELDORDER_BT);
+ break;
+ }
}
}
}
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;
- AVCodecParameters *par = st->codecpar;
+ 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);
@@ -801,13 +1112,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;
@@ -818,13 +1129,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;
@@ -832,40 +1143,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 = par->width / h_width;
- display_height = par->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];
AVCodecParameters *par = st->codecpar;
@@ -875,19 +1178,26 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
int bit_depth = av_get_bits_per_sample(par->codec_id);
int sample_rate = par->sample_rate;
int output_sample_rate = 0;
+ int display_width_div = 1;
+ int display_height_div = 1;
int j, ret;
AVDictionaryEntry *tag;
- // ms precision is the de-facto standard timescale for mkv files
- avpriv_set_pts_info(st, 64, 1, 1000);
-
if (par->codec_type == AVMEDIA_TYPE_ATTACHMENT) {
mkv->have_attachments = 1;
return 0;
}
- if (!bit_depth)
- bit_depth = av_get_bytes_per_sample(par->format) << 3;
+ if (par->codec_type == AVMEDIA_TYPE_AUDIO) {
+ if (!bit_depth && par->codec_id != AV_CODEC_ID_ADPCM_G726) {
+ if (par->bits_per_raw_sample)
+ bit_depth = par->bits_per_raw_sample;
+ else
+ bit_depth = av_get_bytes_per_sample(par->format) << 3;
+ }
+ if (!bit_depth)
+ bit_depth = par->bits_per_coded_sample;
+ }
if (par->codec_id == AV_CODEC_ID_AAC) {
ret = get_aac_sample_rates(s, par->extradata, par->extradata_size, &sample_rate,
@@ -897,49 +1207,92 @@ 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 || par->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 (st->disposition & AV_DISPOSITION_FORCED)
- put_ebml_uint(pb, MATROSKA_ID_TRACKFLAGFORCED, !!(st->disposition & AV_DISPOSITION_FORCED));
- if (par->codec_type == AVMEDIA_TYPE_AUDIO && par->initial_padding) {
- mkv->tracks[i].ts_offset = av_rescale_q(par->initial_padding,
- (AVRational){ 1, par->sample_rate },
- st->time_base);
-
- put_ebml_uint(pb, MATROSKA_ID_CODECDELAY,
- av_rescale_q(par->initial_padding,
- (AVRational){ 1, par->sample_rate },
- (AVRational){ 1, 1000000000 }));
+ if (st->disposition & AV_DISPOSITION_FORCED)
+ put_ebml_uint(pb, MATROSKA_ID_TRACKFLAGFORCED, 1);
+
+ if (mkv->mode == MODE_WEBM && par->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
+ if (par->codec_id != AV_CODEC_ID_RAWVIDEO || par->codec_tag) {
+ for (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) {
+ if (ff_mkv_codec_tags[j].id == par->codec_id && par->codec_id != AV_CODEC_ID_FFV1) {
+ put_ebml_string(pb, MATROSKA_ID_CODECID, ff_mkv_codec_tags[j].str);
+ native_id = 1;
+ break;
+ }
+ }
+ } else {
+ if (mkv->allow_raw_vfw) {
+ native_id = 0;
+ } else {
+ av_log(s, AV_LOG_ERROR, "Raw RGB is not supported Natively in Matroska, you can use AVI or NUT or\n"
+ "If you would like to store it anyway using VFW mode, enable allow_raw_vfw (-allow_raw_vfw 1)\n");
+ return AVERROR(EINVAL);
+ }
+ }
}
- // 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 == par->codec_id) {
- put_ebml_string(pb, MATROSKA_ID_CODECID, ff_mkv_codec_tags[j].str);
- native_id = 1;
- break;
+ if (par->codec_type == AVMEDIA_TYPE_AUDIO && par->initial_padding && par->codec_id == AV_CODEC_ID_OPUS) {
+ int64_t codecdelay = av_rescale_q(par->initial_padding,
+ (AVRational){ 1, 48000 },
+ (AVRational){ 1, 1000000000 });
+ if (codecdelay < 0) {
+ av_log(s, AV_LOG_ERROR, "Initial padding is invalid\n");
+ return AVERROR(EINVAL);
}
+// mkv->tracks[i].ts_offset = av_rescale_q(par->initial_padding,
+// (AVRational){ 1, par->sample_rate },
+// st->time_base);
+
+ put_ebml_uint(pb, MATROSKA_ID_CODECDELAY, codecdelay);
+ }
+ if (par->codec_id == AV_CODEC_ID_OPUS) {
+ put_ebml_uint(pb, MATROSKA_ID_SEEKPREROLL, OPUS_SEEK_PREROLL);
}
if (mkv->mode == MODE_WEBM && !(par->codec_id == AV_CODEC_ID_VP8 ||
par->codec_id == AV_CODEC_ID_VP9 ||
par->codec_id == AV_CODEC_ID_OPUS ||
- par->codec_id == AV_CODEC_ID_VORBIS)) {
+ par->codec_id == AV_CODEC_ID_VORBIS ||
+ par->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);
}
@@ -947,12 +1300,16 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
case AVMEDIA_TYPE_VIDEO:
mkv->have_video = 1;
put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_VIDEO);
- if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0)
- put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, 1E9 / av_q2d(st->avg_frame_rate));
+
+ if( st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0
+ && av_cmp_q(av_inv_q(st->avg_frame_rate), st->time_base) > 0)
+ put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, 1000000000LL * st->avg_frame_rate.den / st->avg_frame_rate.num);
+ else
+ put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, 1000000000LL * st->time_base.num / st->time_base.den);
if (!native_id &&
ff_codec_get_tag(ff_codec_movvideo_tags, par->codec_id) &&
- (!ff_codec_get_tag(ff_codec_bmp_tags, par->codec_id) ||
+ ((!ff_codec_get_tag(ff_codec_bmp_tags, par->codec_id) && par->codec_id != AV_CODEC_ID_RAWVIDEO) ||
par->codec_id == AV_CODEC_ID_SVQ1 ||
par->codec_id == AV_CODEC_ID_SVQ3 ||
par->codec_id == AV_CODEC_ID_CINEPAK))
@@ -964,6 +1321,7 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
// if there is no mkv-specific codec ID, use VFW mode
put_ebml_string(pb, MATROSKA_ID_CODECID, "V_MS/VFW/FOURCC");
mkv->tracks[i].write_dts = 1;
+ s->internal->avoid_negative_ts_use_pts = 0;
}
subinfo = start_ebml_master(pb, MATROSKA_ID_TRACKVIDEO, 0);
@@ -971,17 +1329,61 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
put_ebml_uint (pb, MATROSKA_ID_VIDEOPIXELWIDTH , par->width);
put_ebml_uint (pb, MATROSKA_ID_VIDEOPIXELHEIGHT, par->height);
- mkv_write_field_order(pb, par->field_order);
+ mkv_write_field_order(pb, mkv->mode, par->field_order);
// 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)) ||
+ (par->format == 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(par->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);
+ }
+ if (d_width != par->width || display_width_div != 1 || display_height_div != 1) {
+ if (mkv->mode == MODE_WEBM || display_width_div != 1 || display_height_div != 1) {
+ put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYWIDTH , d_width / display_width_div);
+ put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYHEIGHT, par->height / display_height_div);
+ } else {
+ AVRational display_aspect_ratio;
+ av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
+ par->width * (int64_t)st->sample_aspect_ratio.num,
+ par->height * (int64_t)st->sample_aspect_ratio.den,
+ 1024 * 1024);
+ put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYWIDTH, display_aspect_ratio.num);
+ put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYHEIGHT, display_aspect_ratio.den);
+ put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYUNIT, MATROSKA_VIDEO_DISPLAYUNIT_DAR);
+ }
+ }
+ } else if (display_width_div != 1 || display_height_div != 1) {
+ put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYWIDTH , par->width / display_width_div);
+ put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYHEIGHT, par->height / display_height_div);
+ } else if (mkv->mode != MODE_WEBM)
+ put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYUNIT, MATROSKA_VIDEO_DISPLAYUNIT_UNKNOWN);
+
+ if (par->codec_id == AV_CODEC_ID_RAWVIDEO) {
+ uint32_t color_space = av_le2ne32(par->codec_tag);
+ put_ebml_binary(pb, MATROSKA_ID_VIDEOCOLORSPACE, &color_space, sizeof(color_space));
+ }
+ ret = mkv_write_video_color(pb, par, st);
if (ret < 0)
return ret;
ret = mkv_write_video_projection(s, pb, st);
if (ret < 0)
return ret;
-
end_ebml_master(pb, subinfo);
break;
@@ -1005,21 +1407,27 @@ 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", par->codec_id);
return AVERROR(ENOSYS);
}
+
+ if (mkv->mode != MODE_WEBM || par->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);
}
- mkv->tracks[i].codecpriv_offset = avio_tell(pb);
- ret = mkv_write_codecprivate(s, pb, par, native_id, qt_id);
- if (ret < 0)
- return ret;
+ if (mkv->mode != MODE_WEBM || par->codec_id != AV_CODEC_ID_WEBVTT) {
+ mkv->tracks[i].codecpriv_offset = avio_tell(pb);
+ ret = mkv_write_codecprivate(s, pb, par, native_id, qt_id);
+ if (ret < 0)
+ return ret;
+ }
end_ebml_master(pb, track);
@@ -1030,27 +1438,38 @@ 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)
return ret;
- tracks = start_ebml_master(pb, MATROSKA_ID_TRACKS, 0);
+ ret = start_ebml_master_crc32(pb, &mkv->tracks_bc, mkv, &mkv->tracks_master, MATROSKA_ID_TRACKS, 0);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ default_stream_exists |= st->disposition & AV_DISPOSITION_DEFAULT;
+ }
for (i = 0; i < s->nb_streams; i++) {
- ret = mkv_write_track(s, mkv, i, pb);
+ ret = mkv_write_track(s, mkv, i, mkv->tracks_bc, default_stream_exists);
if (ret < 0)
return ret;
}
- end_ebml_master(pb, tracks);
+
+ if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live)
+ end_ebml_master_crc32_preliminary(pb, &mkv->tracks_bc, mkv, mkv->tracks_master);
+ else
+ end_ebml_master_crc32(pb, &mkv->tracks_bc, mkv, mkv->tracks_master);
+
return 0;
}
static int mkv_write_chapters(AVFormatContext *s)
{
MatroskaMuxContext *mkv = s->priv_data;
- AVIOContext *pb = s->pb;
+ AVIOContext *dyn_cp, *pb = s->pb;
ebml_master chapters, editionentry;
AVRational scale = {1, 1E9};
int i, ret;
@@ -1061,10 +1480,14 @@ static int mkv_write_chapters(AVFormatContext *s)
ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_CHAPTERS, avio_tell(pb));
if (ret < 0) return ret;
- chapters = start_ebml_master(pb, MATROSKA_ID_CHAPTERS , 0);
- editionentry = start_ebml_master(pb, MATROSKA_ID_EDITIONENTRY, 0);
- put_ebml_uint(pb, MATROSKA_ID_EDITIONFLAGDEFAULT, 1);
- put_ebml_uint(pb, MATROSKA_ID_EDITIONFLAGHIDDEN , 0);
+ ret = start_ebml_master_crc32(pb, &dyn_cp, mkv, &chapters, MATROSKA_ID_CHAPTERS, 0);
+ if (ret < 0) return ret;
+
+ editionentry = start_ebml_master(dyn_cp, MATROSKA_ID_EDITIONENTRY, 0);
+ if (mkv->mode != MODE_WEBM) {
+ put_ebml_uint(dyn_cp, MATROSKA_ID_EDITIONFLAGDEFAULT, 1);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_EDITIONFLAGHIDDEN , 0);
+ }
for (i = 0; i < s->nb_chapters; i++) {
ebml_master chapteratom, chapterdisplay;
AVChapter *c = s->chapters[i];
@@ -1078,22 +1501,24 @@ static int mkv_write_chapters(AVFormatContext *s)
return AVERROR_INVALIDDATA;
}
- chapteratom = start_ebml_master(pb, MATROSKA_ID_CHAPTERATOM, 0);
- put_ebml_uint(pb, MATROSKA_ID_CHAPTERUID, c->id);
- put_ebml_uint(pb, MATROSKA_ID_CHAPTERTIMESTART, chapterstart);
- put_ebml_uint(pb, MATROSKA_ID_CHAPTERTIMEEND, chapterend);
- put_ebml_uint(pb, MATROSKA_ID_CHAPTERFLAGHIDDEN , 0);
- put_ebml_uint(pb, MATROSKA_ID_CHAPTERFLAGENABLED, 1);
+ chapteratom = start_ebml_master(dyn_cp, MATROSKA_ID_CHAPTERATOM, 0);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERUID, c->id + mkv->chapter_id_offset);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERTIMESTART, chapterstart);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERTIMEEND, chapterend);
+ if (mkv->mode != MODE_WEBM) {
+ put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERFLAGHIDDEN , 0);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERFLAGENABLED, 1);
+ }
if ((t = av_dict_get(c->metadata, "title", NULL, 0))) {
- chapterdisplay = start_ebml_master(pb, MATROSKA_ID_CHAPTERDISPLAY, 0);
- put_ebml_string(pb, MATROSKA_ID_CHAPSTRING, t->value);
- put_ebml_string(pb, MATROSKA_ID_CHAPLANG , "und");
- end_ebml_master(pb, chapterdisplay);
+ chapterdisplay = start_ebml_master(dyn_cp, MATROSKA_ID_CHAPTERDISPLAY, 0);
+ put_ebml_string(dyn_cp, MATROSKA_ID_CHAPSTRING, t->value);
+ put_ebml_string(dyn_cp, MATROSKA_ID_CHAPLANG , "und");
+ end_ebml_master(dyn_cp, chapterdisplay);
}
- end_ebml_master(pb, chapteratom);
+ end_ebml_master(dyn_cp, chapteratom);
}
- end_ebml_master(pb, editionentry);
- end_ebml_master(pb, chapters);
+ end_ebml_master(dyn_cp, editionentry);
+ end_ebml_master_crc32(pb, &dyn_cp, mkv, chapters);
mkv->wrote_chapters = 1;
return 0;
@@ -1110,7 +1535,7 @@ static int mkv_write_simpletag(AVIOContext *pb, AVDictionaryEntry *t)
return AVERROR(ENOMEM);
if ((p = strrchr(p, '-')) &&
- (lang = av_convert_lang_to(p + 1, AV_LANG_ISO639_2_BIBL)))
+ (lang = ff_convert_lang_to(p + 1, AV_LANG_ISO639_2_BIBL)))
*p = 0;
p = key;
@@ -1133,83 +1558,170 @@ static int mkv_write_simpletag(AVIOContext *pb, AVDictionaryEntry *t)
return 0;
}
-static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, unsigned int elementid,
- unsigned int uid, ebml_master *tags)
+static int mkv_write_tag_targets(AVFormatContext *s,
+ unsigned int elementid, unsigned int uid,
+ ebml_master *tags, ebml_master* tag)
{
+ AVIOContext *pb;
MatroskaMuxContext *mkv = s->priv_data;
- ebml_master tag, targets;
- AVDictionaryEntry *t = NULL;
+ ebml_master targets;
int ret;
if (!tags->pos) {
ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_TAGS, avio_tell(s->pb));
if (ret < 0) return ret;
- *tags = start_ebml_master(s->pb, MATROSKA_ID_TAGS, 0);
+ start_ebml_master_crc32(s->pb, &mkv->tags_bc, mkv, tags, MATROSKA_ID_TAGS, 0);
}
+ pb = mkv->tags_bc;
- tag = start_ebml_master(s->pb, MATROSKA_ID_TAG, 0);
- targets = start_ebml_master(s->pb, MATROSKA_ID_TAGTARGETS, 0);
+ *tag = start_ebml_master(pb, MATROSKA_ID_TAG, 0);
+ targets = start_ebml_master(pb, MATROSKA_ID_TAGTARGETS, 0);
if (elementid)
- put_ebml_uint(s->pb, elementid, uid);
- end_ebml_master(s->pb, targets);
+ put_ebml_uint(pb, elementid, uid);
+ end_ebml_master(pb, targets);
+ return 0;
+}
+
+static int mkv_check_tag_name(const char *name, unsigned int elementid)
+{
+ return av_strcasecmp(name, "title") &&
+ av_strcasecmp(name, "stereo_mode") &&
+ av_strcasecmp(name, "creation_time") &&
+ av_strcasecmp(name, "encoding_tool") &&
+ av_strcasecmp(name, "duration") &&
+ (elementid != MATROSKA_ID_TAGTARGETS_TRACKUID ||
+ av_strcasecmp(name, "language")) &&
+ (elementid != MATROSKA_ID_TAGTARGETS_ATTACHUID ||
+ (av_strcasecmp(name, "filename") &&
+ av_strcasecmp(name, "mimetype")));
+}
+
+static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, unsigned int elementid,
+ unsigned int uid, ebml_master *tags)
+{
+ MatroskaMuxContext *mkv = s->priv_data;
+ ebml_master tag;
+ int ret;
+ AVDictionaryEntry *t = NULL;
+
+ ret = mkv_write_tag_targets(s, elementid, uid, tags, &tag);
+ if (ret < 0)
+ return ret;
while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX))) {
- if (av_strcasecmp(t->key, "title") &&
- av_strcasecmp(t->key, "encoding_tool") &&
- (elementid != MATROSKA_ID_TAGTARGETS_TRACKUID ||
- av_strcasecmp(t->key, "language"))) {
- ret = mkv_write_simpletag(s->pb, t);
+ if (mkv_check_tag_name(t->key, elementid)) {
+ ret = mkv_write_simpletag(mkv->tags_bc, t);
if (ret < 0)
return ret;
}
}
- end_ebml_master(s->pb, tag);
+ end_ebml_master(mkv->tags_bc, tag);
+ return 0;
+}
+
+static int mkv_check_tag(AVDictionary *m, unsigned int elementid)
+{
+ AVDictionaryEntry *t = NULL;
+
+ while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX)))
+ if (mkv_check_tag_name(t->key, elementid))
+ return 1;
+
return 0;
}
static int mkv_write_tags(AVFormatContext *s)
{
- ebml_master tags = {0};
+ MatroskaMuxContext *mkv = s->priv_data;
int i, ret;
ff_metadata_conv_ctx(s, ff_mkv_metadata_conv, NULL);
- if (av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) {
- ret = mkv_write_tag(s, s->metadata, 0, 0, &tags);
+ if (mkv_check_tag(s->metadata, 0)) {
+ ret = mkv_write_tag(s, s->metadata, 0, 0, &mkv->tags);
if (ret < 0) return ret;
}
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 (st->codecpar->codec_type == AVMEDIA_TYPE_ATTACHMENT)
continue;
- ret = mkv_write_tag(s, st->metadata, MATROSKA_ID_TAGTARGETS_TRACKUID, i + 1, &tags);
+ if (!mkv_check_tag(st->metadata, MATROSKA_ID_TAGTARGETS_TRACKUID))
+ continue;
+
+ ret = mkv_write_tag(s, st->metadata, MATROSKA_ID_TAGTARGETS_TRACKUID, i + 1, &mkv->tags);
if (ret < 0) return ret;
}
- for (i = 0; i < s->nb_chapters; i++) {
- AVChapter *ch = s->chapters[i];
+ if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) {
+ for (i = 0; i < s->nb_streams; i++) {
+ AVIOContext *pb;
+ AVStream *st = s->streams[i];
+ ebml_master tag_target;
+ ebml_master tag;
- if (!av_dict_get(ch->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX))
- continue;
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_ATTACHMENT)
+ continue;
- ret = mkv_write_tag(s, ch->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID, ch->id, &tags);
- if (ret < 0) return ret;
+ mkv_write_tag_targets(s, MATROSKA_ID_TAGTARGETS_TRACKUID, i + 1, &mkv->tags, &tag_target);
+ pb = mkv->tags_bc;
+
+ tag = start_ebml_master(pb, MATROSKA_ID_SIMPLETAG, 0);
+ put_ebml_string(pb, MATROSKA_ID_TAGNAME, "DURATION");
+ mkv->stream_duration_offsets[i] = avio_tell(pb);
+
+ // Reserve space to write duration as a 20-byte string.
+ // 2 (ebml id) + 1 (data size) + 20 (data)
+ put_ebml_void(pb, 23);
+ end_ebml_master(pb, tag);
+ end_ebml_master(pb, tag_target);
+ }
}
- if (tags.pos)
- end_ebml_master(s->pb, tags);
+ if (mkv->mode != MODE_WEBM) {
+ for (i = 0; i < s->nb_chapters; i++) {
+ AVChapter *ch = s->chapters[i];
+
+ if (!mkv_check_tag(ch->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID))
+ continue;
+
+ ret = mkv_write_tag(s, ch->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID, ch->id + mkv->chapter_id_offset, &mkv->tags);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ if (mkv->have_attachments && mkv->mode != MODE_WEBM) {
+ for (i = 0; i < mkv->attachments->num_entries; i++) {
+ mkv_attachment *attachment = &mkv->attachments->entries[i];
+ AVStream *st = s->streams[attachment->stream_idx];
+
+ if (!mkv_check_tag(st->metadata, MATROSKA_ID_TAGTARGETS_ATTACHUID))
+ continue;
+
+ ret = mkv_write_tag(s, st->metadata, MATROSKA_ID_TAGTARGETS_ATTACHUID, attachment->fileuid, &mkv->tags);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ if (mkv->tags.pos) {
+ if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live)
+ end_ebml_master_crc32_preliminary(s->pb, &mkv->tags_bc, mkv, mkv->tags);
+ else
+ end_ebml_master_crc32(s->pb, &mkv->tags_bc, mkv, mkv->tags);
+ }
return 0;
}
static int mkv_write_attachments(AVFormatContext *s)
{
MatroskaMuxContext *mkv = s->priv_data;
- AVIOContext *pb = s->pb;
+ AVIOContext *dyn_cp, *pb = s->pb;
ebml_master attachments;
AVLFG c;
int i, ret;
@@ -1217,31 +1729,43 @@ static int mkv_write_attachments(AVFormatContext *s)
if (!mkv->have_attachments)
return 0;
+ mkv->attachments = av_mallocz(sizeof(*mkv->attachments));
+ if (!mkv->attachments)
+ return AVERROR(ENOMEM);
+
av_lfg_init(&c, av_get_random_seed());
ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_ATTACHMENTS, avio_tell(pb));
if (ret < 0) return ret;
- attachments = start_ebml_master(pb, MATROSKA_ID_ATTACHMENTS, 0);
+ ret = start_ebml_master_crc32(pb, &dyn_cp, mkv, &attachments, MATROSKA_ID_ATTACHMENTS, 0);
+ if (ret < 0) return ret;
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
ebml_master attached_file;
+ mkv_attachment *attachment = mkv->attachments->entries;
AVDictionaryEntry *t;
const char *mimetype = NULL;
+ uint32_t fileuid;
if (st->codecpar->codec_type != AVMEDIA_TYPE_ATTACHMENT)
continue;
- attached_file = start_ebml_master(pb, MATROSKA_ID_ATTACHEDFILE, 0);
+ attachment = av_realloc_array(attachment, mkv->attachments->num_entries + 1, sizeof(mkv_attachment));
+ if (!attachment)
+ return AVERROR(ENOMEM);
+ mkv->attachments->entries = attachment;
+
+ attached_file = start_ebml_master(dyn_cp, MATROSKA_ID_ATTACHEDFILE, 0);
if (t = av_dict_get(st->metadata, "title", NULL, 0))
- put_ebml_string(pb, MATROSKA_ID_FILEDESC, t->value);
+ put_ebml_string(dyn_cp, MATROSKA_ID_FILEDESC, t->value);
if (!(t = av_dict_get(st->metadata, "filename", NULL, 0))) {
av_log(s, AV_LOG_ERROR, "Attachment stream %d has no filename tag.\n", i);
return AVERROR(EINVAL);
}
- put_ebml_string(pb, MATROSKA_ID_FILENAME, t->value);
+ put_ebml_string(dyn_cp, MATROSKA_ID_FILENAME, t->value);
if (t = av_dict_get(st->metadata, "mimetype", NULL, 0))
mimetype = t->value;
else if (st->codecpar->codec_id != AV_CODEC_ID_NONE ) {
@@ -1263,40 +1787,108 @@ static int mkv_write_attachments(AVFormatContext *s)
return AVERROR(EINVAL);
}
- put_ebml_string(pb, MATROSKA_ID_FILEMIMETYPE, mimetype);
- put_ebml_binary(pb, MATROSKA_ID_FILEDATA, st->codecpar->extradata, st->codecpar->extradata_size);
- put_ebml_uint(pb, MATROSKA_ID_FILEUID, av_lfg_get(&c));
- end_ebml_master(pb, attached_file);
+ 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->codecpar->extradata, st->codecpar->extradata_size);
+ av_sha_final(sha, digest);
+ av_free(sha);
+ fileuid = AV_RL32(digest);
+ } else {
+ fileuid = av_lfg_get(&c);
+ }
+ av_log(s, AV_LOG_VERBOSE, "Using %.8"PRIx32" for attachment %d\n",
+ fileuid, mkv->attachments->num_entries);
+
+ put_ebml_string(dyn_cp, MATROSKA_ID_FILEMIMETYPE, mimetype);
+ put_ebml_binary(dyn_cp, MATROSKA_ID_FILEDATA, st->codecpar->extradata, st->codecpar->extradata_size);
+ put_ebml_uint(dyn_cp, MATROSKA_ID_FILEUID, fileuid);
+ end_ebml_master(dyn_cp, attached_file);
+
+ mkv->attachments->entries[mkv->attachments->num_entries].stream_idx = i;
+ mkv->attachments->entries[mkv->attachments->num_entries++].fileuid = fileuid;
}
- end_ebml_master(pb, attachments);
+ end_ebml_master_crc32(pb, &dyn_cp, mkv, attachments);
return 0;
}
+static int64_t get_metadata_duration(AVFormatContext *s)
+{
+ int i = 0;
+ int64_t max = 0;
+ int64_t us;
+
+ AVDictionaryEntry *explicitDuration = av_dict_get(s->metadata, "DURATION", NULL, 0);
+ if (explicitDuration && (av_parse_time(&us, explicitDuration->value, 1) == 0) && us > 0) {
+ av_log(s, AV_LOG_DEBUG, "get_metadata_duration found duration in context metadata: %" PRId64 "\n", us);
+ return us;
+ }
+
+ for (i = 0; i < s->nb_streams; i++) {
+ int64_t us;
+ AVDictionaryEntry *duration = av_dict_get(s->streams[i]->metadata, "DURATION", NULL, 0);
+
+ if (duration && (av_parse_time(&us, duration->value, 1) == 0))
+ max = FFMAX(max, us);
+ }
+
+ av_log(s, AV_LOG_DEBUG, "get_metadata_duration returned: %" PRId64 "\n", max);
+ return max;
+}
+
static int mkv_write_header(AVFormatContext *s)
{
MatroskaMuxContext *mkv = s->priv_data;
AVIOContext *pb = s->pb;
- ebml_master ebml_header, segment_info;
+ ebml_master ebml_header;
AVDictionaryEntry *tag;
- int ret, i;
+ int ret, i, version = 2;
+ int64_t creation_time;
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 (!mkv->tracks)
- return AVERROR(ENOMEM);
+ 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]->codecpar->codec_id == AV_CODEC_ID_ATRAC3 ||
+ s->streams[i]->codecpar->codec_id == AV_CODEC_ID_COOK ||
+ s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RA_288 ||
+ s->streams[i]->codecpar->codec_id == AV_CODEC_ID_SIPR ||
+ s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RV10 ||
+ s->streams[i]->codecpar->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]->codecpar->codec_id));
+ return AVERROR_PATCHWELCOME;
+ }
+ if (s->streams[i]->codecpar->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) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
ebml_header = start_ebml_master(pb, EBML_ID_HEADER, 0);
put_ebml_uint (pb, EBML_ID_EBMLVERSION , 1);
put_ebml_uint (pb, EBML_ID_EBMLREADVERSION , 1);
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 , 4);
+ put_ebml_uint (pb, EBML_ID_DOCTYPEVERSION , version);
put_ebml_uint (pb, EBML_ID_DOCTYPEREADVERSION , 2);
end_ebml_master(pb, ebml_header);
@@ -1309,64 +1901,111 @@ static int mkv_write_header(AVFormatContext *s)
// isn't more than 10 elements if we only write one of each other
// currently defined level 1 element
mkv->main_seekhead = mkv_start_seekhead(pb, mkv->segment_offset, 10);
- if (!mkv->main_seekhead)
- return AVERROR(ENOMEM);
+ if (!mkv->main_seekhead) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_INFO, avio_tell(pb));
- if (ret < 0) return ret;
+ if (ret < 0) goto fail;
+
+ ret = start_ebml_master_crc32(pb, &mkv->info_bc, mkv, &mkv->info, MATROSKA_ID_INFO, 0);
+ if (ret < 0)
+ return ret;
+ pb = mkv->info_bc;
- segment_info = start_ebml_master(pb, MATROSKA_ID_INFO, 0);
put_ebml_uint(pb, MATROSKA_ID_TIMECODESCALE, 1000000);
if ((tag = av_dict_get(s->metadata, "title", NULL, 0)))
put_ebml_string(pb, MATROSKA_ID_TITLE, tag->value);
if (!(s->flags & AVFMT_FLAG_BITEXACT)) {
- uint32_t segment_uid[4];
- AVLFG lfg;
-
- av_lfg_init(&lfg, av_get_random_seed());
-
- for (i = 0; i < 4; i++)
- segment_uid[i] = av_lfg_get(&lfg);
-
put_ebml_string(pb, MATROSKA_ID_MUXINGAPP, LIBAVFORMAT_IDENT);
if ((tag = av_dict_get(s->metadata, "encoding_tool", NULL, 0)))
put_ebml_string(pb, MATROSKA_ID_WRITINGAPP, tag->value);
else
put_ebml_string(pb, MATROSKA_ID_WRITINGAPP, LIBAVFORMAT_IDENT);
- put_ebml_binary(pb, MATROSKA_ID_SEGMENTUID, segment_uid, 16);
+
+ if (mkv->mode != MODE_WEBM) {
+ uint32_t segment_uid[4];
+ AVLFG lfg;
+
+ av_lfg_init(&lfg, av_get_random_seed());
+
+ for (i = 0; i < 4; i++)
+ segment_uid[i] = av_lfg_get(&lfg);
+
+ 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 (ff_parse_creation_time_metadata(s, &creation_time, 0) > 0) {
+ // Adjust time so it's relative to 2001-01-01 and convert to nanoseconds.
+ int64_t date_utc = (creation_time - 978307200000000LL) * 1000;
+ uint8_t date_utc_buf[8];
+ AV_WB64(date_utc_buf, date_utc);
+ put_ebml_binary(pb, MATROSKA_ID_DATEUTC, date_utc_buf, 8);
}
// reserve space for the duration
mkv->duration = 0;
mkv->duration_offset = avio_tell(pb);
- put_ebml_void(pb, 11); // assumes double-precision float to be written
- end_ebml_master(pb, segment_info);
+ if (!mkv->is_live) {
+ int64_t metadata_duration = get_metadata_duration(s);
+
+ if (s->duration > 0) {
+ int64_t scaledDuration = av_rescale(s->duration, 1000, AV_TIME_BASE);
+ put_ebml_float(pb, MATROSKA_ID_DURATION, scaledDuration);
+ av_log(s, AV_LOG_DEBUG, "Write early duration from recording time = %" PRIu64 "\n", scaledDuration);
+ } else if (metadata_duration > 0) {
+ int64_t scaledDuration = av_rescale(metadata_duration, 1000, AV_TIME_BASE);
+ put_ebml_float(pb, MATROSKA_ID_DURATION, scaledDuration);
+ av_log(s, AV_LOG_DEBUG, "Write early duration from metadata = %" PRIu64 "\n", scaledDuration);
+ } else {
+ put_ebml_void(pb, 11); // assumes double-precision float to be written
+ }
+ }
+ if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live)
+ end_ebml_master_crc32_preliminary(s->pb, &mkv->info_bc, mkv, mkv->info);
+ else
+ end_ebml_master_crc32(s->pb, &mkv->info_bc, mkv, mkv->info);
+ pb = s->pb;
+
+ // initialize stream_duration fields
+ mkv->stream_durations = av_mallocz(s->nb_streams * sizeof(int64_t));
+ mkv->stream_duration_offsets = av_mallocz(s->nb_streams * sizeof(int64_t));
ret = mkv_write_tracks(s);
if (ret < 0)
- return ret;
+ goto fail;
- if (mkv->mode != MODE_WEBM) {
- ret = mkv_write_chapters(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);
- ret = mkv_write_tags(s);
- if (ret < 0)
- return ret;
+ ret = mkv_write_chapters(s);
+ if (ret < 0)
+ goto fail;
+ if (mkv->mode != MODE_WEBM) {
ret = mkv_write_attachments(s);
if (ret < 0)
- return ret;
+ goto fail;
}
- if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL))
- mkv_write_seekhead(pb, mkv->main_seekhead);
+ ret = mkv_write_tags(s);
+ if (ret < 0)
+ goto fail;
- mkv->cues = mkv_start_cues(mkv->segment_offset);
- if (!mkv->cues)
- return AVERROR(ENOMEM);
+ if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live)
+ mkv_write_seekhead(pb, mkv);
+ mkv->cues = mkv_start_cues(mkv->segment_offset);
+ if (!mkv->cues) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && mkv->reserve_cues_space) {
mkv->cues_pos = avio_tell(pb);
put_ebml_void(pb, mkv->reserve_cues_space);
@@ -1374,6 +2013,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);
@@ -1392,6 +2032,9 @@ static int mkv_write_header(AVFormatContext *s)
}
return 0;
+fail:
+ mkv_free(mkv);
+ return ret;
}
static int mkv_blockgroup_size(int pkt_size)
@@ -1405,85 +2048,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;
@@ -1538,18 +2102,24 @@ fail:
}
static void mkv_write_block(AVFormatContext *s, AVIOContext *pb,
- unsigned int blockid, AVPacket *pkt, int flags)
+ unsigned int blockid, AVPacket *pkt, int keyframe)
{
MatroskaMuxContext *mkv = s->priv_data;
AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar;
- 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;
+ uint8_t track_number = (mkv->is_dash ? mkv->dash_track_number : (pkt->stream_index + 1));
+ ebml_master block_group, block_additions, block_more;
+
ts += mkv->tracks[pkt->stream_index].ts_offset;
av_log(s, AV_LOG_DEBUG, "Writing block at offset %" PRIu64 ", size %d, "
- "pts %" PRId64 ", dts %" PRId64 ", duration %" PRId64 ", flags %d\n",
- avio_tell(pb), pkt->size, pkt->pts, pkt->dts, pkt->duration, flags);
+ "pts %" PRId64 ", dts %" PRId64 ", duration %" PRId64 ", keyframe %d\n",
+ avio_tell(pb), pkt->size, pkt->pts, pkt->dts, pkt->duration,
+ keyframe != 0);
if (par->codec_id == AV_CODEC_ID_H264 && par->extradata_size > 0 &&
(AV_RB24(par->extradata) == 1 || AV_RB32(par->extradata) == 1))
ff_avc_parse_nal_units_buf(pkt->data, &data, &size);
@@ -1566,85 +2136,133 @@ static void mkv_write_block(AVFormatContext *s, AVIOContext *pb,
} else
data = pkt->data;
- if (par->codec_id == AV_CODEC_ID_PRORES) {
+ if (par->codec_id == AV_CODEC_ID_PRORES && size >= 8) {
/* Matroska specification requires to remove the first QuickTime atom
*/
size -= 8;
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, par->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 | track_number);
avio_wb16(pb, ts - mkv->cluster_pts);
- avio_w8(pb, flags);
+ avio_w8(pb, (blockid == MATROSKA_ID_SIMPLEBLOCK && keyframe) ? (1 << 7) : 0);
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 (blockid == MATROSKA_ID_BLOCK && !keyframe) {
+ put_ebml_sint(pb, MATROSKA_ID_BLOCKREFERENCE,
+ mkv->last_track_timestamp[track_number - 1]);
+ }
+ mkv->last_track_timestamp[track_number - 1] = ts - mkv->cluster_pts;
+
+ 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 %" PRId64 ", 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)
+static void mkv_start_new_cluster(AVFormatContext *s, AVPacket *pkt)
{
MatroskaMuxContext *mkv = s->priv_data;
- int bufsize;
- uint8_t *dyn_buf;
- if (!mkv->dyn_bc)
- return;
-
- bufsize = avio_close_dyn_buf(mkv->dyn_bc, &dyn_buf);
- avio_write(s->pb, dyn_buf, bufsize);
- av_free(dyn_buf);
- mkv->dyn_bc = NULL;
+ end_ebml_master_crc32(s->pb, &mkv->dyn_bc, mkv, mkv->cluster);
+ mkv->cluster_pos = -1;
+ if (s->pb->seekable & AVIO_SEEKABLE_NORMAL)
+ av_log(s, AV_LOG_DEBUG,
+ "Starting new cluster at offset %" PRIu64 " bytes, "
+ "pts %" PRIu64 "dts %" PRIu64 "\n",
+ avio_tell(s->pb), pkt->pts, pkt->dts);
+ else
+ av_log(s, AV_LOG_DEBUG, "Starting new cluster, "
+ "pts %" PRIu64 "dts %" PRIu64 "\n",
+ pkt->pts, pkt->dts);
+ avio_flush(s->pb);
}
static int mkv_check_new_extra_data(AVFormatContext *s, AVPacket *pkt)
{
MatroskaMuxContext *mkv = s->priv_data;
- AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar;
mkv_track *track = &mkv->tracks[pkt->stream_index];
+ AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar;
uint8_t *side_data;
int side_data_size = 0, ret;
@@ -1653,8 +2271,8 @@ static int mkv_check_new_extra_data(AVFormatContext *s, AVPacket *pkt)
switch (par->codec_id) {
case AV_CODEC_ID_AAC:
- if (side_data_size && (s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
- int output_sample_rate = 0;
+ if (side_data_size && (s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) {
+ int filler, output_sample_rate = 0;
int64_t curpos;
ret = get_aac_sample_rates(s, side_data, side_data_size, &track->sample_rate,
&output_sample_rate);
@@ -1662,11 +2280,21 @@ static int mkv_check_new_extra_data(AVFormatContext *s, AVPacket *pkt)
return ret;
if (!output_sample_rate)
output_sample_rate = track->sample_rate; // Space is already reserved, so it's this or a void element.
- curpos = avio_tell(s->pb);
- avio_seek(s->pb, track->sample_rate_offset, SEEK_SET);
- put_ebml_float(s->pb, MATROSKA_ID_AUDIOSAMPLINGFREQ, track->sample_rate);
- put_ebml_float(s->pb, MATROSKA_ID_AUDIOOUTSAMPLINGFREQ, output_sample_rate);
- avio_seek(s->pb, curpos, SEEK_SET);
+ av_freep(&par->extradata);
+ ret = ff_alloc_extradata(par, side_data_size);
+ if (ret < 0)
+ return ret;
+ memcpy(par->extradata, side_data, side_data_size);
+ curpos = avio_tell(mkv->tracks_bc);
+ avio_seek(mkv->tracks_bc, track->codecpriv_offset, SEEK_SET);
+ mkv_write_codecprivate(s, mkv->tracks_bc, par, 1, 0);
+ filler = MAX_PCE_SIZE + 2 + 4 - (avio_tell(mkv->tracks_bc) - track->codecpriv_offset);
+ if (filler)
+ put_ebml_void(mkv->tracks_bc, filler);
+ avio_seek(mkv->tracks_bc, track->sample_rate_offset, SEEK_SET);
+ put_ebml_float(mkv->tracks_bc, MATROSKA_ID_AUDIOSAMPLINGFREQ, track->sample_rate);
+ put_ebml_float(mkv->tracks_bc, MATROSKA_ID_AUDIOOUTSAMPLINGFREQ, output_sample_rate);
+ avio_seek(mkv->tracks_bc, curpos, SEEK_SET);
} else if (!par->extradata_size && !track->sample_rate) {
// No extradata (codecpar or packet side data).
av_log(s, AV_LOG_ERROR, "Error parsing AAC extradata, unable to determine samplerate.\n");
@@ -1674,7 +2302,7 @@ static int mkv_check_new_extra_data(AVFormatContext *s, AVPacket *pkt)
}
break;
case AV_CODEC_ID_FLAC:
- if (side_data_size && (s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
+ if (side_data_size && (s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) {
AVCodecParameters *codecpriv_par;
int64_t curpos;
if (side_data_size != par->extradata_size) {
@@ -1691,10 +2319,10 @@ static int mkv_check_new_extra_data(AVFormatContext *s, AVPacket *pkt)
return ret;
}
memcpy(codecpriv_par->extradata, side_data, side_data_size);
- curpos = avio_tell(s->pb);
- avio_seek(s->pb, track->codecpriv_offset, SEEK_SET);
- mkv_write_codecprivate(s, s->pb, codecpriv_par, 1, 0);
- avio_seek(s->pb, curpos, SEEK_SET);
+ curpos = avio_tell(mkv->tracks_bc);
+ avio_seek(mkv->tracks_bc, track->codecpriv_offset, SEEK_SET);
+ mkv_write_codecprivate(s, mkv->tracks_bc, codecpriv_par, 1, 0);
+ avio_seek(mkv->tracks_bc, curpos, SEEK_SET);
avcodec_parameters_free(&codecpriv_par);
}
break;
@@ -1707,7 +2335,7 @@ static int mkv_check_new_extra_data(AVFormatContext *s, AVPacket *pkt)
return 0;
}
-static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
+static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt, int add_cue)
{
MatroskaMuxContext *mkv = s->priv_data;
AVIOContext *pb = s->pb;
@@ -1716,6 +2344,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");
@@ -1723,51 +2353,67 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
}
ts += mkv->tracks[pkt->stream_index].ts_offset;
- if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
- if (!mkv->dyn_bc) {
- ret = avio_open_dyn_buf(&mkv->dyn_bc);
- if (ret < 0)
- return ret;
+ 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);
}
- 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));
+ ret = start_ebml_master_crc32(s->pb, &mkv->dyn_bc, mkv, &mkv->cluster, MATROSKA_ID_CLUSTER, 0);
+ if (ret < 0)
+ return ret;
+ put_ebml_uint(mkv->dyn_bc, MATROSKA_ID_CLUSTERTIMECODE, FFMAX(0, ts));
mkv->cluster_pts = FFMAX(0, ts);
}
+ pb = mkv->dyn_bc;
+
+ relative_packet_pos = avio_tell(pb);
if (par->codec_type != AVMEDIA_TYPE_SUBTITLE) {
- mkv_write_block(s, pb, MATROSKA_ID_SIMPLEBLOCK, pkt, keyframe << 7);
- } else if (par->codec_id == AV_CODEC_ID_SSA) {
- duration = mkv_write_ass_blocks(s, pb, pkt);
- } else if (par->codec_id == AV_CODEC_ID_SRT) {
- duration = mkv_write_srt_blocks(s, pb, pkt);
+ mkv_write_block(s, pb, MATROSKA_ID_SIMPLEBLOCK, pkt, keyframe);
+ if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && (par->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 {
- ebml_master blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP,
- mkv_blockgroup_size(pkt->size));
- duration = pkt->duration;
+ if (par->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));
+
#if FF_API_CONVERGENCE_DURATION
FF_DISABLE_DEPRECATION_WARNINGS
- if (pkt->convergence_duration)
- duration = pkt->convergence_duration;
+ /* For backward compatibility, prefer convergence_duration. */
+ if (pkt->convergence_duration > 0) {
+ duration = pkt->convergence_duration;
+ }
FF_ENABLE_DEPRECATION_WARNINGS
#endif
- mkv_write_block(s, pb, MATROSKA_ID_BLOCK, pkt, 0);
- put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, duration);
- end_ebml_master(pb, blockgroup);
- }
+ /* All subtitle blocks are considered to be keyframes. */
+ mkv_write_block(s, pb, MATROSKA_ID_BLOCK, pkt, 1);
+ put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, duration);
+ end_ebml_master(pb, blockgroup);
+ }
- if (par->codec_type == AVMEDIA_TYPE_VIDEO && keyframe) {
- ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, ts,
- mkv->cluster_pos);
- if (ret < 0)
- return ret;
+ if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ 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);
+
+ if (mkv->stream_durations)
+ mkv->stream_durations[pkt->stream_index] =
+ FFMAX(mkv->stream_durations[pkt->stream_index], ts + duration);
+
return 0;
}
@@ -1778,8 +2424,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;
ret = mkv_check_new_extra_data(s, pkt);
if (ret < 0)
@@ -1793,28 +2439,30 @@ 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 & AVIO_SEEKABLE_NORMAL) {
- pb = s->pb;
- cluster_size = avio_tell(pb) - mkv->cluster_pos;
+ 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 {
- pb = mkv->dyn_bc;
- cluster_size = avio_tell(pb);
+ start_new_cluster = 0;
}
- if (mkv->cluster_pos &&
- (cluster_size > mkv->cluster_size_limit ||
- cluster_time > mkv->cluster_time_limit ||
- (codec_type == AVMEDIA_TYPE_VIDEO && keyframe &&
- cluster_size > 4 * 1024))) {
- av_log(s, AV_LOG_DEBUG,
- "Starting new cluster at offset %" PRIu64 " bytes, "
- "pts %" PRIu64 "dts %" PRIu64 "\n",
- avio_tell(pb), pkt->pts, pkt->dts);
- end_ebml_master(pb, mkv->cluster);
- mkv->cluster_pos = 0;
- if (mkv->dyn_bc)
- mkv_flush_dynbuf(s);
- avio_flush(s->pb);
+ if (mkv->cluster_pos != -1 && start_new_cluster) {
+ mkv_start_new_cluster(s, pkt);
}
if (!mkv->cluster_pos)
@@ -1824,7 +2472,9 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *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_packet_unref(&mkv->cur_audio_pkt);
if (ret < 0) {
av_log(s, AV_LOG_ERROR,
@@ -1838,27 +2488,24 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt)
if (codec_type == AVMEDIA_TYPE_AUDIO) {
ret = av_packet_ref(&mkv->cur_audio_pkt, pkt);
} else
- ret = mkv_write_packet_internal(s, pkt);
+ ret = mkv_write_packet_internal(s, pkt, 0);
return ret;
}
static int mkv_write_flush_packet(AVFormatContext *s, AVPacket *pkt)
{
MatroskaMuxContext *mkv = s->priv_data;
- AVIOContext *pb;
- if (s->pb->seekable & AVIO_SEEKABLE_NORMAL)
- pb = s->pb;
- else
- pb = mkv->dyn_bc;
+
if (!pkt) {
- if (mkv->cluster_pos) {
- 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;
- if (mkv->dyn_bc)
- mkv_flush_dynbuf(s);
+ if (mkv->cluster_pos != -1) {
+ end_ebml_master_crc32(s->pb, &mkv->dyn_bc, mkv, mkv->cluster);
+ mkv->cluster_pos = -1;
+ if (s->pb->seekable & AVIO_SEEKABLE_NORMAL)
+ av_log(s, AV_LOG_DEBUG,
+ "Flushing cluster at offset %" PRIu64 " bytes\n",
+ avio_tell(s->pb));
+ else
+ av_log(s, AV_LOG_DEBUG, "Flushing cluster\n");
avio_flush(s->pb);
}
return 1;
@@ -1875,7 +2522,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_packet_unref(&mkv->cur_audio_pkt);
if (ret < 0) {
av_log(s, AV_LOG_ERROR,
@@ -1885,19 +2532,15 @@ 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) {
- end_ebml_master(pb, mkv->cluster);
+ end_ebml_master_crc32(pb, &mkv->dyn_bc, mkv, mkv->cluster);
}
- if (mkv->mode != MODE_WEBM) {
- ret = mkv_write_chapters(s);
- if (ret < 0)
- return ret;
- }
+ ret = mkv_write_chapters(s);
+ if (ret < 0)
+ return ret;
- if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
+
+ if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) {
if (mkv->cues->num_entries) {
if (mkv->reserve_cues_space) {
int64_t cues_end;
@@ -1905,7 +2548,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,
@@ -1921,7 +2564,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,
@@ -1930,22 +2573,58 @@ static int mkv_write_trailer(AVFormatContext *s)
return ret;
}
- mkv_write_seekhead(pb, mkv->main_seekhead);
+ mkv_write_seekhead(pb, mkv);
// update the duration
av_log(s, AV_LOG_DEBUG, "end duration = %" PRIu64 "\n", mkv->duration);
currentpos = avio_tell(pb);
- avio_seek(pb, mkv->duration_offset, SEEK_SET);
- put_ebml_float(pb, MATROSKA_ID_DURATION, mkv->duration);
+ avio_seek(mkv->info_bc, mkv->duration_offset, SEEK_SET);
+ put_ebml_float(mkv->info_bc, MATROSKA_ID_DURATION, mkv->duration);
+ avio_seek(pb, mkv->info.pos, SEEK_SET);
+ end_ebml_master_crc32(pb, &mkv->info_bc, mkv, mkv->info);
+
+ // write tracks master
+ avio_seek(pb, mkv->tracks_master.pos, SEEK_SET);
+ end_ebml_master_crc32(pb, &mkv->tracks_bc, mkv, mkv->tracks_master);
+
+ // update stream durations
+ if (!mkv->is_live && mkv->stream_durations) {
+ int i;
+ int64_t curr = avio_tell(mkv->tags_bc);
+ for (i = 0; i < s->nb_streams; ++i) {
+ AVStream *st = s->streams[i];
+
+ if (mkv->stream_duration_offsets[i] > 0) {
+ double duration_sec = mkv->stream_durations[i] * av_q2d(st->time_base);
+ char duration_string[20] = "";
+
+ av_log(s, AV_LOG_DEBUG, "stream %d end duration = %" PRIu64 "\n", i,
+ mkv->stream_durations[i]);
+
+ avio_seek(mkv->tags_bc, mkv->stream_duration_offsets[i], SEEK_SET);
+
+ snprintf(duration_string, 20, "%02d:%02d:%012.9f",
+ (int) duration_sec / 3600, ((int) duration_sec / 60) % 60,
+ fmod(duration_sec, 60));
+
+ put_ebml_binary(mkv->tags_bc, MATROSKA_ID_TAGSTRING, duration_string, 20);
+ }
+ }
+ avio_seek(mkv->tags_bc, curr, SEEK_SET);
+ }
+ if (mkv->tags.pos && !mkv->is_live) {
+ avio_seek(pb, mkv->tags.pos, SEEK_SET);
+ end_ebml_master_crc32(pb, &mkv->tags_bc, mkv, mkv->tags);
+ }
avio_seek(pb, currentpos, SEEK_SET);
}
- end_ebml_master(pb, mkv->segment);
- av_free(mkv->tracks);
- av_freep(&mkv->cues->entries);
- av_freep(&mkv->cues);
+ if (!mkv->is_live) {
+ end_ebml_master(pb, mkv->segment);
+ }
+ mkv_free(mkv);
return 0;
}
@@ -1966,12 +2645,81 @@ static int mkv_query_codec(enum AVCodecID codec_id, int std_compliance)
return 0;
}
+static int mkv_init(struct AVFormatContext *s)
+{
+ int i;
+
+ if (s->avoid_negative_ts < 0) {
+ s->avoid_negative_ts = 1;
+ s->internal->avoid_negative_ts_use_pts = 1;
+ }
+
+ for (i = 0; i < s->nb_streams; i++) {
+ // ms precision is the de-facto standard timescale for mkv files
+ avpriv_set_pts_info(s->streams[i], 64, 1, 1000);
+ }
+
+ return 0;
+}
+
+static int mkv_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt)
+{
+ int ret = 1;
+ AVStream *st = s->streams[pkt->stream_index];
+
+ if (st->codecpar->codec_id == AV_CODEC_ID_AAC) {
+ if (pkt->size > 2 && (AV_RB16(pkt->data) & 0xfff0) == 0xfff0)
+ ret = ff_stream_add_bitstream_filter(st, "aac_adtstoasc", NULL);
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_VP9) {
+ ret = ff_stream_add_bitstream_filter(st, "vp9_superframe", NULL);
+ }
+
+ return ret;
+}
+
+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_QDMC, 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 }
+};
+
+static const AVCodecTag additional_subtitle_tags[] = {
+ { AV_CODEC_ID_DVB_SUBTITLE, 0xFFFFFFFF },
+ { AV_CODEC_ID_HDMV_PGS_SUBTITLE, 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_BOOL, { .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 },
+ { "live", "Write files assuming it is a live stream.", OFFSET(is_live), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+ { "allow_raw_vfw", "allow RAW VFW mode", OFFSET(allow_raw_vfw), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
+ { "write_crc32", "write a CRC32 element inside every Level 1 element", OFFSET(write_crc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS },
{ NULL },
};
@@ -1993,16 +2741,19 @@ AVOutputFormat ff_matroska_muxer = {
AV_CODEC_ID_VORBIS : AV_CODEC_ID_AC3,
.video_codec = CONFIG_LIBX264_ENCODER ?
AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4,
+ .init = mkv_init,
.write_header = mkv_write_header,
.write_packet = mkv_write_flush_packet,
.write_trailer = mkv_write_trailer,
.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, additional_subtitle_tags, 0
},
- .subtitle_codec = AV_CODEC_ID_SSA,
+ .subtitle_codec = AV_CODEC_ID_ASS,
.query_codec = mkv_query_codec,
+ .check_bitstream = mkv_check_bitstream,
.priv_class = &matroska_class,
};
#endif
@@ -2023,9 +2774,12 @@ AVOutputFormat ff_webm_muxer = {
.priv_data_size = sizeof(MatroskaMuxContext),
.audio_codec = CONFIG_LIBOPUS_ENCODER ? AV_CODEC_ID_OPUS : AV_CODEC_ID_VORBIS,
.video_codec = CONFIG_LIBVPX_VP9_ENCODER? AV_CODEC_ID_VP9 : AV_CODEC_ID_VP8,
+ .subtitle_codec = AV_CODEC_ID_WEBVTT,
+ .init = mkv_init,
.write_header = mkv_write_header,
.write_packet = mkv_write_flush_packet,
.write_trailer = mkv_write_trailer,
+ .check_bitstream = mkv_check_bitstream,
.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH,
.priv_class = &webm_class,
@@ -2041,19 +2795,23 @@ 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),
.audio_codec = CONFIG_LIBVORBIS_ENCODER ?
AV_CODEC_ID_VORBIS : AV_CODEC_ID_AC3,
.video_codec = AV_CODEC_ID_NONE,
+ .init = mkv_init,
.write_header = mkv_write_header,
.write_packet = mkv_write_flush_packet,
.write_trailer = mkv_write_trailer,
+ .check_bitstream = mkv_check_bitstream,
.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
deleted file mode 100644
index bd10df7..0000000
--- a/libavformat/md5enc.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.
- *
- * Libav is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * Libav is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "libavutil/md5.h"
-#include "avformat.h"
-#include "internal.h"
-
-struct MD5Context {
- struct AVMD5 *md5;
-};
-
-static void md5_finish(struct AVFormatContext *s, char *buf)
-{
- struct MD5Context *c = s->priv_data;
- uint8_t md5[16];
- int i, offset = strlen(buf);
- av_md5_final(c->md5, md5);
- for (i = 0; i < sizeof(md5); i++) {
- snprintf(buf + offset, 3, "%02"PRIx8, md5[i]);
- offset += 2;
- }
- buf[offset] = '\n';
- buf[offset+1] = 0;
-
- avio_write(s->pb, buf, strlen(buf));
- avio_flush(s->pb);
-}
-
-#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);
- 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);
- return 0;
-}
-
-static int write_trailer(struct AVFormatContext *s)
-{
- struct MD5Context *c = s->priv_data;
- char buf[64] = "MD5=";
-
- md5_finish(s, buf);
-
- av_freep(&c->md5);
- 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,
- .write_header = write_header,
- .write_packet = write_packet,
- .write_trailer = write_trailer,
- .flags = AVFMT_NOTIMESTAMPS,
-};
-#endif
-
-#if CONFIG_FRAMEMD5_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);
-}
-
-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);
-
- snprintf(buf, sizeof(buf) - 64, "%d, %10"PRId64", %10"PRId64", %8"PRId64", %8d, ",
- pkt->stream_index, pkt->dts, pkt->pts, pkt->duration, pkt->size);
- md5_finish(s, buf);
- return 0;
-}
-
-static int framemd5_write_trailer(struct AVFormatContext *s)
-{
- struct MD5Context *c = s->priv_data;
- av_freep(&c->md5);
- return 0;
-}
-
-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,
- .write_header = framemd5_write_header,
- .write_packet = framemd5_write_packet,
- .write_trailer = framemd5_write_trailer,
- .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT |
- AVFMT_TS_NEGATIVE,
-};
-#endif
diff --git a/libavformat/md5proto.c b/libavformat/md5proto.c
index 08cdd71..0e04b90 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
*/
@@ -69,9 +69,9 @@ static int md5_close(URLContext *h)
av_strstart(filename, "md5:", &filename);
if (*filename) {
- err = ffurl_open(&out, filename, AVIO_FLAG_WRITE,
- &h->interrupt_callback, NULL,
- h->protocols, h);
+ err = ffurl_open_whitelist(&out, filename, AVIO_FLAG_WRITE,
+ &h->interrupt_callback, NULL,
+ h->protocol_whitelist, h->protocol_blacklist, h);
if (err)
return err;
err = ffurl_write(out, buf, i*2+1);
diff --git a/libavformat/metadata.c b/libavformat/metadata.c
index 77fb298..b9b6de7 100644
--- a/libavformat/metadata.c
+++ b/libavformat/metadata.c
@@ -1,20 +1,20 @@
/*
* copyright (c) 2009 Michael Niedermayer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -33,7 +33,7 @@ void ff_metadata_conv(AVDictionary **pm, const AVMetadataConv *d_conv,
AVDictionary *dst = NULL;
const char *key;
- if (d_conv == s_conv)
+ if (d_conv == s_conv || !pm)
return;
while ((mtag = av_dict_get(*pm, "", mtag, AV_DICT_IGNORE_SUFFIX))) {
diff --git a/libavformat/metadata.h b/libavformat/metadata.h
index eee3ee4..6586094 100644
--- a/libavformat/metadata.h
+++ b/libavformat/metadata.h
@@ -1,20 +1,20 @@
/*
* copyright (c) 2009 Michael Niedermayer
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/mgsts.c b/libavformat/mgsts.c
new file mode 100644
index 0000000..0720de8
--- /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->codecpar->width = avio_rb32(pb);
+ st->codecpar->height = avio_rb32(pb);
+ avio_skip(pb, 12);
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_tag = avio_rb32(pb);
+ st->codecpar->codec_id = ff_codec_get_id(ff_codec_bmp_tags,
+ st->codecpar->codec_tag);
+ avpriv_set_pts_info(st, 64, fps.den, fps.num);
+ avio_skip(pb, 20);
+
+ return 0;
+}
+
+static int read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVIOContext *pb = s->pb;
+ uint32_t chunk_size, payload_size;
+ int ret;
+
+ if (avio_feof(pb))
+ return AVERROR_EOF;
+
+ avio_skip(pb, 4);
+ chunk_size = avio_rb32(pb);
+ avio_skip(pb, 4);
+ payload_size = avio_rb32(pb);
+
+ if (chunk_size < payload_size + 16)
+ return AVERROR(EIO);
+
+ ret = av_get_packet(pb, pkt, payload_size);
+ if (ret < 0)
+ return ret;
+
+ pkt->pos -= 16;
+ pkt->duration = 1;
+ avio_skip(pb, chunk_size - (ret + 16));
+
+ return ret;
+}
+
+AVInputFormat ff_mgsts_demuxer = {
+ .name = "mgsts",
+ .long_name = NULL_IF_CONFIG_SMALL("Metal Gear Solid: The Twin Snakes"),
+ .read_probe = read_probe,
+ .read_header = read_header,
+ .read_packet = read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/microdvddec.c b/libavformat/microdvddec.c
new file mode 100644
index 0000000..c2f1ac4
--- /dev/null
+++ b/libavformat/microdvddec.c
@@ -0,0 +1,204 @@
+/*
+ * 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;
+ continue;
+ }
+ if (!st->codecpar->extradata && sscanf(line, "{DEFAULT}{}%c", &c) == 1) {
+ st->codecpar->extradata = av_strdup(line + 11);
+ if (!st->codecpar->extradata)
+ return AVERROR(ENOMEM);
+ st->codecpar->extradata_size = strlen(st->codecpar->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(s, &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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->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 0000000..04f475b
--- /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)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ AVRational framerate = s->streams[0]->avg_frame_rate;
+
+ if (s->nb_streams != 1 || par->codec_id != AV_CODEC_ID_MICRODVD) {
+ av_log(s, AV_LOG_ERROR, "Exactly one MicroDVD stream is needed.\n");
+ return -1;
+ }
+
+ if (par->extradata && par->extradata_size > 0) {
+ avio_write(s->pb, "{DEFAULT}{}", 11);
+ avio_write(s->pb, par->extradata, par->extradata_size);
+ avio_flush(s->pb);
+ }
+
+ avpriv_set_pts_info(s->streams[0], 64, framerate.num, framerate.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/mj2kdec.c b/libavformat/mj2kdec.c
new file mode 100644
index 0000000..1fb9409
--- /dev/null
+++ b/libavformat/mj2kdec.c
@@ -0,0 +1,54 @@
+/*
+ * MJPEG 2000 Demuxer
+ * Copyright (c) 2016 Ståle Kristoffersen
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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"
+
+#if CONFIG_MJPEG_2000_DEMUXER
+static int mjpeg2000_probe(AVProbeData *p)
+{
+ const uint8_t *b = p->buf;
+ int i, marker, marker_size;
+ int frames = 0, invalid = 0;
+
+ for (i = 0; i < p->buf_size - 5; i++) {
+ if (AV_RB32(b) == 0xff4fff51){
+ marker_size = AV_RB16(b+4);
+ if (marker_size + i < p->buf_size - 4) {
+ marker = AV_RB8(b+4+marker_size);
+ if (marker == 0xff)
+ frames++;
+ else
+ invalid++;
+ }
+ }
+ b += 1;
+ }
+ if (invalid*4 + 1 < frames) {
+ if (invalid == 0 && frames > 2)
+ return AVPROBE_SCORE_EXTENSION / 2;
+ return AVPROBE_SCORE_EXTENSION / 4;
+ }
+ return 0;
+}
+FF_DEF_RAWVIDEO_DEMUXER2(mjpeg_2000, "raw MJPEG 2000 video", mjpeg2000_probe, "j2k", AV_CODEC_ID_JPEG2000, AVFMT_GENERIC_INDEX|AVFMT_NOTIMESTAMPS)
+#endif
diff --git a/libavformat/mkvtimestamp_v2.c b/libavformat/mkvtimestamp_v2.c
new file mode 100644
index 0000000..7ba6691
--- /dev/null
+++ b/libavformat/mkvtimestamp_v2.c
@@ -0,0 +1,50 @@
+/*
+ * extract pts as timecode v2, as defined by mkvtoolnix
+ * Copyright (c) 2009 David Conrad
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "internal.h"
+
+static int write_header(AVFormatContext *s)
+{
+ static const char *header = "# timecode format v2\n";
+ avio_write(s->pb, header, strlen(header));
+ avpriv_set_pts_info(s->streams[0], 64, 1, 1000);
+ return 0;
+}
+
+static int write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ char buf[256];
+ if (pkt->stream_index)
+ av_log(s, AV_LOG_WARNING, "More than one stream unsupported\n");
+ snprintf(buf, sizeof(buf), "%" PRId64 "\n", pkt->dts);
+ avio_write(s->pb, buf, strlen(buf));
+ return 0;
+}
+
+AVOutputFormat ff_mkvtimestamp_v2_muxer = {
+ .name = "mkvtimestamp_v2",
+ .long_name = NULL_IF_CONFIG_SMALL("extract pts as timecode v2 format, as defined by mkvtoolnix"),
+ .audio_codec = AV_CODEC_ID_NONE,
+ .video_codec = AV_CODEC_ID_RAWVIDEO,
+ .write_header = write_header,
+ .write_packet = write_packet,
+};
diff --git a/libavformat/mlpdec.c b/libavformat/mlpdec.c
new file mode 100644
index 0000000..d82df21
--- /dev/null
+++ b/libavformat/mlpdec.c
@@ -0,0 +1,88 @@
+/*
+ * MLP and TrueHD demuxer
+ * Copyright (c) 2001 Fabrice Bellard
+ * Copyright (c) 2005 Alex Beregszaszi
+ * Copyright (c) 2015 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 "rawdec.h"
+#include "libavutil/intreadwrite.h"
+
+static int av_always_inline mlp_thd_probe(AVProbeData *p, uint32_t sync)
+{
+ const uint8_t *buf, *last_buf = p->buf, *end = p->buf + p->buf_size;
+ int frames = 0, valid = 0, size = 0;
+ int nsubframes = 0;
+
+ for (buf = p->buf; buf + 8 <= end; buf++) {
+ if (AV_RB32(buf + 4) == sync) {
+ frames++;
+ if (last_buf + size == buf) {
+ valid += 1 + nsubframes / 8;
+ }
+ nsubframes = 0;
+ last_buf = buf;
+ size = (AV_RB16(buf) & 0xfff) * 2;
+ } else if (buf - last_buf == size) {
+ nsubframes++;
+ size += (AV_RB16(buf) & 0xfff) * 2;
+ }
+ }
+ if (valid >= 100)
+ return AVPROBE_SCORE_MAX;
+ return 0;
+}
+
+#if CONFIG_MLP_DEMUXER
+static int mlp_probe(AVProbeData *p)
+{
+ return mlp_thd_probe(p, 0xf8726fbb);
+}
+
+AVInputFormat ff_mlp_demuxer = {
+ .name = "mlp",
+ .long_name = NULL_IF_CONFIG_SMALL("raw MLP"),
+ .read_probe = mlp_probe,
+ .read_header = ff_raw_audio_read_header,
+ .read_packet = ff_raw_read_partial_packet,
+ .flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS,
+ .extensions = "mlp",
+ .raw_codec_id = AV_CODEC_ID_MLP,
+};
+#endif
+
+#if CONFIG_TRUEHD_DEMUXER
+static int thd_probe(AVProbeData *p)
+{
+ return mlp_thd_probe(p, 0xf8726fba);
+}
+
+AVInputFormat ff_truehd_demuxer = {
+ .name = "truehd",
+ .long_name = NULL_IF_CONFIG_SMALL("raw TrueHD"),
+ .read_probe = thd_probe,
+ .read_header = ff_raw_audio_read_header,
+ .read_packet = ff_raw_read_partial_packet,
+ .flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS,
+ .extensions = "thd",
+ .raw_codec_id = AV_CODEC_ID_TRUEHD,
+};
+#endif
+
diff --git a/libavformat/mlvdec.c b/libavformat/mlvdec.c
new file mode 100644
index 0000000..319cd26
--- /dev/null
+++ b/libavformat/mlvdec.c
@@ -0,0 +1,479 @@
+/*
+ * 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/imgutils.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/rational.h"
+#include "avformat.h"
+#include "avio_internal.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->codecpar->width = avio_rl16(pb);
+ vst->codecpar->height = avio_rl16(pb);
+ ret = av_image_check_size(vst->codecpar->width, vst->codecpar->height, 0, avctx);
+ if (ret < 0)
+ return ret;
+ if (avio_rl32(pb) != 1)
+ avpriv_request_sample(avctx, "raw api version");
+ avio_skip(pb, 20); // pointer, width, height, pitch, frame_size
+ vst->codecpar->bits_per_coded_sample = avio_rl32(pb);
+ if (vst->codecpar->bits_per_coded_sample < 0 ||
+ vst->codecpar->bits_per_coded_sample > (INT_MAX - 7) / (vst->codecpar->width * vst->codecpar->height)) {
+ av_log(avctx, AV_LOG_ERROR,
+ "invalid bits_per_coded_sample %d (size: %dx%d)\n",
+ vst->codecpar->bits_per_coded_sample,
+ vst->codecpar->width, vst->codecpar->height);
+ return AVERROR_INVALIDDATA;
+ }
+ 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->codecpar->format = AV_PIX_FMT_BAYER_RGGB16LE;
+ vst->codecpar->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(avctx, pb, ast->codecpar, 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 %s, size %u\n",
+ av_fourcc2str(type), 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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ switch (mlv->class[0] & ~(MLV_CLASS_FLAG_DELTA|MLV_CLASS_FLAG_LZMA)) {
+ case MLV_VIDEO_CLASS_RAW:
+ vst->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
+ break;
+ case MLV_VIDEO_CLASS_YUV:
+ vst->codecpar->format = AV_PIX_FMT_YUV420P;
+ vst->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
+ vst->codecpar->codec_tag = 0;
+ break;
+ case MLV_VIDEO_CLASS_JPEG:
+ vst->codecpar->codec_id = AV_CODEC_ID_MJPEG;
+ vst->codecpar->codec_tag = 0;
+ break;
+ case MLV_VIDEO_CLASS_H264:
+ vst->codecpar->codec_id = AV_CODEC_ID_H264;
+ vst->codecpar->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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ avpriv_set_pts_info(ast, 33, 1, ast->codecpar->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 (avctx->io_open(avctx, &mlv->pb[i], filename, AVIO_FLAG_READ, 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);
+ ff_format_io_close(avctx, &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));
+ ff_format_io_close(avctx, &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 && !vst->nb_index_entries) || (ast && !ast->nb_index_entries)) {
+ av_log(avctx, AV_LOG_ERROR, "no index entries found\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ 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->codecpar->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->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ ret = av_get_packet(pb, pkt, (st->codecpar->width * st->codecpar->height * st->codecpar->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 & AVIO_SEEKABLE_NORMAL))
+ 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])
+ ff_format_io_close(s, &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 1650550..8a1382e 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 454e609..1393627 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]->codecpar->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]->codecpar->sample_rate);
- return -1;
+ return AVERROR(EINVAL);
+ }
+
+ mmf->stereo = s->streams[0]->codecpar->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]->codecpar->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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = AV_CODEC_ID_ADPCM_YAMAHA;
st->codecpar->sample_rate = rate;
- st->codecpar->channels = 1;
- st->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
+ st->codecpar->channels = (params >> 7) + 1;
+ st->codecpar->channel_layout = params >> 7 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO;
st->codecpar->bits_per_coded_sample = 4;
st->codecpar->bit_rate = st->codecpar->sample_rate *
st->codecpar->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_packet_unref(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 fc967c1..17fa76a 100644
--- a/libavformat/mms.c
+++ b/libavformat/mms.c
@@ -4,20 +4,20 @@
* Copyright (c) 2007 Björn Axelsson
* Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mms.h"
diff --git a/libavformat/mms.h b/libavformat/mms.h
index e89da41..57e3d7e 100644
--- a/libavformat/mms.h
+++ b/libavformat/mms.h
@@ -2,20 +2,20 @@
* MMS protocol common definitions.
* Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef AVFORMAT_MMS_H
diff --git a/libavformat/mmsh.c b/libavformat/mmsh.c
index 4029b86..13c0ffe 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,16 +222,16 @@ 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);
if (ffurl_alloc(&mms->mms_hd, httpname, AVIO_FLAG_READ,
- &h->interrupt_callback, h->protocols) < 0) {
+ &h->interrupt_callback) < 0) {
return AVERROR(EIO);
}
@@ -241,10 +242,18 @@ 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);
+ if (!mms->mms_hd->protocol_whitelist && h->protocol_whitelist) {
+ mms->mms_hd->protocol_whitelist = av_strdup(h->protocol_whitelist);
+ if (!mms->mms_hd->protocol_whitelist) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+ }
+
err = ffurl_connect(mms->mms_hd, NULL);
if (err) {
goto fail;
@@ -259,7 +268,7 @@ static int mmsh_open(URLContext *h, const char *uri, int flags)
ffurl_close(mms->mms_hd);
memset(headers, 0, sizeof(headers));
if ((err = ffurl_alloc(&mms->mms_hd, httpname, AVIO_FLAG_READ,
- &h->interrupt_callback, h->protocols)) < 0) {
+ &h->interrupt_callback)) < 0) {
goto fail;
}
stream_selection = av_mallocz(mms->stream_num * 19 + 1);
@@ -282,8 +291,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 +322,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 +373,50 @@ 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);
+}
+
const 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,
+ .default_whitelist = "http,tcp",
};
diff --git a/libavformat/mmst.c b/libavformat/mmst.c
index f3a5157..a97c2e0 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;
}
@@ -519,8 +528,9 @@ static int mms_open(URLContext *h, const char *uri, int flags)
// establish tcp connection.
ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, mmst->host, port, NULL);
- err = ffurl_open(&mms->mms_hd, tcpname, AVIO_FLAG_READ_WRITE,
- &h->interrupt_callback, NULL, h->protocols, h);
+ err = ffurl_open_whitelist(&mms->mms_hd, tcpname, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, NULL,
+ h->protocol_whitelist, h->protocol_blacklist, h);
if (err)
goto fail;
diff --git a/libavformat/mov.c b/libavformat/mov.c
index c6e7a38..573f134 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
*/
@@ -29,25 +29,32 @@
#include "libavutil/attributes.h"
#include "libavutil/channel_layout.h"
+#include "libavutil/internal.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/intfloat.h"
#include "libavutil/mathematics.h"
#include "libavutil/time_internal.h"
+#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/dict.h"
+#include "libavutil/display.h"
#include "libavutil/opt.h"
+#include "libavutil/aes.h"
+#include "libavutil/aes_ctr.h"
#include "libavutil/pixdesc.h"
+#include "libavutil/sha.h"
#include "libavutil/spherical.h"
#include "libavutil/stereo3d.h"
-
+#include "libavutil/timecode.h"
#include "libavcodec/ac3tab.h"
-#include "libavcodec/bitstream.h"
-
+#include "libavcodec/flac.h"
+#include "libavcodec/mpegaudiodecheader.h"
#include "avformat.h"
#include "internal.h"
#include "avio_internal.h"
#include "riff.h"
#include "isom.h"
+#include "libavcodec/get_bits.h"
#include "id3v1.h"
#include "mov_chan.h"
#include "replaygain.h"
@@ -58,7 +65,6 @@
#include "qtpalette.h"
-
/* those functions parse an atom */
/* links atom IDs to parse functions */
typedef struct MOVParseTableEntry {
@@ -67,6 +73,9 @@ 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 int64_t add_ctts_entry(MOVStts** ctts_data, unsigned int* ctts_count, unsigned int* allocated_size,
+ int count, int duration);
static int mov_metadata_track_or_disc_number(MOVContext *c, AVIOContext *pb,
unsigned len, const char *key)
@@ -91,16 +100,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;
}
@@ -108,11 +114,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;
}
@@ -121,16 +124,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;
}
@@ -169,7 +170,7 @@ static int mov_read_mac_string(MOVContext *c, AVIOContext *pb, int len,
if (c < 0x80)
*p++ = c;
- else
+ else if (p < end)
PUT_UTF8(mac_to_unicode[c-0x80], t, if (p < end) *p++ = t;);
}
*p = 0;
@@ -277,15 +278,34 @@ static int mov_metadata_loci(MOVContext *c, AVIOContext *pb, unsigned len)
return av_dict_set(&c->fc->metadata, key, buf, 0);
}
+static int mov_metadata_hmmt(MOVContext *c, AVIOContext *pb, unsigned len)
+{
+ int i, n_hmmt;
+
+ if (len < 2)
+ return 0;
+ if (c->ignore_chapters)
+ return 0;
+
+ n_hmmt = avio_rb32(pb);
+ for (i = 0; i < n_hmmt && !pb->eof_reached; i++) {
+ int moment_time = avio_rb32(pb);
+ avpriv_new_chapter(c->fc, i, av_make_q(1, 1000), moment_time, AV_NOPTS_VALUE, NULL);
+ }
+ return 0;
+}
+
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;
int (*parse)(MOVContext*, AVIOContext*, unsigned, const char*) = NULL;
int raw = 0;
+ int num = 0;
switch (atom.type) {
case MKTAG( '@','P','R','M'): key = "premiere_version"; raw = 1; break;
@@ -305,10 +325,13 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
parse = mov_metadata_track_or_disc_number; break;
case MKTAG( 'e','g','i','d'): key = "episode_uid";
parse = mov_metadata_int8_no_padding; break;
+ case MKTAG( 'F','I','R','M'): key = "firmware"; raw = 1; break;
case MKTAG( 'g','n','r','e'): key = "genre";
parse = mov_metadata_gnre; break;
case MKTAG( 'h','d','v','d'): key = "hd_video";
parse = mov_metadata_int8_no_padding; break;
+ case MKTAG( 'H','M','M','T'):
+ return mov_metadata_hmmt(c, pb, atom.size);
case MKTAG( 'k','e','y','w'): key = "keywords"; break;
case MKTAG( 'l','d','e','s'): key = "synopsis"; break;
case MKTAG( 'l','o','c','i'):
@@ -373,11 +396,11 @@ 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);
- if (tag == MKTAG('d','a','t','a')) {
+ if (tag == MKTAG('d','a','t','a') && data_size <= atom.size) {
data_type = avio_rb32(pb); // type
avio_rb32(pb); // unknown
str_size = data_size - 16;
@@ -387,12 +410,27 @@ 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 if (!key && c->found_hdlr_mdta && c->meta_keys) {
+ uint32_t index = AV_RB32(&atom.type);
+ if (index < c->meta_keys_count && index > 0) {
+ key = c->meta_keys[index];
+ } else {
+ av_log(c->fc, AV_LOG_WARNING,
+ "The index of 'data' is out of range: %"PRId32" < 1 or >= %d.\n",
+ index, c->meta_keys_count);
}
}
} 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;
@@ -406,12 +444,14 @@ 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
- str_size_alloc = (raw ? str_size : str_size * 2) + 1;
- str = av_malloc(str_size_alloc);
+ // Allocates enough space if data_type is a int32 or float32 number, otherwise
+ // worst-case requirement for output string in case of utf8 coded input
+ num = (data_type >= 21 && data_type <= 23);
+ str_size_alloc = (num ? 512 : (raw ? str_size : str_size * 2)) + 1;
+ str = av_mallocz(str_size_alloc);
if (!str)
return AVERROR(ENOMEM);
@@ -420,6 +460,46 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
else {
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 if (data_type == 21) { // BE signed integer, variable size
+ int val = 0;
+ if (str_size == 1)
+ val = (int8_t)avio_r8(pb);
+ else if (str_size == 2)
+ val = (int16_t)avio_rb16(pb);
+ else if (str_size == 3)
+ val = ((int32_t)(avio_rb24(pb)<<8))>>8;
+ else if (str_size == 4)
+ val = (int32_t)avio_rb32(pb);
+ if (snprintf(str, str_size_alloc, "%d", val) >= str_size_alloc) {
+ av_log(c->fc, AV_LOG_ERROR,
+ "Failed to store the number (%d) in string.\n", val);
+ av_free(str);
+ return AVERROR_INVALIDDATA;
+ }
+ } else if (data_type == 22) { // BE unsigned integer, variable size
+ unsigned int val = 0;
+ if (str_size == 1)
+ val = avio_r8(pb);
+ else if (str_size == 2)
+ val = avio_rb16(pb);
+ else if (str_size == 3)
+ val = avio_rb24(pb);
+ else if (str_size == 4)
+ val = avio_rb32(pb);
+ if (snprintf(str, str_size_alloc, "%u", val) >= str_size_alloc) {
+ av_log(c->fc, AV_LOG_ERROR,
+ "Failed to store the number (%u) in string.\n", val);
+ av_free(str);
+ return AVERROR_INVALIDDATA;
+ }
+ } else if (data_type == 23 && str_size >= 4) { // BE float32
+ float val = av_int2float(avio_rb32(pb));
+ if (snprintf(str, str_size_alloc, "%f", val) >= str_size_alloc) {
+ av_log(c->fc, AV_LOG_ERROR,
+ "Failed to store the float32 number (%f) in string.\n", val);
+ av_free(str);
+ return AVERROR_INVALIDDATA;
+ }
} else {
int ret = ffio_read_size(pb, str, str_size);
if (ret < 0) {
@@ -434,6 +514,12 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
snprintf(key2, sizeof(key2), "%s-%s", key, language);
av_dict_set(&c->fc->metadata, key2, str, 0);
}
+ if (!strcmp(key, "encoder")) {
+ int major, minor, micro;
+ if (sscanf(str, "HandBrake %d.%d.%d", &major, &minor, &micro) == 3) {
+ c->handbrake_version = 1000000*major + 1000*minor + micro;
+ }
+ }
}
av_freep(&str);
@@ -447,6 +533,9 @@ static int mov_read_chpl(MOVContext *c, AVIOContext *pb, MOVAtom atom)
char str[256+1];
int ret;
+ if (c->ignore_chapters)
+ return 0;
+
if ((atom.size -= 5) < 0)
return 0;
@@ -495,6 +584,7 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return AVERROR_INVALIDDATA;
sc->drefs_count = 0;
av_free(sc->drefs);
+ sc->drefs_count = 0;
sc->drefs = av_mallocz(entries * sizeof(*sc->drefs));
if (!sc->drefs)
return AVERROR(ENOMEM);
@@ -525,8 +615,7 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (ret < 0)
return ret;
dref->volume[volume_len] = 0;
- av_log(c->fc, AV_LOG_DEBUG, "volume %s, len %"PRIu16"\n",
- dref->volume, volume_len);
+ av_log(c->fc, AV_LOG_DEBUG, "volume %s, len %d\n", dref->volume, volume_len);
avio_skip(pb, 12);
@@ -536,26 +625,24 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (ret < 0)
return ret;
dref->filename[len] = 0;
- av_log(c->fc, AV_LOG_DEBUG, "filename %s, len %"PRIu16"\n",
- dref->filename, len);
+ av_log(c->fc, AV_LOG_DEBUG, "filename %s, len %d\n", dref->filename, len);
avio_skip(pb, 16);
/* read next level up_from_alias/down_to_target */
dref->nlvl_from = avio_rb16(pb);
dref->nlvl_to = avio_rb16(pb);
- av_log(c->fc, AV_LOG_DEBUG, "nlvl from %"PRId16", nlvl to %"PRId16"\n",
+ av_log(c->fc, AV_LOG_DEBUG, "nlvl from %d, nlvl to %d\n",
dref->nlvl_from, dref->nlvl_to);
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);
- av_log(c->fc, AV_LOG_DEBUG, "type %"PRId16", len %"PRIu16"\n",
- type, len);
+ av_log(c->fc, AV_LOG_DEBUG, "type %d, len %d\n", type, len);
if (len&1)
len += 1;
if (type == 2) { // absolute path
@@ -605,7 +692,7 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom)
avio_skip(pb, len);
}
} else {
- av_log(c->fc, AV_LOG_DEBUG, "Unknown dref type 0x08%"PRIx32" size %"PRIu32"\n",
+ av_log(c->fc, AV_LOG_DEBUG, "Unknown dref type 0x%08"PRIx32" size %"PRIu32"\n",
dref->type, size);
entries--;
i--;
@@ -619,16 +706,11 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
uint32_t type;
- uint32_t av_unused ctype;
+ uint32_t ctype;
int64_t title_size;
char *title_str;
int ret;
- if (c->fc->nb_streams < 1) // meta before first trak
- return 0;
-
- st = c->fc->streams[c->fc->nb_streams-1];
-
avio_r8(pb); /* version */
avio_rb24(pb); /* flags */
@@ -636,8 +718,17 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
ctype = avio_rl32(pb);
type = avio_rl32(pb); /* component subtype */
- av_log(c->fc, AV_LOG_TRACE, "ctype= %.4s (0x%08"PRIx32")\n", (char *)&ctype, ctype);
- av_log(c->fc, AV_LOG_TRACE, "stype= %.4s\n", (char*)&type);
+ av_log(c->fc, AV_LOG_TRACE, "ctype=%s\n", av_fourcc2str(ctype));
+ av_log(c->fc, AV_LOG_TRACE, "stype=%s\n", av_fourcc2str(type));
+
+ if (c->trak_index < 0) { // meta not inside a trak
+ if (type == MKTAG('m','d','t','a')) {
+ c->found_hdlr_mdta = 1;
+ }
+ return 0;
+ }
+
+ st = c->fc->streams[c->fc->nb_streams-1];
if (type == MKTAG('v','i','d','e'))
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
@@ -654,6 +745,8 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
title_size = atom.size - 24;
if (title_size > 0) {
+ if (title_size > FFMIN(INT_MAX, SIZE_MAX-1))
+ return AVERROR_INVALIDDATA;
title_str = av_malloc(title_size + 1); /* Add null terminator */
if (!title_str)
return AVERROR(ENOMEM);
@@ -777,6 +870,68 @@ static int mov_read_dec3(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
}
+static int mov_read_ddts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ const uint32_t ddts_size = 20;
+ AVStream *st = NULL;
+ uint8_t *buf = NULL;
+ uint32_t frame_duration_code = 0;
+ uint32_t channel_layout_code = 0;
+ GetBitContext gb;
+
+ buf = av_malloc(ddts_size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!buf) {
+ return AVERROR(ENOMEM);
+ }
+ if (avio_read(pb, buf, ddts_size) < ddts_size) {
+ av_free(buf);
+ return AVERROR_INVALIDDATA;
+ }
+
+ init_get_bits(&gb, buf, 8*ddts_size);
+
+ if (c->fc->nb_streams < 1) {
+ av_free(buf);
+ return 0;
+ }
+ st = c->fc->streams[c->fc->nb_streams-1];
+
+ st->codecpar->sample_rate = get_bits_long(&gb, 32);
+ if (st->codecpar->sample_rate <= 0) {
+ av_log(c->fc, AV_LOG_ERROR, "Invalid sample rate %d\n", st->codecpar->sample_rate);
+ av_free(buf);
+ return AVERROR_INVALIDDATA;
+ }
+ skip_bits_long(&gb, 32); /* max bitrate */
+ st->codecpar->bit_rate = get_bits_long(&gb, 32);
+ st->codecpar->bits_per_coded_sample = get_bits(&gb, 8);
+ frame_duration_code = get_bits(&gb, 2);
+ skip_bits(&gb, 30); /* various fields */
+ channel_layout_code = get_bits(&gb, 16);
+
+ st->codecpar->frame_size =
+ (frame_duration_code == 0) ? 512 :
+ (frame_duration_code == 1) ? 1024 :
+ (frame_duration_code == 2) ? 2048 :
+ (frame_duration_code == 3) ? 4096 : 0;
+
+ if (channel_layout_code > 0xff) {
+ av_log(c->fc, AV_LOG_WARNING, "Unsupported DTS audio channel layout");
+ }
+ st->codecpar->channel_layout =
+ ((channel_layout_code & 0x1) ? AV_CH_FRONT_CENTER : 0) |
+ ((channel_layout_code & 0x2) ? AV_CH_FRONT_LEFT : 0) |
+ ((channel_layout_code & 0x2) ? AV_CH_FRONT_RIGHT : 0) |
+ ((channel_layout_code & 0x4) ? AV_CH_SIDE_LEFT : 0) |
+ ((channel_layout_code & 0x4) ? AV_CH_SIDE_RIGHT : 0) |
+ ((channel_layout_code & 0x8) ? AV_CH_LOW_FREQUENCY : 0);
+
+ st->codecpar->channels = av_get_channel_layout_nb_channels(st->codecpar->channel_layout);
+ av_free(buf);
+
+ return 0;
+}
+
static int mov_read_chan(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
@@ -799,12 +954,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(c->fc, pb, st->codecpar, atom.size);
+ if ((ret = ff_get_wav_header(c->fc, pb, st->codecpar, 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)
@@ -824,8 +983,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;
}
@@ -839,12 +998,125 @@ static int mov_read_mdat(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0; /* now go for moov */
}
+#define DRM_BLOB_SIZE 56
+
+static int mov_read_adrm(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ uint8_t intermediate_key[20];
+ uint8_t intermediate_iv[20];
+ uint8_t input[64];
+ uint8_t output[64];
+ uint8_t file_checksum[20];
+ uint8_t calculated_checksum[20];
+ struct AVSHA *sha;
+ int i;
+ int ret = 0;
+ uint8_t *activation_bytes = c->activation_bytes;
+ uint8_t *fixed_key = c->audible_fixed_key;
+
+ c->aax_mode = 1;
+
+ sha = av_sha_alloc();
+ if (!sha)
+ return AVERROR(ENOMEM);
+ c->aes_decrypt = av_aes_alloc();
+ if (!c->aes_decrypt) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ /* drm blob processing */
+ avio_read(pb, output, 8); // go to offset 8, absolute position 0x251
+ avio_read(pb, input, DRM_BLOB_SIZE);
+ avio_read(pb, output, 4); // go to offset 4, absolute position 0x28d
+ avio_read(pb, file_checksum, 20);
+
+ av_log(c->fc, AV_LOG_INFO, "[aax] file checksum == "); // required by external tools
+ for (i = 0; i < 20; i++)
+ av_log(c->fc, AV_LOG_INFO, "%02x", file_checksum[i]);
+ av_log(c->fc, AV_LOG_INFO, "\n");
+
+ /* verify activation data */
+ if (!activation_bytes) {
+ av_log(c->fc, AV_LOG_WARNING, "[aax] activation_bytes option is missing!\n");
+ ret = 0; /* allow ffprobe to continue working on .aax files */
+ goto fail;
+ }
+ if (c->activation_bytes_size != 4) {
+ av_log(c->fc, AV_LOG_FATAL, "[aax] activation_bytes value needs to be 4 bytes!\n");
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+
+ /* verify fixed key */
+ if (c->audible_fixed_key_size != 16) {
+ av_log(c->fc, AV_LOG_FATAL, "[aax] audible_fixed_key value needs to be 16 bytes!\n");
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+
+ /* AAX (and AAX+) key derivation */
+ av_sha_init(sha, 160);
+ av_sha_update(sha, fixed_key, 16);
+ av_sha_update(sha, activation_bytes, 4);
+ av_sha_final(sha, intermediate_key);
+ av_sha_init(sha, 160);
+ av_sha_update(sha, fixed_key, 16);
+ av_sha_update(sha, intermediate_key, 20);
+ av_sha_update(sha, activation_bytes, 4);
+ av_sha_final(sha, intermediate_iv);
+ av_sha_init(sha, 160);
+ av_sha_update(sha, intermediate_key, 16);
+ av_sha_update(sha, intermediate_iv, 16);
+ av_sha_final(sha, calculated_checksum);
+ if (memcmp(calculated_checksum, file_checksum, 20)) { // critical error
+ av_log(c->fc, AV_LOG_ERROR, "[aax] mismatch in checksums!\n");
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ av_aes_init(c->aes_decrypt, intermediate_key, 128, 1);
+ av_aes_crypt(c->aes_decrypt, output, input, DRM_BLOB_SIZE >> 4, intermediate_iv, 1);
+ for (i = 0; i < 4; i++) {
+ // file data (in output) is stored in big-endian mode
+ if (activation_bytes[i] != output[3 - i]) { // critical error
+ av_log(c->fc, AV_LOG_ERROR, "[aax] error in drm blob decryption!\n");
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ }
+ memcpy(c->file_key, output + 8, 16);
+ memcpy(input, output + 26, 16);
+ av_sha_init(sha, 160);
+ av_sha_update(sha, input, 16);
+ av_sha_update(sha, c->file_key, 16);
+ av_sha_update(sha, fixed_key, 16);
+ av_sha_final(sha, c->file_iv);
+
+fail:
+ av_free(sha);
+
+ return ret;
+}
+
+// Audible AAX (and AAX+) bytestream decryption
+static int aax_filter(uint8_t *input, int size, MOVContext *c)
+{
+ int blocks = 0;
+ unsigned char iv[16];
+
+ memcpy(iv, c->file_iv, 16); // iv is overwritten
+ blocks = size >> 4; // trailing bytes are not encrypted!
+ av_aes_init(c->aes_decrypt, c->file_key, 128, 1);
+ av_aes_crypt(c->aes_decrypt, input, input, blocks, iv, 1);
+
+ return 0;
+}
+
/* read major brand, minor version and compatible brands and store them as metadata */
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};
int ret = ffio_read_size(pb, type, 4);
@@ -856,8 +1128,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)
@@ -883,6 +1154,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' */
@@ -893,21 +1170,38 @@ 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 & AVIO_SEEKABLE_NORMAL) {
+ 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_log(c->fc, AV_LOG_TRACE, "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);
- if (!ptm) return;
- if (strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", ptm))
- av_dict_set(metadata, "creation_time", buffer, 0);
+ if(time >= 2082844800)
+ time -= 2082844800; /* seconds between 1904-01-01 and Epoch */
+
+ if ((int64_t)(time * 1000000ULL) / 1000000 != time) {
+ av_log(NULL, AV_LOG_DEBUG, "creation_time is not representable\n");
+ return;
+ }
+
+ avpriv_dict_set_timestamp(metadata, "creation_time", time * 1000000);
}
}
@@ -918,7 +1212,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;
@@ -946,6 +1240,10 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
mov_metadata_creation_time(&st->metadata, creation_time);
sc->time_scale = avio_rb32(pb);
+ if (sc->time_scale <= 0) {
+ av_log(c->fc, AV_LOG_ERROR, "Invalid mdhd time scale %d, defaulting to 1\n", sc->time_scale);
+ sc->time_scale = 1;
+ }
st->duration = (version == 1) ? avio_rb64(pb) : avio_rb32(pb); /* duration */
lang = avio_rb16(pb); /* language */
@@ -959,7 +1257,7 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
static int mov_read_mvhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
int i;
- time_t creation_time;
+ int64_t creation_time;
int version = avio_r8(pb); /* version */
avio_rb24(pb); /* flags */
@@ -972,10 +1270,17 @@ static int mov_read_mvhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
}
mov_metadata_creation_time(&c->fc->metadata, creation_time);
c->time_scale = avio_rb32(pb); /* time scale */
-
+ if (c->time_scale <= 0) {
+ av_log(c->fc, AV_LOG_ERROR, "Invalid mvhd time scale %d, defaulting to 1\n", c->time_scale);
+ c->time_scale = 1;
+ }
av_log(c->fc, AV_LOG_TRACE, "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 */
@@ -1000,35 +1305,6 @@ static int mov_read_mvhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
}
-static int mov_read_smi(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];
-
- 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->codecpar->extradata);
- st->codecpar->extradata = av_mallocz(atom.size + 0x5a + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
- return AVERROR(ENOMEM);
- st->codecpar->extradata_size = 0x5a + atom.size;
- memcpy(st->codecpar->extradata, "SVQ3", 4); // fake
-
- ret = ffio_read_size(pb, st->codecpar->extradata + 0x5a, atom.size);
- if (ret < 0)
- return ret;
-
- av_log(c->fc, AV_LOG_TRACE, "Reading SMI %"PRId64" %s\n", atom.size, st->codecpar->extradata + 0x5a);
- return 0;
-}
-
static int mov_read_enda(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
@@ -1038,7 +1314,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_log(c->fc, AV_LOG_TRACE, "enda %d\n", little_endian);
if (little_endian == 1) {
switch (st->codecpar->codec_id) {
@@ -1087,7 +1363,7 @@ static int mov_read_colr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
color_matrix = avio_rb16(pb);
av_log(c->fc, AV_LOG_TRACE,
- "%s: pri %"PRIu16" trc %"PRIu16" matrix %"PRIu16"",
+ "%s: pri %d trc %d matrix %d",
color_parameter_type, color_primaries, color_trc, color_matrix);
if (!strncmp(color_parameter_type, "nclx", 4)) {
@@ -1097,41 +1373,18 @@ static int mov_read_colr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
st->codecpar->color_range = AVCOL_RANGE_JPEG;
else
st->codecpar->color_range = AVCOL_RANGE_MPEG;
-
- if (!av_color_primaries_name(color_primaries))
- color_primaries = AVCOL_PRI_UNSPECIFIED;
- if (!av_color_transfer_name(color_trc))
- color_trc = AVCOL_TRC_UNSPECIFIED;
- if (!av_color_space_name(color_matrix))
- color_matrix = AVCOL_SPC_UNSPECIFIED;
-
- st->codecpar->color_primaries = color_primaries;
- st->codecpar->color_trc = color_trc;
- st->codecpar->color_space = color_matrix;
- } else if (!strncmp(color_parameter_type, "nclc", 4)) {
- /* color primaries, Table 4-4 */
- switch (color_primaries) {
- case 1: st->codecpar->color_primaries = AVCOL_PRI_BT709; break;
- case 5: st->codecpar->color_primaries = AVCOL_PRI_SMPTE170M; break;
- case 6: st->codecpar->color_primaries = AVCOL_PRI_SMPTE240M; break;
- case 9: st->codecpar->color_primaries = AVCOL_PRI_BT2020; break;
- case 10: st->codecpar->color_primaries = AVCOL_PRI_SMPTE431; break;
- case 11: st->codecpar->color_primaries = AVCOL_PRI_SMPTE432; break;
- }
- /* color transfer, Table 4-5 */
- switch (color_trc) {
- case 1: st->codecpar->color_trc = AVCOL_TRC_BT709; break;
- case 7: st->codecpar->color_trc = AVCOL_TRC_SMPTE240M; break;
- case 17: st->codecpar->color_trc = AVCOL_TRC_SMPTE428; break;
- }
- /* color matrix, Table 4-6 */
- switch (color_matrix) {
- case 1: st->codecpar->color_space = AVCOL_SPC_BT709; break;
- case 6: st->codecpar->color_space = AVCOL_SPC_BT470BG; break;
- case 7: st->codecpar->color_space = AVCOL_SPC_SMPTE240M; break;
- case 9: st->codecpar->color_space = AVCOL_SPC_BT2020_NCL; break;
- }
}
+
+ if (!av_color_primaries_name(color_primaries))
+ color_primaries = AVCOL_PRI_UNSPECIFIED;
+ if (!av_color_transfer_name(color_trc))
+ color_trc = AVCOL_TRC_UNSPECIFIED;
+ if (!av_color_space_name(color_matrix))
+ color_matrix = AVCOL_SPC_UNSPECIFIED;
+
+ st->codecpar->color_primaries = color_primaries;
+ st->codecpar->color_trc = color_trc;
+ st->codecpar->color_space = color_matrix;
av_log(c->fc, AV_LOG_TRACE, "\n");
return 0;
@@ -1171,34 +1424,197 @@ static int mov_read_fiel(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
}
+static int mov_realloc_extradata(AVCodecParameters *par, MOVAtom atom)
+{
+ int err = 0;
+ uint64_t size = (uint64_t)par->extradata_size + atom.size + 8 + AV_INPUT_BUFFER_PADDING_SIZE;
+ if (size > INT_MAX || (uint64_t)atom.size > INT_MAX)
+ return AVERROR_INVALIDDATA;
+ if ((err = av_reallocp(&par->extradata, size)) < 0) {
+ par->extradata_size = 0;
+ return err;
+ }
+ par->extradata_size = size - AV_INPUT_BUFFER_PADDING_SIZE;
+ return 0;
+}
+
+/* Read a whole atom into the extradata return the size of the atom read, possibly truncated if != atom.size */
+static int64_t mov_read_atom_into_extradata(MOVContext *c, AVIOContext *pb, MOVAtom atom,
+ AVCodecParameters *par, uint8_t *buf)
+{
+ int64_t result = atom.size;
+ int err;
+
+ AV_WB32(buf , atom.size + 8);
+ AV_WL32(buf + 4, atom.type);
+ err = ffio_read_size(pb, buf + 8, atom.size);
+ if (err < 0) {
+ par->extradata_size -= atom.size;
+ return err;
+ } else if (err < atom.size) {
+ av_log(c->fc, AV_LOG_WARNING, "truncated extradata\n");
+ par->extradata_size -= atom.size - err;
+ result = err;
+ }
+ memset(buf + 8 + err, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+ return result;
+}
+
/* FIXME modify QDM2/SVQ3/H.264 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;
- uint8_t *buf;
+ uint64_t original_size;
int err;
if (c->fc->nb_streams < 1) // will happen with jp2 files
return 0;
- st= c->fc->streams[c->fc->nb_streams-1];
- size= (uint64_t)st->codecpar->extradata_size + atom.size + 8 + AV_INPUT_BUFFER_PADDING_SIZE;
- if (size > INT_MAX || (uint64_t)atom.size > INT_MAX)
- return AVERROR_INVALIDDATA;
- if ((err = av_reallocp(&st->codecpar->extradata, size)) < 0) {
- st->codecpar->extradata_size = 0;
+ st = c->fc->streams[c->fc->nb_streams-1];
+
+ if (st->codecpar->codec_id != codec_id)
+ return 0; /* unexpected codec_id - don't mess with extradata */
+
+ original_size = st->codecpar->extradata_size;
+ err = mov_realloc_extradata(st->codecpar, atom);
+ if (err)
return err;
- }
- buf = st->codecpar->extradata + st->codecpar->extradata_size;
- st->codecpar->extradata_size= size - AV_INPUT_BUFFER_PADDING_SIZE;
- AV_WB32( buf , atom.size + 8);
- AV_WL32( buf + 4, atom.type);
- err = ffio_read_size(pb, buf + 8, atom.size);
+ err = mov_read_atom_into_extradata(c, pb, atom, st->codecpar, st->codecpar->extradata + original_size);
if (err < 0)
return err;
+ return 0; // Note: this is the original behavior to ignore truncation.
+}
- 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) {
+ AVCodecParameters *par = c->fc->streams[c->fc->nb_streams-1]->codecpar;
+ if (par->extradata_size >= 40) {
+ par->height = AV_RB16(&par->extradata[36]);
+ par->width = AV_RB16(&par->extradata[38]);
+ }
+ }
+ return ret;
+}
+
+static int mov_read_ares(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ if (c->fc->nb_streams >= 1) {
+ AVCodecParameters *par = c->fc->streams[c->fc->nb_streams-1]->codecpar;
+ if (par->codec_tag == MKTAG('A', 'V', 'i', 'n') &&
+ par->codec_id == AV_CODEC_ID_H264 &&
+ atom.size > 11) {
+ int cid;
+ avio_skip(pb, 10);
+ cid = avio_rb16(pb);
+ /* For AVID AVCI50, force width of 1440 to be able to select the correct SPS and PPS */
+ if (cid == 0xd4d || cid == 0xd4e)
+ par->width = 1440;
+ return 0;
+ } else if ((par->codec_tag == MKTAG('A', 'V', 'd', '1') ||
+ par->codec_tag == MKTAG('A', 'V', 'd', 'n')) &&
+ atom.size >= 24) {
+ int num, den;
+ avio_skip(pb, 12);
+ num = avio_rb32(pb);
+ den = avio_rb32(pb);
+ if (num <= 0 || den <= 0)
+ return 0;
+ switch (avio_rb32(pb)) {
+ case 2:
+ if (den >= INT_MAX / 2)
+ return 0;
+ den *= 2;
+ case 1:
+ c->fc->streams[c->fc->nb_streams-1]->display_aspect_ratio.num = num;
+ c->fc->streams[c->fc->nb_streams-1]->display_aspect_ratio.den = den;
+ default:
+ return 0;
+ }
+ }
+ }
+
+ return mov_read_avid(c, pb, atom);
+}
+
+static int mov_read_aclr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ int ret = 0;
+ int length = 0;
+ uint64_t original_size;
+ if (c->fc->nb_streams >= 1) {
+ AVCodecParameters *par = c->fc->streams[c->fc->nb_streams-1]->codecpar;
+ if (par->codec_id == AV_CODEC_ID_H264)
+ return 0;
+ if (atom.size == 16) {
+ original_size = par->extradata_size;
+ ret = mov_realloc_extradata(par, atom);
+ if (!ret) {
+ length = mov_read_atom_into_extradata(c, pb, atom, par, par->extradata + original_size);
+ if (length == atom.size) {
+ const uint8_t range_value = par->extradata[original_size + 19];
+ switch (range_value) {
+ case 1:
+ par->color_range = AVCOL_RANGE_MPEG;
+ break;
+ case 2:
+ par->color_range = AVCOL_RANGE_JPEG;
+ break;
+ default:
+ av_log(c, AV_LOG_WARNING, "ignored unknown aclr value (%d)\n", range_value);
+ break;
+ }
+ ff_dlog(c, "color_range: %d\n", par->color_range);
+ } else {
+ /* For some reason the whole atom was not added to the extradata */
+ av_log(c, AV_LOG_ERROR, "aclr not decoded - incomplete atom\n");
+ }
+ } else {
+ av_log(c, AV_LOG_ERROR, "aclr not decoded - unable to add atom to extradata\n");
+ }
+ } else {
+ av_log(c, AV_LOG_WARNING, "aclr not decoded - unexpected size %"PRId64"\n", atom.size);
+ }
+ }
+
+ return ret;
+}
+
+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)
@@ -1213,18 +1629,41 @@ static int mov_read_wave(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if ((uint64_t)atom.size > (1<<30))
return AVERROR_INVALIDDATA;
- if (st->codecpar->codec_id == AV_CODEC_ID_QDM2 || st->codecpar->codec_id == AV_CODEC_ID_QDMC) {
+ if (st->codecpar->codec_id == AV_CODEC_ID_QDM2 ||
+ st->codecpar->codec_id == AV_CODEC_ID_QDMC ||
+ st->codecpar->codec_id == AV_CODEC_ID_SPEEX) {
// pass all frma atom to codec, needed at least for QDMC and QDM2
- av_free(st->codecpar->extradata);
- st->codecpar->extradata = av_mallocz(atom.size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
- return AVERROR(ENOMEM);
- st->codecpar->extradata_size = atom.size;
-
- ret = ffio_read_size(pb, st->codecpar->extradata, atom.size);
+ av_freep(&st->codecpar->extradata);
+ ret = ff_get_extradata(c->fc, st->codecpar, pb, atom.size);
if (ret < 0)
return ret;
} else if (atom.size > 8) { /* to read frma, esds atoms */
+ if (st->codecpar->codec_id == AV_CODEC_ID_ALAC && atom.size >= 24) {
+ uint64_t buffer;
+ ret = ffio_ensure_seekback(pb, 8);
+ if (ret < 0)
+ return ret;
+ buffer = avio_rb64(pb);
+ atom.size -= 8;
+ if ( (buffer & 0xFFFFFFFF) == MKBETAG('f','r','m','a')
+ && buffer >> 32 <= atom.size
+ && buffer >> 32 >= 8) {
+ avio_skip(pb, -8);
+ atom.size += 8;
+ } else if (!st->codecpar->extradata_size) {
+#define ALAC_EXTRADATA_SIZE 36
+ st->codecpar->extradata = av_mallocz(ALAC_EXTRADATA_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!st->codecpar->extradata)
+ return AVERROR(ENOMEM);
+ st->codecpar->extradata_size = ALAC_EXTRADATA_SIZE;
+ AV_WB32(st->codecpar->extradata , ALAC_EXTRADATA_SIZE);
+ AV_WB32(st->codecpar->extradata + 4, MKTAG('a','l','a','c'));
+ AV_WB64(st->codecpar->extradata + 12, buffer);
+ avio_read(pb, st->codecpar->extradata + 20, 16);
+ avio_skip(pb, atom.size - 24);
+ return 0;
+ }
+ }
if ((ret = mov_read_default(c, pb, atom)) < 0)
return ret;
} else
@@ -1257,13 +1696,12 @@ 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->codecpar->extradata);
- st->codecpar->extradata = av_mallocz(atom.size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
- return AVERROR(ENOMEM);
- st->codecpar->extradata_size = atom.size;
-
- ret = ffio_read_size(pb, st->codecpar->extradata, atom.size);
+ if (st->codecpar->extradata_size > 1 && st->codecpar->extradata) {
+ av_log(c, AV_LOG_WARNING, "ignoring multiple glbl\n");
+ return 0;
+ }
+ av_freep(&st->codecpar->extradata);
+ ret = ff_get_extradata(c->fc, st->codecpar, pb, atom.size);
if (ret < 0)
return ret;
@@ -1287,14 +1725,9 @@ static int mov_read_dvc1(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if ((profile_level & 0xf0) != 0xc0)
return 0;
- av_free(st->codecpar->extradata);
- st->codecpar->extradata = av_mallocz(atom.size - 7 + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
- return AVERROR(ENOMEM);
- st->codecpar->extradata_size = atom.size - 7;
avio_seek(pb, 6, SEEK_CUR);
-
- ret = ffio_read_size(pb, st->codecpar->extradata, st->codecpar->extradata_size);
+ av_freep(&st->codecpar->extradata);
+ ret = ff_get_extradata(c->fc, st->codecpar, pb, atom.size - 7);
if (ret < 0)
return ret;
@@ -1320,14 +1753,9 @@ static int mov_read_strf(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if ((uint64_t)atom.size > (1<<30))
return AVERROR_INVALIDDATA;
- av_free(st->codecpar->extradata);
- st->codecpar->extradata = av_mallocz(atom.size - 40 + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
- return AVERROR(ENOMEM);
- st->codecpar->extradata_size = atom.size - 40;
avio_skip(pb, 40);
-
- ret = ffio_read_size(pb, st->codecpar->extradata, atom.size - 40);
+ av_freep(&st->codecpar->extradata);
+ ret = ff_get_extradata(c->fc, st->codecpar, pb, atom.size - 40);
if (ret < 0)
return ret;
@@ -1352,10 +1780,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;
@@ -1410,7 +1840,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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
- else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
+ else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA ||
+ (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE &&
+ st->codecpar->codec_id == AV_CODEC_ID_NONE)) {
id = ff_codec_get_id(ff_codec_movsubtitle_tags, format);
if (id > 0)
st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
@@ -1425,10 +1857,13 @@ static int mov_codec_id(AVStream *st, uint32_t format)
static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb,
AVStream *st, MOVStreamContext *sc)
{
- uint8_t codec_name[32];
- unsigned int color_depth, len, j;
- int color_greyscale;
- int color_table_id;
+ uint8_t codec_name[32] = { 0 };
+ int64_t stsd_start;
+ unsigned int len;
+
+ /* The first 16 bytes of the video sample description are already
+ * read in ff_mov_read_stsd_entries() */
+ stsd_start = avio_tell(pb) - 16;
avio_rb16(pb); /* version */
avio_rb16(pb); /* revision level */
@@ -1455,80 +1890,22 @@ 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 (!strncmp(codec_name, "Planar Y'CbCr 8-bit 4:2:0", 25))
+ if (!strncmp(codec_name, "Planar Y'CbCr 8-bit 4:2:0", 25)) {
st->codecpar->codec_tag = MKTAG('I', '4', '2', '0');
+ st->codecpar->width &= ~1;
+ st->codecpar->height &= ~1;
+ }
/* Flash Media Server uses tag H.263 with Sorenson Spark */
if (st->codecpar->codec_tag == MKTAG('H','2','6','3') &&
!strncmp(codec_name, "Sorenson H263", 13))
st->codecpar->codec_id = AV_CODEC_ID_FLV1;
st->codecpar->bits_per_coded_sample = avio_rb16(pb); /* depth */
- color_table_id = avio_rb16(pb); /* colortable id */
- av_log(c->fc, AV_LOG_TRACE, "depth %d, ctab id %d\n",
- st->codecpar->bits_per_coded_sample, color_table_id);
- /* figure out the palette situation */
- color_depth = st->codecpar->bits_per_coded_sample & 0x1F;
- color_greyscale = st->codecpar->bits_per_coded_sample & 0x20;
-
- /* 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;
-
- if (color_greyscale) {
- int color_index, color_dec;
- /* compute the greyscale palette */
- st->codecpar->bits_per_coded_sample = color_depth;
- color_count = 1 << color_depth;
- color_index = 255;
- 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);
- color_index -= color_dec;
- if (color_index < 0)
- color_index = 0;
- }
- } else if (color_table_id) {
- const uint8_t *color_table;
- /* if flag bit 3 is set, use the default palette */
- color_count = 1 << color_depth;
- if (color_depth == 2)
- color_table = ff_qt_default_palette_4;
- else if (color_depth == 4)
- color_table = ff_qt_default_palette_16;
- else
- color_table = ff_qt_default_palette_256;
- for (j = 0; j < color_count; j++) {
- 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);
- }
- } else {
- /* load the palette from the file */
- color_start = avio_rb32(pb);
- color_count = avio_rb16(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);
- avio_r8(pb);
- r = avio_r8(pb);
- avio_r8(pb);
- g = avio_r8(pb);
- avio_r8(pb);
- b = avio_r8(pb);
- avio_r8(pb);
- sc->palette[j] = (r << 16) | (g << 8) | (b);
- }
- }
- }
+ avio_seek(pb, stsd_start, SEEK_SET);
+
+ if (ff_get_qtpalette(st->codecpar->codec_id, pb, sc->palette)) {
+ st->codecpar->bits_per_coded_sample &= 0x1F;
sc->has_palette = 1;
}
}
@@ -1538,6 +1915,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 */
@@ -1552,8 +1930,10 @@ static void mov_parse_stsd_audio(MOVContext *c, AVIOContext *pb,
st->codecpar->sample_rate = ((avio_rb32(pb) >> 16));
// Read QT version 1 fields. In version 0 these do not exist.
- av_log(c->fc, AV_LOG_TRACE, "version =%"PRIu16", isom =%d\n", version, c->isom);
- if (!c->isom) {
+ av_log(c->fc, AV_LOG_TRACE, "version =%d, isom =%d\n", version, 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 */
@@ -1585,6 +1965,13 @@ static void mov_parse_stsd_audio(MOVContext *c, AVIOContext *pb,
}
}
+ if (sc->format == 0) {
+ if (st->codecpar->bits_per_coded_sample == 8)
+ st->codecpar->codec_id = mov_codec_id(st, MKTAG('r','a','w',' '));
+ else if (st->codecpar->bits_per_coded_sample == 16)
+ st->codecpar->codec_id = mov_codec_id(st, MKTAG('t','w','o','s'));
+ }
+
switch (st->codecpar->codec_id) {
case AV_CODEC_ID_PCM_S8:
case AV_CODEC_ID_PCM_U8:
@@ -1599,6 +1986,10 @@ static void mov_parse_stsd_audio(MOVContext *c, AVIOContext *pb,
st->codecpar->codec_id =
st->codecpar->codec_id == AV_CODEC_ID_PCM_S16BE ?
AV_CODEC_ID_PCM_S24BE : AV_CODEC_ID_PCM_S24LE;
+ else if (st->codecpar->bits_per_coded_sample == 32)
+ st->codecpar->codec_id =
+ st->codecpar->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:
@@ -1651,9 +2042,9 @@ static uint32_t yuv_to_rgba(uint32_t ycbcr)
cr = (ycbcr >> 8) & 0xFF;
cb = ycbcr & 0xFF;
- b = av_clip_uint8(1.164 * (y - 16) + 2.018 * (cb - 128));
- g = av_clip_uint8(1.164 * (y - 16) - 0.813 * (cr - 128) - 0.391 * (cb - 128));
- r = av_clip_uint8(1.164 * (y - 16) + 1.596 * (cr - 128));
+ b = av_clip_uint8((1164 * (y - 16) + 2018 * (cb - 128)) / 1000);
+ g = av_clip_uint8((1164 * (y - 16) - 813 * (cr - 128) - 391 * (cb - 128)) / 1000);
+ r = av_clip_uint8((1164 * (y - 16) + 1596 * (cr - 128) ) / 1000);
return (r << 16) | (g << 8) | b;
}
@@ -1700,13 +2091,58 @@ static int mov_parse_stsd_data(MOVContext *c, AVIOContext *pb,
int ret;
if (st->codecpar->codec_tag == MKTAG('t','m','c','d')) {
- st->codecpar->extradata_size = size;
- st->codecpar->extradata = av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
+ if ((int)size != size)
return AVERROR(ENOMEM);
- ret = ffio_read_size(pb, st->codecpar->extradata, size);
+
+ ret = ff_get_extradata(c->fc, st->codecpar, pb, size);
if (ret < 0)
return ret;
+ if (size > 16) {
+ MOVStreamContext *tmcd_ctx = st->priv_data;
+ int val;
+ val = AV_RB32(st->codecpar->extradata + 4);
+ tmcd_ctx->tmcd_flags = val;
+ st->avg_frame_rate.num = st->codecpar->extradata[16]; /* number of frame */
+ st->avg_frame_rate.den = 1;
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ st->codec->time_base = av_inv_q(st->avg_frame_rate);
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+ /* adjust for per frame dur in counter mode */
+ if (tmcd_ctx->tmcd_flags & 0x0008) {
+ int timescale = AV_RB32(st->codecpar->extradata + 8);
+ int framedur = AV_RB32(st->codecpar->extradata + 12);
+ st->avg_frame_rate.num *= timescale;
+ st->avg_frame_rate.den *= framedur;
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ st->codec->time_base.den *= timescale;
+ st->codec->time_base.num *= framedur;
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+ }
+ if (size > 30) {
+ uint32_t len = AV_RB32(st->codecpar->extradata + 18); /* name atom length */
+ uint32_t format = AV_RB32(st->codecpar->extradata + 22);
+ if (format == AV_RB32("name") && (int64_t)size >= (int64_t)len + 18) {
+ uint16_t str_size = AV_RB16(st->codecpar->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->codecpar->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);
@@ -1745,6 +2181,10 @@ static int mov_finalize_stsd_codec(MOVContext *c, AVIOContext *pb,
// force sample rate for qcelp when not stored in mov
if (st->codecpar->codec_tag != MKTAG('Q','c','l','p'))
st->codecpar->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->codecpar->channels = 1;
@@ -1764,6 +2204,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->codecpar->block_align = sc->bytes_per_frame;
break;
case AV_CODEC_ID_ALAC:
@@ -1772,7 +2215,11 @@ static int mov_finalize_stsd_codec(MOVContext *c, AVIOContext *pb,
st->codecpar->sample_rate = AV_RB32(st->codecpar->extradata + 32);
}
break;
+ case AV_CODEC_ID_AC3:
+ case AV_CODEC_ID_EAC3:
+ case AV_CODEC_ID_MPEG1VIDEO:
case AV_CODEC_ID_VC1:
+ case AV_CODEC_ID_VP9:
st->need_parsing = AVSTREAM_PARSE_FULL;
break;
default:
@@ -1788,13 +2235,15 @@ static int mov_skip_multiple_stsd(MOVContext *c, AVIOContext *pb,
int video_codec_id = ff_codec_get_id(ff_codec_movvideo_tags, format);
if (codec_tag &&
- (codec_tag != format &&
- // prores is allowed to have differing data format and codec tag
- codec_tag != AV_RL32("apcn") && codec_tag != AV_RL32("apch") &&
- // so is dv (sigh)
- codec_tag != AV_RL32("dvpp") && codec_tag != AV_RL32("dvcp") &&
- (c->fc->video_codec_id ? video_codec_id != c->fc->video_codec_id
- : codec_tag != MKTAG('j','p','e','g')))) {
+ (codec_tag != format &&
+ // AVID 1:1 samples with differing data format and codec tag exist
+ (codec_tag != AV_RL32("AV1x") || format != AV_RL32("AVup")) &&
+ // prores is allowed to have differing data format and codec tag
+ codec_tag != AV_RL32("apcn") && codec_tag != AV_RL32("apch") &&
+ // so is dv (sigh)
+ codec_tag != AV_RL32("dvpp") && codec_tag != AV_RL32("dvcp") &&
+ (c->fc->video_codec_id ? video_codec_id != c->fc->video_codec_id
+ : 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. */
@@ -1833,7 +2282,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries)
avio_rb32(pb); /* reserved */
avio_rb16(pb); /* reserved */
dref_id = avio_rb16(pb);
- } else {
+ } else if (size <= 7) {
av_log(c->fc, AV_LOG_ERROR,
"invalid size %"PRId64" in stsd\n", size);
return AVERROR_INVALIDDATA;
@@ -1845,12 +2294,13 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries)
sc->pseudo_stream_id = st->codecpar->codec_tag ? -1 : pseudo_stream_id;
sc->dref_id= dref_id;
+ sc->format = format;
id = mov_codec_id(st, format);
av_log(c->fc, AV_LOG_TRACE,
- "size=%"PRId64" format=0x%08"PRIx32" codec_type=%d\n",
- size, format, st->codecpar->codec_type);
+ "size=%"PRId64" 4CC=%s codec_type=%d\n", size,
+ av_fourcc2str(format), st->codecpar->codec_type);
if (st->codecpar->codec_type==AVMEDIA_TYPE_VIDEO) {
st->codecpar->codec_id = id;
@@ -1858,6 +2308,10 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries)
} else if (st->codecpar->codec_type==AVMEDIA_TYPE_AUDIO) {
st->codecpar->codec_id = id;
mov_parse_stsd_audio(c, pb, st, sc);
+ if (st->codecpar->sample_rate < 0) {
+ av_log(c->fc, AV_LOG_ERROR, "Invalid sample rate %d\n", st->codecpar->sample_rate);
+ return AVERROR_INVALIDDATA;
+ }
} else if (st->codecpar->codec_type==AVMEDIA_TYPE_SUBTITLE){
st->codecpar->codec_id = id;
mov_parse_stsd_subtitle(c, pb, st, sc,
@@ -1876,7 +2330,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries)
} else if (a.size > 0)
avio_skip(pb, a.size);
- if (sc->extradata) {
+ if (sc->extradata && st->codecpar->extradata) {
int extra_size = st->codecpar->extradata_size;
/* Move the current stream extradata to the stream context one. */
@@ -1893,7 +2347,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries)
if (pb->eof_reached)
return AVERROR_EOF;
- return mov_finalize_stsd_codec(c, pb, st, sc);
+ return 0;
}
static int mov_read_stsd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
@@ -1911,6 +2365,11 @@ static int mov_read_stsd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
avio_rb24(pb); /* flags */
entries = avio_rb32(pb);
+ if (entries <= 0) {
+ av_log(c->fc, AV_LOG_ERROR, "invalid STSD entries %d\n", entries);
+ return AVERROR_INVALIDDATA;
+ }
+
if (sc->extradata) {
av_log(c->fc, AV_LOG_ERROR,
"Duplicate stsd found in this track.\n");
@@ -1919,27 +2378,33 @@ static int mov_read_stsd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
/* Prepare space for hosting multiple extradata. */
sc->extradata = av_mallocz_array(entries, sizeof(*sc->extradata));
- if (!sc->extradata)
- return AVERROR(ENOMEM);
-
- sc->stsd_count = entries;
- sc->extradata_size = av_mallocz_array(sc->stsd_count, sizeof(*sc->extradata_size));
- if (!sc->extradata_size)
- return AVERROR(ENOMEM);
+ sc->extradata_size = av_mallocz_array(entries, sizeof(*sc->extradata_size));
+ if (!sc->extradata_size || !sc->extradata) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
- ret = ff_mov_read_stsd_entries(c, pb, sc->stsd_count);
+ ret = ff_mov_read_stsd_entries(c, pb, entries);
if (ret < 0)
return ret;
+ sc->stsd_count = entries;
+
/* Restore back the primary extradata. */
- av_free(st->codecpar->extradata);
+ av_freep(&st->codecpar->extradata);
st->codecpar->extradata_size = sc->extradata_size[0];
- st->codecpar->extradata = av_mallocz(sc->extradata_size[0] + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
- return AVERROR(ENOMEM);
- memcpy(st->codecpar->extradata, sc->extradata[0], sc->extradata_size[0]);
+ if (sc->extradata_size[0]) {
+ st->codecpar->extradata = av_mallocz(sc->extradata_size[0] + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!st->codecpar->extradata)
+ return AVERROR(ENOMEM);
+ memcpy(st->codecpar->extradata, sc->extradata[0], sc->extradata_size[0]);
+ }
- return 0;
+ return mov_finalize_stsd_codec(c, pb, st, sc);
+fail:
+ av_freep(&sc->extradata);
+ av_freep(&sc->extradata_size);
+ return ret;
}
static int mov_read_stsc(MOVContext *c, AVIOContext *pb, MOVAtom atom)
@@ -1962,9 +2427,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);
@@ -1972,13 +2439,6 @@ static int mov_read_stsc(MOVContext *c, AVIOContext *pb, MOVAtom atom)
sc->stsc_data[i].first = avio_rb32(pb);
sc->stsc_data[i].count = avio_rb32(pb);
sc->stsc_data[i].id = avio_rb32(pb);
- if (sc->stsc_data[i].id <= 0 || sc->stsc_data[i].id > sc->stsd_count) {
- sc->stsc_data[i].id = 1;
- if (c->fc->error_recognition & AV_EF_EXPLODE) {
- av_log(c->fc, AV_LOG_ERROR, "Invalid stsc index.\n");
- return AVERROR_INVALIDDATA;
- }
- }
}
sc->stsc_count = i;
@@ -2021,9 +2481,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);
@@ -2060,12 +2522,17 @@ static int mov_read_stss(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (!entries)
{
sc->keyframe_absent = 1;
+ if (!st->need_parsing && st->codecpar->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);
@@ -2086,7 +2553,7 @@ static int mov_read_stsz(MOVContext *c, AVIOContext *pb, MOVAtom atom)
AVStream *st;
MOVStreamContext *sc;
unsigned int i, entries, sample_size, field_size, num_bytes;
- BitstreamContext bc;
+ GetBitContext gb;
unsigned char* buf;
int ret;
@@ -2102,6 +2569,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;
@@ -2123,9 +2591,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);
@@ -2144,10 +2616,10 @@ static int mov_read_stsz(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return ret;
}
- bitstream_init8(&bc, buf, num_bytes);
+ init_get_bits(&gb, buf, 8*num_bytes);
for (i = 0; i < entries && !pb->eof_reached; i++) {
- sc->sample_sizes[i] = bitstream_read(&bc, field_size);
+ sc->sample_sizes[i] = get_bits_long(&gb, field_size);
sc->data_size += sc->sample_sizes[i];
}
@@ -2181,38 +2653,42 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_log(c->fc, AV_LOG_TRACE, "track[%u].stts.entries = %u\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);
for (i = 0; i < entries && !pb->eof_reached; i++) {
int sample_duration;
- int sample_count;
+ unsigned int sample_count;
sample_count=avio_rb32(pb);
sample_duration = avio_rb32(pb);
- if (sample_count < 0) {
- av_log(c->fc, AV_LOG_ERROR, "Invalid sample_count=%d\n", sample_count);
- return AVERROR_INVALIDDATA;
- }
+
sc->stts_data[i].count= sample_count;
sc->stts_data[i].duration= sample_duration;
av_log(c->fc, AV_LOG_TRACE, "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;
@@ -2223,11 +2699,22 @@ 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) {
+ if (duration == INT_MIN) {
+ av_log(NULL, AV_LOG_WARNING, "mov_update_dts_shift(): dts_shift set to %d\n", INT_MAX);
+ duration++;
+ }
+ sc->dts_shift = FFMAX(sc->dts_shift, -duration);
+ }
+}
+
static int mov_read_ctts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
MOVStreamContext *sc;
- unsigned int i, entries;
+ unsigned int i, j, entries, ctts_count = 0;
if (c->fc->nb_streams < 1)
return 0;
@@ -2240,13 +2727,12 @@ static int mov_read_ctts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_log(c->fc, AV_LOG_TRACE, "track[%u].ctts.entries = %u\n", c->fc->nb_streams - 1, entries);
- av_freep(&sc->ctts_data);
-
if (!entries)
return 0;
if (entries >= UINT_MAX / sizeof(*sc->ctts_data))
return AVERROR_INVALIDDATA;
- sc->ctts_data = av_realloc(NULL, entries * sizeof(*sc->ctts_data));
+ av_freep(&sc->ctts_data);
+ sc->ctts_data = av_fast_realloc(NULL, &sc->ctts_allocated_size, entries * sizeof(*sc->ctts_data));
if (!sc->ctts_data)
return AVERROR(ENOMEM);
@@ -2254,13 +2740,32 @@ static int mov_read_ctts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
int count =avio_rb32(pb);
int duration =avio_rb32(pb);
- sc->ctts_data[i].count = count;
- sc->ctts_data[i].duration= duration;
- if (duration < 0)
- sc->dts_shift = FFMAX(sc->dts_shift, -duration);
+ if (count <= 0) {
+ av_log(c->fc, AV_LOG_TRACE,
+ "ignoring CTTS entry with count=%d duration=%d\n",
+ count, duration);
+ continue;
+ }
+
+ /* Expand entries such that we have a 1-1 mapping with samples. */
+ for (j = 0; j < count; j++)
+ add_ctts_entry(&sc->ctts_data, &ctts_count, &sc->ctts_allocated_size, 1, duration);
+
+ av_log(c->fc, AV_LOG_TRACE, "count=%d, duration=%d\n",
+ count, duration);
+
+ if (FFNABS(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;
+ sc->ctts_count = ctts_count;
if (pb->eof_reached)
return AVERROR_EOF;
@@ -2294,9 +2799,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);
@@ -2310,6 +2817,524 @@ static int mov_read_sbgp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return pb->eof_reached ? AVERROR_EOF : 0;
}
+/**
+ * Get ith edit list entry (media time, duration).
+ */
+static int get_edit_list_entry(MOVContext *mov,
+ const MOVStreamContext *msc,
+ unsigned int edit_list_index,
+ int64_t *edit_list_media_time,
+ int64_t *edit_list_duration,
+ int64_t global_timescale)
+{
+ if (edit_list_index == msc->elst_count) {
+ return 0;
+ }
+ *edit_list_media_time = msc->elst_data[edit_list_index].time;
+ *edit_list_duration = msc->elst_data[edit_list_index].duration;
+
+ /* duration is in global timescale units;convert to msc timescale */
+ if (global_timescale == 0) {
+ avpriv_request_sample(mov->fc, "Support for mvhd.timescale = 0 with editlists");
+ return 0;
+ }
+ *edit_list_duration = av_rescale(*edit_list_duration, msc->time_scale,
+ global_timescale);
+ return 1;
+}
+
+/**
+ * Find the closest previous frame to the timestamp, in e_old index
+ * entries. Searching for just any frame / just key frames can be controlled by
+ * last argument 'flag'.
+ * Returns the index of the entry in st->index_entries if successful,
+ * else returns -1.
+ */
+static int64_t find_prev_closest_index(AVStream *st,
+ AVIndexEntry *e_old,
+ int nb_old,
+ int64_t timestamp,
+ int flag)
+{
+ AVIndexEntry *e_keep = st->index_entries;
+ int nb_keep = st->nb_index_entries;
+ int64_t found = -1;
+ int64_t i = 0;
+
+ st->index_entries = e_old;
+ st->nb_index_entries = nb_old;
+ found = av_index_search_timestamp(st, timestamp, flag | AVSEEK_FLAG_BACKWARD);
+
+ // Keep going backwards in the index entries until the timestamp is the same.
+ if (found >= 0) {
+ for (i = found; i > 0 && e_old[i].timestamp == e_old[i - 1].timestamp;
+ i--) {
+ if ((flag & AVSEEK_FLAG_ANY) ||
+ (e_old[i - 1].flags & AVINDEX_KEYFRAME)) {
+ found = i - 1;
+ }
+ }
+ }
+
+ /* restore AVStream state*/
+ st->index_entries = e_keep;
+ st->nb_index_entries = nb_keep;
+ return found;
+}
+
+/**
+ * Add index entry with the given values, to the end of st->index_entries.
+ * Returns the new size st->index_entries if successful, else returns -1.
+ *
+ * This function is similar to ff_add_index_entry in libavformat/utils.c
+ * except that here we are always unconditionally adding an index entry to
+ * the end, instead of searching the entries list and skipping the add if
+ * there is an existing entry with the same timestamp.
+ * This is needed because the mov_fix_index calls this func with the same
+ * unincremented timestamp for successive discarded frames.
+ */
+static int64_t add_index_entry(AVStream *st, int64_t pos, int64_t timestamp,
+ int size, int distance, int flags)
+{
+ AVIndexEntry *entries, *ie;
+ int64_t index = -1;
+ const size_t min_size_needed = (st->nb_index_entries + 1) * sizeof(AVIndexEntry);
+
+ // Double the allocation each time, to lower memory fragmentation.
+ // Another difference from ff_add_index_entry function.
+ const size_t requested_size =
+ min_size_needed > st->index_entries_allocated_size ?
+ FFMAX(min_size_needed, 2 * st->index_entries_allocated_size) :
+ min_size_needed;
+
+ if((unsigned)st->nb_index_entries + 1 >= UINT_MAX / sizeof(AVIndexEntry))
+ return -1;
+
+ entries = av_fast_realloc(st->index_entries,
+ &st->index_entries_allocated_size,
+ requested_size);
+ if(!entries)
+ return -1;
+
+ st->index_entries= entries;
+
+ index= st->nb_index_entries++;
+ ie= &entries[index];
+
+ ie->pos = pos;
+ ie->timestamp = timestamp;
+ ie->min_distance= distance;
+ ie->size= size;
+ ie->flags = flags;
+ return index;
+}
+
+/**
+ * Rewrite timestamps of index entries in the range [end_index - frame_duration_buffer_size, end_index)
+ * by subtracting end_ts successively by the amounts given in frame_duration_buffer.
+ */
+static void fix_index_entry_timestamps(AVStream* st, int end_index, int64_t end_ts,
+ int64_t* frame_duration_buffer,
+ int frame_duration_buffer_size) {
+ int i = 0;
+ av_assert0(end_index >= 0 && end_index <= st->nb_index_entries);
+ for (i = 0; i < frame_duration_buffer_size; i++) {
+ end_ts -= frame_duration_buffer[frame_duration_buffer_size - 1 - i];
+ st->index_entries[end_index - 1 - i].timestamp = end_ts;
+ }
+}
+
+/**
+ * Append a new ctts entry to ctts_data.
+ * Returns the new ctts_count if successful, else returns -1.
+ */
+static int64_t add_ctts_entry(MOVStts** ctts_data, unsigned int* ctts_count, unsigned int* allocated_size,
+ int count, int duration)
+{
+ MOVStts *ctts_buf_new;
+ const size_t min_size_needed = (*ctts_count + 1) * sizeof(MOVStts);
+ const size_t requested_size =
+ min_size_needed > *allocated_size ?
+ FFMAX(min_size_needed, 2 * (*allocated_size)) :
+ min_size_needed;
+
+ if((unsigned)(*ctts_count) + 1 >= UINT_MAX / sizeof(MOVStts))
+ return -1;
+
+ ctts_buf_new = av_fast_realloc(*ctts_data, allocated_size, requested_size);
+
+ if(!ctts_buf_new)
+ return -1;
+
+ *ctts_data = ctts_buf_new;
+
+ ctts_buf_new[*ctts_count].count = count;
+ ctts_buf_new[*ctts_count].duration = duration;
+
+ *ctts_count = (*ctts_count) + 1;
+ return *ctts_count;
+}
+
+static void mov_current_sample_inc(MOVStreamContext *sc)
+{
+ sc->current_sample++;
+ sc->current_index++;
+ if (sc->index_ranges &&
+ sc->current_index >= sc->current_index_range->end &&
+ sc->current_index_range->end) {
+ sc->current_index_range++;
+ sc->current_index = sc->current_index_range->start;
+ }
+}
+
+static void mov_current_sample_dec(MOVStreamContext *sc)
+{
+ sc->current_sample--;
+ sc->current_index--;
+ if (sc->index_ranges &&
+ sc->current_index < sc->current_index_range->start &&
+ sc->current_index_range > sc->index_ranges) {
+ sc->current_index_range--;
+ sc->current_index = sc->current_index_range->end - 1;
+ }
+}
+
+static void mov_current_sample_set(MOVStreamContext *sc, int current_sample)
+{
+ int64_t range_size;
+
+ sc->current_sample = current_sample;
+ sc->current_index = current_sample;
+ if (!sc->index_ranges) {
+ return;
+ }
+
+ for (sc->current_index_range = sc->index_ranges;
+ sc->current_index_range->end;
+ sc->current_index_range++) {
+ range_size = sc->current_index_range->end - sc->current_index_range->start;
+ if (range_size > current_sample) {
+ sc->current_index = sc->current_index_range->start + current_sample;
+ break;
+ }
+ current_sample -= range_size;
+ }
+}
+
+/**
+ * Fix st->index_entries, so that it contains only the entries (and the entries
+ * which are needed to decode them) that fall in the edit list time ranges.
+ * Also fixes the timestamps of the index entries to match the timeline
+ * specified the edit lists.
+ */
+static void mov_fix_index(MOVContext *mov, AVStream *st)
+{
+ MOVStreamContext *msc = st->priv_data;
+ AVIndexEntry *e_old = st->index_entries;
+ int nb_old = st->nb_index_entries;
+ const AVIndexEntry *e_old_end = e_old + nb_old;
+ const AVIndexEntry *current = NULL;
+ MOVStts *ctts_data_old = msc->ctts_data;
+ int64_t ctts_index_old = 0;
+ int64_t ctts_sample_old = 0;
+ int64_t ctts_count_old = msc->ctts_count;
+ int64_t edit_list_media_time = 0;
+ int64_t edit_list_duration = 0;
+ int64_t frame_duration = 0;
+ int64_t edit_list_dts_counter = 0;
+ int64_t edit_list_dts_entry_end = 0;
+ int64_t edit_list_start_ctts_sample = 0;
+ int64_t curr_cts;
+ int64_t curr_ctts = 0;
+ int64_t min_corrected_pts = -1;
+ int64_t empty_edits_sum_duration = 0;
+ int64_t edit_list_index = 0;
+ int64_t index;
+ int64_t index_ctts_count;
+ int flags;
+ int64_t start_dts = 0;
+ int64_t edit_list_media_time_dts = 0;
+ int64_t edit_list_start_encountered = 0;
+ int64_t search_timestamp = 0;
+ int64_t* frame_duration_buffer = NULL;
+ int num_discarded_begin = 0;
+ int first_non_zero_audio_edit = -1;
+ int packet_skip_samples = 0;
+ MOVIndexRange *current_index_range;
+ int i;
+
+ if (!msc->elst_data || msc->elst_count <= 0 || nb_old <= 0) {
+ return;
+ }
+
+ // allocate the index ranges array
+ msc->index_ranges = av_malloc((msc->elst_count + 1) * sizeof(msc->index_ranges[0]));
+ if (!msc->index_ranges) {
+ av_log(mov->fc, AV_LOG_ERROR, "Cannot allocate index ranges buffer\n");
+ return;
+ }
+ msc->current_index_range = msc->index_ranges;
+ current_index_range = msc->index_ranges - 1;
+
+ // Clean AVStream from traces of old index
+ st->index_entries = NULL;
+ st->index_entries_allocated_size = 0;
+ st->nb_index_entries = 0;
+
+ // Clean ctts fields of MOVStreamContext
+ msc->ctts_data = NULL;
+ msc->ctts_count = 0;
+ msc->ctts_index = 0;
+ msc->ctts_sample = 0;
+ msc->ctts_allocated_size = 0;
+
+ // If the dts_shift is positive (in case of negative ctts values in mov),
+ // then negate the DTS by dts_shift
+ if (msc->dts_shift > 0) {
+ edit_list_dts_entry_end -= msc->dts_shift;
+ av_log(mov->fc, AV_LOG_DEBUG, "Shifting DTS by %d because of negative CTTS.\n", msc->dts_shift);
+ }
+
+ start_dts = edit_list_dts_entry_end;
+
+ while (get_edit_list_entry(mov, msc, edit_list_index, &edit_list_media_time,
+ &edit_list_duration, mov->time_scale)) {
+ av_log(mov->fc, AV_LOG_DEBUG, "Processing st: %d, edit list %"PRId64" - media time: %"PRId64", duration: %"PRId64"\n",
+ st->index, edit_list_index, edit_list_media_time, edit_list_duration);
+ edit_list_index++;
+ edit_list_dts_counter = edit_list_dts_entry_end;
+ edit_list_dts_entry_end += edit_list_duration;
+ num_discarded_begin = 0;
+ if (edit_list_media_time == -1) {
+ empty_edits_sum_duration += edit_list_duration;
+ continue;
+ }
+
+ // If we encounter a non-negative edit list reset the skip_samples/start_pad fields and set them
+ // according to the edit list below.
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ if (first_non_zero_audio_edit < 0) {
+ first_non_zero_audio_edit = 1;
+ } else {
+ first_non_zero_audio_edit = 0;
+ }
+
+ if (first_non_zero_audio_edit > 0)
+ st->skip_samples = msc->start_pad = 0;
+ }
+
+ //find closest previous key frame
+ edit_list_media_time_dts = edit_list_media_time;
+ if (msc->dts_shift > 0) {
+ edit_list_media_time_dts -= msc->dts_shift;
+ }
+
+ // While reordering frame index according to edit list we must handle properly
+ // the scenario when edit list entry starts from none key frame.
+ // We find closest previous key frame and preserve it and consequent frames in index.
+ // All frames which are outside edit list entry time boundaries will be dropped after decoding.
+ search_timestamp = edit_list_media_time_dts;
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ // Audio decoders like AAC need need a decoder delay samples previous to the current sample,
+ // to correctly decode this frame. Hence for audio we seek to a frame 1 sec. before the
+ // edit_list_media_time to cover the decoder delay.
+ search_timestamp = FFMAX(search_timestamp - msc->time_scale, e_old[0].timestamp);
+ }
+
+ index = find_prev_closest_index(st, e_old, nb_old, search_timestamp, 0);
+ if (index == -1) {
+ av_log(mov->fc, AV_LOG_WARNING,
+ "st: %d edit list: %"PRId64" Missing key frame while searching for timestamp: %"PRId64"\n",
+ st->index, edit_list_index, search_timestamp);
+ index = find_prev_closest_index(st, e_old, nb_old, search_timestamp, AVSEEK_FLAG_ANY);
+
+ if (index == -1) {
+ av_log(mov->fc, AV_LOG_WARNING,
+ "st: %d edit list %"PRId64" Cannot find an index entry before timestamp: %"PRId64".\n"
+ "Rounding edit list media time to zero.\n",
+ st->index, edit_list_index, search_timestamp);
+ index = 0;
+ edit_list_media_time = 0;
+ }
+ }
+ current = e_old + index;
+
+ ctts_index_old = 0;
+ ctts_sample_old = 0;
+
+ // set ctts_index properly for the found key frame
+ for (index_ctts_count = 0; index_ctts_count < index; index_ctts_count++) {
+ if (ctts_data_old && ctts_index_old < ctts_count_old) {
+ ctts_sample_old++;
+ if (ctts_data_old[ctts_index_old].count == ctts_sample_old) {
+ ctts_index_old++;
+ ctts_sample_old = 0;
+ }
+ }
+ }
+
+ edit_list_start_ctts_sample = ctts_sample_old;
+
+ // Iterate over index and arrange it according to edit list
+ edit_list_start_encountered = 0;
+ for (; current < e_old_end; current++, index++) {
+ // check if frame outside edit list mark it for discard
+ frame_duration = (current + 1 < e_old_end) ?
+ ((current + 1)->timestamp - current->timestamp) : edit_list_duration;
+
+ flags = current->flags;
+
+ // frames (pts) before or after edit list
+ curr_cts = current->timestamp + msc->dts_shift;
+ curr_ctts = 0;
+
+ if (ctts_data_old && ctts_index_old < ctts_count_old) {
+ curr_ctts = ctts_data_old[ctts_index_old].duration;
+ av_log(mov->fc, AV_LOG_DEBUG, "stts: %"PRId64" ctts: %"PRId64", ctts_index: %"PRId64", ctts_count: %"PRId64"\n",
+ curr_cts, curr_ctts, ctts_index_old, ctts_count_old);
+ curr_cts += curr_ctts;
+ ctts_sample_old++;
+ if (ctts_sample_old == ctts_data_old[ctts_index_old].count) {
+ if (add_ctts_entry(&msc->ctts_data, &msc->ctts_count,
+ &msc->ctts_allocated_size,
+ ctts_data_old[ctts_index_old].count - edit_list_start_ctts_sample,
+ ctts_data_old[ctts_index_old].duration) == -1) {
+ av_log(mov->fc, AV_LOG_ERROR, "Cannot add CTTS entry %"PRId64" - {%"PRId64", %d}\n",
+ ctts_index_old,
+ ctts_data_old[ctts_index_old].count - edit_list_start_ctts_sample,
+ ctts_data_old[ctts_index_old].duration);
+ break;
+ }
+ ctts_index_old++;
+ ctts_sample_old = 0;
+ edit_list_start_ctts_sample = 0;
+ }
+ }
+
+ if (curr_cts < edit_list_media_time || curr_cts >= (edit_list_duration + edit_list_media_time)) {
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->codec_id != AV_CODEC_ID_VORBIS &&
+ curr_cts < edit_list_media_time && curr_cts + frame_duration > edit_list_media_time &&
+ first_non_zero_audio_edit > 0) {
+ packet_skip_samples = edit_list_media_time - curr_cts;
+ st->skip_samples += packet_skip_samples;
+
+ // Shift the index entry timestamp by packet_skip_samples to be correct.
+ edit_list_dts_counter -= packet_skip_samples;
+ if (edit_list_start_encountered == 0) {
+ edit_list_start_encountered = 1;
+ // Make timestamps strictly monotonically increasing for audio, by rewriting timestamps for
+ // discarded packets.
+ if (frame_duration_buffer) {
+ fix_index_entry_timestamps(st, st->nb_index_entries, edit_list_dts_counter,
+ frame_duration_buffer, num_discarded_begin);
+ av_freep(&frame_duration_buffer);
+ }
+ }
+
+ av_log(mov->fc, AV_LOG_DEBUG, "skip %d audio samples from curr_cts: %"PRId64"\n", packet_skip_samples, curr_cts);
+ } else {
+ flags |= AVINDEX_DISCARD_FRAME;
+ av_log(mov->fc, AV_LOG_DEBUG, "drop a frame at curr_cts: %"PRId64" @ %"PRId64"\n", curr_cts, index);
+
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && edit_list_start_encountered == 0) {
+ num_discarded_begin++;
+ frame_duration_buffer = av_realloc(frame_duration_buffer,
+ num_discarded_begin * sizeof(int64_t));
+ if (!frame_duration_buffer) {
+ av_log(mov->fc, AV_LOG_ERROR, "Cannot reallocate frame duration buffer\n");
+ break;
+ }
+ frame_duration_buffer[num_discarded_begin - 1] = frame_duration;
+
+ // Increment skip_samples for the first non-zero audio edit list
+ if (first_non_zero_audio_edit > 0 && st->codecpar->codec_id != AV_CODEC_ID_VORBIS) {
+ st->skip_samples += frame_duration;
+ }
+ }
+ }
+ } else {
+ if (min_corrected_pts < 0) {
+ min_corrected_pts = edit_list_dts_counter + curr_ctts + msc->dts_shift;
+ } else {
+ min_corrected_pts = FFMIN(min_corrected_pts, edit_list_dts_counter + curr_ctts + msc->dts_shift);
+ }
+ if (edit_list_start_encountered == 0) {
+ edit_list_start_encountered = 1;
+ // Make timestamps strictly monotonically increasing for audio, by rewriting timestamps for
+ // discarded packets.
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && frame_duration_buffer) {
+ fix_index_entry_timestamps(st, st->nb_index_entries, edit_list_dts_counter,
+ frame_duration_buffer, num_discarded_begin);
+ av_freep(&frame_duration_buffer);
+ }
+ }
+ }
+
+ if (add_index_entry(st, current->pos, edit_list_dts_counter, current->size,
+ current->min_distance, flags) == -1) {
+ av_log(mov->fc, AV_LOG_ERROR, "Cannot add index entry\n");
+ break;
+ }
+
+ // Update the index ranges array
+ if (current_index_range < msc->index_ranges || index != current_index_range->end) {
+ current_index_range++;
+ current_index_range->start = index;
+ }
+ current_index_range->end = index + 1;
+
+ // Only start incrementing DTS in frame_duration amounts, when we encounter a frame in edit list.
+ if (edit_list_start_encountered > 0) {
+ edit_list_dts_counter = edit_list_dts_counter + frame_duration;
+ }
+
+ // Break when found first key frame after edit entry completion
+ if (((curr_cts + frame_duration) >= (edit_list_duration + edit_list_media_time)) &&
+ ((flags & AVINDEX_KEYFRAME) || ((st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)))) {
+
+ if (ctts_data_old && ctts_sample_old != 0) {
+ if (add_ctts_entry(&msc->ctts_data, &msc->ctts_count,
+ &msc->ctts_allocated_size,
+ ctts_sample_old - edit_list_start_ctts_sample,
+ ctts_data_old[ctts_index_old].duration) == -1) {
+ av_log(mov->fc, AV_LOG_ERROR, "Cannot add CTTS entry %"PRId64" - {%"PRId64", %d}\n",
+ ctts_index_old, ctts_sample_old - edit_list_start_ctts_sample,
+ ctts_data_old[ctts_index_old].duration);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ // If there are empty edits, then min_corrected_pts might be positive intentionally. So we subtract the
+ // sum duration of emtpy edits here.
+ min_corrected_pts -= empty_edits_sum_duration;
+
+ // If the minimum pts turns out to be greater than zero after fixing the index, then we subtract the
+ // dts by that amount to make the first pts zero.
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && min_corrected_pts > 0) {
+ av_log(mov->fc, AV_LOG_DEBUG, "Offset DTS by %"PRId64" to make first pts zero.\n", min_corrected_pts);
+ for (i = 0; i < st->nb_index_entries; ++i) {
+ st->index_entries[i].timestamp -= min_corrected_pts;
+ }
+ }
+
+ // Update av stream length
+ st->duration = edit_list_dts_entry_end - start_dts;
+ msc->start_pad = st->skip_samples;
+
+ // Free the old index and the old CTTS structures
+ av_free(e_old);
+ av_free(ctts_data_old);
+
+ // Null terminate the index ranges array
+ current_index_range++;
+ current_index_range->start = 0;
+ current_index_range->end = 0;
+ msc->current_index = msc->index_ranges[0].start;
+}
+
static void mov_build_index(MOVContext *mov, AVStream *st)
{
MOVStreamContext *sc = st->priv_data;
@@ -2322,11 +3347,42 @@ 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->elst_count) {
+ int i, edit_start_index = 0, multiple_edits = 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 {
+ multiple_edits = 1;
+ }
+ }
+
+ if (multiple_edits && !mov->advanced_editlist)
+ av_log(mov->fc, AV_LOG_WARNING, "multiple edit list entries, "
+ "Use -advanced_editlist to correctly decode otherwise "
+ "a/v desync might occur\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;
+ if (!mov->advanced_editlist)
+ current_dts = -sc->time_offset;
+ }
+
+ if (!multiple_edits && !mov->advanced_editlist &&
+ st->codecpar->codec_id == AV_CODEC_ID_AAC && start_time > 0)
+ sc->start_pad = start_time;
}
/* only use old uncompressed audio chunk demuxing when stts specifies it */
@@ -2338,12 +3394,15 @@ static void mov_build_index(MOVContext *mov, AVStream *st)
unsigned int distance = 0;
unsigned int rap_group_index = 0;
unsigned int rap_group_sample = 0;
+ int64_t last_dts = 0;
+ int64_t dts_correction = 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;
+ last_dts = current_dts;
- 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;
@@ -2356,10 +3415,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 (mov_stsc_index_valid(stsc_index, 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) {
@@ -2384,25 +3455,57 @@ static void mov_build_index(MOVContext *mov, AVStream *st)
rap_group_index++;
}
}
+ if (sc->keyframe_absent
+ && !sc->stps_count
+ && !rap_group_present
+ && (st->codecpar->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++];
+ AVIndexEntry *e;
+ if (sample_size > 0x3FFFFFFF) {
+ av_log(mov->fc, AV_LOG_ERROR, "Sample size %u is too large\n", sample_size);
+ return;
+ }
+ e = &st->index_entries[st->nb_index_entries++];
e->pos = current_offset;
e->timestamp = current_dts;
e->size = sample_size;
e->min_distance = distance;
e->flags = keyframe ? AVINDEX_KEYFRAME : 0;
av_log(mov->fc, AV_LOG_TRACE, "AVIndex stream %d, sample %u, offset %"PRIx64", dts %"PRId64", "
- "size %u, distance %u, keyframe %d\n", st->index, current_sample,
- current_offset, current_dts, sample_size, distance, keyframe);
+ "size %u, distance %u, keyframe %d\n", st->index, current_sample,
+ current_offset, current_dts, sample_size, distance, keyframe);
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && st->nb_index_entries < 100)
+ ff_rfps_add_frame(mov->fc, st, current_dts);
}
current_offset += sample_size;
stream_size += sample_size;
+
+ /* A negative sample duration is invalid based on the spec,
+ * but some samples need it to correct the DTS. */
+ if (sc->stts_data[stts_index].duration < 0) {
+ av_log(mov->fc, AV_LOG_WARNING,
+ "Invalid SampleDelta %d in STTS, at %d st:%d\n",
+ sc->stts_data[stts_index].duration, stts_index,
+ st->index);
+ dts_correction += sc->stts_data[stts_index].duration - 1;
+ sc->stts_data[stts_index].duration = 1;
+ }
current_dts += sc->stts_data[stts_index].duration;
+ if (!dts_correction || current_dts + dts_correction > last_dts) {
+ current_dts += dts_correction;
+ dts_correction = 0;
+ } else {
+ /* Avoid creating non-monotonous DTS */
+ dts_correction += current_dts - last_dts - 1;
+ current_dts = last_dts + 1;
+ }
+ last_dts = current_dts;
distance++;
stts_sample++;
current_sample++;
@@ -2492,6 +3595,10 @@ static void mov_build_index(MOVContext *mov, AVStream *st)
av_log(mov->fc, AV_LOG_ERROR, "wrong chunk count %u\n", total);
return;
}
+ if (size > 0x3FFFFFFF) {
+ av_log(mov->fc, AV_LOG_ERROR, "Sample size %u is too large\n", size);
+ return;
+ }
e = &st->index_entries[st->nb_index_entries++];
e->pos = current_offset;
e->timestamp = current_dts;
@@ -2508,16 +3615,49 @@ static void mov_build_index(MOVContext *mov, AVStream *st)
}
}
}
+
+ if (!mov->ignore_editlist && mov->advanced_editlist) {
+ // Fix index according to edit lists.
+ mov_fix_index(mov, st);
+ }
+}
+
+static int test_same_origin(const char *src, const char *ref) {
+ char src_proto[64];
+ char ref_proto[64];
+ char src_auth[256];
+ char ref_auth[256];
+ char src_host[256];
+ char ref_host[256];
+ int src_port=-1;
+ int ref_port=-1;
+
+ av_url_split(src_proto, sizeof(src_proto), src_auth, sizeof(src_auth), src_host, sizeof(src_host), &src_port, NULL, 0, src);
+ av_url_split(ref_proto, sizeof(ref_proto), ref_auth, sizeof(ref_auth), ref_host, sizeof(ref_host), &ref_port, NULL, 0, ref);
+
+ if (strlen(src) == 0) {
+ return -1;
+ } else if (strlen(src_auth) + 1 >= sizeof(src_auth) ||
+ strlen(ref_auth) + 1 >= sizeof(ref_auth) ||
+ strlen(src_host) + 1 >= sizeof(src_host) ||
+ strlen(ref_host) + 1 >= sizeof(ref_host)) {
+ return 0;
+ } else if (strcmp(src_proto, ref_proto) ||
+ strcmp(src_auth, ref_auth) ||
+ strcmp(src_host, ref_host) ||
+ src_port != ref_port) {
+ return 0;
+ } else
+ return 1;
}
-static int mov_open_dref(AVFormatContext *s, AVIOContext **pb, char *src,
- MOVDref *ref)
+static int mov_open_dref(MOVContext *c, AVIOContext **pb, const char *src, MOVDref *ref)
{
/* 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;
+ char filename[1025];
+ const char *src_path;
int i, l;
/* find a source dir */
@@ -2542,18 +3682,57 @@ static int mov_open_dref(AVFormatContext *s, AVIOContext **pb, char *src,
filename[src_path - src] = 0;
for (i = 1; i < ref->nlvl_from; i++)
- av_strlcat(filename, "../", 1024);
+ av_strlcat(filename, "../", sizeof(filename));
+
+ av_strlcat(filename, ref->path + l + 1, sizeof(filename));
+ if (!c->use_absolute_path) {
+ int same_origin = test_same_origin(src, filename);
+
+ if (!same_origin) {
+ av_log(c->fc, AV_LOG_ERROR,
+ "Reference with mismatching origin, %s not tried for security reasons, "
+ "set demuxer option use_absolute_path to allow it anyway\n",
+ ref->path);
+ return AVERROR(ENOENT);
+ }
- av_strlcat(filename, ref->path + l + 1, 1024);
+ if(strstr(ref->path + l + 1, "..") ||
+ strstr(ref->path + l + 1, ":") ||
+ (ref->nlvl_from > 1 && same_origin < 0) ||
+ (filename[0] == '/' && src_path == src))
+ return AVERROR(ENOENT);
+ }
- if (!s->io_open(s, pb, filename, AVIO_FLAG_READ, NULL))
+ if (strlen(filename) + 1 == sizeof(filename))
+ return AVERROR(ENOENT);
+ if (!c->fc->io_open(c->fc, pb, filename, AVIO_FLAG_READ, NULL))
return 0;
}
+ } else if (c->use_absolute_path) {
+ av_log(c->fc, AV_LOG_WARNING, "Using absolute path on user request, "
+ "this is a possible security issue\n");
+ if (!c->fc->io_open(c->fc, pb, ref->path, AVIO_FLAG_READ, NULL))
+ return 0;
+ } else {
+ av_log(c->fc, AV_LOG_ERROR,
+ "Absolute path %s not tried for security reasons, "
+ "set demuxer option use_absolute_path to allow absolute paths\n",
+ ref->path);
}
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;
@@ -2569,24 +3748,23 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom)
st->priv_data = sc;
st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
sc->ffindex = st->index;
+ c->trak_index = st->index;
if ((ret = mov_read_default(c, pb, atom)) < 0)
return ret;
+ c->trak_index = -1;
+
/* sanity checks */
- if (sc->chunk_count && (!sc->stts_count || !sc->stsc_count ||
- (!sc->sample_size && !sc->sample_count))) {
+ if ((sc->chunk_count && (!sc->stts_count || !sc->stsc_count ||
+ (!sc->sample_size && !sc->sample_count))) ||
+ (!sc->chunk_count && sc->sample_count)) {
av_log(c->fc, AV_LOG_ERROR, "stream %d, missing mandatory atoms, broken header\n",
st->index);
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);
@@ -2595,30 +3773,39 @@ 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 (c->enable_drefs) {
- if (mov_open_dref(c->fc, &sc->pb, c->fc->filename, dref) < 0)
+ if (mov_open_dref(c, &sc->pb, c->fc->filename, dref) < 0)
av_log(c->fc, AV_LOG_ERROR,
"stream %d, error opening alias: path='%s', dir='%s', "
- "filename='%s', volume='%s', nlvl_from=%"PRId16", nlvl_to=%"PRId16"\n",
+ "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 {
av_log(c->fc, AV_LOG_WARNING,
"Skipped opening external track: "
"stream %d, alias: path='%s', dir='%s', "
- "filename='%s', volume='%s', nlvl_from=%"PRId16", nlvl_to=%"PRId16"."
+ "filename='%s', volume='%s', nlvl_from=%d, nlvl_to=%d."
"Set enable_drefs to allow this.\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->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
- if (!st->sample_aspect_ratio.num &&
+ if (!st->sample_aspect_ratio.num && st->codecpar->width && st->codecpar->height &&
+ sc->height && sc->width &&
(st->codecpar->width != sc->width || st->codecpar->height != sc->height)) {
st->sample_aspect_ratio = av_d2q(((double)st->codecpar->height * sc->width) /
((double)st->codecpar->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.
@@ -2642,17 +3829,22 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom)
st->codecpar->width = 0; /* let decoder init width/height */
st->codecpar->height= 0;
break;
- case AV_CODEC_ID_MP3:
- st->need_parsing = AVSTREAM_PARSE_FULL;
- break;
}
+ // If the duration of the mp3 packets is not constant, then they could need a parser
+ if (st->codecpar->codec_id == AV_CODEC_ID_MP3
+ && sc->stts_count > 3
+ && sc->stts_count*10 > st->nb_frames
+ && sc->time_scale == st->codecpar->sample_rate) {
+ st->need_parsing = AVSTREAM_PARSE_FULL;
+ }
/* Do not need those anymore. */
av_freep(&sc->chunk_offsets);
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);
return 0;
@@ -2667,16 +3859,66 @@ static int mov_read_ilst(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return ret;
}
-static int mov_read_replaygain(MOVContext *c, AVIOContext *pb, int64_t size)
+static int mov_read_keys(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ uint32_t count;
+ uint32_t i;
+
+ if (atom.size < 8)
+ return 0;
+
+ avio_skip(pb, 4);
+ count = avio_rb32(pb);
+ if (count > UINT_MAX / sizeof(*c->meta_keys) - 1) {
+ av_log(c->fc, AV_LOG_ERROR,
+ "The 'keys' atom with the invalid key count: %"PRIu32"\n", count);
+ return AVERROR_INVALIDDATA;
+ }
+
+ c->meta_keys_count = count + 1;
+ c->meta_keys = av_mallocz(c->meta_keys_count * sizeof(*c->meta_keys));
+ if (!c->meta_keys)
+ return AVERROR(ENOMEM);
+
+ for (i = 1; i <= count; ++i) {
+ uint32_t key_size = avio_rb32(pb);
+ uint32_t type = avio_rl32(pb);
+ if (key_size < 8) {
+ av_log(c->fc, AV_LOG_ERROR,
+ "The key# %"PRIu32" in meta has invalid size:"
+ "%"PRIu32"\n", i, key_size);
+ return AVERROR_INVALIDDATA;
+ }
+ key_size -= 8;
+ if (type != MKTAG('m','d','t','a')) {
+ avio_skip(pb, key_size);
+ }
+ c->meta_keys[i] = av_mallocz(key_size + 1);
+ if (!c->meta_keys[i])
+ return AVERROR(ENOMEM);
+ avio_read(pb, c->meta_keys[i], key_size);
+ }
+
+ return 0;
+}
+
+static int mov_read_custom(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
- int64_t end = avio_tell(pb) + size;
- uint8_t *key = NULL, *val = NULL;
+ int64_t end = avio_tell(pb) + atom.size;
+ uint8_t *key = NULL, *val = NULL, *mean = NULL;
int i;
+ int ret = 0;
+ 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++) {
+ for (i = 0; i < 3; i++) {
uint8_t **p;
uint32_t len, tag;
- int ret;
if (end - avio_tell(pb) <= 12)
break;
@@ -2689,7 +3931,9 @@ static int mov_read_replaygain(MOVContext *c, AVIOContext *pb, int64_t size)
break;
len -= 12;
- if (tag == MKTAG('n', 'a', 'm', 'e'))
+ if (tag == MKTAG('m', 'e', 'a', 'n'))
+ p = &mean;
+ else if (tag == MKTAG('n', 'a', 'm', 'e'))
p = &key;
else if (tag == MKTAG('d', 'a', 't', 'a') && len > 4) {
avio_skip(pb, 4);
@@ -2704,54 +3948,34 @@ static int mov_read_replaygain(MOVContext *c, AVIOContext *pb, int64_t size)
ret = ffio_read_size(pb, *p, len);
if (ret < 0) {
av_freep(p);
- return ret;
+ break;
}
(*p)[len] = 0;
}
- 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 (mean && key && val) {
+ 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;
+ }
+ } else {
+ av_log(c->fc, AV_LOG_VERBOSE,
+ "Unhandled or malformed custom metadata of size %"PRId64"\n", atom.size);
}
avio_seek(pb, end, SEEK_SET);
av_freep(&key);
av_freep(&val);
- return 0;
-}
-
-static int mov_read_custom(MOVContext *c, AVIOContext *pb, MOVAtom atom)
-{
- int64_t end = avio_tell(pb) + atom.size;
- uint32_t tag, len;
-
- if (atom.size < 8)
- goto fail;
-
- len = avio_rb32(pb);
- tag = avio_rl32(pb);
-
- if (len > atom.size)
- goto fail;
-
- if (tag == MKTAG('m', 'e', 'a', 'n') && len > 12) {
- uint8_t domain[128];
- int domain_len;
-
- avio_skip(pb, 4); // flags
- len -= 12;
-
- 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));
- }
-
-fail:
- av_log(c->fc, AV_LOG_VERBOSE,
- "Unhandled or malformed custom metadata of size %"PRId64"\n", atom.size);
- return 0;
+ av_freep(&mean);
+ return ret;
}
static int mov_read_meta(MOVContext *c, AVIOContext *pb, MOVAtom atom)
@@ -2782,7 +4006,6 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
int i, j, e;
int width;
int height;
- int64_t disp_transform[2];
int display_matrix[3][3];
int res_display_matrix[3][3] = { { 0 } };
AVStream *st;
@@ -2848,6 +4071,8 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
// save the matrix when it is not the default identity
if (!IS_MATRIX_IDENT(res_display_matrix)) {
+ double rotate;
+
av_freep(&sc->display_matrix);
sc->display_matrix = av_malloc(sizeof(int32_t) * 9);
if (!sc->display_matrix)
@@ -2856,23 +4081,35 @@ 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] = res_display_matrix[i][j];
+
+#if FF_API_OLD_ROTATE_API
+ 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);
+ }
+#endif
}
// 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];
+
for (i = 0; i < 2; i++)
- disp_transform[i] =
- (int64_t) width * sc->display_matrix[0 + i] +
- (int64_t) height * sc->display_matrix[3 + i] +
- ((int64_t) sc->display_matrix[6 + i] << 16);
+ disp_transform[i] = hypot(sc->display_matrix[0 + i],
+ sc->display_matrix[3 + i]);
- //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;
}
@@ -2881,7 +4118,8 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
MOVFragment *frag = &c->fragment;
MOVTrackExt *trex = NULL;
- int flags, track_id, i;
+ MOVFragmentIndex* index = NULL;
+ int flags, track_id, i, found = 0;
avio_r8(pb); /* version */
flags = avio_rb24(pb);
@@ -2911,13 +4149,54 @@ 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;
+ for (i = 0; i < c->fragment_index_count; i++) {
+ int j;
+ 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;
+ for (j = index->current_item; j < index->item_count; j++) {
+ if (frag->implicit_offset == index->items[j].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[j].moof_offset);
+ frag->time = index->items[j].time;
+ index->current_item = j + 1;
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ }
+ if (index && !found) {
+ av_log(c->fc, AV_LOG_DEBUG, "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_log(c->fc, AV_LOG_TRACE, "frag flags 0x%x\n", frag->flags);
return 0;
}
static int mov_read_chap(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
- c->chapter_track = avio_rb32(pb);
+ unsigned i, num;
+ void *new_tracks;
+
+ num = atom.size / 4;
+ if (!(new_tracks = av_malloc_array(num, sizeof(int))))
+ return AVERROR(ENOMEM);
+
+ av_free(c->chapter_tracks);
+ c->chapter_tracks = new_tracks;
+ c->nb_chapter_tracks = num;
+
+ for (i = 0; i < num && !pb->eof_reached; i++)
+ c->chapter_tracks[i] = avio_rb32(pb);
+
return 0;
}
@@ -2933,6 +4212,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 */
@@ -2984,7 +4266,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
int64_t dts;
int data_offset = 0;
unsigned entries, first_sample_flags = frag->flags;
- int flags, distance, i, err;
+ int flags, distance, i;
for (i = 0; i < c->fc->nb_streams; i++) {
if (c->fc->streams[i]->id == frag->track_id) {
@@ -2997,36 +4279,15 @@ 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);
entries = avio_rb32(pb);
av_log(c->fc, AV_LOG_TRACE, "flags 0x%x entries %u\n", flags, entries);
- /* Always assume the presence of composition time offsets.
- * Without this assumption, for instance, we cannot deal with a track in fragmented movies that meet the following.
- * 1) in the initial movie, there are no samples.
- * 2) in the first movie fragment, there is only one sample without composition time offset.
- * 3) in the subsequent movie fragments, there are samples with composition time offset. */
- if (!sc->ctts_count && sc->sample_count)
- {
- /* Complement ctts table if moov atom doesn't have ctts atom. */
- ctts_data = av_realloc(NULL, sizeof(*sc->ctts_data));
- if (!ctts_data)
- return AVERROR(ENOMEM);
- sc->ctts_data = ctts_data;
- sc->ctts_data[sc->ctts_count].count = sc->sample_count;
- sc->ctts_data[sc->ctts_count].duration = 0;
- sc->ctts_count++;
- }
if ((uint64_t)entries+sc->ctts_count >= UINT_MAX/sizeof(*sc->ctts_data))
return AVERROR_INVALIDDATA;
- if ((err = av_reallocp_array(&sc->ctts_data, entries + sc->ctts_count,
- sizeof(*sc->ctts_data))) < 0) {
- sc->ctts_count = 0;
- return err;
- }
if (flags & MOV_TRUN_DATA_OFFSET) data_offset = avio_rb32(pb);
if (flags & MOV_TRUN_FIRST_SAMPLE_FLAGS) first_sample_flags = avio_rb32(pb);
dts = sc->track_end - sc->time_offset;
@@ -3037,15 +4298,40 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
unsigned sample_size = frag->size;
int sample_flags = i ? frag->flags : first_sample_flags;
unsigned sample_duration = frag->duration;
+ unsigned ctts_duration = 0;
int keyframe = 0;
+ int ctts_index = 0;
+ int old_nb_index_entries = st->nb_index_entries;
if (flags & MOV_TRUN_SAMPLE_DURATION) sample_duration = avio_rb32(pb);
if (flags & MOV_TRUN_SAMPLE_SIZE) sample_size = avio_rb32(pb);
if (flags & MOV_TRUN_SAMPLE_FLAGS) sample_flags = avio_rb32(pb);
- sc->ctts_data[sc->ctts_count].count = 1;
- sc->ctts_data[sc->ctts_count].duration = (flags & MOV_TRUN_SAMPLE_CTS) ?
- avio_rb32(pb) : 0;
- sc->ctts_count++;
+ if (flags & MOV_TRUN_SAMPLE_CTS) ctts_duration = avio_rb32(pb);
+
+ mov_update_dts_shift(sc, ctts_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, ctts_duration,
+ sc->time_offset, flags & MOV_TRUN_SAMPLE_CTS);
+ dts = pts - sc->dts_shift;
+ if (flags & MOV_TRUN_SAMPLE_CTS) {
+ dts -= ctts_duration;
+ } else {
+ dts -= sc->time_offset;
+ }
+ av_log(c->fc, AV_LOG_DEBUG, "calculated into dts %"PRId64"\n", dts);
+ } else {
+ dts = frag->time - sc->time_offset;
+ av_log(c->fc, AV_LOG_DEBUG, "found frag time %"PRId64
+ ", using it for dts\n", dts);
+ }
+ frag->time = AV_NOPTS_VALUE;
+ }
+
if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
keyframe = 1;
else
@@ -3054,22 +4340,180 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
MOV_FRAG_SAMPLE_FLAG_DEPENDS_YES));
if (keyframe)
distance = 0;
- av_add_index_entry(st, offset, dts, sample_size, distance,
- keyframe ? AVINDEX_KEYFRAME : 0);
- av_log(c->fc, AV_LOG_TRACE, "AVIndex stream %d, sample %u, offset %"PRIx64", dts %"PRId64", "
- "size %u, distance %d, keyframe %d\n", st->index, sc->sample_count+i,
- offset, dts, sample_size, distance, keyframe);
+ ctts_index = av_add_index_entry(st, offset, dts, sample_size, distance,
+ keyframe ? AVINDEX_KEYFRAME : 0);
+ if (ctts_index >= 0 && old_nb_index_entries < st->nb_index_entries) {
+ unsigned int size_needed = st->nb_index_entries * sizeof(*sc->ctts_data);
+ unsigned int request_size = size_needed > sc->ctts_allocated_size ?
+ FFMAX(size_needed, 2 * sc->ctts_allocated_size) : size_needed;
+ unsigned int old_ctts_size = sc->ctts_allocated_size;
+ ctts_data = av_fast_realloc(sc->ctts_data, &sc->ctts_allocated_size, request_size);
+ if (!ctts_data) {
+ av_freep(&sc->ctts_data);
+ return AVERROR(ENOMEM);
+ }
+ sc->ctts_data = ctts_data;
+
+ // In case there were samples without ctts entries, ensure they get
+ // zero valued entries. This ensures clips which mix boxes with and
+ // without ctts entries don't pickup uninitialized data.
+ memset((uint8_t*)(sc->ctts_data) + old_ctts_size, 0, sc->ctts_allocated_size - old_ctts_size);
+
+ if (ctts_index != old_nb_index_entries) {
+ memmove(sc->ctts_data + ctts_index + 1, sc->ctts_data + ctts_index,
+ sizeof(*sc->ctts_data) * (sc->ctts_count - ctts_index));
+ if (ctts_index <= sc->current_sample) {
+ // if we inserted a new item before the current sample, move the
+ // counter ahead so it is still pointing to the same sample.
+ sc->current_sample++;
+ }
+ }
+
+ sc->ctts_data[ctts_index].count = 1;
+ sc->ctts_data[ctts_index].duration = ctts_duration;
+ sc->ctts_count++;
+ } else {
+ av_log(c->fc, AV_LOG_ERROR, "Failed to add index entry\n");
+ }
+
+ av_log(c->fc, AV_LOG_TRACE, "AVIndex stream %d, sample %d, offset %"PRIx64", dts %"PRId64", "
+ "size %u, distance %d, keyframe %d\n", st->index, ctts_index,
+ offset, dts, sample_size, distance, keyframe);
distance++;
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)
return AVERROR_EOF;
frag->implicit_offset = offset;
- st->duration = sc->track_end = dts + sc->time_offset;
+
+ sc->track_end = dts + sc->time_offset;
+ if (st->duration < sc->track_end)
+ st->duration = sc->track_end;
+
+ return 0;
+}
+
+static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ int64_t offset = avio_tell(pb) + atom.size, pts;
+ uint8_t version;
+ unsigned i, track_id;
+ AVStream *st = NULL;
+ AVStream *ref_st = NULL;
+ MOVStreamContext *sc, *ref_sc = NULL;
+ MOVFragmentIndex *index = NULL;
+ MOVFragmentIndex **tmp;
+ AVRational timescale;
+
+ version = avio_r8(pb);
+ if (version > 1) {
+ avpriv_request_sample(c->fc, "sidx version %u", version);
+ return 0;
+ }
+
+ avio_rb24(pb); // flags
+
+ track_id = avio_rb32(pb); // Reference ID
+ for (i = 0; i < c->fc->nb_streams; i++) {
+ if (c->fc->streams[i]->id == track_id) {
+ st = c->fc->streams[i];
+ break;
+ }
+ }
+ if (!st) {
+ av_log(c->fc, AV_LOG_WARNING, "could not find corresponding track id %d\n", track_id);
+ return 0;
+ }
+
+ sc = st->priv_data;
+
+ timescale = av_make_q(1, avio_rb32(pb));
+
+ if (timescale.den <= 0) {
+ av_log(c->fc, AV_LOG_ERROR, "Invalid sidx timescale 1/%d\n", timescale.den);
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (version == 0) {
+ pts = avio_rb32(pb);
+ offset += avio_rb32(pb);
+ } else {
+ pts = avio_rb64(pb);
+ offset += avio_rb64(pb);
+ }
+
+ avio_rb16(pb); // reserved
+
+ index = av_mallocz(sizeof(MOVFragmentIndex));
+ if (!index)
+ return AVERROR(ENOMEM);
+
+ index->track_id = track_id;
+
+ index->item_count = avio_rb16(pb);
+ index->items = av_mallocz_array(index->item_count, sizeof(MOVFragmentIndexItem));
+
+ if (!index->items) {
+ av_freep(&index);
+ return AVERROR(ENOMEM);
+ }
+
+ for (i = 0; i < index->item_count; i++) {
+ uint32_t size = avio_rb32(pb);
+ uint32_t duration = avio_rb32(pb);
+ if (size & 0x80000000) {
+ avpriv_request_sample(c->fc, "sidx reference_type 1");
+ av_freep(&index->items);
+ av_freep(&index);
+ return AVERROR_PATCHWELCOME;
+ }
+ avio_rb32(pb); // sap_flags
+ index->items[i].moof_offset = offset;
+ index->items[i].time = av_rescale_q(pts, st->time_base, timescale);
+ offset += size;
+ pts += duration;
+ }
+
+ st->duration = sc->track_end = pts;
+
+ tmp = av_realloc_array(c->fragment_index_data,
+ c->fragment_index_count + 1,
+ sizeof(MOVFragmentIndex*));
+ if (!tmp) {
+ av_freep(&index->items);
+ av_freep(&index);
+ return AVERROR(ENOMEM);
+ }
+
+ c->fragment_index_data = tmp;
+ c->fragment_index_data[c->fragment_index_count++] = index;
+ sc->has_sidx = 1;
+
+ if (offset == avio_size(pb)) {
+ for (i = 0; i < c->fc->nb_streams; i++) {
+ if (c->fc->streams[i]->id == c->fragment_index_data[0]->track_id) {
+ ref_st = c->fc->streams[i];
+ ref_sc = ref_st->priv_data;
+ break;
+ }
+ }
+ for (i = 0; i < c->fc->nb_streams; i++) {
+ st = c->fc->streams[i];
+ sc = st->priv_data;
+ if (!sc->has_sidx) {
+ st->duration = sc->track_end = av_rescale(ref_st->duration, sc->time_scale, ref_sc->time_scale);
+ }
+ }
+
+ c->fragment_index_complete = 1;
+ }
+
return 0;
}
@@ -3109,7 +4553,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 */
@@ -3134,6 +4578,7 @@ static int mov_read_cmov(MOVContext *c, AVIOContext *pb, MOVAtom atom)
goto free_and_return;
if (ffio_init_context(&ctx, moov_data, moov_len, 0, NULL, NULL, NULL, NULL) != 0)
goto free_and_return;
+ ctx.seekable = AVIO_SEEKABLE_NORMAL;
atom.type = MKTAG('m','o','o','v');
atom.size = moov_len;
ret = mov_read_default(c, &ctx, atom);
@@ -3153,7 +4598,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;
@@ -3161,30 +4606,173 @@ 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_log(c->fc, AV_LOG_TRACE, "track[%u].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 */
}
- avio_rb32(pb); /* Media rate */
- if (i == 0 && time >= -1) {
- sc->time_offset = time != -1 ? time : -duration;
+ e->rate = avio_rb32(pb) / 65536.0;
+ av_log(c->fc, AV_LOG_TRACE, "duration=%"PRId64" time=%"PRId64" rate=%f\n",
+ e->duration, e->time, e->rate);
+
+ if (e->time < 0 && e->time != -1 &&
+ c->fc->strict_std_compliance >= FF_COMPLIANCE_STRICT) {
+ av_log(c->fc, AV_LOG_ERROR, "Track %d, edit %d: Invalid edit list media time=%"PRId64"\n",
+ c->fc->nb_streams-1, i, e->time);
+ return AVERROR_INVALIDDATA;
}
}
+ sc->elst_count = i;
- if (edit_count > 1)
- av_log(c->fc, AV_LOG_WARNING, "multiple edit list entries, "
- "a/v desync might occur, patch welcome\n");
+ 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_vpcc(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ AVStream *st;
+ int version, color_range, color_primaries, color_trc, color_space;
+
+ if (c->fc->nb_streams < 1)
+ return 0;
+ st = c->fc->streams[c->fc->nb_streams - 1];
+
+ if (atom.size < 5) {
+ av_log(c->fc, AV_LOG_ERROR, "Empty VP Codec Configuration box\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ version = avio_r8(pb);
+ if (version != 1) {
+ av_log(c->fc, AV_LOG_WARNING, "Unsupported VP Codec Configuration box version %d\n", version);
+ return 0;
+ }
+ avio_skip(pb, 3); /* flags */
+
+ avio_skip(pb, 2); /* profile + level */
+ color_range = avio_r8(pb); /* bitDepth, chromaSubsampling, videoFullRangeFlag */
+ color_primaries = avio_r8(pb);
+ color_trc = avio_r8(pb);
+ color_space = avio_r8(pb);
+ if (avio_rb16(pb)) /* codecIntializationDataSize */
+ return AVERROR_INVALIDDATA;
+
+ if (!av_color_primaries_name(color_primaries))
+ color_primaries = AVCOL_PRI_UNSPECIFIED;
+ if (!av_color_transfer_name(color_trc))
+ color_trc = AVCOL_TRC_UNSPECIFIED;
+ if (!av_color_space_name(color_space))
+ color_space = AVCOL_SPC_UNSPECIFIED;
+
+ st->codecpar->color_range = (color_range & 1) ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
+ st->codecpar->color_primaries = color_primaries;
+ st->codecpar->color_trc = color_trc;
+ st->codecpar->color_space = color_space;
+
+ return 0;
+}
+
+static int mov_read_smdm(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ MOVStreamContext *sc;
+ const int chroma_den = 50000;
+ const int luma_den = 10000;
+ int i, j, version;
+
+ if (c->fc->nb_streams < 1)
+ return AVERROR_INVALIDDATA;
+
+ sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data;
+
+ if (atom.size < 5) {
+ av_log(c->fc, AV_LOG_ERROR, "Empty Mastering Display Metadata box\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ version = avio_r8(pb);
+ if (version) {
+ av_log(c->fc, AV_LOG_WARNING, "Unsupported Mastering Display Metadata box version %d\n", version);
+ return 0;
+ }
+ avio_skip(pb, 3); /* flags */
+
+ sc->mastering = av_mastering_display_metadata_alloc();
+ if (!sc->mastering)
+ return AVERROR(ENOMEM);
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
+ sc->mastering->display_primaries[i][j] =
+ av_make_q(lrint(((double)avio_rb16(pb) / (1 << 16)) * chroma_den), chroma_den);
+ for (i = 0; i < 2; i++)
+ sc->mastering->white_point[i] =
+ av_make_q(lrint(((double)avio_rb16(pb) / (1 << 16)) * chroma_den), chroma_den);
+ sc->mastering->max_luminance =
+ av_make_q(lrint(((double)avio_rb32(pb) / (1 << 8)) * luma_den), luma_den);
+ sc->mastering->min_luminance =
+ av_make_q(lrint(((double)avio_rb32(pb) / (1 << 14)) * luma_den), luma_den);
+
+ sc->mastering->has_primaries = 1;
+ sc->mastering->has_luminance = 1;
+
+ return 0;
+}
+
+static int mov_read_coll(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ MOVStreamContext *sc;
+ int version;
+
+ if (c->fc->nb_streams < 1)
+ return AVERROR_INVALIDDATA;
+
+ sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data;
+
+ if (atom.size < 5) {
+ av_log(c->fc, AV_LOG_ERROR, "Empty Content Light Level box\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ version = avio_r8(pb);
+ if (version) {
+ av_log(c->fc, AV_LOG_WARNING, "Unsupported Content Light Level box version %d\n", version);
+ return 0;
+ }
+ avio_skip(pb, 3); /* flags */
+
+ sc->coll = av_content_light_metadata_alloc(&sc->coll_size);
+ if (!sc->coll)
+ return AVERROR(ENOMEM);
+
+ sc->coll->MaxCLL = avio_rb16(pb);
+ sc->coll->MaxFALL = avio_rb16(pb);
- av_log(c->fc, AV_LOG_TRACE, "track[%u].edit_count = %i\n", c->fc->nb_streams - 1, edit_count);
return 0;
}
@@ -3237,9 +4825,8 @@ static int mov_read_sv3d(MOVContext *c, AVIOContext *pb, MOVAtom atom)
MOVStreamContext *sc;
int size, version, layout;
int32_t yaw, pitch, roll;
- size_t l = 0, t = 0, r = 0, b = 0;
- size_t padding = 0;
- uint32_t tag;
+ uint32_t l = 0, t = 0, r = 0, b = 0;
+ uint32_t tag, padding = 0;
enum AVSphericalProjection projection;
if (c->fc->nb_streams < 1)
@@ -3335,7 +4922,7 @@ static int mov_read_sv3d(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (b >= UINT_MAX - t || r >= UINT_MAX - l) {
av_log(c->fc, AV_LOG_ERROR,
"Invalid bounding rectangle coordinates "
- "%zu,%zu,%zu,%zu\n", l, t, r, b);
+ "%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"\n", l, t, r, b);
return AVERROR_INVALIDDATA;
}
@@ -3436,15 +5023,22 @@ static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
MOVStreamContext *sc;
- int ret;
-
+ int64_t 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
+ };
+ static const uint8_t uuid_xmp[] = {
+ 0xbe, 0x7a, 0xcf, 0xcb, 0x97, 0xa9, 0x42, 0xe8,
+ 0x9c, 0x71, 0x99, 0x94, 0x91, 0xe3, 0xaf, 0xac
+ };
static const uint8_t uuid_spherical[] = {
0xff, 0xcc, 0x82, 0x63, 0xf8, 0x55, 0x4a, 0x93,
0x88, 0x14, 0x58, 0x7a, 0x02, 0x52, 0x1f, 0xdd,
};
- if (atom.size < sizeof(uuid) || atom.size == INT64_MAX)
+ if (atom.size < sizeof(uuid) || atom.size >= FFMIN(INT_MAX, SIZE_MAX))
return AVERROR_INVALIDDATA;
if (c->fc->nb_streams < 1)
@@ -3452,45 +5046,482 @@ static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom)
st = c->fc->streams[c->fc->nb_streams - 1];
sc = st->priv_data;
- ret = ffio_read_size(pb, uuid, sizeof(uuid));
- if (ret < 0)
+ ret = avio_read(pb, uuid, sizeof(uuid));
+ if (ret < 0) {
return ret;
+ } else if (ret != sizeof(uuid)) {
+ return AVERROR_INVALIDDATA;
+ }
+ if (!memcmp(uuid, uuid_isml_manifest, sizeof(uuid))) {
+ uint8_t *buffer, *ptr;
+ char *endptr;
+ size_t len = atom.size - sizeof(uuid);
+
+ if (len < 4) {
+ return AVERROR_INVALIDDATA;
+ }
+ ret = avio_skip(pb, 4); // zeroes
+ len -= 4;
+
+ buffer = av_mallocz(len + 1);
+ if (!buffer) {
+ return AVERROR(ENOMEM);
+ }
+ ret = avio_read(pb, buffer, len);
+ if (ret < 0) {
+ av_free(buffer);
+ return ret;
+ } else if (ret != len) {
+ av_free(buffer);
+ return AVERROR_INVALIDDATA;
+ }
+
+ ptr = buffer;
+ while ((ptr = av_stristr(ptr, "systemBitrate=\""))) {
+ ptr += sizeof("systemBitrate=\"") - 1;
+ c->bitrates_count++;
+ c->bitrates = av_realloc_f(c->bitrates, c->bitrates_count, sizeof(*c->bitrates));
+ if (!c->bitrates) {
+ c->bitrates_count = 0;
+ av_free(buffer);
+ return AVERROR(ENOMEM);
+ }
+ errno = 0;
+ ret = strtol(ptr, &endptr, 10);
+ if (ret < 0 || errno || *endptr != '"') {
+ c->bitrates[c->bitrates_count - 1] = 0;
+ } else {
+ c->bitrates[c->bitrates_count - 1] = ret;
+ }
+ }
- if (!memcmp(uuid, uuid_spherical, sizeof(uuid)) && !sc->spherical) {
+ av_free(buffer);
+ } else if (!memcmp(uuid, uuid_xmp, sizeof(uuid))) {
+ uint8_t *buffer;
+ size_t len = atom.size - sizeof(uuid);
+ if (c->export_xmp) {
+ buffer = av_mallocz(len + 1);
+ if (!buffer) {
+ return AVERROR(ENOMEM);
+ }
+ ret = avio_read(pb, buffer, len);
+ if (ret < 0) {
+ av_free(buffer);
+ return ret;
+ } else if (ret != len) {
+ av_free(buffer);
+ return AVERROR_INVALIDDATA;
+ }
+ buffer[len] = '\0';
+ av_dict_set(&c->fc->metadata, "xmp", buffer, 0);
+ av_free(buffer);
+ } else {
+ // skip all uuid atom, which makes it fast for long uuid-xmp file
+ ret = avio_skip(pb, len);
+ if (ret < 0)
+ return ret;
+ }
+ } else if (!memcmp(uuid, uuid_spherical, sizeof(uuid))) {
size_t len = atom.size - sizeof(uuid);
ret = mov_parse_uuid_spherical(sc, pb, len);
if (ret < 0)
return ret;
if (!sc->spherical)
- av_log(c->fc, AV_LOG_WARNING, "Invalid spherical metadata found\n");
- } else {
- int i;
- av_log(c->fc, AV_LOG_VERBOSE, "Unknown UUID found: 0x");
- for (i = 0; i < sizeof(uuid); i++)
- av_log(c->fc, AV_LOG_WARNING, "%02x", uuid[i]);
- av_log(c->fc, AV_LOG_WARNING, "\n");
+ av_log(c->fc, AV_LOG_WARNING, "Invalid spherical metadata found\n"); }
+
+ return 0;
+}
+
+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;
+ }
+
+ return 0;
+}
+
+static int mov_read_frma(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ uint32_t format = avio_rl32(pb);
+ MOVStreamContext *sc;
+ enum AVCodecID id;
+ AVStream *st;
+
+ if (c->fc->nb_streams < 1)
+ return 0;
+ st = c->fc->streams[c->fc->nb_streams - 1];
+ sc = st->priv_data;
+
+ switch (sc->format)
+ {
+ case MKTAG('e','n','c','v'): // encrypted video
+ case MKTAG('e','n','c','a'): // encrypted audio
+ id = mov_codec_id(st, format);
+ if (st->codecpar->codec_id != AV_CODEC_ID_NONE &&
+ st->codecpar->codec_id != id) {
+ av_log(c->fc, AV_LOG_WARNING,
+ "ignoring 'frma' atom of '%.4s', stream has codec id %d\n",
+ (char*)&format, st->codecpar->codec_id);
+ break;
+ }
+
+ st->codecpar->codec_id = id;
+ sc->format = format;
+ break;
+
+ default:
+ if (format != sc->format) {
+ av_log(c->fc, AV_LOG_WARNING,
+ "ignoring 'frma' atom of '%.4s', stream format is '%.4s'\n",
+ (char*)&format, (char*)&sc->format);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int mov_read_senc(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ AVStream *st;
+ MOVStreamContext *sc;
+ size_t auxiliary_info_size;
+
+ if (c->decryption_key_len == 0 || c->fc->nb_streams < 1)
+ return 0;
+
+ st = c->fc->streams[c->fc->nb_streams - 1];
+ sc = st->priv_data;
+
+ if (sc->cenc.aes_ctr) {
+ av_log(c->fc, AV_LOG_ERROR, "duplicate senc atom\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ avio_r8(pb); /* version */
+ sc->cenc.use_subsamples = avio_rb24(pb) & 0x02; /* flags */
+
+ avio_rb32(pb); /* entries */
+
+ if (atom.size < 8 || atom.size > FFMIN(INT_MAX, SIZE_MAX)) {
+ av_log(c->fc, AV_LOG_ERROR, "senc atom size %"PRId64" invalid\n", atom.size);
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* save the auxiliary info as is */
+ auxiliary_info_size = atom.size - 8;
+
+ sc->cenc.auxiliary_info = av_malloc(auxiliary_info_size);
+ if (!sc->cenc.auxiliary_info) {
+ return AVERROR(ENOMEM);
+ }
+
+ sc->cenc.auxiliary_info_end = sc->cenc.auxiliary_info + auxiliary_info_size;
+ sc->cenc.auxiliary_info_pos = sc->cenc.auxiliary_info;
+ sc->cenc.auxiliary_info_index = 0;
+
+ if (avio_read(pb, sc->cenc.auxiliary_info, auxiliary_info_size) != auxiliary_info_size) {
+ av_log(c->fc, AV_LOG_ERROR, "failed to read the auxiliary info");
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* initialize the cipher */
+ sc->cenc.aes_ctr = av_aes_ctr_alloc();
+ if (!sc->cenc.aes_ctr) {
+ return AVERROR(ENOMEM);
+ }
+
+ return av_aes_ctr_init(sc->cenc.aes_ctr, c->decryption_key);
+}
+
+static int mov_read_saiz(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ AVStream *st;
+ MOVStreamContext *sc;
+ size_t data_size;
+ int atom_header_size;
+ int flags;
+
+ if (c->decryption_key_len == 0 || c->fc->nb_streams < 1)
+ return 0;
+
+ st = c->fc->streams[c->fc->nb_streams - 1];
+ sc = st->priv_data;
+
+ if (sc->cenc.auxiliary_info_sizes || sc->cenc.auxiliary_info_default_size) {
+ av_log(c->fc, AV_LOG_ERROR, "duplicate saiz atom\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ atom_header_size = 9;
+
+ avio_r8(pb); /* version */
+ flags = avio_rb24(pb);
+
+ if ((flags & 0x01) != 0) {
+ atom_header_size += 8;
+
+ avio_rb32(pb); /* info type */
+ avio_rb32(pb); /* info type param */
+ }
+
+ sc->cenc.auxiliary_info_default_size = avio_r8(pb);
+ avio_rb32(pb); /* entries */
+
+ if (atom.size <= atom_header_size) {
+ return 0;
+ }
+
+ if (atom.size > FFMIN(INT_MAX, SIZE_MAX)) {
+ av_log(c->fc, AV_LOG_ERROR, "saiz atom auxiliary_info_sizes size %"PRId64" invalid\n", atom.size);
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* save the auxiliary info sizes as is */
+ data_size = atom.size - atom_header_size;
+
+ sc->cenc.auxiliary_info_sizes = av_malloc(data_size);
+ if (!sc->cenc.auxiliary_info_sizes) {
+ return AVERROR(ENOMEM);
+ }
+
+ sc->cenc.auxiliary_info_sizes_count = data_size;
+
+ if (avio_read(pb, sc->cenc.auxiliary_info_sizes, data_size) != data_size) {
+ av_log(c->fc, AV_LOG_ERROR, "failed to read the auxiliary info sizes");
+ return AVERROR_INVALIDDATA;
+ }
+
+ return 0;
+}
+
+static int mov_read_dfla(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ AVStream *st;
+ int last, type, size, ret;
+ uint8_t buf[4];
+
+ if (c->fc->nb_streams < 1)
+ return 0;
+ st = c->fc->streams[c->fc->nb_streams-1];
+
+ if ((uint64_t)atom.size > (1<<30) || atom.size < 42)
+ return AVERROR_INVALIDDATA;
+
+ /* Check FlacSpecificBox version. */
+ if (avio_r8(pb) != 0)
+ return AVERROR_INVALIDDATA;
+
+ avio_rb24(pb); /* Flags */
+
+ avio_read(pb, buf, sizeof(buf));
+ flac_parse_block_header(buf, &last, &type, &size);
+
+ if (type != FLAC_METADATA_TYPE_STREAMINFO || size != FLAC_STREAMINFO_SIZE) {
+ av_log(c->fc, AV_LOG_ERROR, "STREAMINFO must be first FLACMetadataBlock\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ ret = ff_get_extradata(c->fc, st->codecpar, pb, size);
+ if (ret < 0)
+ return ret;
+
+ if (!last)
+ av_log(c->fc, AV_LOG_WARNING, "non-STREAMINFO FLACMetadataBlock(s) ignored\n");
+
+ return 0;
+}
+
+static int mov_seek_auxiliary_info(MOVContext *c, MOVStreamContext *sc, int64_t index)
+{
+ size_t auxiliary_info_seek_offset = 0;
+ int i;
+
+ if (sc->cenc.auxiliary_info_default_size) {
+ auxiliary_info_seek_offset = (size_t)sc->cenc.auxiliary_info_default_size * index;
+ } else if (sc->cenc.auxiliary_info_sizes) {
+ if (index > sc->cenc.auxiliary_info_sizes_count) {
+ av_log(c, AV_LOG_ERROR, "current sample %"PRId64" greater than the number of auxiliary info sample sizes %"SIZE_SPECIFIER"\n",
+ index, sc->cenc.auxiliary_info_sizes_count);
+ return AVERROR_INVALIDDATA;
+ }
+
+ for (i = 0; i < index; i++) {
+ auxiliary_info_seek_offset += sc->cenc.auxiliary_info_sizes[i];
+ }
+ }
+
+ if (auxiliary_info_seek_offset > sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info) {
+ av_log(c, AV_LOG_ERROR, "auxiliary info offset %"SIZE_SPECIFIER" greater than auxiliary info size %"SIZE_SPECIFIER"\n",
+ auxiliary_info_seek_offset, (size_t)(sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info));
+ return AVERROR_INVALIDDATA;
+ }
+
+ sc->cenc.auxiliary_info_pos = sc->cenc.auxiliary_info + auxiliary_info_seek_offset;
+ sc->cenc.auxiliary_info_index = index;
+ return 0;
+}
+
+static int cenc_filter(MOVContext *c, MOVStreamContext *sc, int64_t index, uint8_t *input, int size)
+{
+ uint32_t encrypted_bytes;
+ uint16_t subsample_count;
+ uint16_t clear_bytes;
+ uint8_t* input_end = input + size;
+ int ret;
+
+ if (index != sc->cenc.auxiliary_info_index) {
+ ret = mov_seek_auxiliary_info(c, sc, index);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ /* read the iv */
+ if (AES_CTR_IV_SIZE > sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info_pos) {
+ av_log(c->fc, AV_LOG_ERROR, "failed to read iv from the auxiliary info\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ av_aes_ctr_set_iv(sc->cenc.aes_ctr, sc->cenc.auxiliary_info_pos);
+ sc->cenc.auxiliary_info_pos += AES_CTR_IV_SIZE;
+
+ if (!sc->cenc.use_subsamples)
+ {
+ /* decrypt the whole packet */
+ av_aes_ctr_crypt(sc->cenc.aes_ctr, input, input, size);
+ return 0;
+ }
+
+ /* read the subsample count */
+ if (sizeof(uint16_t) > sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info_pos) {
+ av_log(c->fc, AV_LOG_ERROR, "failed to read subsample count from the auxiliary info\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ subsample_count = AV_RB16(sc->cenc.auxiliary_info_pos);
+ sc->cenc.auxiliary_info_pos += sizeof(uint16_t);
+
+ for (; subsample_count > 0; subsample_count--)
+ {
+ if (6 > sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info_pos) {
+ av_log(c->fc, AV_LOG_ERROR, "failed to read subsample from the auxiliary info\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* read the number of clear / encrypted bytes */
+ clear_bytes = AV_RB16(sc->cenc.auxiliary_info_pos);
+ sc->cenc.auxiliary_info_pos += sizeof(uint16_t);
+ encrypted_bytes = AV_RB32(sc->cenc.auxiliary_info_pos);
+ sc->cenc.auxiliary_info_pos += sizeof(uint32_t);
+
+ if ((uint64_t)clear_bytes + encrypted_bytes > input_end - input) {
+ av_log(c->fc, AV_LOG_ERROR, "subsample size exceeds the packet size left\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* skip the clear bytes */
+ input += clear_bytes;
+
+ /* decrypt the encrypted bytes */
+ av_aes_ctr_crypt(sc->cenc.aes_ctr, input, input, encrypted_bytes);
+ input += encrypted_bytes;
+ }
+
+ if (input < input_end) {
+ av_log(c->fc, AV_LOG_ERROR, "leftover packet bytes after subsample processing\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ sc->cenc.auxiliary_info_index++;
+ return 0;
+}
+
+static int mov_read_dops(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ const int OPUS_SEEK_PREROLL_MS = 80;
+ AVStream *st;
+ size_t size;
+ int16_t pre_skip;
+
+ if (c->fc->nb_streams < 1)
+ return 0;
+ st = c->fc->streams[c->fc->nb_streams-1];
+
+ if ((uint64_t)atom.size > (1<<30) || atom.size < 11)
+ return AVERROR_INVALIDDATA;
+
+ /* Check OpusSpecificBox version. */
+ if (avio_r8(pb) != 0) {
+ av_log(c->fc, AV_LOG_ERROR, "unsupported OpusSpecificBox version\n");
+ return AVERROR_INVALIDDATA;
}
+ /* OpusSpecificBox size plus magic for Ogg OpusHead header. */
+ size = atom.size + 8;
+
+ if (ff_alloc_extradata(st->codecpar, size))
+ return AVERROR(ENOMEM);
+
+ AV_WL32(st->codecpar->extradata, MKTAG('O','p','u','s'));
+ AV_WL32(st->codecpar->extradata + 4, MKTAG('H','e','a','d'));
+ AV_WB8(st->codecpar->extradata + 8, 1); /* OpusHead version */
+ avio_read(pb, st->codecpar->extradata + 9, size - 9);
+
+ /* OpusSpecificBox is stored in big-endian, but OpusHead is
+ little-endian; aside from the preceeding magic and version they're
+ otherwise currently identical. Data after output gain at offset 16
+ doesn't need to be bytewapped. */
+ pre_skip = AV_RB16(st->codecpar->extradata + 10);
+ AV_WL16(st->codecpar->extradata + 10, pre_skip);
+ AV_WL32(st->codecpar->extradata + 12, AV_RB32(st->codecpar->extradata + 12));
+ AV_WL16(st->codecpar->extradata + 16, AV_RB16(st->codecpar->extradata + 16));
+
+ st->codecpar->initial_padding = pre_skip;
+ st->codecpar->seek_preroll = av_rescale_q(OPUS_SEEK_PREROLL_MS,
+ (AVRational){1, 1000},
+ (AVRational){1, 48000});
+
return 0;
}
static const MOVParseTableEntry mov_default_parse_table[] = {
-{ MKTAG('a','v','s','s'), mov_read_extradata },
+{ MKTAG('A','C','L','R'), mov_read_aclr },
+{ 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 },
{ MKTAG('e','n','d','a'), mov_read_enda },
{ MKTAG('f','i','e','l'), mov_read_fiel },
+{ MKTAG('a','d','r','m'), mov_read_adrm },
{ MKTAG('f','t','y','p'), mov_read_ftyp },
{ 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 },
@@ -3500,10 +5531,11 @@ 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','i','d','x'), mov_read_sidx },
{ MKTAG('s','t','b','l'), mov_read_default },
{ MKTAG('s','t','c','o'), mov_read_stco },
{ MKTAG('s','t','p','s'), mov_read_stps },
@@ -3520,6 +5552,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 },
@@ -3528,6 +5561,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
{ MKTAG('e','s','d','s'), mov_read_esds },
{ MKTAG('d','a','c','3'), mov_read_dac3 }, /* AC-3 info */
{ MKTAG('d','e','c','3'), mov_read_dec3 }, /* EAC-3 info */
+{ MKTAG('d','d','t','s'), mov_read_ddts }, /* DTS audio descriptor */
{ MKTAG('w','i','d','e'), mov_read_wide }, /* place holder */
{ MKTAG('w','f','e','x'), mov_read_wfex },
{ MKTAG('c','m','o','v'), mov_read_cmov },
@@ -3535,10 +5569,21 @@ 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 },
+{ MKTAG('s','i','n','f'), mov_read_default },
+{ MKTAG('f','r','m','a'), mov_read_frma },
+{ MKTAG('s','e','n','c'), mov_read_senc },
+{ MKTAG('s','a','i','z'), mov_read_saiz },
+{ MKTAG('d','f','L','a'), mov_read_dfla },
{ MKTAG('s','t','3','d'), mov_read_st3d }, /* stereoscopic 3D video box */
{ MKTAG('s','v','3','d'), mov_read_sv3d }, /* spherical video box */
-{ MKTAG('u','u','i','d'), mov_read_uuid }, /* universal unique identifier */
-{ MKTAG('-','-','-','-'), mov_read_custom },
+{ MKTAG('d','O','p','s'), mov_read_dops },
+{ MKTAG('S','m','D','m'), mov_read_smdm },
+{ MKTAG('C','o','L','L'), mov_read_coll },
+{ MKTAG('v','p','c','C'), mov_read_vpcc },
{ 0, NULL }
};
@@ -3548,27 +5593,57 @@ 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 <= atom.size - 8 && !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);
- }
- av_log(c->fc, AV_LOG_TRACE, "type: %08"PRIx32" '%.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;
+ if (a.type == MKTAG('f','r','e','e') &&
+ a.size >= 8 &&
+ c->fc->strict_std_compliance < FF_COMPLIANCE_STRICT &&
+ c->moov_retry) {
+ uint8_t buf[8];
+ uint32_t *type = (uint32_t *)buf + 1;
+ if (avio_read(pb, buf, 8) != 8)
+ return AVERROR_INVALIDDATA;
+ 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_log(c->fc, AV_LOG_TRACE, "type:'%s' parent:'%s' sz: %"PRId64" %"PRId64" %"PRId64"\n",
+ av_fourcc2str(a.type), av_fourcc2str(atom.type), a.size, total_size, atom.size);
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)
@@ -3586,19 +5661,30 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom)
atom.type == MKTAG('i','l','s','t')))
parse = mov_read_udta_string;
+ // Supports parsing the QuickTime Metadata Keys.
+ // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html
+ if (!parse && c->found_hdlr_mdta &&
+ atom.type == MKTAG('m','e','t','a') &&
+ a.type == MKTAG('k','e','y','s')) {
+ parse = mov_read_keys;
+ }
+
if (!parse) { /* skip leaf atoms data */
avio_skip(pb, a.size);
} else {
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 & AVIO_SEEKABLE_NORMAL) || c->fc->flags & AVFMT_FLAG_IGNIDX) ||
+ ((!(pb->seekable & AVIO_SEEKABLE_NORMAL) || c->fc->flags & AVFMT_FLAG_IGNIDX || c->fragment_index_complete) ||
start_pos + a.size == avio_size(pb))) {
- if (!(pb->seekable & AVIO_SEEKABLE_NORMAL) || c->fc->flags & AVFMT_FLAG_IGNIDX)
+ if (!(pb->seekable & AVIO_SEEKABLE_NORMAL) || c->fc->flags & AVFMT_FLAG_IGNIDX || c->fragment_index_complete)
c->next_root_atom = start_pos + a.size;
+ c->atom_depth --;
return 0;
}
left = a.size - avio_tell(pb) + start_pos;
@@ -3618,121 +5704,260 @@ 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',' ')
+ || AV_RL32(p->buf + offset + 8) == MKTAG('j','p','x',' ')
+ )) {
+ 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
static void mov_read_chapters(AVFormatContext *s)
{
MOVContext *mov = s->priv_data;
- AVStream *st = NULL;
+ AVStream *st;
MOVStreamContext *sc;
int64_t cur_pos;
- int i;
-
- for (i = 0; i < s->nb_streams; i++)
- if (s->streams[i]->id == mov->chapter_track) {
- st = s->streams[i];
- break;
+ int i, j;
+ int chapter_track;
+
+ for (j = 0; j < mov->nb_chapter_tracks; j++) {
+ chapter_track = mov->chapter_tracks[j];
+ st = NULL;
+ for (i = 0; i < s->nb_streams; i++)
+ if (s->streams[i]->id == chapter_track) {
+ st = s->streams[i];
+ break;
+ }
+ if (!st) {
+ av_log(s, AV_LOG_ERROR, "Referenced QT chapter track not found\n");
+ continue;
}
- if (!st) {
- av_log(s, AV_LOG_ERROR, "Referenced QT chapter track not found\n");
- return;
- }
- st->discard = AVDISCARD_ALL;
- sc = st->priv_data;
- cur_pos = avio_tell(sc->pb);
-
- for (i = 0; i < st->nb_index_entries; i++) {
- AVIndexEntry *sample = &st->index_entries[i];
- int64_t end = i+1 < st->nb_index_entries ? st->index_entries[i+1].timestamp : st->duration;
- uint8_t *title;
- uint16_t ch;
- int len, title_len;
-
- 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;
- }
+ sc = st->priv_data;
+ cur_pos = avio_tell(sc->pb);
+
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ st->disposition |= AV_DISPOSITION_ATTACHED_PIC | AV_DISPOSITION_TIMED_THUMBNAILS;
+ if (st->nb_index_entries) {
+ // Retrieve the first frame, if possible
+ AVPacket pkt;
+ AVIndexEntry *sample = &st->index_entries[0];
+ if (avio_seek(sc->pb, sample->pos, SEEK_SET) != sample->pos) {
+ av_log(s, AV_LOG_ERROR, "Failed to retrieve first frame\n");
+ goto finish;
+ }
- // the first two bytes are the length of the title
- len = avio_rb16(sc->pb);
- if (len > sample->size-2)
- continue;
- title_len = 2*len + 1;
- if (!(title = av_mallocz(title_len)))
- goto finish;
-
- // The samples could theoretically be in any encoding if there's an encd
- // atom following, but in practice are only utf-8 or utf-16, distinguished
- // instead by the presence of a BOM
- if (!len) {
- title[0] = 0;
+ if (av_get_packet(sc->pb, &pkt, sample->size) < 0)
+ goto finish;
+
+ st->attached_pic = pkt;
+ st->attached_pic.stream_index = st->index;
+ st->attached_pic.flags |= AV_PKT_FLAG_KEY;
+ }
} else {
- ch = avio_rb16(sc->pb);
- if (ch == 0xfeff)
- avio_get_str16be(sc->pb, len, title, title_len);
- else if (ch == 0xfffe)
- avio_get_str16le(sc->pb, len, title, title_len);
- else {
- AV_WB16(title, ch);
- if (len == 1 || len == 2)
- title[len] = 0;
- else
- avio_get_str(sc->pb, len - 2, title + 2, title_len - 2);
+ st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
+ st->codecpar->codec_id = AV_CODEC_ID_BIN_DATA;
+ st->discard = AVDISCARD_ALL;
+ for (i = 0; i < st->nb_index_entries; i++) {
+ AVIndexEntry *sample = &st->index_entries[i];
+ int64_t end = i+1 < st->nb_index_entries ? st->index_entries[i+1].timestamp : st->duration;
+ uint8_t *title;
+ 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;
+ }
+
+ // the first two bytes are the length of the title
+ len = avio_rb16(sc->pb);
+ if (len > sample->size-2)
+ continue;
+ title_len = 2*len + 1;
+ if (!(title = av_mallocz(title_len)))
+ goto finish;
+
+ // The samples could theoretically be in any encoding if there's an encd
+ // atom following, but in practice are only utf-8 or utf-16, distinguished
+ // instead by the presence of a BOM
+ if (!len) {
+ title[0] = 0;
+ } else {
+ ch = avio_rb16(sc->pb);
+ if (ch == 0xfeff)
+ avio_get_str16be(sc->pb, len, title, title_len);
+ else if (ch == 0xfffe)
+ avio_get_str16le(sc->pb, len, title, title_len);
+ else {
+ AV_WB16(title, ch);
+ if (len == 1 || len == 2)
+ title[len] = 0;
+ else
+ avio_get_str(sc->pb, INT_MAX, title + 2, len - 1);
+ }
+ }
+
+ avpriv_new_chapter(s, i, st->time_base, sample->timestamp, end, title);
+ av_freep(&title);
}
}
-
- avpriv_new_chapter(s, i, st->time_base, sample->timestamp, end, title);
- av_freep(&title);
- }
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->avg_frame_rate;
+ 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_rtmd_track(AVFormatContext *s, AVStream *st)
+{
+ MOVStreamContext *sc = st->priv_data;
+ char buf[AV_TIMECODE_STR_SIZE];
+ int64_t cur_pos = avio_tell(sc->pb);
+ int hh, mm, ss, ff, drop;
+
+ if (!st->nb_index_entries)
+ return -1;
+
+ avio_seek(sc->pb, st->index_entries->pos, SEEK_SET);
+ avio_skip(s->pb, 13);
+ hh = avio_r8(s->pb);
+ mm = avio_r8(s->pb);
+ ss = avio_r8(s->pb);
+ drop = avio_r8(s->pb);
+ ff = avio_r8(s->pb);
+ snprintf(buf, AV_TIMECODE_STR_SIZE, "%02d:%02d:%02d%c%02d",
+ hh, mm, ss, drop ? ';' : ':', ff);
+ av_dict_set(&st->metadata, "timecode", buf, 0);
+
+ avio_seek(sc->pb, cur_pos, SEEK_SET);
+ 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)
@@ -3744,31 +5969,47 @@ static int mov_read_close(AVFormatContext *s)
AVStream *st = s->streams[i];
MOVStreamContext *sc = st->priv_data;
+ if (!sc)
+ continue;
+
av_freep(&sc->ctts_data);
for (j = 0; j < sc->drefs_count; j++) {
av_freep(&sc->drefs[j].path);
av_freep(&sc->drefs[j].dir);
}
av_freep(&sc->drefs);
- if (sc->pb && sc->pb != s->pb)
+
+ sc->drefs_count = 0;
+
+ if (!sc->pb_is_copied)
ff_format_io_close(s, &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);
+ av_freep(&sc->index_ranges);
- for (j = 0; j < sc->stsd_count; j++)
- av_free(sc->extradata[j]);
+ if (sc->extradata)
+ for (j = 0; j < sc->stsd_count; j++)
+ av_free(sc->extradata[j]);
av_freep(&sc->extradata);
av_freep(&sc->extradata_size);
+ av_freep(&sc->cenc.auxiliary_info);
+ av_freep(&sc->cenc.auxiliary_info_sizes);
+ av_aes_ctr_free(sc->cenc.aes_ctr);
+
av_freep(&sc->stereo3d);
av_freep(&sc->spherical);
+ av_freep(&sc->mastering);
+ av_freep(&sc->coll);
}
if (mov->dv_demux) {
@@ -3776,20 +6017,192 @@ static int mov_read_close(AVFormatContext *s)
mov->dv_fctx = NULL;
}
+ if (mov->meta_keys) {
+ for (i = 1; i < mov->meta_keys_count; i++) {
+ av_freep(&mov->meta_keys[i]);
+ }
+ av_freep(&mov->meta_keys);
+ }
+
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);
+
+ av_freep(&mov->aes_decrypt);
+ av_freep(&mov->chapter_tracks);
+
+ 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->codecpar->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->codecpar->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 (avio_feof(f)) {
+ index->item_count = 0;
+ av_freep(&index->items);
+ return AVERROR_INVALIDDATA;
+ }
+
+ 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;
+ if (mov->decryption_key_len != 0 && mov->decryption_key_len != AES_CTR_KEY_SIZE) {
+ av_log(s, AV_LOG_ERROR, "Invalid decryption key len %d expected %d\n",
+ mov->decryption_key_len, AES_CTR_KEY_SIZE);
+ return AVERROR(EINVAL);
+ }
+
mov->fc = s;
+ mov->trak_index = -1;
/* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */
if (pb->seekable & AVIO_SEEKABLE_NORMAL)
atom.size = avio_size(pb);
@@ -3797,11 +6210,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 & AVIO_SEEKABLE_NORMAL) && !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);
@@ -3809,13 +6226,48 @@ static int mov_read_header(AVFormatContext *s)
}
av_log(mov->fc, AV_LOG_TRACE, "on_parse_exit_offset=%"PRId64"\n", avio_tell(pb));
- if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && mov->chapter_track > 0)
- mov_read_chapters(s);
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ if (mov->nb_chapter_tracks > 0 && !mov->ignore_chapters)
+ mov_read_chapters(s);
+ for (i = 0; i < s->nb_streams; i++)
+ if (s->streams[i]->codecpar->codec_tag == AV_RL32("tmcd")) {
+ mov_read_timecode_track(s, s->streams[i]);
+ } else if (s->streams[i]->codecpar->codec_tag == AV_RL32("rtmd")) {
+ mov_read_rtmd_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->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->codec_id == AV_CODEC_ID_AAC) {
+ st->skip_samples = sc->start_pad;
+ }
+ if (st->codecpar->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->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
if (st->codecpar->width <= 0 || st->codecpar->height <= 0) {
st->codecpar->width = sc->width;
@@ -3826,17 +6278,56 @@ static int mov_read_header(AVFormatContext *s)
return err;
}
}
+ if (mov->handbrake_version &&
+ mov->handbrake_version <= 1000000*0 + 1000*10 + 2 && // 0.10.2
+ st->codecpar->codec_id == AV_CODEC_ID_MP3
+ ) {
+ av_log(s, AV_LOG_VERBOSE, "Forcing full parsing for mp3 stream\n");
+ st->need_parsing = AVSTREAM_PARSE_FULL;
+ }
}
if (mov->trex_data) {
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MOVStreamContext *sc = st->priv_data;
- if (st->duration > 0)
+ if (st->duration > 0) {
+ if (sc->data_size > INT64_MAX / sc->time_scale / 8) {
+ av_log(s, AV_LOG_ERROR, "Overflow during bit rate calculation %"PRId64" * 8 * %d\n",
+ sc->data_size, sc->time_scale);
+ mov_read_close(s);
+ return AVERROR_INVALIDDATA;
+ }
st->codecpar->bit_rate = sc->data_size * 8 * sc->time_scale / st->duration;
+ }
+ }
+ }
+
+ 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) {
+ if (sc->data_size > INT64_MAX / sc->time_scale / 8) {
+ av_log(s, AV_LOG_ERROR, "Overflow during bit rate calculation %"PRId64" * 8 * %d\n",
+ sc->data_size, sc->time_scale);
+ mov_read_close(s);
+ return AVERROR_INVALIDDATA;
+ }
+ st->codecpar->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]->codecpar->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;
@@ -3851,8 +6342,7 @@ static int mov_read_header(AVFormatContext *s)
break;
case AVMEDIA_TYPE_VIDEO:
if (sc->display_matrix) {
- err = av_stream_add_side_data(st, AV_PKT_DATA_DISPLAYMATRIX,
- (uint8_t *)sc->display_matrix,
+ err = av_stream_add_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, (uint8_t*)sc->display_matrix,
sizeof(int32_t) * 9);
if (err < 0)
return err;
@@ -3877,9 +6367,35 @@ static int mov_read_header(AVFormatContext *s)
sc->spherical = NULL;
}
+ if (sc->mastering) {
+ err = av_stream_add_side_data(st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
+ (uint8_t *)sc->mastering,
+ sizeof(*sc->mastering));
+ if (err < 0)
+ return err;
+
+ sc->mastering = NULL;
+ }
+ if (sc->coll) {
+ err = av_stream_add_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
+ (uint8_t *)sc->coll,
+ sc->coll_size);
+ if (err < 0)
+ return err;
+
+ sc->coll = NULL;
+ }
break;
}
}
+ ff_configure_buffers_for_index(s, AV_TIME_BASE);
+
+ for (i = 0; i < mov->fragment_index_count; i++) {
+ MOVFragmentIndex *idx = mov->fragment_index_data[i];
+ for (j = 0; j < idx->item_count; j++)
+ if (idx->items[j].moof_offset <= mov->fragment.moof_offset)
+ idx->items[j].headers_read = 1;
+ }
return 0;
}
@@ -3910,6 +6426,59 @@ static AVIndexEntry *mov_find_next_sample(AVFormatContext *s, AVStream **st)
return sample;
}
+static int should_retry(AVIOContext *pb, int error_code) {
+ if (error_code == AVERROR_EOF || avio_feof(pb))
+ return 0;
+
+ return 1;
+}
+
+static int mov_switch_root(AVFormatContext *s, int64_t target)
+{
+ MOVContext *mov = s->priv_data;
+ int i, j;
+ int already_read = 0;
+
+ if (avio_seek(s->pb, target, SEEK_SET) != target) {
+ av_log(mov->fc, AV_LOG_ERROR, "root atom offset 0x%"PRIx64": partial file\n", target);
+ return AVERROR_INVALIDDATA;
+ }
+
+ mov->next_root_atom = 0;
+
+ for (i = 0; i < mov->fragment_index_count; i++) {
+ MOVFragmentIndex *index = mov->fragment_index_data[i];
+ int found = 0;
+ for (j = 0; j < index->item_count; j++) {
+ MOVFragmentIndexItem *item = &index->items[j];
+ if (found) {
+ mov->next_root_atom = item->moof_offset;
+ break; // Advance to next index in outer loop
+ } else if (item->moof_offset == target) {
+ index->current_item = FFMIN(j, index->current_item);
+ if (item->headers_read)
+ already_read = 1;
+ item->headers_read = 1;
+ found = 1;
+ }
+ }
+ if (!found)
+ index->current_item = 0;
+ }
+
+ if (already_read)
+ return 0;
+
+ mov->found_mdat = 0;
+
+ if (mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX }) < 0 ||
+ avio_feof(s->pb))
+ return AVERROR_EOF;
+ av_log(s, AV_LOG_TRACE, "read fragments, offset 0x%"PRIx64"\n", avio_tell(s->pb));
+
+ return 1;
+}
+
static int mov_change_extradata(MOVStreamContext *sc, AVPacket *pkt)
{
uint8_t *side, *extradata;
@@ -3939,34 +6508,51 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
MOVStreamContext *sc;
AVIndexEntry *sample;
AVStream *st = NULL;
+ int64_t current_index;
int ret;
+ mov->fc = s;
retry:
sample = mov_find_next_sample(s, &st);
- if (!sample) {
- mov->found_mdat = 0;
+ if (!sample || (mov->next_root_atom && sample->pos > mov->next_root_atom)) {
if (!mov->next_root_atom)
return AVERROR_EOF;
- 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)
- return AVERROR_EOF;
- av_log(s, AV_LOG_TRACE, "read fragments, offset 0x%"PRIx64"\n", avio_tell(s->pb));
+ if ((ret = mov_switch_root(s, mov->next_root_atom)) < 0)
+ return ret;
goto retry;
}
sc = st->priv_data;
/* must be done just before reading, to avoid infinite loop on sample */
- sc->current_sample++;
+ current_index = sc->current_index;
+ mov_current_sample_inc(sc);
+
+ 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) {
+ int64_t ret64 = avio_seek(sc->pb, sample->pos, SEEK_SET);
+ if (ret64 != sample->pos) {
av_log(mov->fc, AV_LOG_ERROR, "stream %d, offset 0x%"PRIx64": partial file\n",
sc->ffindex, sample->pos);
+ if (should_retry(sc->pb, ret64)) {
+ mov_current_sample_dec(sc);
+ }
return AVERROR_INVALIDDATA;
}
+
+ if( st->discard == AVDISCARD_NONKEY && 0==(sample->flags & AVINDEX_KEYFRAME) ) {
+ av_log(mov->fc, AV_LOG_DEBUG, "Nonkey frame from stream %d discarded due to AVDISCARD_NONKEY\n", sc->ffindex);
+ goto retry;
+ }
+
ret = av_get_packet(sc->pb, pkt, sample->size);
- if (ret < 0)
+ if (ret < 0) {
+ if (should_retry(sc->pb, ret)) {
+ mov_current_sample_dec(sc);
+ }
return ret;
+ }
if (sc->has_palette) {
uint8_t *pal;
@@ -3980,18 +6566,25 @@ 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)
return ret;
}
#endif
+ if (st->codecpar->codec_id == AV_CODEC_ID_MP3 && !st->need_parsing && pkt->size > 4) {
+ if (ff_mpa_check_header(AV_RB32(pkt->data)) < 0)
+ st->need_parsing = AVSTREAM_PARSE_FULL;
+ }
}
pkt->stream_index = sc->ffindex;
pkt->dts = sample->timestamp;
+ if (sample->flags & AVINDEX_DISCARD_FRAME) {
+ pkt->flags |= AV_PKT_FLAG_DISCARD;
+ }
if (sc->ctts_data && sc->ctts_index < sc->ctts_count) {
pkt->pts = pkt->dts + sc->dts_shift + sc->ctts_data[sc->ctts_index].duration;
/* update ctts context */
@@ -4011,8 +6604,6 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
goto retry;
pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? AV_PKT_FLAG_KEY : 0;
pkt->pos = sample->pos;
- av_log(s, AV_LOG_TRACE, "stream %d, pts %"PRId64", dts %"PRId64", pos 0x%"PRIx64", duration %"PRId64"\n",
- pkt->stream_index, pkt->pts, pkt->dts, pkt->pos, pkt->duration);
/* Multiple stsd handling. */
if (sc->stsc_data) {
@@ -4024,13 +6615,51 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
sc->stsc_index++;
sc->stsc_sample = 0;
/* Do not check indexes after a switch. */
- } else if (sc->stsc_data[sc->stsc_index].id - 1 != sc->last_stsd_index) {
+ } else if (sc->stsc_data[sc->stsc_index].id > 0 &&
+ sc->stsc_data[sc->stsc_index].id - 1 < sc->stsd_count &&
+ sc->stsc_data[sc->stsc_index].id - 1 != sc->last_stsd_index) {
ret = mov_change_extradata(sc, pkt);
if (ret < 0)
return ret;
}
}
+ if (mov->aax_mode)
+ aax_filter(pkt->data, pkt->size, mov);
+
+ if (sc->cenc.aes_ctr) {
+ ret = cenc_filter(mov, sc, current_index, pkt->data, pkt->size);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int mov_seek_fragment(AVFormatContext *s, AVStream *st, int64_t timestamp)
+{
+ MOVContext *mov = s->priv_data;
+ MOVStreamContext *sc = st->priv_data;
+ int i, j;
+
+ if (!mov->fragment_index_complete)
+ return 0;
+
+ for (i = 0; i < mov->fragment_index_count; i++) {
+ if (mov->fragment_index_data[i]->track_id == st->id || !sc->has_sidx) {
+ MOVFragmentIndex *index = mov->fragment_index_data[i];
+ for (j = index->item_count - 1; j >= 0; j--) {
+ if (index->items[j].time <= timestamp) {
+ if (index->items[j].headers_read)
+ return 0;
+
+ return mov_switch_root(s, index->items[j].moof_offset);
+ }
+ }
+ }
+ }
+
return 0;
}
@@ -4040,13 +6669,17 @@ static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp,
int sample, time_sample;
unsigned int i;
+ int ret = mov_seek_fragment(s, st, timestamp);
+ if (ret < 0)
+ return ret;
+
sample = av_index_search_timestamp(st, timestamp, flags);
av_log(s, AV_LOG_TRACE, "stream %d, timestamp %"PRId64", sample %d\n", st->index, timestamp, sample);
if (sample < 0 && st->nb_index_entries && timestamp < st->index_entries[0].timestamp)
sample = 0;
if (sample < 0) /* not sure what to do */
return AVERROR_INVALIDDATA;
- sc->current_sample = sample;
+ mov_current_sample_set(sc, sample);
av_log(s, AV_LOG_TRACE, "stream %d, found sample %d\n", st->index, sc->current_sample);
/* adjust ctts index */
if (sc->ctts_data) {
@@ -4086,8 +6719,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);
@@ -4100,7 +6731,10 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
for (i = 0; i < s->nb_streams; i++) {
int64_t timestamp;
+ 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;
@@ -4112,7 +6746,7 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
MOVStreamContext *sc;
st = s->streams[i];
sc = st->priv_data;
- sc->current_sample = 0;
+ mov_current_sample_set(sc, 0);
}
while (1) {
MOVStreamContext *sc;
@@ -4122,7 +6756,7 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
sc = st->priv_data;
if (sc->ffindex == stream_index && sc->current_sample == sample)
break;
- sc->current_sample++;
+ mov_current_sample_inc(sc);
}
}
return 0;
@@ -4131,16 +6765,47 @@ 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), AV_OPT_TYPE_BOOL, {.i64 = 0},
+ 0, 1, FLAGS},
{"seek_streams_individually",
"Seek each stream individually to the to the closest point",
- OFFSET(seek_individually), AV_OPT_TYPE_INT, { .i64 = 1 },
+ OFFSET(seek_individually), AV_OPT_TYPE_BOOL, { .i64 = 1 },
0, 1, FLAGS},
+ {"ignore_editlist", "Ignore the edit list atom.", OFFSET(ignore_editlist), AV_OPT_TYPE_BOOL, {.i64 = 0},
+ 0, 1, FLAGS},
+ {"advanced_editlist",
+ "Modify the AVIndex according to the editlists. Use this option to decode in the order specified by the edits.",
+ OFFSET(advanced_editlist), AV_OPT_TYPE_BOOL, {.i64 = 1},
+ 0, 1, FLAGS},
+ {"ignore_chapters", "", OFFSET(ignore_chapters), AV_OPT_TYPE_BOOL, {.i64 = 0},
+ 0, 1, FLAGS},
+ {"use_mfra_for",
+ "use mfra for fragment timestamps",
+ OFFSET(use_mfra_for), AV_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 },
+ AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = FLAGS },
{ "export_xmp", "Export full XMP metadata", OFFSET(export_xmp),
- AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = FLAGS },
- { "enable_drefs", "Enable external track support.", OFFSET(enable_drefs),
- AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = FLAGS },
+ AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = FLAGS },
+ { "activation_bytes", "Secret bytes for Audible AAX files", OFFSET(activation_bytes),
+ AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM },
+ { "audible_fixed_key", // extracted from libAAX_SDK.so and AAXSDKWin.dll files!
+ "Fixed key used for handling Audible AAX files", OFFSET(audible_fixed_key),
+ AV_OPT_TYPE_BINARY, {.str="77214d4b196a87cd520045fd20a51d67"},
+ .flags = AV_OPT_FLAG_DECODING_PARAM },
+ { "decryption_key", "The media decryption key (hex)", OFFSET(decryption_key), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM },
+ { "enable_drefs", "Enable external track support.", OFFSET(enable_drefs), AV_OPT_TYPE_BOOL,
+ {.i64 = 0}, 0, 1, FLAGS },
+
{ NULL },
};
@@ -4162,4 +6827,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 42decb1..324dd5f 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
*/
@@ -45,7 +45,7 @@
* do not specify a particular ordering of those channels."
*/
enum MovChannelLayoutTag {
- MOV_CH_LAYOUT_UNKNOWN = 0xFFFF0000,
+#define MOV_CH_LAYOUT_UNKNOWN 0xFFFF0000
MOV_CH_LAYOUT_USE_DESCRIPTIONS = ( 0 << 16) | 0,
MOV_CH_LAYOUT_USE_BITMAP = ( 1 << 16) | 0,
MOV_CH_LAYOUT_DISCRETEINORDER = (147 << 16) | 0,
@@ -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 {
@@ -556,8 +557,8 @@ int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st,
bitmap = avio_rb32(pb);
num_descr = avio_rb32(pb);
- av_log(s, AV_LOG_TRACE,
- "chan: layout=%"PRIu32" bitmap=%"PRIu32" num_descr=%"PRIu32"\n",
+ av_log(s, AV_LOG_TRACE, "chan: layout=%"PRIu32" "
+ "bitmap=%"PRIu32" num_descr=%"PRIu32"\n",
layout_tag, bitmap, num_descr);
if (size < 12ULL + num_descr * 20ULL)
@@ -576,6 +577,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) {
@@ -590,6 +592,7 @@ int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st,
st->codecpar->channel_layout = label_mask;
} else
st->codecpar->channel_layout = ff_mov_get_channel_layout(layout_tag, bitmap);
+ avio_skip(pb, size - 12);
return 0;
}
diff --git a/libavformat/mov_chan.h b/libavformat/mov_chan.h
index 3fae939..ca28345 100644
--- a/libavformat/mov_chan.h
+++ b/libavformat/mov_chan.h
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2011 Justin Ruggles
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 840190d..2838286 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,23 +31,35 @@
#include "avio.h"
#include "isom.h"
#include "avc.h"
+#include "libavcodec/ac3_parser.h"
+#include "libavcodec/dnxhddata.h"
+#include "libavcodec/flac.h"
+#include "libavcodec/get_bits.h"
-#include "libavcodec/bitstream.h"
+#include "libavcodec/internal.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/libm.h"
#include "libavutil/opt.h"
#include "libavutil/dict.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/stereo3d.h"
+#include "libavutil/timecode.h"
+#include "libavutil/color_utils.h"
#include "hevc.h"
#include "rtpenc.h"
#include "mov_chan.h"
+#include "vpcc.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" },
@@ -61,19 +73,30 @@ static const AVOption options[] = {
{ "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" },
{ "global_sidx", "Write a global sidx index at the start of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_GLOBAL_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
+ { "write_colr", "Write colr atom (Experimental, may be renamed or changed, do not use from scripts)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_COLR}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
+ { "write_gama", "Write deprecated gama atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_GAMA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
+ { "use_metadata_tags", "Use mdta atom for metadata.", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_USE_MDTA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
{ "skip_trailer", "Skip writing the mfra/tfra/mfro trailer for fragmented files", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SKIP_TRAILER}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
+ { "negative_cts_offsets", "Use negative CTS offsets (reducing the need for edit lists)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS}, 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_BOOL, {.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},
+ { "use_editlist", "use edit list", offsetof(MOVMuxContext, use_editlist), AV_OPT_TYPE_BOOL, {.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 = 1}, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
+ { "mov_gamma", "gamma value for gama atom", offsetof(MOVMuxContext, gamma), AV_OPT_TYPE_FLOAT, {.dbl = 0.0 }, 0.0, 10, AV_OPT_FLAG_ENCODING_PARAM},
{ "frag_interleave", "Interleave samples within fragments (max number of consecutive samples, lower is tighter interleaving, but with more overhead)", offsetof(MOVMuxContext, frag_interleave), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "encryption_scheme", "Configures the encryption scheme, allowed values are none, cenc-aes-ctr", offsetof(MOVMuxContext, encryption_scheme_str), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = AV_OPT_FLAG_ENCODING_PARAM },
+ { "encryption_key", "The media encryption key (hex)", offsetof(MOVMuxContext, encryption_key), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_ENCODING_PARAM },
+ { "encryption_kid", "The media encryption key identifier (hex)", offsetof(MOVMuxContext, encryption_kid), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_ENCODING_PARAM },
+ { "use_stream_ids_as_track_ids", "use stream ids as track ids", offsetof(MOVMuxContext, use_stream_ids_as_track_ids), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
+ { "write_tmcd", "force or disable writing tmcd", offsetof(MOVMuxContext, write_tmcd), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM},
{ NULL },
};
@@ -85,6 +108,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;
@@ -121,13 +146,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
@@ -183,10 +210,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;
@@ -241,7 +268,7 @@ static int mov_write_amr_tag(AVIOContext *pb, MOVTrack *track)
static int mov_write_ac3_tag(AVIOContext *pb, MOVTrack *track)
{
- BitstreamContext bc;
+ GetBitContext gbc;
PutBitContext pbc;
uint8_t buf[3];
int fscod, bsid, bsmod, acmod, lfeon, frmsizecod;
@@ -252,21 +279,21 @@ static int mov_write_ac3_tag(AVIOContext *pb, MOVTrack *track)
avio_wb32(pb, 11);
ffio_wfourcc(pb, "dac3");
- bitstream_init8(&bc, track->vos_data + 4, track->vos_len - 4);
- fscod = bitstream_read(&bc, 2);
- frmsizecod = bitstream_read(&bc, 6);
- bsid = bitstream_read(&bc, 5);
- bsmod = bitstream_read(&bc, 3);
- acmod = bitstream_read(&bc, 3);
+ init_get_bits(&gbc, track->vos_data + 4, (track->vos_len - 4) * 8);
+ fscod = get_bits(&gbc, 2);
+ frmsizecod = get_bits(&gbc, 6);
+ bsid = get_bits(&gbc, 5);
+ bsmod = get_bits(&gbc, 3);
+ acmod = get_bits(&gbc, 3);
if (acmod == 2) {
- bitstream_skip(&bc, 2); // dsurmod
+ skip_bits(&gbc, 2); // dsurmod
} else {
if ((acmod & 1) && acmod != 1)
- bitstream_skip(&bc, 2); // cmixlev
+ skip_bits(&gbc, 2); // cmixlev
if (acmod & 4)
- bitstream_skip(&bc, 2); // surmixlev
+ skip_bits(&gbc, 2); // surmixlev
}
- lfeon = bitstream_read_bit(&bc);
+ lfeon = get_bits1(&gbc);
init_put_bits(&pbc, buf, sizeof(buf));
put_bits(&pbc, 2, fscod);
@@ -283,6 +310,215 @@ 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_header(&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->par, "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_header(&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 = av_packet_ref(&info->pkt, pkt);
+ if (ret < 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_packet_unref(pkt);
+ ret = av_packet_ref(pkt, &info->pkt);
+ if (ret < 0)
+ return ret;
+ av_packet_unref(&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_packet_unref(&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).
@@ -293,6 +529,22 @@ static int mov_write_extradata_tag(AVIOContext *pb, MOVTrack *track)
return track->par->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;
@@ -302,11 +554,23 @@ 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
{
AVCPBProperties *props;
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");
@@ -342,11 +606,9 @@ static int mov_write_esds_tag(AVIOContext *pb, MOVTrack *track) // Basic
avio_wb24(pb, props ? props->buffer_size / 8 : 0); // Buffersize DB
- avio_wb32(pb, props ? FFMAX(props->max_bitrate, props->avg_bitrate) : track->par->bit_rate); // maxbitrate (FIXME should be max rate in any 1 sec window)
- if (!props || !props->min_bitrate || props->max_bitrate != props->min_bitrate)
- avio_wb32(pb, 0); // vbr
- else
- avio_wb32(pb, props->max_bitrate); // avg bitrate
+ avg_bitrate = compute_avg_bitrate(track);
+ avio_wb32(pb, props ? FFMAX3(props->max_bitrate, props->avg_bitrate, avg_bitrate) : FFMAX(track->par->bit_rate, avg_bitrate)); // maxbitrate (FIXME should be max rate in any 1 sec window)
+ avio_wb32(pb, avg_bitrate);
if (track->vos_len) {
// DecoderSpecific info descriptor
@@ -360,22 +622,85 @@ 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(AVFormatContext *s, 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->par->codec_tag = av_bswap16(track->tag >> 16);
- ff_put_wav_header(s, pb, track->st->codecpar);
+ if ((ret = ff_put_wav_header(s, pb, track->par, 0)) < 0)
+ return ret;
return update_size(pb, pos);
}
static int mov_write_wfex_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track)
{
+ int ret;
int64_t pos = avio_tell(pb);
avio_wb32(pb, 0);
ffio_wfourcc(pb, "wfex");
- ff_put_wav_header(s, pb, track->st->codecpar);
+ if ((ret = ff_put_wav_header(s, pb, track->st->codecpar, FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX)) < 0)
+ return ret;
+ return update_size(pb, pos);
+}
+
+static int mov_write_dfla_tag(AVIOContext *pb, MOVTrack *track)
+{
+ int64_t pos = avio_tell(pb);
+ avio_wb32(pb, 0);
+ ffio_wfourcc(pb, "dfLa");
+ avio_w8(pb, 0); /* version */
+ avio_wb24(pb, 0); /* flags */
+
+ /* Expect the encoder to pass a METADATA_BLOCK_TYPE_STREAMINFO. */
+ if (track->par->extradata_size != FLAC_STREAMINFO_SIZE)
+ return AVERROR_INVALIDDATA;
+
+ /* TODO: Write other METADATA_BLOCK_TYPEs if the encoder makes them available. */
+ avio_w8(pb, 1 << 7 | FLAC_METADATA_TYPE_STREAMINFO); /* LastMetadataBlockFlag << 7 | BlockType */
+ avio_wb24(pb, track->par->extradata_size); /* Length */
+ avio_write(pb, track->par->extradata, track->par->extradata_size); /* BlockData[Length] */
+
+ return update_size(pb, pos);
+}
+
+static int mov_write_dops_tag(AVIOContext *pb, MOVTrack *track)
+{
+ int64_t pos = avio_tell(pb);
+ avio_wb32(pb, 0);
+ ffio_wfourcc(pb, "dOps");
+ avio_w8(pb, 0); /* Version */
+ if (track->par->extradata_size < 19) {
+ av_log(pb, AV_LOG_ERROR, "invalid extradata size\n");
+ return AVERROR_INVALIDDATA;
+ }
+ /* extradata contains an Ogg OpusHead, other than byte-ordering and
+ OpusHead's preceeding magic/version, OpusSpecificBox is currently
+ identical. */
+ avio_w8(pb, AV_RB8(track->par->extradata + 9)); /* OuputChannelCount */
+ avio_wb16(pb, AV_RL16(track->par->extradata + 10)); /* PreSkip */
+ avio_wb32(pb, AV_RL32(track->par->extradata + 12)); /* InputSampleRate */
+ avio_wb16(pb, AV_RL16(track->par->extradata + 16)); /* OutputGain */
+ /* Write the rest of the header out without byte-swapping. */
+ avio_write(pb, track->par->extradata + 18, track->par->extradata_size - 18);
+
return update_size(pb, pos);
}
@@ -393,6 +718,9 @@ static int mov_write_chan_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra
return 0;
}
+ if (track->multichannel_as_mono)
+ return 0;
+
avio_wb32(pb, 0); // Size
ffio_wfourcc(pb, "chan"); // Type
avio_w8(pb, 0); // Version
@@ -411,9 +739,11 @@ static int mov_write_wave_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "wave");
+ if (track->par->codec_id != AV_CODEC_ID_QDM2) {
avio_wb32(pb, 12); /* size */
ffio_wfourcc(pb, "frma");
avio_wl32(pb, track->tag);
+ }
if (track->par->codec_id == AV_CODEC_ID_AAC) {
/* useless atom needed by mplayer, ipod, not needed by quicktime */
@@ -421,11 +751,18 @@ static int mov_write_wave_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra
ffio_wfourcc(pb, "mp4a");
avio_wb32(pb, 0);
mov_write_esds_tag(pb, track);
+ } else if (mov_pcm_le_gt16(track->par->codec_id)) {
+ mov_write_enda_tag(pb);
+ } else if (mov_pcm_be_gt16(track->par->codec_id)) {
+ mov_write_enda_tag_be(pb);
} else if (track->par->codec_id == AV_CODEC_ID_AMR_NB) {
mov_write_amr_tag(pb, track);
} else if (track->par->codec_id == AV_CODEC_ID_AC3) {
mov_write_ac3_tag(pb, track);
- } else if (track->par->codec_id == AV_CODEC_ID_ALAC) {
+ } else if (track->par->codec_id == AV_CODEC_ID_EAC3) {
+ mov_write_eac3_tag(pb, track);
+ } else if (track->par->codec_id == AV_CODEC_ID_ALAC ||
+ track->par->codec_id == AV_CODEC_ID_QDM2) {
mov_write_extradata_tag(pb, track);
} else if (track->par->codec_id == AV_CODEC_ID_ADPCM_MS ||
track->par->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV) {
@@ -463,28 +800,28 @@ static int mov_write_dvc1_structs(MOVTrack *track, uint8_t *buf)
return AVERROR(ENOMEM);
start = find_next_marker(track->vos_data, end);
for (next = start; next < end; start = next) {
- BitstreamContext bc;
+ GetBitContext gb;
int size;
next = find_next_marker(start + 4, end);
size = next - start - 4;
if (size <= 0)
continue;
unescaped_size = vc1_unescape_buffer(start + 4, size, unescaped);
- bitstream_init8(&bc, unescaped, unescaped_size);
+ init_get_bits(&gb, unescaped, 8 * unescaped_size);
if (AV_RB32(start) == VC1_CODE_SEQHDR) {
- int profile = bitstream_read(&bc, 2);
+ int profile = get_bits(&gb, 2);
if (profile != PROFILE_ADVANCED) {
av_free(unescaped);
return AVERROR(ENOSYS);
}
seq_found = 1;
- level = bitstream_read(&bc, 3);
+ level = get_bits(&gb, 3);
/* chromaformat, frmrtq_postproc, bitrtq_postproc, postprocflag,
* width, height */
- bitstream_skip(&bc, 2 + 3 + 5 + 1 + 2 * 12);
- bitstream_skip(&bc, 1); /* broadcast */
- interlace = bitstream_read_bit(&bc);
- bitstream_skip(&bc, 4); /* tfcntrflag, finterpflag, reserved, psf */
+ skip_bits_long(&gb, 2 + 3 + 5 + 1 + 2*12);
+ skip_bits(&gb, 1); /* broadcast */
+ interlace = get_bits1(&gb);
+ skip_bits(&gb, 4); /* tfcntrflag, finterpflag, reserved, psf */
}
}
if (!seq_found) {
@@ -586,13 +923,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->par->frame_size;
+
/* use 1 for raw PCM */
if (!track->audio_vbr)
return 1;
@@ -608,20 +952,32 @@ static int get_samples_per_packet(MOVTrack *track)
return first_duration;
}
-static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track)
+static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track)
{
int64_t pos = avio_tell(pb);
int version = 0;
uint32_t tag = track->tag;
if (track->mode == MODE_MOV) {
- if (mov_get_lpcm_flags(track->par->codec_id))
- tag = AV_RL32("lpcm");
- version = 2;
+ if (track->timescale > UINT16_MAX) {
+ if (mov_get_lpcm_flags(track->par->codec_id))
+ tag = AV_RL32("lpcm");
+ version = 2;
+ } else if (track->audio_vbr || mov_pcm_le_gt16(track->par->codec_id) ||
+ mov_pcm_be_gt16(track->par->codec_id) ||
+ track->par->codec_id == AV_CODEC_ID_ADPCM_MS ||
+ track->par->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV ||
+ track->par->codec_id == AV_CODEC_ID_QDM2) {
+ version = 1;
+ }
}
avio_wb32(pb, 0); /* size */
- avio_wl32(pb, tag); // store it byteswapped
+ if (mov->encryption_scheme != MOV_ENC_NONE) {
+ ffio_wfourcc(pb, "enca");
+ } else {
+ avio_wl32(pb, tag); // store it byteswapped
+ }
avio_wb32(pb, 0); /* Reserved */
avio_wb16(pb, 0); /* Reserved */
avio_wb16(pb, 1); /* Data-reference index, XXX == 1 */
@@ -646,24 +1002,62 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tr
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->par->channels);
+ if (track->par->codec_id == AV_CODEC_ID_PCM_U8 ||
+ track->par->codec_id == AV_CODEC_ID_PCM_S8)
+ avio_wb16(pb, 8); /* bits per sample */
+ else if (track->par->codec_id == AV_CODEC_ID_ADPCM_G726)
+ avio_wb16(pb, track->par->bits_per_coded_sample);
+ else
+ avio_wb16(pb, 16);
+ avio_wb16(pb, track->audio_vbr ? -2 : 0); /* compression ID */
+ } else { /* reserved for mp4/3gp */
+ if (track->par->codec_id == AV_CODEC_ID_FLAC ||
+ track->par->codec_id == AV_CODEC_ID_OPUS) {
+ avio_wb16(pb, track->par->channels);
+ } else {
+ avio_wb16(pb, 2);
+ }
+ if (track->par->codec_id == AV_CODEC_ID_FLAC) {
+ avio_wb16(pb, track->par->bits_per_raw_sample);
+ } else {
+ avio_wb16(pb, 16);
+ }
+ avio_wb16(pb, 0);
+ }
avio_wb16(pb, 0); /* packet size (= 0) */
- avio_wb16(pb, track->par->sample_rate <= UINT16_MAX ?
- track->par->sample_rate : 0);
+ if (track->par->codec_id == AV_CODEC_ID_OPUS)
+ avio_wb16(pb, 48000);
+ else
+ avio_wb16(pb, track->par->sample_rate <= UINT16_MAX ?
+ track->par->sample_rate : 0);
avio_wb16(pb, 0); /* Reserved */
}
+ if (version == 1) { /* SoundDescription V1 extended info */
+ if (mov_pcm_le_gt16(track->par->codec_id) ||
+ mov_pcm_be_gt16(track->par->codec_id))
+ avio_wb32(pb, 1); /* must be 1 for uncompressed formats */
+ else
+ avio_wb32(pb, track->par->frame_size); /* Samples per packet */
+ avio_wb32(pb, track->sample_size / track->par->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->par->codec_id == AV_CODEC_ID_AAC ||
track->par->codec_id == AV_CODEC_ID_AC3 ||
+ track->par->codec_id == AV_CODEC_ID_EAC3 ||
track->par->codec_id == AV_CODEC_ID_AMR_NB ||
track->par->codec_id == AV_CODEC_ID_ALAC ||
track->par->codec_id == AV_CODEC_ID_ADPCM_MS ||
- track->par->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV))
+ track->par->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV ||
+ track->par->codec_id == AV_CODEC_ID_QDM2 ||
+ (mov_pcm_le_gt16(track->par->codec_id) && version==1) ||
+ (mov_pcm_be_gt16(track->par->codec_id) && version==1)))
mov_write_wave_tag(s, pb, track);
else if (track->tag == MKTAG('m','p','4','a'))
mov_write_esds_tag(pb, track);
@@ -671,16 +1065,26 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tr
mov_write_amr_tag(pb, track);
else if (track->par->codec_id == AV_CODEC_ID_AC3)
mov_write_ac3_tag(pb, track);
+ else if (track->par->codec_id == AV_CODEC_ID_EAC3)
+ mov_write_eac3_tag(pb, track);
else if (track->par->codec_id == AV_CODEC_ID_ALAC)
mov_write_extradata_tag(pb, track);
else if (track->par->codec_id == AV_CODEC_ID_WMAPRO)
mov_write_wfex_tag(s, pb, track);
+ else if (track->par->codec_id == AV_CODEC_ID_FLAC)
+ mov_write_dfla_tag(pb, track);
+ else if (track->par->codec_id == AV_CODEC_ID_OPUS)
+ mov_write_dops_tag(pb, track);
else if (track->vos_len > 0)
mov_write_glbl_tag(pb, track);
if (track->mode == MODE_MOV && track->par->codec_type == AVMEDIA_TYPE_AUDIO)
mov_write_chan_tag(s, pb, track);
+ if (mov->encryption_scheme != MOV_ENC_NONE) {
+ ff_mov_cenc_write_sinf_tag(track, pb, mov->encryption_kid);
+ }
+
return update_size(pb, pos);
}
@@ -696,19 +1100,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);
@@ -719,13 +1110,28 @@ static int mov_write_avcc_tag(AVIOContext *pb, MOVTrack *track)
return update_size(pb, pos);
}
+static int mov_write_vpcc_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track)
+{
+ int64_t pos = avio_tell(pb);
+
+ avio_wb32(pb, 0);
+ ffio_wfourcc(pb, "vpcC");
+ avio_w8(pb, 1); /* version */
+ avio_wb24(pb, 0); /* flags */
+ ff_isom_write_vpcc(s, pb, track->par);
+ return update_size(pb, pos);
+}
+
static int mov_write_hvcc_tag(AVIOContext *pb, MOVTrack *track)
{
int64_t pos = avio_tell(pb);
avio_wb32(pb, 0);
ffio_wfourcc(pb, "hvcC");
- ff_isom_write_hvcc(pb, track->vos_data, track->vos_len, 0);
+ if (track->tag == MKTAG('h','v','c','1'))
+ ff_isom_write_hvcc(pb, track->vos_data, track->vos_len, 1);
+ else
+ ff_isom_write_hvcc(pb, track->vos_data, track->vos_len, 0);
return update_size(pb, pos);
}
@@ -733,13 +1139,48 @@ static int mov_write_hvcc_tag(AVIOContext *pb, MOVTrack *track)
static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track)
{
int i;
+ int interlaced;
+ int cid;
+ int display_width = track->par->width;
+
+ if (track->vos_data && track->vos_len > 0x29) {
+ if (ff_dnxhd_parse_header_prefix(track->vos_data) != 0) {
+ /* looks like a DNxHD bit stream */
+ interlaced = (track->vos_data[5] & 2);
+ cid = AV_RB32(track->vos_data + 0x28);
+ } else {
+ av_log(NULL, AV_LOG_WARNING, "Could not locate DNxHD bit stream in vos_data\n");
+ return 0;
+ }
+ } else {
+ av_log(NULL, AV_LOG_WARNING, "Could not locate DNxHD bit stream, vos_data too small\n");
+ return 0;
+ }
+
avio_wb32(pb, 24); /* size */
ffio_wfourcc(pb, "ACLR");
ffio_wfourcc(pb, "ACLR");
ffio_wfourcc(pb, "0001");
- avio_wb32(pb, 2); /* yuv range: full 1 / normal 2 */
+ if (track->par->color_range == AVCOL_RANGE_MPEG || /* Legal range (16-235) */
+ track->par->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 */
+ if (track->tag == MKTAG('A','V','d','h')) {
+ avio_wb32(pb, 32);
+ ffio_wfourcc(pb, "ADHR");
+ ffio_wfourcc(pb, "0001");
+ avio_wb32(pb, cid);
+ avio_wb32(pb, 0); /* unknown */
+ avio_wb32(pb, 1); /* unknown */
+ avio_wb32(pb, 0); /* unknown */
+ avio_wb32(pb, 0); /* unknown */
+ return 0;
+ }
+
avio_wb32(pb, 24); /* size */
ffio_wfourcc(pb, "APRG");
ffio_wfourcc(pb, "APRG");
@@ -751,10 +1192,13 @@ static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track)
ffio_wfourcc(pb, "ARES");
ffio_wfourcc(pb, "ARES");
ffio_wfourcc(pb, "0001");
- avio_wb32(pb, AV_RB32(track->vos_data + 0x28)); /* dnxhd cid, some id ? */
- avio_wb32(pb, track->par->width);
+ avio_wb32(pb, cid); /* dnxhd cid, some id ? */
+ if ( track->par->sample_aspect_ratio.num > 0
+ && track->par->sample_aspect_ratio.den > 0)
+ display_width = display_width * track->par->sample_aspect_ratio.num / track->par->sample_aspect_ratio.den;
+ avio_wb32(pb, display_width);
/* values below are based on samples created with quicktime and avid codecs */
- if (track->vos_data[5] & 2) { // interlaced
+ if (interlaced) {
avio_wb32(pb, track->par->height / 2);
avio_wb32(pb, 2); /* unknown */
avio_wb32(pb, 0); /* unknown */
@@ -772,77 +1216,40 @@ static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track)
for (i = 0; i < 10; i++)
avio_wb64(pb, 0);
- /* extra padding for stsd needed */
- avio_wb32(pb, 0);
return 0;
}
-static int mp4_get_codec_tag(AVFormatContext *s, MOVTrack *track)
-{
- int tag = track->par->codec_tag;
-
- if (!ff_codec_get_tag(ff_mp4_obj_type, track->par->codec_id))
- return 0;
-
- if (track->par->codec_id == AV_CODEC_ID_H264) tag = MKTAG('a','v','c','1');
- else if (track->par->codec_id == AV_CODEC_ID_HEVC) tag = MKTAG('h','e','v','1');
- else if (track->par->codec_id == AV_CODEC_ID_AC3) tag = MKTAG('a','c','-','3');
- else if (track->par->codec_id == AV_CODEC_ID_DIRAC) tag = MKTAG('d','r','a','c');
- else if (track->par->codec_id == AV_CODEC_ID_MOV_TEXT) tag = MKTAG('t','x','3','g');
- else if (track->par->codec_id == AV_CODEC_ID_VC1) tag = MKTAG('v','c','-','1');
- else if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) tag = MKTAG('m','p','4','v');
- else if (track->par->codec_type == AVMEDIA_TYPE_AUDIO) tag = MKTAG('m','p','4','a');
- else if (track->par->codec_id == AV_CODEC_ID_DVD_SUBTITLE) tag = MKTAG('m','p','4','s');
-
- return tag;
-}
-
-static const AVCodecTag codec_ipod_tags[] = {
- { AV_CODEC_ID_H264, MKTAG('a','v','c','1') },
- { AV_CODEC_ID_MPEG4, MKTAG('m','p','4','v') },
- { AV_CODEC_ID_AAC, MKTAG('m','p','4','a') },
- { AV_CODEC_ID_ALAC, MKTAG('a','l','a','c') },
- { AV_CODEC_ID_AC3, MKTAG('a','c','-','3') },
- { AV_CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') },
- { AV_CODEC_ID_MOV_TEXT, MKTAG('t','e','x','t') },
- { AV_CODEC_ID_NONE, 0 },
-};
-
-static int ipod_get_codec_tag(AVFormatContext *s, MOVTrack *track)
+static int mov_write_dpxe_tag(AVIOContext *pb, MOVTrack *track)
{
- int tag = track->par->codec_tag;
-
- // keep original tag for subs, ipod supports both formats
- if (!(track->par->codec_type == AVMEDIA_TYPE_SUBTITLE &&
- (tag == MKTAG('t', 'x', '3', 'g') ||
- tag == MKTAG('t', 'e', 'x', 't'))))
- tag = ff_codec_get_tag(codec_ipod_tags, track->par->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 "
- "Quicktime/Ipod might not play the file\n");
-
- return tag;
+ avio_wb32(pb, 12);
+ ffio_wfourcc(pb, "DpxE");
+ if (track->par->extradata_size >= 12 &&
+ !memcmp(&track->par->extradata[4], "DpxE", 4)) {
+ avio_wb32(pb, track->par->extradata[11]);
+ } else {
+ avio_wb32(pb, 1);
+ }
+ return 0;
}
static int mov_get_dv_codec_tag(AVFormatContext *s, MOVTrack *track)
{
int tag;
- if (track->par->width == 720) /* SD */
- if (track->par->height == 480) /* NTSC */
- if (track->par->format == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','n');
+ if (track->par->width == 720) { /* SD */
+ if (track->par->height == 480) { /* NTSC */
+ if (track->par->format == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','n');
else tag = MKTAG('d','v','c',' ');
- else if (track->par->format == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','p');
- else if (track->par->format == AV_PIX_FMT_YUV420P) tag = MKTAG('d','v','c','p');
+ }else if (track->par->format == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','p');
+ else if (track->par->format == AV_PIX_FMT_YUV420P) tag = MKTAG('d','v','c','p');
else tag = MKTAG('d','v','p','p');
- else if (track->par->height == 720) /* HD 720 line */
+ } else if (track->par->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->par->height == 1080) /* HD 1080 line */
+ } else if (track->par->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;
}
@@ -850,11 +1257,159 @@ static int mov_get_dv_codec_tag(AVFormatContext *s, MOVTrack *track)
return tag;
}
+static AVRational find_fps(AVFormatContext *s, AVStream *st)
+{
+ AVRational rate = st->avg_frame_rate;
+
+#if FF_API_LAVF_AVCTX
+ FF_DISABLE_DEPRECATION_WARNINGS
+ rate = av_inv_q(st->codec->time_base);
+ 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;
+ }
+ FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+ return rate;
+}
+
+static int defined_frame_rate(AVFormatContext *s, AVStream *st)
+{
+ AVRational rational_framerate = find_fps(s, st);
+ int rate = 0;
+ if (rational_framerate.den != 0)
+ rate = av_q2d(rational_framerate);
+ return rate;
+}
+
+static int mov_get_mpeg2_xdcam_codec_tag(AVFormatContext *s, MOVTrack *track)
+{
+ int tag = track->par->codec_tag;
+ int interlaced = track->par->field_order > AV_FIELD_PROGRESSIVE;
+ AVStream *st = track->st;
+ int rate = defined_frame_rate(s, st);
+
+ if (!tag)
+ tag = MKTAG('m', '2', 'v', '1'); //fallback tag
+
+ if (track->par->format == AV_PIX_FMT_YUV420P) {
+ if (track->par->width == 1280 && track->par->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->par->width == 1440 && track->par->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->par->width == 1920 && track->par->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->par->format == AV_PIX_FMT_YUV422P) {
+ if (track->par->width == 1280 && track->par->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->par->width == 1920 && track->par->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->par->codec_tag;
+ int interlaced = track->par->field_order > AV_FIELD_PROGRESSIVE;
+ AVStream *st = track->st;
+ int rate = defined_frame_rate(s, st);
+
+ if (!tag)
+ tag = MKTAG('a', 'v', 'c', 'i'); //fallback tag
+
+ if (track->par->format == AV_PIX_FMT_YUV420P10) {
+ if (track->par->width == 960 && track->par->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->par->width == 1440 && track->par->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->par->format == AV_PIX_FMT_YUV422P10) {
+ if (track->par->width == 1280 && track->par->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->par->width == 1920 && track->par->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->par->width == 4096 && track->par->height == 2160
+ || track->par->width == 3840 && track->par->height == 2160
+ || track->par->width == 2048 && track->par->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,19 +1426,38 @@ static const struct {
{ AV_PIX_FMT_RGB48BE, MKTAG('b','4','8','r'), 48 },
};
+static int mov_get_dnxhd_codec_tag(AVFormatContext *s, MOVTrack *track)
+{
+ int tag = MKTAG('A','V','d','n');
+ if (track->par->profile != FF_PROFILE_UNKNOWN &&
+ track->par->profile != FF_PROFILE_DNXHD)
+ tag = MKTAG('A','V','d','h');
+ return tag;
+}
+
static int mov_get_rawvideo_codec_tag(AVFormatContext *s, MOVTrack *track)
{
int tag = track->par->codec_tag;
int i;
+ enum AVPixelFormat pix_fmt;
for (i = 0; i < FF_ARRAY_ELEMS(mov_pix_fmt_tags); i++) {
if (track->par->format == mov_pix_fmt_tags[i].pix_fmt) {
tag = mov_pix_fmt_tags[i].tag;
track->par->bits_per_coded_sample = mov_pix_fmt_tags[i].bps;
- break;
+ if (track->par->codec_tag == mov_pix_fmt_tags[i].tag)
+ break;
}
}
+ pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_mov,
+ track->par->bits_per_coded_sample);
+ if (tag == MKTAG('r','a','w',' ') &&
+ track->par->format != pix_fmt &&
+ track->par->format != AV_PIX_FMT_GRAY8 &&
+ track->par->format != 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->par->format));
return tag;
}
@@ -895,11 +1469,20 @@ static int mov_get_codec_tag(AVFormatContext *s, MOVTrack *track)
(track->par->codec_id == AV_CODEC_ID_DVVIDEO ||
track->par->codec_id == AV_CODEC_ID_RAWVIDEO ||
track->par->codec_id == AV_CODEC_ID_H263 ||
+ track->par->codec_id == AV_CODEC_ID_H264 ||
+ track->par->codec_id == AV_CODEC_ID_DNXHD ||
+ track->par->codec_id == AV_CODEC_ID_MPEG2VIDEO ||
av_get_bits_per_sample(track->par->codec_id)))) { // pcm audio
if (track->par->codec_id == AV_CODEC_ID_DVVIDEO)
tag = mov_get_dv_codec_tag(s, track);
else if (track->par->codec_id == AV_CODEC_ID_RAWVIDEO)
tag = mov_get_rawvideo_codec_tag(s, track);
+ else if (track->par->codec_id == AV_CODEC_ID_MPEG2VIDEO)
+ tag = mov_get_mpeg2_xdcam_codec_tag(s, track);
+ else if (track->par->codec_id == AV_CODEC_ID_H264)
+ tag = mov_get_h264_codec_tag(s, track);
+ else if (track->par->codec_id == AV_CODEC_ID_DNXHD)
+ tag = mov_get_dnxhd_codec_tag(s, track);
else if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) {
tag = ff_codec_get_tag(ff_codec_movvideo_tags, track->par->codec_id);
if (!tag) { // if no mac fcc found, try with Microsoft tags
@@ -925,42 +1508,25 @@ static int mov_get_codec_tag(AVFormatContext *s, MOVTrack *track)
return tag;
}
-static const AVCodecTag codec_3gp_tags[] = {
- { AV_CODEC_ID_H263, MKTAG('s','2','6','3') },
- { AV_CODEC_ID_H264, MKTAG('a','v','c','1') },
- { AV_CODEC_ID_MPEG4, MKTAG('m','p','4','v') },
- { AV_CODEC_ID_AAC, MKTAG('m','p','4','a') },
- { AV_CODEC_ID_AMR_NB, MKTAG('s','a','m','r') },
- { AV_CODEC_ID_AMR_WB, MKTAG('s','a','w','b') },
- { AV_CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') },
- { AV_CODEC_ID_NONE, 0 },
-};
-
-static const AVCodecTag codec_f4v_tags[] = {
- { AV_CODEC_ID_MP3, MKTAG('.','m','p','3') },
- { AV_CODEC_ID_AAC, MKTAG('m','p','4','a') },
- { AV_CODEC_ID_H264, MKTAG('a','v','c','1') },
- { AV_CODEC_ID_VP6A, MKTAG('V','P','6','A') },
- { AV_CODEC_ID_VP6F, MKTAG('V','P','6','F') },
- { AV_CODEC_ID_NONE, 0 },
-};
-
static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track)
{
int tag;
if (track->mode == MODE_MP4 || track->mode == MODE_PSP)
- tag = mp4_get_codec_tag(s, track);
- else if (track->mode == MODE_ISM) {
- tag = mp4_get_codec_tag(s, track);
- if (!tag && track->par->codec_id == AV_CODEC_ID_WMAPRO)
- tag = MKTAG('w', 'm', 'a', ' ');
- } else if (track->mode == MODE_IPOD)
- tag = ipod_get_codec_tag(s, track);
- else if (track->mode & MODE_3GP)
- tag = ff_codec_get_tag(codec_3gp_tags, track->par->codec_id);
+ tag = track->par->codec_tag;
+ else if (track->mode == MODE_ISM)
+ tag = track->par->codec_tag;
+ else if (track->mode == MODE_IPOD) {
+ if (!av_match_ext(s->filename, "m4a") &&
+ !av_match_ext(s->filename, "m4v") &&
+ !av_match_ext(s->filename, "m4b"))
+ av_log(s, AV_LOG_WARNING, "Warning, extension is not .m4a nor .m4v "
+ "Quicktime/Ipod might not play the file\n");
+ tag = track->par->codec_tag;
+ } else if (track->mode & MODE_3GP)
+ tag = track->par->codec_tag;
else if (track->mode == MODE_F4V)
- tag = ff_codec_get_tag(codec_f4v_tags, track->par->codec_id);
+ tag = track->par->codec_tag;
else
tag = mov_get_codec_tag(s, track);
@@ -987,11 +1553,11 @@ static const uint16_t fiel_data[] = {
0x0000, 0x0100, 0x0201, 0x0206, 0x0209, 0x020e
};
-static int mov_write_fiel_tag(AVIOContext *pb, MOVTrack *track)
+static int mov_write_fiel_tag(AVIOContext *pb, MOVTrack *track, int field_order)
{
unsigned mov_field_order = 0;
- if (track->par->field_order < FF_ARRAY_ELEMS(fiel_data))
- mov_field_order = fiel_data[track->par->field_order];
+ if (field_order < FF_ARRAY_ELEMS(fiel_data))
+ mov_field_order = fiel_data[field_order];
else
return 0;
avio_wb32(pb, 10);
@@ -1017,6 +1583,94 @@ static int mov_write_subtitle_tag(AVIOContext *pb, MOVTrack *track)
return update_size(pb, pos);
}
+static int mov_write_st3d_tag(AVIOContext *pb, AVStereo3D *stereo_3d)
+{
+ int8_t stereo_mode;
+
+ if (stereo_3d->flags != 0) {
+ av_log(pb, AV_LOG_WARNING, "Unsupported stereo_3d flags %x. st3d not written.\n", stereo_3d->flags);
+ return 0;
+ }
+
+ switch (stereo_3d->type) {
+ case AV_STEREO3D_2D:
+ stereo_mode = 0;
+ break;
+ case AV_STEREO3D_TOPBOTTOM:
+ stereo_mode = 1;
+ break;
+ case AV_STEREO3D_SIDEBYSIDE:
+ stereo_mode = 2;
+ break;
+ default:
+ av_log(pb, AV_LOG_WARNING, "Unsupported stereo_3d type %s. st3d not written.\n", av_stereo3d_type_name(stereo_3d->type));
+ return 0;
+ }
+ avio_wb32(pb, 13); /* size */
+ ffio_wfourcc(pb, "st3d");
+ avio_wb32(pb, 0); /* version = 0 & flags = 0 */
+ avio_w8(pb, stereo_mode);
+ return 13;
+}
+
+static int mov_write_sv3d_tag(AVFormatContext *s, AVIOContext *pb, AVSphericalMapping *spherical_mapping)
+{
+ int64_t sv3d_pos, svhd_pos, proj_pos;
+ const char* metadata_source = s->flags & AVFMT_FLAG_BITEXACT ? "Lavf" : LIBAVFORMAT_IDENT;
+
+ if (spherical_mapping->projection != AV_SPHERICAL_EQUIRECTANGULAR &&
+ spherical_mapping->projection != AV_SPHERICAL_EQUIRECTANGULAR_TILE &&
+ spherical_mapping->projection != AV_SPHERICAL_CUBEMAP) {
+ av_log(pb, AV_LOG_WARNING, "Unsupported projection %d. sv3d not written.\n", spherical_mapping->projection);
+ return 0;
+ }
+
+ sv3d_pos = avio_tell(pb);
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "sv3d");
+
+ svhd_pos = avio_tell(pb);
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "svhd");
+ avio_wb32(pb, 0); /* version = 0 & flags = 0 */
+ avio_put_str(pb, metadata_source);
+ update_size(pb, svhd_pos);
+
+ proj_pos = avio_tell(pb);
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "proj");
+
+ avio_wb32(pb, 24); /* size */
+ ffio_wfourcc(pb, "prhd");
+ avio_wb32(pb, 0); /* version = 0 & flags = 0 */
+ avio_wb32(pb, spherical_mapping->yaw);
+ avio_wb32(pb, spherical_mapping->pitch);
+ avio_wb32(pb, spherical_mapping->roll);
+
+ switch (spherical_mapping->projection) {
+ case AV_SPHERICAL_EQUIRECTANGULAR:
+ case AV_SPHERICAL_EQUIRECTANGULAR_TILE:
+ avio_wb32(pb, 28); /* size */
+ ffio_wfourcc(pb, "equi");
+ avio_wb32(pb, 0); /* version = 0 & flags = 0 */
+ avio_wb32(pb, spherical_mapping->bound_top);
+ avio_wb32(pb, spherical_mapping->bound_bottom);
+ avio_wb32(pb, spherical_mapping->bound_left);
+ avio_wb32(pb, spherical_mapping->bound_right);
+ break;
+ case AV_SPHERICAL_CUBEMAP:
+ avio_wb32(pb, 20); /* size */
+ ffio_wfourcc(pb, "cbmp");
+ avio_wb32(pb, 0); /* version = 0 & flags = 0 */
+ avio_wb32(pb, 0); /* layout */
+ avio_wb32(pb, spherical_mapping->padding); /* padding */
+ break;
+ }
+ update_size(pb, proj_pos);
+
+ return update_size(pb, sv3d_pos);
+}
+
static int mov_write_pasp_tag(AVIOContext *pb, MOVTrack *track)
{
AVRational sar;
@@ -1030,14 +1684,145 @@ 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_gama_tag(AVIOContext *pb, MOVTrack *track, double gamma)
+{
+ uint32_t gama = 0;
+ if (gamma <= 0.0)
+ {
+ gamma = avpriv_get_gamma_from_trc(track->par->color_trc);
+ }
+ av_log(pb, AV_LOG_DEBUG, "gamma value %g\n", gamma);
+
+ if (gamma > 1e-6) {
+ gama = (uint32_t)lrint((double)(1<<16) * gamma);
+ av_log(pb, AV_LOG_DEBUG, "writing gama value %"PRId32"\n", gama);
+
+ av_assert0(track->mode == MODE_MOV);
+ avio_wb32(pb, 12);
+ ffio_wfourcc(pb, "gama");
+ avio_wb32(pb, gama);
+ return 12;
+ }
+ else {
+ av_log(pb, AV_LOG_WARNING, "gamma value unknown, unable to write gama atom\n");
+ }
+ return 0;
+}
+
+static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track)
+{
+ // Ref (MOV): https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9
+ // Ref (MP4): ISO/IEC 14496-12:2012
+
+ if (track->par->color_primaries == AVCOL_PRI_UNSPECIFIED &&
+ track->par->color_trc == AVCOL_TRC_UNSPECIFIED &&
+ track->par->color_space == AVCOL_SPC_UNSPECIFIED) {
+ if ((track->par->width >= 1920 && track->par->height >= 1080)
+ || (track->par->width == 1280 && track->par->height == 720)) {
+ av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming bt709\n");
+ track->par->color_primaries = AVCOL_PRI_BT709;
+ } else if (track->par->width == 720 && track->height == 576) {
+ av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming bt470bg\n");
+ track->par->color_primaries = AVCOL_PRI_BT470BG;
+ } else if (track->par->width == 720 &&
+ (track->height == 486 || track->height == 480)) {
+ av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming smpte170\n");
+ track->par->color_primaries = AVCOL_PRI_SMPTE170M;
+ } else {
+ av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, unable to assume anything\n");
+ }
+ switch (track->par->color_primaries) {
+ case AVCOL_PRI_BT709:
+ track->par->color_trc = AVCOL_TRC_BT709;
+ track->par->color_space = AVCOL_SPC_BT709;
+ break;
+ case AVCOL_PRI_SMPTE170M:
+ case AVCOL_PRI_BT470BG:
+ track->par->color_trc = AVCOL_TRC_BT709;
+ track->par->color_space = AVCOL_SPC_SMPTE170M;
+ break;
+ }
+ }
+
+ /* We should only ever be called by MOV or MP4. */
+ av_assert0(track->mode == MODE_MOV || track->mode == MODE_MP4);
+
+ avio_wb32(pb, 18 + (track->mode == MODE_MP4));
+ ffio_wfourcc(pb, "colr");
+ if (track->mode == MODE_MP4)
+ ffio_wfourcc(pb, "nclx");
+ else
+ ffio_wfourcc(pb, "nclc");
+ switch (track->par->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->par->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->par->color_space) {
+ case AVCOL_SPC_BT709: avio_wb16(pb, 1); break;
+ case AVCOL_SPC_BT470BG:
+ case AVCOL_SPC_SMPTE170M: avio_wb16(pb, 6); break;
+ case AVCOL_SPC_SMPTE240M: avio_wb16(pb, 7); break;
+ default: avio_wb16(pb, 2);
+ }
+
+ if (track->mode == MODE_MP4) {
+ int full_range = track->par->color_range == AVCOL_RANGE_JPEG;
+ avio_w8(pb, full_range << 7);
+ return 19;
+ } else {
+ return 18;
+ }
+}
+
+static void find_compressor(char * compressor_name, int len, MOVTrack *track)
{
AVDictionaryEntry *encoder;
+ int xdcam_res = (track->par->width == 1280 && track->par->height == 720)
+ || (track->par->width == 1440 && track->par->height == 1080)
+ || (track->par->width == 1920 && track->par->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->par->codec_id == AV_CODEC_ID_MPEG2VIDEO && xdcam_res) {
+ int interlaced = track->par->field_order > AV_FIELD_PROGRESSIVE;
+ AVStream *st = track->st;
+ int rate = defined_frame_rate(NULL, st);
+ av_strlcatf(compressor_name, len, "XDCAM");
+ if (track->par->format == AV_PIX_FMT_YUV422P) {
+ av_strlcatf(compressor_name, len, " HD422");
+ } else if(track->par->width == 1440) {
+ av_strlcatf(compressor_name, len, " HD");
+ } else
+ av_strlcatf(compressor_name, len, " EX");
+
+ av_strlcatf(compressor_name, len, " %d%c", track->par->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 };
+ int avid = 0;
avio_wb32(pb, 0); /* size */
- avio_wl32(pb, track->tag); // store it byteswapped
+ if (mov->encryption_scheme != MOV_ENC_NONE) {
+ ffio_wfourcc(pb, "encv");
+ } else {
+ avio_wl32(pb, track->tag); // store it byteswapped
+ }
avio_wb32(pb, 0); /* Reserved */
avio_wb16(pb, 0); /* Reserved */
avio_wb16(pb, 1); /* Data-reference index */
@@ -1066,47 +1851,119 @@ 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);
if (track->mode == MODE_MOV && track->par->bits_per_coded_sample)
- avio_wb16(pb, track->par->bits_per_coded_sample);
+ avio_wb16(pb, track->par->bits_per_coded_sample |
+ (track->par->format == AV_PIX_FMT_GRAY8 ? 0x20 : 0));
else
avio_wb16(pb, 0x18); /* Reserved */
- avio_wb16(pb, 0xffff); /* Reserved */
+
+ if (track->mode == MODE_MOV && track->par->format == AV_PIX_FMT_PAL8) {
+ int pal_size = 1 << track->par->bits_per_coded_sample;
+ int i;
+ avio_wb16(pb, 0); /* Color table ID */
+ avio_wb32(pb, 0); /* Color table seed */
+ avio_wb16(pb, 0x8000); /* Color table flags */
+ avio_wb16(pb, pal_size - 1); /* Color table size (zero-relative) */
+ for (i = 0; i < pal_size; i++) {
+ uint32_t rgb = track->palette[i];
+ uint16_t r = (rgb >> 16) & 0xff;
+ uint16_t g = (rgb >> 8) & 0xff;
+ uint16_t b = rgb & 0xff;
+ avio_wb16(pb, 0);
+ avio_wb16(pb, (r << 8) | r);
+ avio_wb16(pb, (g << 8) | g);
+ avio_wb16(pb, (b << 8) | b);
+ }
+ } else
+ avio_wb16(pb, 0xffff); /* Reserved */
+
if (track->tag == MKTAG('m','p','4','v'))
mov_write_esds_tag(pb, track);
else if (track->par->codec_id == AV_CODEC_ID_H263)
mov_write_d263_tag(pb);
- else if (track->par->codec_id == AV_CODEC_ID_SVQ3)
- mov_write_svq3_tag(pb);
- else if (track->par->codec_id == AV_CODEC_ID_DNXHD)
+ else if (track->par->codec_id == AV_CODEC_ID_AVUI ||
+ track->par->codec_id == AV_CODEC_ID_SVQ3) {
+ mov_write_extradata_tag(pb, track);
+ avio_wb32(pb, 0);
+ } else if (track->par->codec_id == AV_CODEC_ID_DNXHD) {
mov_write_avid_tag(pb, track);
- else if (track->par->codec_id == AV_CODEC_ID_HEVC)
+ avid = 1;
+ } else if (track->par->codec_id == AV_CODEC_ID_HEVC)
mov_write_hvcc_tag(pb, track);
- else if (track->par->codec_id == AV_CODEC_ID_H264) {
+ else if (track->par->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->par->field_order != AV_FIELD_UNKNOWN)
- mov_write_fiel_tag(pb, track);
- else if (track->par->codec_id == AV_CODEC_ID_VC1 && track->vos_len > 0)
+ } else if (track->par->codec_id == AV_CODEC_ID_VP9) {
+ mov_write_vpcc_tag(mov->fc, pb, track);
+ } else if (track->par->codec_id == AV_CODEC_ID_VC1 && track->vos_len > 0)
mov_write_dvc1_tag(pb, track);
else if (track->par->codec_id == AV_CODEC_ID_VP6F ||
track->par->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->par->codec_id == AV_CODEC_ID_R10K) {
+ if (track->par->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->par->sample_aspect_ratio.den && track->par->sample_aspect_ratio.num &&
- track->par->sample_aspect_ratio.den != track->par->sample_aspect_ratio.num) {
+ if (track->par->codec_id != AV_CODEC_ID_H264 &&
+ track->par->codec_id != AV_CODEC_ID_MPEG4 &&
+ track->par->codec_id != AV_CODEC_ID_DNXHD) {
+ int field_order = track->par->field_order;
+
+#if FF_API_LAVF_AVCTX
+ FF_DISABLE_DEPRECATION_WARNINGS
+ if (field_order != track->st->codec->field_order && track->st->codec->field_order != AV_FIELD_UNKNOWN)
+ field_order = track->st->codec->field_order;
+ FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+ if (field_order != AV_FIELD_UNKNOWN)
+ mov_write_fiel_tag(pb, track, field_order);
+ }
+
+ if (mov->flags & FF_MOV_FLAG_WRITE_GAMA) {
+ if (track->mode == MODE_MOV)
+ mov_write_gama_tag(pb, track, mov->gamma);
+ else
+ av_log(mov->fc, AV_LOG_WARNING, "Not writing 'gama' atom. Format is not MOV.\n");
+ }
+ if (mov->flags & FF_MOV_FLAG_WRITE_COLR) {
+ if (track->mode == MODE_MOV || track->mode == MODE_MP4)
+ mov_write_colr_tag(pb, track);
+ else
+ av_log(mov->fc, AV_LOG_WARNING, "Not writing 'colr' atom. Format is not MOV or MP4.\n");
+ }
+
+ if (track->mode == MODE_MP4 && mov->fc->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL) {
+ AVStereo3D* stereo_3d = (AVStereo3D*) av_stream_get_side_data(track->st, AV_PKT_DATA_STEREO3D, NULL);
+ AVSphericalMapping* spherical_mapping = (AVSphericalMapping*)av_stream_get_side_data(track->st, AV_PKT_DATA_SPHERICAL, NULL);
+
+ if (stereo_3d)
+ mov_write_st3d_tag(pb, stereo_3d);
+ if (spherical_mapping)
+ mov_write_sv3d_tag(mov->fc, pb, spherical_mapping);
+ }
+
+ if (track->par->sample_aspect_ratio.den && track->par->sample_aspect_ratio.num) {
mov_write_pasp_tag(pb, track);
}
+ if (mov->encryption_scheme != MOV_ENC_NONE) {
+ ff_mov_cenc_write_sinf_tag(track, pb, mov->encryption_kid);
+ }
+
+ /* extra padding for avid stsd */
+ /* https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-61112 */
+ if (avid)
+ avio_wb32(pb, 0);
+
return update_size(pb, pos);
}
@@ -1130,9 +1987,70 @@ 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;
+ int nb_frames;
+ AVDictionaryEntry *t = NULL;
+
+ if (!track->st->avg_frame_rate.num || !track->st->avg_frame_rate.den) {
+#if FF_API_LAVF_AVCTX
+ FF_DISABLE_DEPRECATION_WARNINGS
+ frame_duration = av_rescale(track->timescale, track->st->codec->time_base.num, track->st->codec->time_base.den);
+ nb_frames = ROUNDED_DIV(track->st->codec->time_base.den, track->st->codec->time_base.num);
+ FF_ENABLE_DEPRECATION_WARNINGS
+#else
+ av_log(NULL, AV_LOG_ERROR, "avg_frame_rate not set for tmcd track.\n");
+ return AVERROR(EINVAL);
+#endif
+ } else {
+ frame_duration = av_rescale(track->timescale, track->st->avg_frame_rate.num, track->st->avg_frame_rate.den);
+ nb_frames = ROUNDED_DIV(track->st->avg_frame_rate.den, track->st->avg_frame_rate.num);
+ }
+
+ 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 */
+
+ t = av_dict_get(track->st->metadata, "reel_name", NULL, 0);
+ if (t && utf8len(t->value) && track->mode != MODE_MP4)
+ 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 */
@@ -1140,10 +2058,23 @@ static int mov_write_tmcd_tag(AVIOContext *pb, MOVTrack *track)
avio_wb32(pb, 1); /* Data reference index */
if (track->par->extradata_size)
avio_write(pb, track->par->extradata, track->par->extradata_size);
+#endif
+ return update_size(pb, pos);
+}
+
+static int mov_write_gpmd_tag(AVIOContext *pb, const MOVTrack *track)
+{
+ int64_t pos = avio_tell(pb);
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "gpmd");
+ avio_wb32(pb, 0); /* Reserved */
+ avio_wb16(pb, 0); /* Reserved */
+ avio_wb16(pb, 1); /* Data-reference index */
+ avio_wb32(pb, 0); /* Reserved */
return update_size(pb, pos);
}
-static int mov_write_stsd_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track)
+static int mov_write_stsd_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track)
{
int64_t pos = avio_tell(pb);
avio_wb32(pb, 0); /* size */
@@ -1151,26 +2082,29 @@ static int mov_write_stsd_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra
avio_wb32(pb, 0); /* version & flags */
avio_wb32(pb, 1); /* entry count */
if (track->par->codec_type == AVMEDIA_TYPE_VIDEO)
- mov_write_video_tag(pb, track);
+ mov_write_video_tag(pb, mov, track);
else if (track->par->codec_type == AVMEDIA_TYPE_AUDIO)
- mov_write_audio_tag(s, pb, track);
+ mov_write_audio_tag(s, pb, mov, track);
else if (track->par->codec_type == AVMEDIA_TYPE_SUBTITLE)
mov_write_subtitle_tag(pb, track);
else if (track->par->codec_tag == MKTAG('r','t','p',' '))
mov_write_rtp_tag(pb, track);
else if (track->par->codec_tag == MKTAG('t','m','c','d'))
mov_write_tmcd_tag(pb, track);
+ else if (track->par->codec_tag == MKTAG('g','p','m','d'))
+ mov_write_gpmd_tag(pb, track);
return update_size(pb, pos);
}
-static int mov_write_ctts_tag(AVIOContext *pb, MOVTrack *track)
+static int mov_write_ctts_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track)
{
+ MOVMuxContext *mov = s->priv_data;
MOVStts *ctts_entries;
uint32_t entries = 0;
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;
@@ -1188,7 +2122,11 @@ static int mov_write_ctts_tag(AVIOContext *pb, MOVTrack *track)
atom_size = 16 + (entries * 8);
avio_wb32(pb, atom_size); /* size */
ffio_wfourcc(pb, "ctts");
- avio_wb32(pb, 0); /* version & flags */
+ if (mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS)
+ avio_w8(pb, 1); /* version */
+ else
+ avio_w8(pb, 0); /* version */
+ avio_wb24(pb, 0); /* flags */
avio_wb32(pb, entries); /* entry count */
for (i = 0; i < entries; i++) {
avio_wb32(pb, ctts_entries[i].count);
@@ -1215,7 +2153,7 @@ static int mov_write_stts_tag(AVIOContext *pb, MOVTrack *track)
entries = 1;
} else {
if (track->entry) {
- stts_entries = av_malloc(track->entry * sizeof(*stts_entries)); /* worst case */
+ stts_entries = av_malloc_array(track->entry, sizeof(*stts_entries)); /* worst case */
if (!stts_entries)
return AVERROR(ENOMEM);
}
@@ -1252,18 +2190,116 @@ 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(AVFormatContext *s, AVIOContext *pb, MOVTrack *track)
+static int mov_preroll_write_stbl_atoms(AVIOContext *pb, MOVTrack *track)
+{
+ struct sgpd_entry {
+ int count;
+ int16_t roll_distance;
+ int group_description_index;
+ };
+
+ struct sgpd_entry *sgpd_entries = NULL;
+ int entries = -1;
+ int group = 0;
+ int i, j;
+
+ const int OPUS_SEEK_PREROLL_MS = 80;
+ int roll_samples = av_rescale_q(OPUS_SEEK_PREROLL_MS,
+ (AVRational){1, 1000},
+ (AVRational){1, 48000});
+
+ if (!track->entry)
+ return 0;
+
+ sgpd_entries = av_malloc_array(track->entry, sizeof(*sgpd_entries));
+ if (!sgpd_entries)
+ return AVERROR(ENOMEM);
+
+ av_assert0(track->par->codec_id == AV_CODEC_ID_OPUS || track->par->codec_id == AV_CODEC_ID_AAC);
+
+ if (track->par->codec_id == AV_CODEC_ID_OPUS) {
+ for (i = 0; i < track->entry; i++) {
+ int roll_samples_remaining = roll_samples;
+ int distance = 0;
+ for (j = i - 1; j >= 0; j--) {
+ roll_samples_remaining -= get_cluster_duration(track, j);
+ distance++;
+ if (roll_samples_remaining <= 0)
+ break;
+ }
+ /* We don't have enough preceeding samples to compute a valid
+ roll_distance here, so this sample can't be independently
+ decoded. */
+ if (roll_samples_remaining > 0)
+ distance = 0;
+ /* Verify distance is a minimum of 2 (60ms) packets and a maximum of
+ 32 (2.5ms) packets. */
+ av_assert0(distance == 0 || (distance >= 2 && distance <= 32));
+ if (i && distance == sgpd_entries[entries].roll_distance) {
+ sgpd_entries[entries].count++;
+ } else {
+ entries++;
+ sgpd_entries[entries].count = 1;
+ sgpd_entries[entries].roll_distance = distance;
+ sgpd_entries[entries].group_description_index = distance ? ++group : 0;
+ }
+ }
+ } else {
+ entries++;
+ sgpd_entries[entries].count = track->sample_count;
+ sgpd_entries[entries].roll_distance = 1;
+ sgpd_entries[entries].group_description_index = ++group;
+ }
+ entries++;
+
+ if (!group) {
+ av_free(sgpd_entries);
+ return 0;
+ }
+
+ /* Write sgpd tag */
+ avio_wb32(pb, 24 + (group * 2)); /* size */
+ ffio_wfourcc(pb, "sgpd");
+ avio_wb32(pb, 1 << 24); /* fullbox */
+ ffio_wfourcc(pb, "roll");
+ avio_wb32(pb, 2); /* default_length */
+ avio_wb32(pb, group); /* entry_count */
+ for (i = 0; i < entries; i++) {
+ if (sgpd_entries[i].group_description_index) {
+ avio_wb16(pb, -sgpd_entries[i].roll_distance); /* roll_distance */
+ }
+ }
+
+ /* Write sbgp tag */
+ avio_wb32(pb, 20 + (entries * 8)); /* size */
+ ffio_wfourcc(pb, "sbgp");
+ avio_wb32(pb, 0); /* fullbox */
+ ffio_wfourcc(pb, "roll");
+ avio_wb32(pb, entries); /* entry_count */
+ for (i = 0; i < entries; i++) {
+ avio_wb32(pb, sgpd_entries[i].count); /* sample_count */
+ avio_wb32(pb, sgpd_entries[i].group_description_index); /* group_description_index */
+ }
+
+ av_free(sgpd_entries);
+ return 0;
+}
+
+static int mov_write_stbl_tag(AVFormatContext *s, 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(s, pb, track);
+ mov_write_stsd_tag(s, pb, mov, track);
mov_write_stts_tag(pb, track);
if ((track->par->codec_type == AVMEDIA_TYPE_VIDEO ||
track->par->codec_tag == MKTAG('r','t','p',' ')) &&
@@ -1272,11 +2308,20 @@ static int mov_write_stbl_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra
if (track->mode == MODE_MOV && track->flags & MOV_TRACK_STPS)
mov_write_stss_tag(pb, track, MOV_PARTIAL_SYNC_SAMPLE);
if (track->par->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(s, pb, track)) < 0)
+ return ret;
+ }
mov_write_stsc_tag(pb, track);
mov_write_stsz_tag(pb, track);
mov_write_stco_tag(pb, track);
+ if (track->cenc.aes_ctr) {
+ ff_mov_cenc_write_stbl_atoms(&track->cenc, pb);
+ }
+ if (track->par->codec_id == AV_CODEC_ID_OPUS || track->par->codec_id == AV_CODEC_ID_AAC) {
+ mov_preroll_write_stbl_atoms(pb, track);
+ }
return update_size(pb, pos);
}
@@ -1297,9 +2342,32 @@ static int mov_write_nmhd_tag(AVIOContext *pb)
return 12;
}
-static int mov_write_gmhd_tag(AVIOContext *pb)
+static int mov_write_tcmi_tag(AVIOContext *pb, MOVTrack *track)
{
- avio_wb32(pb, 0x20); /* size */
+ int64_t pos = avio_tell(pb);
+ const char *font = "Lucida Grande";
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "tcmi"); /* timecode media information atom */
+ avio_wb32(pb, 0); /* version & flags */
+ avio_wb16(pb, 0); /* text font */
+ avio_wb16(pb, 0); /* text face */
+ avio_wb16(pb, 12); /* text size */
+ avio_wb16(pb, 0); /* (unknown, not in the QT specs...) */
+ avio_wb16(pb, 0x0000); /* text color (red) */
+ avio_wb16(pb, 0x0000); /* text color (green) */
+ avio_wb16(pb, 0x0000); /* text color (blue) */
+ avio_wb16(pb, 0xffff); /* background color (red) */
+ avio_wb16(pb, 0xffff); /* background color (green) */
+ avio_wb16(pb, 0xffff); /* background color (blue) */
+ avio_w8(pb, strlen(font)); /* font len (part of the pascal string) */
+ avio_write(pb, font, strlen(font)); /* font name */
+ return update_size(pb, pos);
+}
+
+static int mov_write_gmhd_tag(AVIOContext *pb, MOVTrack *track)
+{
+ int64_t pos = avio_tell(pb);
+ avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "gmhd");
avio_wb32(pb, 0x18); /* gmin size */
ffio_wfourcc(pb, "gmin");/* generic media info */
@@ -1310,7 +2378,42 @@ 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->par->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);
+ } else if (track->par->codec_tag == MKTAG('g','p','m','d')) {
+ int64_t gpmd_pos = avio_tell(pb);
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "gpmd");
+ avio_wb32(pb, 0); /* version */
+ update_size(pb, gpmd_pos);
+ }
+ return update_size(pb, pos);
}
static int mov_write_smhd_tag(AVIOContext *pb)
@@ -1356,30 +2459,32 @@ static int mov_write_hdlr_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra
hdlr_type = "soun";
descr = "SoundHandler";
} else if (track->par->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->par->codec_tag == MKTAG('r','t','p',' ')) {
hdlr_type = "hint";
descr = "HintHandler";
} else if (track->par->codec_tag == MKTAG('t','m','c','d')) {
hdlr_type = "tmcd";
descr = "TimeCodeHandler";
+ } else if (track->par->codec_tag == MKTAG('g','p','m','d')) {
+ hdlr_type = "meta";
+ descr = "GoPro MET"; // GoPro Metadata
} else {
- char tag_buf[32];
- av_get_codec_tag_string(tag_buf, sizeof(tag_buf),
- track->par->codec_tag);
-
av_log(s, AV_LOG_WARNING,
- "Unknown hldr_type for %s / 0x%04"PRIX32", writing dummy values\n",
- tag_buf, track->par->codec_tag);
+ "Unknown hldr_type for %s, writing dummy values\n",
+ av_fourcc2str(track->par->codec_tag));
}
if (track->st) {
// hdlr.name is used by some players to identify the content title
@@ -1423,9 +2528,11 @@ static int mov_write_hmhd_tag(AVIOContext *pb)
return 28;
}
-static int mov_write_minf_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track)
+static int mov_write_minf_tag(AVFormatContext *s, 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->par->codec_type == AVMEDIA_TYPE_VIDEO)
@@ -1434,19 +2541,25 @@ static int mov_write_minf_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra
mov_write_smhd_tag(pb);
else if (track->par->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);
+ if (track->mode != MODE_MOV)
+ mov_write_nmhd_tag(pb);
+ else
+ mov_write_gmhd_tag(pb, track);
+ } else if (track->tag == MKTAG('g','p','m','d')) {
+ mov_write_gmhd_tag(pb, track);
}
if (track->mode == MODE_MOV) /* FIXME: Why do it for MODE_MOV only ? */
mov_write_hdlr_tag(s, pb, NULL);
mov_write_dinf_tag(pb);
- mov_write_stbl_tag(s, pb, track);
+ if ((ret = mov_write_stbl_tag(s, pb, mov, track)) < 0)
+ return ret;
return update_size(pb, pos);
}
@@ -1493,14 +2606,35 @@ static int mov_write_mdia_tag(AVFormatContext *s, 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(s, pb, track);
- mov_write_minf_tag(s, pb, track);
+ if ((ret = mov_write_minf_tag(s, 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)
{
@@ -1508,6 +2642,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;
uint32_t *display_matrix = NULL;
@@ -1563,34 +2698,51 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov,
avio_wb16(pb, 0); /* reserved */
/* Matrix structure */
+#if FF_API_OLD_ROTATE_API
+ if (st && st->metadata) {
+ AVDictionaryEntry *rot = av_dict_get(st->metadata, "rotate", NULL, 0);
+ rotation = (rot && rot->value) ? atoi(rot->value) : 0;
+ }
+#endif
if (display_matrix) {
for (i = 0; i < 9; i++)
avio_wb32(pb, display_matrix[i]);
+#if FF_API_OLD_ROTATE_API
+ } else if (rotation == 90) {
+ write_matrix(pb, 0, 1, -1, 0, track->par->height, 0);
+ } else if (rotation == 180) {
+ write_matrix(pb, -1, 0, 0, -1, track->par->width, track->par->height);
+ } else if (rotation == 270) {
+ write_matrix(pb, 0, -1, 1, 0, 0, track->par->width);
+#endif
} else {
- 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);
}
-
/* Track width and height, for visual only */
if (st && (track->par->codec_type == AVMEDIA_TYPE_VIDEO ||
track->par->codec_type == AVMEDIA_TYPE_SUBTITLE)) {
+ int64_t track_width_1616;
if (track->mode == MODE_MOV) {
- avio_wb32(pb, track->par->width << 16);
- avio_wb32(pb, track->height << 16);
+ track_width_1616 = track->par->width * 0x10000ULL;
} else {
- double sample_aspect_ratio = av_q2d(st->sample_aspect_ratio);
- if (!sample_aspect_ratio || track->height != track->par->height)
- sample_aspect_ratio = 1;
- avio_wb32(pb, sample_aspect_ratio * track->par->width * 0x10000);
- avio_wb32(pb, track->height * 0x10000);
+ track_width_1616 = av_rescale(st->sample_aspect_ratio.num,
+ track->par->width * 0x10000LL,
+ st->sample_aspect_ratio.den);
+ if (!track_width_1616 ||
+ track->height != track->par->height ||
+ track_width_1616 > UINT32_MAX)
+ track_width_1616 = track->par->width * 0x10000ULL;
+ }
+ if (track_width_1616 > UINT32_MAX) {
+ av_log(mov->fc, AV_LOG_WARNING, "track width is too large\n");
+ track_width_1616 = 0;
}
+ avio_wb32(pb, track_width_1616);
+ if (track->height > 0xFFFF) {
+ av_log(mov->fc, AV_LOG_WARNING, "track height is too large\n");
+ avio_wb32(pb, 0);
+ } else
+ avio_wb32(pb, track->height * 0x10000U);
} else {
avio_wb32(pb, 0);
avio_wb32(pb, 0);
@@ -1638,7 +2790,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;
@@ -1672,9 +2838,10 @@ static int mov_write_edts_tag(AVIOContext *pb, MOVMuxContext *mov,
} else {
/* Avoid accidentally ending up with start_ct = -1 which has got a
* special meaning. Normally start_ct should end up positive or zero
- * here, but use FFMIN in case dts is a a small positive integer
+ * here, but use FFMIN in case dts is 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. */
@@ -1791,31 +2958,38 @@ static int mov_write_track_udta_tag(AVIOContext *pb, MOVMuxContext *mov,
return 0;
}
-static int mov_write_trak_tag(AVIOContext *pb, MOVMuxContext *mov,
+static int mov_write_trak_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext *mov,
MOVTrack *track, AVStream *st)
{
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(mov->fc, pb, mov, track);
+
+ if ((ret = mov_write_mdia_tag(s, 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',' '))
@@ -1823,16 +2997,17 @@ static int mov_write_trak_tag(AVIOContext *pb, MOVMuxContext *mov,
if (track->mode == MODE_MOV) {
if (track->par->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);
}
@@ -1892,12 +3067,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);
@@ -1915,7 +3090,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 */
@@ -1936,15 +3112,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) */
@@ -2042,6 +3210,24 @@ static int mov_write_string_metadata(AVFormatContext *s, AVIOContext *pb,
return mov_write_string_tag(pb, name, t->value, lang, long_style);
}
+/* iTunes bpm number */
+static int mov_write_tmpo_tag(AVIOContext *pb, AVFormatContext *s)
+{
+ AVDictionaryEntry *t = av_dict_get(s->metadata, "tmpo", NULL, 0);
+ int size = 0, tmpo = t ? atoi(t->value) : 0;
+ if (tmpo) {
+ size = 26;
+ avio_wb32(pb, size);
+ ffio_wfourcc(pb, "tmpo");
+ avio_wb32(pb, size-8); /* size */
+ ffio_wfourcc(pb, "data");
+ avio_wb32(pb, 0x15); //type specifier
+ avio_wb32(pb, 0);
+ avio_wb16(pb, tmpo); // data
+ }
+ return size;
+}
+
/* 3GPP TS 26.244 */
static int mov_write_loci_tag(AVFormatContext *s, AVIOContext *pb)
{
@@ -2052,7 +3238,7 @@ static int mov_write_loci_tag(AVFormatContext *s, AVIOContext *pb)
AVDictionaryEntry *t = get_metadata_lang(s, "location", &lang);
const char *ptr, *place = "";
char *end;
- const char *astronomical_body = "earth";
+ static const char *astronomical_body = "earth";
if (!t)
return 0;
@@ -2093,28 +3279,61 @@ static int mov_write_loci_tag(AVFormatContext *s, AVIOContext *pb)
return update_size(pb, pos);
}
-/* iTunes track number */
+/* 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)
@@ -2142,11 +3361,84 @@ 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_string_metadata(s, pb, "keyw", "keywords" , 1);
+ 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);
}
-/* iTunes meta data tag */
+static int mov_write_mdta_hdlr_tag(AVIOContext *pb, MOVMuxContext *mov,
+ AVFormatContext *s)
+{
+ avio_wb32(pb, 33); /* size */
+ ffio_wfourcc(pb, "hdlr");
+ avio_wb32(pb, 0);
+ avio_wb32(pb, 0);
+ ffio_wfourcc(pb, "mdta");
+ avio_wb32(pb, 0);
+ avio_wb32(pb, 0);
+ avio_wb32(pb, 0);
+ avio_w8(pb, 0);
+ return 33;
+}
+
+static int mov_write_mdta_keys_tag(AVIOContext *pb, MOVMuxContext *mov,
+ AVFormatContext *s)
+{
+ AVDictionaryEntry *t = NULL;
+ int64_t pos = avio_tell(pb);
+ int64_t curpos, entry_pos;
+ int count = 0;
+
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "keys");
+ avio_wb32(pb, 0);
+ entry_pos = avio_tell(pb);
+ avio_wb32(pb, 0); /* entry count */
+
+ while (t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX)) {
+ avio_wb32(pb, strlen(t->key) + 8);
+ ffio_wfourcc(pb, "mdta");
+ avio_write(pb, t->key, strlen(t->key));
+ count += 1;
+ }
+ curpos = avio_tell(pb);
+ avio_seek(pb, entry_pos, SEEK_SET);
+ avio_wb32(pb, count); // rewrite entry count
+ avio_seek(pb, curpos, SEEK_SET);
+
+ return update_size(pb, pos);
+}
+
+static int mov_write_mdta_ilst_tag(AVIOContext *pb, MOVMuxContext *mov,
+ AVFormatContext *s)
+{
+ AVDictionaryEntry *t = NULL;
+ int64_t pos = avio_tell(pb);
+ int count = 1; /* keys are 1-index based */
+
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "ilst");
+
+ while (t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX)) {
+ int64_t entry_pos = avio_tell(pb);
+ avio_wb32(pb, 0); /* size */
+ avio_wb32(pb, count); /* key */
+ mov_write_string_data_tag(pb, t->value, 0, 1);
+ update_size(pb, entry_pos);
+ count += 1;
+ }
+ return update_size(pb, pos);
+}
+
+/* meta data tags */
static int mov_write_meta_tag(AVIOContext *pb, MOVMuxContext *mov,
AVFormatContext *s)
{
@@ -2155,12 +3447,40 @@ static int mov_write_meta_tag(AVIOContext *pb, MOVMuxContext *mov,
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "meta");
avio_wb32(pb, 0);
- mov_write_itunes_hdlr_tag(pb, mov, s);
- mov_write_ilst_tag(pb, mov, s);
+ if (mov->flags & FF_MOV_FLAG_USE_MDTA) {
+ mov_write_mdta_hdlr_tag(pb, mov, s);
+ mov_write_mdta_keys_tag(pb, mov, s);
+ mov_write_mdta_ilst_tag(pb, mov, s);
+ }
+ else {
+ /* iTunes metadata tag */
+ mov_write_itunes_hdlr_tag(pb, mov, s);
+ mov_write_ilst_tag(pb, mov, s);
+ }
size = update_size(pb, pos);
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;
@@ -2248,20 +3568,24 @@ static int mov_write_udta_tag(AVIOContext *pb, MOVMuxContext *mov,
mov_write_3gp_udta_tag(pb_buf, s, "cprt", "copyright");
mov_write_3gp_udta_tag(pb_buf, s, "yrrc", "date");
mov_write_loci_tag(s, pb_buf);
- } else if (mov->mode == MODE_MOV) { // the title field breaks gtkpod with mp4 and my suspicion is that stuff is not valid in mp4
+ } else if (mov->mode == MODE_MOV && !(mov->flags & FF_MOV_FLAG_USE_MDTA)) { // the title field breaks gtkpod with mp4 and my suspicion is that stuff is not valid in mp4
mov_write_string_metadata(s, pb_buf, "\251ART", "artist", 0);
mov_write_string_metadata(s, pb_buf, "\251nam", "title", 0);
mov_write_string_metadata(s, pb_buf, "\251aut", "author", 0);
mov_write_string_metadata(s, pb_buf, "\251alb", "album", 0);
mov_write_string_metadata(s, pb_buf, "\251day", "date", 0);
- if (!(s->flags & AVFMT_FLAG_BITEXACT))
- mov_write_string_metadata(s, pb_buf, "\251swr", "encoder", 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_string_metadata(s, pb_buf, "\251mak", "make", 0);
mov_write_string_metadata(s, pb_buf, "\251mod", "model", 0);
mov_write_string_metadata(s, pb_buf, "\251xyz", "location", 0);
+ mov_write_string_metadata(s, pb_buf, "\251key", "keywords", 0);
+ mov_write_raw_metadata_tag(s, pb_buf, "XMP_", "xmp");
} else {
/* iTunes meta data */
mov_write_meta_tag(pb_buf, mov, s);
@@ -2320,7 +3644,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 (!(s->flags & AVFMT_FLAG_BITEXACT))
+ 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);
@@ -2331,6 +3656,75 @@ 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++;
+ }
+ }
+}
+
+/**
+ * Assign track ids. If option "use_stream_ids_as_track_ids" is set,
+ * the stream ids are used as track ids.
+ *
+ * This assumes mov->tracks and s->streams are in the same order and
+ * there are no gaps in either of them (so mov->tracks[n] refers to
+ * s->streams[n]).
+ *
+ * As an exception, there can be more entries in
+ * s->streams than in mov->tracks, in which case new track ids are
+ * generated (starting after the largest found stream id).
+ */
+static int mov_setup_track_ids(MOVMuxContext *mov, AVFormatContext *s)
+{
+ int i;
+
+ if (mov->track_ids_ok)
+ return 0;
+
+ if (mov->use_stream_ids_as_track_ids) {
+ int next_generated_track_id = 0;
+ for (i = 0; i < s->nb_streams; i++) {
+ if (s->streams[i]->id > next_generated_track_id)
+ next_generated_track_id = s->streams[i]->id;
+ }
+
+ for (i = 0; i < mov->nb_streams; i++) {
+ if (mov->tracks[i].entry <= 0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT))
+ continue;
+
+ mov->tracks[i].track_id = i >= s->nb_streams ? ++next_generated_track_id : s->streams[i]->id;
+ }
+ } else {
+ for (i = 0; i < mov->nb_streams; i++) {
+ if (mov->tracks[i].entry <= 0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT))
+ continue;
+
+ mov->tracks[i].track_id = i + 1;
+ }
+ }
+
+ mov->track_ids_ok = 1;
+
+ return 0;
+}
+
static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov,
AVFormatContext *s)
{
@@ -2339,12 +3733,16 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov,
avio_wb32(pb, 0); /* size placeholder*/
ffio_wfourcc(pb, "moov");
+ mov_setup_track_ids(mov, s);
+
for (i = 0; i < mov->nb_streams; i++) {
if (mov->tracks[i].entry <= 0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT))
continue;
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)
@@ -2370,13 +3768,26 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov,
}
}
}
+ 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(s, 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)
@@ -2409,10 +3820,13 @@ static void param_write_hex(AVIOContext *pb, const char *name, const uint8_t *va
avio_printf(pb, "<param name=\"%s\" value=\"%s\" valuetype=\"data\"/>\n", name, buf);
}
-static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov)
+static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s)
{
int64_t pos = avio_tell(pb);
int i;
+ int64_t manifest_bit_rate = 0;
+ AVCPBProperties *props = NULL;
+
static const uint8_t uuid[] = {
0xa5, 0xd4, 0x0b, 0x30, 0xe8, 0x14, 0x11, 0xdd,
0xba, 0x2f, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66
@@ -2426,17 +3840,23 @@ 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->fc->flags & AVFMT_FLAG_BITEXACT))
+ avio_printf(pb, "<meta name=\"creator\" content=\"%s\" />\n",
LIBAVFORMAT_IDENT);
avio_printf(pb, "</head>\n");
avio_printf(pb, "<body>\n");
avio_printf(pb, "<switch>\n");
+
+ mov_setup_track_ids(mov, s);
+
for (i = 0; i < mov->nb_streams; i++) {
MOVTrack *track = &mov->tracks[i];
const char *type;
- /* track->track_id is initialized in write_moov, and thus isn't known
- * here yet */
- int track_id = i + 1;
+ int track_id = track->track_id;
+ char track_name_buf[32] = { 0 };
+
+ AVStream *st = track->st;
+ AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL,0);
if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) {
type = "video";
@@ -2445,10 +3865,37 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov)
} else {
continue;
}
- avio_printf(pb, "<%s systemBitrate=\"%d\">\n", type,
- track->par->bit_rate);
- param_write_int(pb, "systemBitrate", track->par->bit_rate);
+
+ props = (AVCPBProperties*)av_stream_get_side_data(track->st, AV_PKT_DATA_CPB_PROPERTIES, NULL);
+
+ if (track->par->bit_rate) {
+ manifest_bit_rate = track->par->bit_rate;
+ } else if (props) {
+ manifest_bit_rate = props->max_bitrate;
+ }
+
+ avio_printf(pb, "<%s systemBitrate=\"%"PRId64"\">\n", type,
+ manifest_bit_rate);
+ param_write_int(pb, "systemBitrate", manifest_bit_rate);
param_write_int(pb, "trackID", track_id);
+ param_write_string(pb, "systemLanguage", lang ? lang->value : "und");
+
+ /* Build track name piece by piece: */
+ /* 1. track type */
+ av_strlcat(track_name_buf, type, sizeof(track_name_buf));
+ /* 2. track language, if available */
+ if (lang)
+ av_strlcatf(track_name_buf, sizeof(track_name_buf),
+ "_%s", lang->value);
+ /* 3. special type suffix */
+ /* "_cc" = closed captions, "_ad" = audio_description */
+ if (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED)
+ av_strlcat(track_name_buf, "_cc", sizeof(track_name_buf));
+ else if (st->disposition & AV_DISPOSITION_VISUAL_IMPAIRED)
+ av_strlcat(track_name_buf, "_ad", sizeof(track_name_buf));
+
+ param_write_string(pb, "trackName", track_name_buf);
+
if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) {
if (track->par->codec_id == AV_CODEC_ID_H264) {
uint8_t *ptr;
@@ -2472,7 +3919,17 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov)
param_write_int(pb, "DisplayHeight", track->par->height);
} else {
if (track->par->codec_id == AV_CODEC_ID_AAC) {
- param_write_string(pb, "FourCC", "AACL");
+ switch (track->par->profile)
+ {
+ case FF_PROFILE_AAC_HE_V2:
+ param_write_string(pb, "FourCC", "AACP");
+ break;
+ case FF_PROFILE_AAC_HE:
+ param_write_string(pb, "FourCC", "AACH");
+ break;
+ default:
+ param_write_string(pb, "FourCC", "AACL");
+ }
} else if (track->par->codec_id == AV_CODEC_ID_WMAPRO) {
param_write_string(pb, "FourCC", "WMAP");
}
@@ -2594,7 +4051,10 @@ static int mov_write_trun_tag(AVIOContext *pb, MOVMuxContext *mov,
avio_wb32(pb, 0); /* size placeholder */
ffio_wfourcc(pb, "trun");
- avio_w8(pb, 0); /* version */
+ if (mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS)
+ avio_w8(pb, 1); /* version */
+ else
+ avio_w8(pb, 0); /* version */
avio_wb24(pb, flags);
avio_wb32(pb, end - first); /* sample count */
@@ -2673,8 +4133,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;
@@ -3040,6 +4499,8 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
ffio_wfourcc(pb, "MSNV");
else if (mov->mode == MODE_MP4 && mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF)
ffio_wfourcc(pb, "iso5"); // Required when using default-base-is-moof
+ else if (mov->mode == MODE_MP4 && mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS)
+ ffio_wfourcc(pb, "iso4");
else if (mov->mode == MODE_MP4)
ffio_wfourcc(pb, "isom");
else if (mov->mode == MODE_IPOD)
@@ -3084,17 +4545,23 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
return update_size(pb, pos);
}
-static void mov_write_uuidprof_tag(AVIOContext *pb, AVFormatContext *s)
+static int mov_write_uuidprof_tag(AVIOContext *pb, AVFormatContext *s)
{
AVStream *video_st = s->streams[0];
AVCodecParameters *video_par = s->streams[0]->codecpar;
AVCodecParameters *audio_par = s->streams[1]->codecpar;
int audio_rate = audio_par->sample_rate;
- // TODO: should be avg_frame_rate
- int frame_rate = ((video_st->time_base.den) * (0x10000)) / (video_st->time_base.num);
+ int64_t frame_rate = video_st->avg_frame_rate.den ?
+ (video_st->avg_frame_rate.num * 0x10000LL) / video_st->avg_frame_rate.den :
+ 0;
int audio_kbitrate = audio_par->bit_rate / 1000;
int video_kbitrate = FFMIN(video_par->bit_rate / 1000, 800 - audio_kbitrate);
+ if (frame_rate < 0 || frame_rate > INT32_MAX) {
+ av_log(s, AV_LOG_ERROR, "Frame rate %f outside supported range\n", frame_rate / (double)0x10000);
+ return AVERROR(EINVAL);
+ }
+
avio_wb32(pb, 0x94); /* size */
ffio_wfourcc(pb, "uuid");
ffio_wfourcc(pb, "PROF");
@@ -3145,18 +4612,33 @@ static void mov_write_uuidprof_tag(AVIOContext *pb, AVFormatContext *s)
avio_wb16(pb, video_par->width);
avio_wb16(pb, video_par->height);
avio_wb32(pb, 0x010001); /* ? */
+
+ return 0;
}
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->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+ video_streams_nb++;
+ else if (st->codecpar->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);
}
- mov_write_uuidprof_tag(pb, s);
+ return mov_write_uuidprof_tag(pb, s);
}
return 0;
}
@@ -3273,6 +4755,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force)
MOVMuxContext *mov = s->priv_data;
int i, first_track = -1;
int64_t mdat_size = 0;
+ int ret;
int has_video = 0, starts_with_key = 0, first_video_track = 1;
if (!(mov->flags & FF_MOV_FLAG_FRAGMENT))
@@ -3288,6 +4771,8 @@ static int mov_flush_fragment(AVFormatContext *s, int force)
if (!track->end_reliable) {
AVPacket pkt;
if (!ff_interleaved_peek(s, i, &pkt, 1)) {
+ if (track->dts_shift != AV_NOPTS_VALUE)
+ pkt.dts += track->dts_shift;
track->track_duration = pkt.dts - track->start_dts;
if (pkt.pts != AV_NOPTS_VALUE)
track->end_pts = pkt.pts;
@@ -3326,10 +4811,8 @@ static int mov_flush_fragment(AVFormatContext *s, int force)
if (!mov->moov_written) {
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)
@@ -3338,17 +4821,15 @@ static int mov_flush_fragment(AVFormatContext *s, int force)
if (i < mov->nb_streams && !force)
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;
avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_HEADER);
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_GLOBAL_SIDX)
@@ -3493,6 +4974,44 @@ static int mov_auto_flush_fragment(AVFormatContext *s, int force)
return ret;
}
+static int check_pkt(AVFormatContext *s, AVPacket *pkt)
+{
+ MOVMuxContext *mov = s->priv_data;
+ MOVTrack *trk = &mov->tracks[pkt->stream_index];
+ int64_t ref;
+ uint64_t duration;
+
+ if (trk->entry) {
+ ref = trk->cluster[trk->entry - 1].dts;
+ } else if ( trk->start_dts != AV_NOPTS_VALUE
+ && !trk->frag_discont) {
+ ref = trk->start_dts + trk->track_duration;
+ } else
+ ref = pkt->dts; // Skip tests for the first packet
+
+ if (trk->dts_shift != AV_NOPTS_VALUE) {
+ /* With negative CTS offsets we have set an offset to the DTS,
+ * reverse this for the check. */
+ ref -= trk->dts_shift;
+ }
+
+ duration = pkt->dts - ref;
+ if (pkt->dts < ref || 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 = ref + 1;
+ pkt->pts = AV_NOPTS_VALUE;
+ }
+
+ if (pkt->duration < 0 || pkt->duration > INT_MAX) {
+ av_log(s, AV_LOG_ERROR, "Application provided duration: %"PRId64" is invalid\n", pkt->duration);
+ return AVERROR(EINVAL);
+ }
+ return 0;
+}
+
int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
{
MOVMuxContext *mov = s->priv_data;
@@ -3503,6 +5022,10 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
int size = pkt->size, ret = 0;
uint8_t *reformatted_data = NULL;
+ ret = check_pkt(s, pkt);
+ if (ret < 0)
+ return ret;
+
if (mov->flags & FF_MOV_FLAG_FRAGMENT) {
int ret;
if (mov->moov_written || mov->flags & FF_MOV_FLAG_EMPTY_MOOV) {
@@ -3541,13 +5064,18 @@ 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 (par->codec_id == AV_CODEC_ID_ADPCM_MS ||
+ par->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV) {
+ samples_in_chunk = trk->par->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 && par->extradata_size > 0) {
+ if (trk->vos_len == 0 && par->extradata_size > 0 &&
+ !TAG_IS_AVCI(trk->tag) &&
+ (par->codec_id != AV_CODEC_ID_DNXHD)) {
trk->vos_len = par->extradata_size;
trk->vos_data = av_malloc(trk->vos_len);
if (!trk->vos_data) {
@@ -3557,7 +5085,17 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
memcpy(trk->vos_data, par->extradata, trk->vos_len);
}
- if (par->codec_id == AV_CODEC_ID_H264 && trk->vos_len > 0 && *(uint8_t *)trk->vos_data != 1) {
+ if (par->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 (par->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 H.264 */
/* NAL reformatting needed */
if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) {
@@ -3565,7 +5103,15 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
&size);
avio_write(pb, reformatted_data, size);
} else {
- size = ff_avc_parse_nal_units(pb, pkt->data, pkt->size);
+ if (trk->cenc.aes_ctr) {
+ size = ff_mov_cenc_avc_parse_nal_units(&trk->cenc, pb, pkt->data, size);
+ if (size < 0) {
+ ret = size;
+ goto err;
+ }
+ } else {
+ size = ff_avc_parse_nal_units(pb, pkt->data, pkt->size);
+ }
}
} else if (par->codec_id == AV_CODEC_ID_HEVC && trk->vos_len > 6 &&
(AV_RB24(trk->vos_data) == 1 || AV_RB32(trk->vos_data) == 1)) {
@@ -3576,8 +5122,30 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
} else {
size = ff_hevc_annexb2mp4(pb, pkt->data, pkt->size, 0, NULL);
}
- } else {
+#if CONFIG_AC3_PARSER
+ } else if (par->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 {
+ if (trk->cenc.aes_ctr) {
+ if (par->codec_id == AV_CODEC_ID_H264 && par->extradata_size > 4) {
+ int nal_size_length = (par->extradata[4] & 0x3) + 1;
+ ret = ff_mov_cenc_avc_write_nal_units(s, &trk->cenc, nal_size_length, pb, pkt->data, size);
+ } else {
+ ret = ff_mov_cenc_write_packet(&trk->cenc, pb, pkt->data, size);
+ }
+
+ if (ret) {
+ goto err;
+ }
+ } else {
+ avio_write(pb, pkt->data, size);
+ }
}
if ((par->codec_id == AV_CODEC_ID_DNXHD ||
@@ -3604,6 +5172,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;
@@ -3631,6 +5200,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.
@@ -3664,6 +5234,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");
@@ -3705,23 +5276,25 @@ 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, 1);
- return 1;
- } else {
MOVMuxContext *mov = s->priv_data;
MOVTrack *trk = &mov->tracks[pkt->stream_index];
AVCodecParameters *par = trk->par;
int64_t frag_duration = 0;
int size = pkt->size;
+ int ret = check_pkt(s, pkt);
+ if (ret < 0)
+ return ret;
+
if (mov->flags & FF_MOV_FLAG_FRAG_DISCONT) {
int i;
for (i = 0; i < s->nb_streams; i++)
@@ -3729,6 +5302,30 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
mov->flags &= ~FF_MOV_FLAG_FRAG_DISCONT;
}
+ if (mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS) {
+ if (trk->dts_shift == AV_NOPTS_VALUE)
+ trk->dts_shift = pkt->pts - pkt->dts;
+ pkt->dts += trk->dts_shift;
+ }
+
+ if (trk->par->codec_id == AV_CODEC_ID_MP4ALS ||
+ trk->par->codec_id == AV_CODEC_ID_AAC ||
+ trk->par->codec_id == AV_CODEC_ID_FLAC) {
+ int side_size = 0;
+ uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size);
+ if (side && side_size > 0 && (side_size != par->extradata_size || memcmp(side, par->extradata, side_size))) {
+ void *newextra = av_mallocz(side_size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!newextra)
+ return AVERROR(ENOMEM);
+ av_free(par->extradata);
+ par->extradata = newextra;
+ memcpy(par->extradata, side, side_size);
+ par->extradata_size = side_size;
+ if (!pkt->size) // Flush packet
+ mov->need_rewrite_extradata = 1;
+ }
+ }
+
if (!pkt->size) {
if (trk->start_dts == AV_NOPTS_VALUE && trk->frag_discont) {
trk->start_dts = pkt->dts;
@@ -3741,7 +5338,7 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
return 0; /* Discard 0 sized packets */
}
- if (trk->entry)
+ if (trk->entry && pkt->stream_index < s->nb_streams)
frag_duration = av_rescale_q(pkt->dts - trk->cluster[0].dts,
s->streams[pkt->stream_index]->time_base,
AV_TIME_BASE_Q);
@@ -3767,6 +5364,107 @@ 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_packet_unref(&end);
+
+ return ret;
+}
+
+static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ if (!pkt) {
+ mov_flush_fragment(s, 1);
+ return 1;
+ } else {
+ int i;
+ MOVMuxContext *mov = s->priv_data;
+ MOVTrack *trk = &mov->tracks[pkt->stream_index];
+
+ if (!pkt->size)
+ return mov_write_single_packet(s, pkt); /* Passthrough. */
+
+ /*
+ * 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->par->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;
+ }
+ }
+
+ if (trk->mode == MODE_MOV && trk->par->codec_type == AVMEDIA_TYPE_VIDEO) {
+ AVPacket *opkt = pkt;
+ int reshuffle_ret, ret;
+ if (trk->is_unaligned_qt_rgb) {
+ int64_t bpc = trk->par->bits_per_coded_sample != 15 ? trk->par->bits_per_coded_sample : 16;
+ int expected_stride = ((trk->par->width * bpc + 15) >> 4)*2;
+ reshuffle_ret = ff_reshuffle_raw_rgb(s, &pkt, trk->par, expected_stride);
+ if (reshuffle_ret < 0)
+ return reshuffle_ret;
+ } else
+ reshuffle_ret = 0;
+ if (trk->par->format == AV_PIX_FMT_PAL8 && !trk->pal_done) {
+ ret = ff_get_packet_palette(s, opkt, reshuffle_ret, trk->palette);
+ if (ret < 0)
+ goto fail;
+ if (ret)
+ trk->pal_done++;
+ } else if (trk->par->codec_id == AV_CODEC_ID_RAWVIDEO &&
+ (trk->par->format == AV_PIX_FMT_GRAY8 ||
+ trk->par->format == AV_PIX_FMT_MONOBLACK)) {
+ for (i = 0; i < pkt->size; i++)
+ pkt->data[i] = ~pkt->data[i];
+ }
+ if (reshuffle_ret) {
+ ret = mov_write_single_packet(s, pkt);
+fail:
+ if (reshuffle_ret)
+ av_packet_free(&pkt);
+ return ret;
+ }
+ }
+
+ return mov_write_single_packet(s, pkt);
}
}
@@ -3774,12 +5472,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');
@@ -3788,11 +5486,57 @@ static int mov_create_chapter_track(AVFormatContext *s, int tracknum)
if (!track->par)
return AVERROR(ENOMEM);
track->par->codec_type = AVMEDIA_TYPE_SUBTITLE;
- track->par->extradata = av_malloc(sizeof(chapter_properties));
- if (!track->par->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->par, sizeof(chapter_properties)))
return AVERROR(ENOMEM);
- track->par->extradata_size = sizeof(chapter_properties);
memcpy(track->par->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->par->extradata = buf;
+ track->par->extradata_size = size;
+ } else {
+ av_freep(&buf);
+ }
+ }
+#endif
for (i = 0; i < s->nb_chapters; i++) {
AVChapter *c = s->chapters[i];
@@ -3823,6 +5567,54 @@ static int mov_create_chapter_track(AVFormatContext *s, int tracknum)
return 0;
}
+
+static int mov_check_timecode_track(AVFormatContext *s, AVTimecode *tc, int src_index, const char *tcstr)
+{
+ int ret;
+
+ /* compute the frame number */
+ ret = av_timecode_init_from_string(tc, find_fps(s, s->streams[src_index]), tcstr, s);
+ return ret;
+}
+
+static int mov_create_timecode_track(AVFormatContext *s, int index, int src_index, AVTimecode tc)
+{
+ int ret;
+ MOVMuxContext *mov = s->priv_data;
+ MOVTrack *track = &mov->tracks[index];
+ AVStream *src_st = s->streams[src_index];
+ AVPacket pkt = {.stream_index = index, .flags = AV_PKT_FLAG_KEY, .size = 4};
+ AVRational rate = find_fps(s, src_st);
+
+ /* 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->par = avcodec_parameters_alloc();
+ if (!track->par)
+ return AVERROR(ENOMEM);
+ track->par->codec_type = AVMEDIA_TYPE_DATA;
+ track->par->codec_tag = track->tag;
+ track->st->avg_frame_rate = av_inv_q(rate);
+
+ /* the tmcd track just contains one packet with the frame number */
+ pkt.data = av_malloc(pkt.size);
+ if (!pkt.data)
+ return AVERROR(ENOMEM);
+ 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
@@ -3880,17 +5672,24 @@ static void mov_free(AVFormatContext *s)
MOVMuxContext *mov = s->priv_data;
int i;
- if (mov->chapter_track)
- avcodec_parameters_free(&mov->tracks[mov->chapter_track].par);
+ if (mov->chapter_track) {
+ if (mov->tracks[mov->chapter_track].par)
+ av_freep(&mov->tracks[mov->chapter_track].par->extradata);
+ av_freep(&mov->tracks[mov->chapter_track].par);
+ }
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].par);
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);
+
+ ff_mov_cenc_free(&mov->tracks[i].cenc);
}
av_freep(&mov->tracks);
@@ -3905,9 +5704,9 @@ static uint32_t rgb_to_yuv(uint32_t rgb)
g = (rgb >> 8) & 0xFF;
b = (rgb ) & 0xFF;
- y = av_clip_uint8( 16. + 0.257 * r + 0.504 * g + 0.098 * b);
- cb = av_clip_uint8(128. - 0.148 * r - 0.291 * g + 0.439 * b);
- cr = av_clip_uint8(128. + 0.439 * r - 0.368 * g - 0.071 * b);
+ y = av_clip_uint8(( 16000 + 257 * r + 504 * g + 98 * b)/1000);
+ cb = av_clip_uint8((128000 - 148 * r - 291 * g + 439 * b)/1000);
+ cr = av_clip_uint8((128000 + 439 * r - 368 * g - 71 * b)/1000);
return (y << 16) | (cr << 8) | cb;
}
@@ -3961,12 +5760,11 @@ static int mov_create_dvd_sub_decoder_specific_info(MOVTrack *track,
return 0;
}
-static int mov_write_header(AVFormatContext *s)
+static int mov_init(AVFormatContext *s)
{
- AVIOContext *pb = s->pb;
MOVMuxContext *mov = s->priv_data;
- AVDictionaryEntry *t;
- int i, ret, hint_track = 0;
+ AVDictionaryEntry *global_tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
+ int i, ret;
mov->fc = s;
@@ -4002,6 +5800,15 @@ 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_EMPTY_MOOV && s->flags & AVFMT_FLAG_AUTO_BSF) {
+ av_log(s, AV_LOG_VERBOSE, "Empty MOOV enabled; disabling automatic bitstream filtering\n");
+ s->flags &= ~AVFMT_FLAG_AUTO_BSF;
+ }
+
+ 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 &&
@@ -4044,19 +5851,12 @@ 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;
- }
-
mov->nb_streams = s->nb_streams;
if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters)
mov->chapter_track = mov->nb_streams++;
if (mov->flags & FF_MOV_FLAG_RTP_HINT) {
/* Add hint tracks for each audio and video stream */
- hint_track = mov->nb_streams;
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
@@ -4066,12 +5866,64 @@ static int mov_write_header(AVFormatContext *s)
}
}
+ if ( mov->write_tmcd == -1 && (mov->mode == MODE_MOV || mov->mode == MODE_MP4)
+ || mov->write_tmcd == 1) {
+ /* +1 tmcd track for each video stream with a timecode */
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ AVDictionaryEntry *t = global_tcr;
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
+ (t || (t=av_dict_get(st->metadata, "timecode", NULL, 0)))) {
+ AVTimecode tc;
+ ret = mov_check_timecode_track(s, &tc, i, t->value);
+ if (ret >= 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->codecpar->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);
+ if (mov->encryption_scheme_str != NULL && strcmp(mov->encryption_scheme_str, "none") != 0) {
+ if (strcmp(mov->encryption_scheme_str, "cenc-aes-ctr") == 0) {
+ mov->encryption_scheme = MOV_ENC_CENC_AES_CTR;
+
+ if (mov->encryption_key_len != AES_CTR_KEY_SIZE) {
+ av_log(s, AV_LOG_ERROR, "Invalid encryption key len %d expected %d\n",
+ mov->encryption_key_len, AES_CTR_KEY_SIZE);
+ return AVERROR(EINVAL);
+ }
+
+ if (mov->encryption_kid_len != CENC_KID_SIZE) {
+ av_log(s, AV_LOG_ERROR, "Invalid encryption kid len %d expected %d\n",
+ mov->encryption_kid_len, CENC_KID_SIZE);
+ return AVERROR(EINVAL);
+ }
+ } else {
+ av_log(s, AV_LOG_ERROR, "unsupported encryption scheme %s\n",
+ mov->encryption_scheme_str);
+ return AVERROR(EINVAL);
+ }
+ }
+
for (i = 0; i < s->nb_streams; i++) {
AVStream *st= s->streams[i];
MOVTrack *track= &mov->tracks[i];
@@ -4085,9 +5937,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);
- goto error;
+ 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->codecpar->codec_id), i);
+ return AVERROR(EINVAL);
}
/* If hinting of this track is enabled by a later hint track,
* this is updated. */
@@ -4095,47 +5948,106 @@ static int mov_write_header(AVFormatContext *s)
track->start_dts = AV_NOPTS_VALUE;
track->start_cts = AV_NOPTS_VALUE;
track->end_pts = AV_NOPTS_VALUE;
+ track->dts_shift = AV_NOPTS_VALUE;
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (track->tag == MKTAG('m','x','3','p') || track->tag == MKTAG('m','x','3','n') ||
track->tag == MKTAG('m','x','4','p') || track->tag == MKTAG('m','x','4','n') ||
track->tag == MKTAG('m','x','5','p') || track->tag == MKTAG('m','x','5','n')) {
if (st->codecpar->width != 720 || (st->codecpar->height != 608 && st->codecpar->height != 512)) {
av_log(s, AV_LOG_ERROR, "D-10/IMX must use 720x608 or 720x512 video resolution\n");
- goto error;
+ return AVERROR(EINVAL);
}
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->codecpar->width > 65535 || st->codecpar->height > 65535) {
+ av_log(s, AV_LOG_ERROR, "Resolution %dx%d too large for mov/mp4\n", st->codecpar->width, st->codecpar->height);
+ return AVERROR(EINVAL);
+ }
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"
"file may not be playable by quicktime. Specify a shorter timebase\n"
"or choose different container.\n");
+ if (track->mode == MODE_MOV &&
+ track->par->codec_id == AV_CODEC_ID_RAWVIDEO &&
+ track->tag == MKTAG('r','a','w',' ')) {
+ enum AVPixelFormat pix_fmt = track->par->format;
+ if (pix_fmt == AV_PIX_FMT_NONE && track->par->bits_per_coded_sample == 1)
+ pix_fmt = AV_PIX_FMT_MONOWHITE;
+ track->is_unaligned_qt_rgb =
+ pix_fmt == AV_PIX_FMT_RGB24 ||
+ pix_fmt == AV_PIX_FMT_BGR24 ||
+ pix_fmt == AV_PIX_FMT_PAL8 ||
+ pix_fmt == AV_PIX_FMT_GRAY8 ||
+ pix_fmt == AV_PIX_FMT_MONOWHITE ||
+ pix_fmt == AV_PIX_FMT_MONOBLACK;
+ }
+ if (track->par->codec_id == AV_CODEC_ID_VP9) {
+ if (track->mode != MODE_MP4) {
+ av_log(s, AV_LOG_ERROR, "VP9 only supported in MP4.\n");
+ return AVERROR(EINVAL);
+ }
+ }
} else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
track->timescale = st->codecpar->sample_rate;
- /* set sample_size for PCM and ADPCM */
- if (av_get_bits_per_sample(st->codecpar->codec_id) ||
- st->codecpar->codec_id == AV_CODEC_ID_ILBC) {
+ if (!st->codecpar->frame_size && !av_get_bits_per_sample(st->codecpar->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->codecpar->codec_id == AV_CODEC_ID_ADPCM_MS ||
+ st->codecpar->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV ||
+ st->codecpar->codec_id == AV_CODEC_ID_ILBC){
if (!st->codecpar->block_align) {
- av_log(s, AV_LOG_ERROR, "track %d: codec block align is not set\n", i);
- goto error;
+ av_log(s, AV_LOG_ERROR, "track %d: codec block align is not set for adpcm\n", i);
+ return AVERROR(EINVAL);
}
track->sample_size = st->codecpar->block_align;
+ }else if (st->codecpar->frame_size > 1){ /* assume compressed audio */
+ track->audio_vbr = 1;
+ }else{
+ track->sample_size = (av_get_bits_per_sample(st->codecpar->codec_id) >> 3) * st->codecpar->channels;
}
- /* set audio_vbr for compressed audio */
- if (av_get_bits_per_sample(st->codecpar->codec_id) < 8) {
+ if (st->codecpar->codec_id == AV_CODEC_ID_ILBC ||
+ st->codecpar->codec_id == AV_CODEC_ID_ADPCM_IMA_QT) {
track->audio_vbr = 1;
}
if (track->mode != MODE_MOV &&
track->par->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->par->sample_rate);
- goto error;
+ if (s->strict_std_compliance >= FF_COMPLIANCE_NORMAL) {
+ av_log(s, AV_LOG_ERROR, "track %d: muxing mp3 at %dhz is not standard, to mux anyway set strict to -1\n",
+ i, track->par->sample_rate);
+ return AVERROR(EINVAL);
+ } else {
+ av_log(s, AV_LOG_WARNING, "track %d: muxing mp3 at %dhz is not standard in MP4\n",
+ i, track->par->sample_rate);
+ }
+ }
+ if (track->par->codec_id == AV_CODEC_ID_FLAC ||
+ track->par->codec_id == AV_CODEC_ID_OPUS) {
+ if (track->mode != MODE_MP4) {
+ av_log(s, AV_LOG_ERROR, "%s only supported in MP4.\n", avcodec_get_name(track->par->codec_id));
+ return AVERROR(EINVAL);
+ }
+ if (s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
+ av_log(s, AV_LOG_ERROR,
+ "%s in MP4 support is experimental, add "
+ "'-strict %d' if you want to use it.\n",
+ avcodec_get_name(track->par->codec_id), FF_COMPLIANCE_EXPERIMENTAL);
+ return AVERROR_EXPERIMENTAL;
+ }
}
} else if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
track->timescale = st->time_base.den;
} else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
track->timescale = st->time_base.den;
+ } else {
+ track->timescale = MOV_TIMESCALE;
}
if (!track->height)
track->height = st->codecpar->height;
@@ -4146,21 +6058,92 @@ static int mov_write_header(AVFormatContext *s)
avpriv_set_pts_info(st, 64, 1, track->timescale);
+ if (mov->encryption_scheme == MOV_ENC_CENC_AES_CTR) {
+ ret = ff_mov_cenc_init(&track->cenc, mov->encryption_key,
+ track->par->codec_id == AV_CODEC_ID_H264, s->flags & AVFMT_FLAG_BITEXACT);
+ if (ret)
+ return ret;
+ }
+ }
+
+ enable_tracks(s);
+ return 0;
+}
+
+static int mov_write_header(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ MOVMuxContext *mov = s->priv_data;
+ AVDictionaryEntry *t, *global_tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
+ int i, ret, hint_track = 0, tmcd_track = 0, nb_tracks = s->nb_streams;
+
+ if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters)
+ nb_tracks++;
+
+ if (mov->flags & FF_MOV_FLAG_RTP_HINT) {
+ /* Add hint tracks for each audio and video stream */
+ hint_track = nb_tracks;
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
+ st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ nb_tracks++;
+ }
+ }
+ }
+
+ if (mov->mode == MODE_MOV || mov->mode == MODE_MP4)
+ tmcd_track = nb_tracks;
+
+ for (i = 0; i < s->nb_streams; i++) {
+ int j;
+ AVStream *st= s->streams[i];
+ MOVTrack *track= &mov->tracks[i];
+
/* copy extradata if it exists */
if (st->codecpar->extradata_size) {
if (st->codecpar->codec_id == AV_CODEC_ID_DVD_SUBTITLE)
mov_create_dvd_sub_decoder_specific_info(track, st);
- else {
+ else if (!TAG_IS_AVCI(track->tag) && st->codecpar->codec_id != AV_CODEC_ID_DNXHD) {
track->vos_len = st->codecpar->extradata_size;
track->vos_data = av_malloc(track->vos_len);
- if (!track->vos_data)
- goto error;
+ if (!track->vos_data) {
+ return AVERROR(ENOMEM);
+ }
memcpy(track->vos_data, st->codecpar->extradata, track->vos_len);
}
}
+
+ if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO ||
+ track->par->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->codecpar->codec_type != AVMEDIA_TYPE_AUDIO ||
+ trackj->par->channel_layout != AV_CH_LAYOUT_MONO ||
+ trackj->language != track->language ||
+ trackj->tag != track->tag
+ )
+ continue;
+ track->multichannel_as_mono++;
+ }
}
- enable_tracks(s);
+ if (!(mov->flags & FF_MOV_FLAG_DELAY_MOOV)) {
+ if ((ret = mov_write_identification(pb, s)) < 0)
+ return ret;
+ }
+
+ if (mov->reserved_moov_size){
+ mov->reserved_header_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. */
@@ -4174,14 +6157,13 @@ static int mov_write_header(AVFormatContext *s)
mov_write_mdat_tag(pb, mov);
}
- if (t = av_dict_get(s->metadata, "creation_time", NULL, 0))
- mov->time = ff_iso8601_to_unix_time(t->value);
+ ff_parse_creation_time_metadata(s, &mov->time, 1);
if (mov->time)
mov->time += 0x7C25B080; // 1970 based -> 1904 based
if (mov->chapter_track)
- if (mov_create_chapter_track(s, mov->chapter_track) < 0)
- goto error;
+ if ((ret = mov_create_chapter_track(s, mov->chapter_track)) < 0)
+ return ret;
if (mov->flags & FF_MOV_FLAG_RTP_HINT) {
/* Initialize the hint tracks for each audio and video stream */
@@ -4189,20 +6171,43 @@ static int mov_write_header(AVFormatContext *s)
AVStream *st = s->streams[i];
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
- ff_mov_init_hinting(s, hint_track, i);
+ if ((ret = ff_mov_init_hinting(s, hint_track, i)) < 0)
+ return ret;
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->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ AVTimecode tc;
+ if (!t)
+ t = av_dict_get(st->metadata, "timecode", NULL, 0);
+ if (!t)
+ continue;
+ if (mov_check_timecode_track(s, &tc, i, t->value) < 0)
+ continue;
+ if ((ret = mov_create_timecode_track(s, tmcd_track, i, tc)) < 0)
+ return ret;
+ tmcd_track++;
+ }
+ }
+ }
+
avio_flush(pb);
if (mov->flags & FF_MOV_FLAG_ISML)
- mov_write_isml_manifest(pb, mov);
+ mov_write_isml_manifest(pb, mov, 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;
avio_flush(pb);
mov->moov_written = 1;
if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX)
@@ -4210,9 +6215,6 @@ static int mov_write_header(AVFormatContext *s)
}
return 0;
- error:
- mov_free(s);
- return -1;
}
static int get_moov_size(AVFormatContext *s)
@@ -4223,7 +6225,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);
}
@@ -4359,6 +6362,33 @@ static int mov_write_trailer(AVFormatContext *s)
int i;
int64_t moov_pos;
+ if (mov->need_rewrite_extradata) {
+ for (i = 0; i < s->nb_streams; i++) {
+ MOVTrack *track = &mov->tracks[i];
+ AVCodecParameters *par = track->par;
+
+ track->vos_len = par->extradata_size;
+ track->vos_data = av_malloc(track->vos_len);
+ if (!track->vos_data)
+ return AVERROR(ENOMEM);
+ memcpy(track->vos_data, par->extradata, track->vos_len);
+ }
+ mov->need_rewrite_extradata = 0;
+ }
+
+ /*
+ * 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->par->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.
@@ -4366,7 +6396,7 @@ static int mov_write_trailer(AVFormatContext *s)
if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters) {
mov->chapter_track = mov->nb_streams++;
if ((res = mov_create_chapter_track(s, mov->chapter_track)) < 0)
- goto error;
+ return res;
}
}
@@ -4386,45 +6416,141 @@ 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_header_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_header_pos, SEEK_SET);
- mov_write_moov_tag(pb, mov, s);
+ if (res < 0)
+ return res;
+ avio_seek(pb, mov->reserved_header_pos, SEEK_SET);
+ if ((res = mov_write_moov_tag(pb, mov, s)) < 0)
+ return res;
+ } else if (mov->reserved_moov_size > 0) {
+ int64_t size;
+ if ((res = mov_write_moov_tag(pb, mov, s)) < 0)
+ return res;
+ size = mov->reserved_moov_size - (avio_tell(pb) - mov->reserved_header_pos);
+ if (size < 8){
+ av_log(s, AV_LOG_ERROR, "reserved_moov_size is too small, needed %"PRId64" additional\n", 8-size);
+ return AVERROR(EINVAL);
}
+ 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)
+ return res;
}
+ res = 0;
} else {
mov_auto_flush_fragment(s, 1);
for (i = 0; i < mov->nb_streams; i++)
mov->tracks[i].data_offset = 0;
if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX) {
+ int64_t end;
av_log(s, AV_LOG_INFO, "Starting second pass: inserting sidx atoms\n");
res = shift_data(s);
- if (res == 0) {
- int64_t end = avio_tell(pb);
- avio_seek(pb, mov->reserved_header_pos, SEEK_SET);
- mov_write_sidx_tags(pb, mov, -1, 0);
- avio_seek(pb, end, SEEK_SET);
- avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_TRAILER);
- mov_write_mfra_tag(pb, mov);
- }
+ if (res < 0)
+ return res;
+ end = avio_tell(pb);
+ avio_seek(pb, mov->reserved_header_pos, SEEK_SET);
+ mov_write_sidx_tags(pb, mov, -1, 0);
+ avio_seek(pb, end, SEEK_SET);
+ avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_TRAILER);
+ mov_write_mfra_tag(pb, mov);
} else if (!(mov->flags & FF_MOV_FLAG_SKIP_TRAILER)) {
avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_TRAILER);
mov_write_mfra_tag(pb, mov);
}
}
-error:
- mov_free(s);
-
return res;
}
+static int mov_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt)
+{
+ int ret = 1;
+ AVStream *st = s->streams[pkt->stream_index];
+
+ if (st->codecpar->codec_id == AV_CODEC_ID_AAC) {
+ if (pkt->size > 2 && (AV_RB16(pkt->data) & 0xfff0) == 0xfff0)
+ ret = ff_stream_add_bitstream_filter(st, "aac_adtstoasc", NULL);
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_VP9) {
+ ret = ff_stream_add_bitstream_filter(st, "vp9_superframe", NULL);
+ }
+
+ return ret;
+}
+
+static const AVCodecTag codec_3gp_tags[] = {
+ { AV_CODEC_ID_H263, MKTAG('s','2','6','3') },
+ { AV_CODEC_ID_H264, MKTAG('a','v','c','1') },
+ { AV_CODEC_ID_MPEG4, MKTAG('m','p','4','v') },
+ { AV_CODEC_ID_AAC, MKTAG('m','p','4','a') },
+ { AV_CODEC_ID_AMR_NB, MKTAG('s','a','m','r') },
+ { AV_CODEC_ID_AMR_WB, MKTAG('s','a','w','b') },
+ { AV_CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') },
+ { AV_CODEC_ID_NONE, 0 },
+};
+
+const AVCodecTag codec_mp4_tags[] = {
+ { AV_CODEC_ID_MPEG4 , MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_H264 , MKTAG('a', 'v', 'c', '1') },
+ { AV_CODEC_ID_HEVC , MKTAG('h', 'e', 'v', '1') },
+ { AV_CODEC_ID_HEVC , MKTAG('h', 'v', 'c', '1') },
+ { AV_CODEC_ID_MPEG2VIDEO , MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_MPEG1VIDEO , MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_MJPEG , MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_PNG , MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_JPEG2000 , MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_VC1 , MKTAG('v', 'c', '-', '1') },
+ { AV_CODEC_ID_DIRAC , MKTAG('d', 'r', 'a', 'c') },
+ { AV_CODEC_ID_TSCC2 , MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_VP9 , MKTAG('v', 'p', '0', '9') },
+ { AV_CODEC_ID_AAC , MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_MP4ALS , MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_MP3 , MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_MP2 , MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_AC3 , MKTAG('a', 'c', '-', '3') },
+ { AV_CODEC_ID_EAC3 , MKTAG('e', 'c', '-', '3') },
+ { AV_CODEC_ID_DTS , MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_FLAC , MKTAG('f', 'L', 'a', 'C') },
+ { AV_CODEC_ID_OPUS , MKTAG('O', 'p', 'u', 's') },
+ { AV_CODEC_ID_VORBIS , MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_QCELP , MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_EVRC , MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_DVD_SUBTITLE, MKTAG('m', 'p', '4', 's') },
+ { AV_CODEC_ID_MOV_TEXT , MKTAG('t', 'x', '3', 'g') },
+ { AV_CODEC_ID_NONE , 0 },
+};
+
+const AVCodecTag codec_ism_tags[] = {
+ { AV_CODEC_ID_WMAPRO , MKTAG('w', 'm', 'a', ' ') },
+ { AV_CODEC_ID_NONE , 0 },
+};
+
+static const AVCodecTag codec_ipod_tags[] = {
+ { AV_CODEC_ID_H264, MKTAG('a','v','c','1') },
+ { AV_CODEC_ID_MPEG4, MKTAG('m','p','4','v') },
+ { AV_CODEC_ID_AAC, MKTAG('m','p','4','a') },
+ { AV_CODEC_ID_ALAC, MKTAG('a','l','a','c') },
+ { AV_CODEC_ID_AC3, MKTAG('a','c','-','3') },
+ { AV_CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') },
+ { AV_CODEC_ID_MOV_TEXT, MKTAG('t','e','x','t') },
+ { AV_CODEC_ID_NONE, 0 },
+};
+
+static const AVCodecTag codec_f4v_tags[] = {
+ { AV_CODEC_ID_MP3, MKTAG('.','m','p','3') },
+ { AV_CODEC_ID_AAC, MKTAG('m','p','4','a') },
+ { AV_CODEC_ID_H264, MKTAG('a','v','c','1') },
+ { AV_CODEC_ID_VP6A, MKTAG('V','P','6','A') },
+ { AV_CODEC_ID_VP6F, MKTAG('V','P','6','F') },
+ { AV_CODEC_ID_NONE, 0 },
+};
+
#if CONFIG_MOV_MUXER
MOV_CLASS(mov)
AVOutputFormat ff_mov_muxer = {
@@ -4435,13 +6561,16 @@ AVOutputFormat ff_mov_muxer = {
.audio_codec = AV_CODEC_ID_AAC,
.video_codec = CONFIG_LIBX264_ENCODER ?
AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4,
+ .init = mov_init,
.write_header = mov_write_header,
.write_packet = mov_write_packet,
.write_trailer = mov_write_trailer,
+ .deinit = mov_free,
.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
.codec_tag = (const AVCodecTag* const []){
ff_codec_movvideo_tags, ff_codec_movaudio_tags, 0
},
+ .check_bitstream = mov_check_bitstream,
.priv_class = &mov_muxer_class,
};
#endif
@@ -4454,11 +6583,14 @@ AVOutputFormat ff_tgp_muxer = {
.priv_data_size = sizeof(MOVMuxContext),
.audio_codec = AV_CODEC_ID_AMR_NB,
.video_codec = AV_CODEC_ID_H263,
+ .init = mov_init,
.write_header = mov_write_header,
.write_packet = mov_write_packet,
.write_trailer = mov_write_trailer,
+ .deinit = mov_free,
.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
.codec_tag = (const AVCodecTag* const []){ codec_3gp_tags, 0 },
+ .check_bitstream = mov_check_bitstream,
.priv_class = &tgp_muxer_class,
};
#endif
@@ -4467,17 +6599,20 @@ MOV_CLASS(mp4)
AVOutputFormat ff_mp4_muxer = {
.name = "mp4",
.long_name = NULL_IF_CONFIG_SMALL("MP4 (MPEG-4 Part 14)"),
- .mime_type = "application/mp4",
+ .mime_type = "video/mp4",
.extensions = "mp4",
.priv_data_size = sizeof(MOVMuxContext),
.audio_codec = AV_CODEC_ID_AAC,
.video_codec = CONFIG_LIBX264_ENCODER ?
AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4,
+ .init = mov_init,
.write_header = mov_write_header,
.write_packet = mov_write_packet,
.write_trailer = mov_write_trailer,
+ .deinit = mov_free,
.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
- .codec_tag = (const AVCodecTag* const []){ ff_mp4_obj_type, 0 },
+ .codec_tag = (const AVCodecTag* const []){ codec_mp4_tags, 0 },
+ .check_bitstream = mov_check_bitstream,
.priv_class = &mp4_muxer_class,
};
#endif
@@ -4491,11 +6626,14 @@ AVOutputFormat ff_psp_muxer = {
.audio_codec = AV_CODEC_ID_AAC,
.video_codec = CONFIG_LIBX264_ENCODER ?
AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4,
+ .init = mov_init,
.write_header = mov_write_header,
.write_packet = mov_write_packet,
.write_trailer = mov_write_trailer,
+ .deinit = mov_free,
.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
- .codec_tag = (const AVCodecTag* const []){ ff_mp4_obj_type, 0 },
+ .codec_tag = (const AVCodecTag* const []){ codec_mp4_tags, 0 },
+ .check_bitstream = mov_check_bitstream,
.priv_class = &psp_muxer_class,
};
#endif
@@ -4508,11 +6646,14 @@ AVOutputFormat ff_tg2_muxer = {
.priv_data_size = sizeof(MOVMuxContext),
.audio_codec = AV_CODEC_ID_AMR_NB,
.video_codec = AV_CODEC_ID_H263,
+ .init = mov_init,
.write_header = mov_write_header,
.write_packet = mov_write_packet,
.write_trailer = mov_write_trailer,
+ .deinit = mov_free,
.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
.codec_tag = (const AVCodecTag* const []){ codec_3gp_tags, 0 },
+ .check_bitstream = mov_check_bitstream,
.priv_class = &tg2_muxer_class,
};
#endif
@@ -4521,16 +6662,19 @@ MOV_CLASS(ipod)
AVOutputFormat ff_ipod_muxer = {
.name = "ipod",
.long_name = NULL_IF_CONFIG_SMALL("iPod H.264 MP4 (MPEG-4 Part 14)"),
- .mime_type = "application/mp4",
+ .mime_type = "video/mp4",
.extensions = "m4v,m4a",
.priv_data_size = sizeof(MOVMuxContext),
.audio_codec = AV_CODEC_ID_AAC,
.video_codec = AV_CODEC_ID_H264,
+ .init = mov_init,
.write_header = mov_write_header,
.write_packet = mov_write_packet,
.write_trailer = mov_write_trailer,
+ .deinit = mov_free,
.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
.codec_tag = (const AVCodecTag* const []){ codec_ipod_tags, 0 },
+ .check_bitstream = mov_check_bitstream,
.priv_class = &ipod_muxer_class,
};
#endif
@@ -4539,16 +6683,20 @@ MOV_CLASS(ismv)
AVOutputFormat ff_ismv_muxer = {
.name = "ismv",
.long_name = NULL_IF_CONFIG_SMALL("ISMV/ISMA (Smooth Streaming)"),
- .mime_type = "application/mp4",
+ .mime_type = "video/mp4",
.extensions = "ismv,isma",
.priv_data_size = sizeof(MOVMuxContext),
.audio_codec = AV_CODEC_ID_AAC,
.video_codec = AV_CODEC_ID_H264,
+ .init = mov_init,
.write_header = mov_write_header,
.write_packet = mov_write_packet,
.write_trailer = mov_write_trailer,
+ .deinit = mov_free,
.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
- .codec_tag = (const AVCodecTag* const []){ ff_mp4_obj_type, 0 },
+ .codec_tag = (const AVCodecTag* const []){
+ codec_mp4_tags, codec_ism_tags, 0 },
+ .check_bitstream = mov_check_bitstream,
.priv_class = &ismv_muxer_class,
};
#endif
@@ -4562,11 +6710,14 @@ AVOutputFormat ff_f4v_muxer = {
.priv_data_size = sizeof(MOVMuxContext),
.audio_codec = AV_CODEC_ID_AAC,
.video_codec = AV_CODEC_ID_H264,
+ .init = mov_init,
.write_header = mov_write_header,
.write_packet = mov_write_packet,
.write_trailer = mov_write_trailer,
- .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
+ .deinit = mov_free,
+ .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH,
.codec_tag = (const AVCodecTag* const []){ codec_f4v_tags, 0 },
+ .check_bitstream = mov_check_bitstream,
.priv_class = &f4v_muxer_class,
};
#endif
diff --git a/libavformat/movenc.h b/libavformat/movenc.h
index f4ed188..cc2a155 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
*/
@@ -25,6 +25,7 @@
#define AVFORMAT_MOVENC_H
#include "avformat.h"
+#include "movenccenc.h"
#define MOV_FRAG_INFO_ALLOC_INCREMENT 64
#define MOV_INDEX_CLUSTER_SIZE 1024
@@ -36,7 +37,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 +48,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 +84,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;
AVCodecParameters *par;
+ int multichannel_as_mono;
int vos_len;
uint8_t *vos_data;
@@ -107,9 +116,10 @@ typedef struct MOVTrack {
int64_t start_cts;
int64_t end_pts;
int end_reliable;
+ int64_t dts_shift;
int hint_track; ///< the track that hints this track, -1 if no hint track is set
- int src_track; ///< the track that this hint track describes
+ int src_track; ///< the track that this hint (or tmcd) track describes
AVFormatContext *rtp_ctx; ///< the format context for the hinting rtp muxer
uint32_t prev_rtp_ts;
int64_t cur_rtp_ts_unwrapped;
@@ -140,13 +150,28 @@ typedef struct MOVTrack {
int packet_entry;
int slices;
} vc1_info;
+
+ void *eac3_priv;
+
+ MOVMuxCencContext cenc;
+
+ uint32_t palette[AVPALETTE_COUNT];
+ int pal_done;
+
+ int is_unaligned_qt_rgb;
} MOVTrack;
+typedef enum {
+ MOV_ENC_NONE = 0,
+ MOV_ENC_CENC_AES_CTR,
+} MOVEncryptionScheme;
+
typedef struct MOVMuxContext {
const AVClass *av_class;
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;
@@ -154,6 +179,7 @@ typedef struct MOVMuxContext {
int flags;
int rtp_flags;
+
int iods_skip;
int iods_video_profile;
int iods_audio_profile;
@@ -167,6 +193,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_header_pos;
char *major_brand;
@@ -175,8 +204,23 @@ typedef struct MOVMuxContext {
AVFormatContext *fc;
int use_editlist;
+ float gamma;
+
int frag_interleave;
int missing_duration_warned;
+
+ char *encryption_scheme_str;
+ MOVEncryptionScheme encryption_scheme;
+ uint8_t *encryption_key;
+ int encryption_key_len;
+ uint8_t *encryption_kid;
+ int encryption_kid_len;
+
+ int need_rewrite_extradata;
+
+ int use_stream_ids_as_track_ids;
+ int track_ids_ok;
+ int write_tmcd;
} MOVMuxContext;
#define FF_MOV_FLAG_RTP_HINT (1 << 0)
@@ -194,7 +238,11 @@ typedef struct MOVMuxContext {
#define FF_MOV_FLAG_FRAG_DISCONT (1 << 12)
#define FF_MOV_FLAG_DELAY_MOOV (1 << 13)
#define FF_MOV_FLAG_GLOBAL_SIDX (1 << 14)
-#define FF_MOV_FLAG_SKIP_TRAILER (1 << 15)
+#define FF_MOV_FLAG_WRITE_COLR (1 << 15)
+#define FF_MOV_FLAG_WRITE_GAMA (1 << 16)
+#define FF_MOV_FLAG_USE_MDTA (1 << 17)
+#define FF_MOV_FLAG_SKIP_TRAILER (1 << 18)
+#define FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS (1 << 19)
int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt);
diff --git a/libavformat/movenccenc.c b/libavformat/movenccenc.c
new file mode 100644
index 0000000..b91294f
--- /dev/null
+++ b/libavformat/movenccenc.c
@@ -0,0 +1,415 @@
+/*
+ * MOV CENC (Common Encryption) writer
+ * Copyright (c) 2015 Eran Kornblau <erankor at gmail dot 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 "movenccenc.h"
+#include "libavutil/intreadwrite.h"
+#include "avio_internal.h"
+#include "movenc.h"
+#include "avc.h"
+
+static int auxiliary_info_alloc_size(MOVMuxCencContext* ctx, int size)
+{
+ size_t new_alloc_size;
+
+ if (ctx->auxiliary_info_size + size > ctx->auxiliary_info_alloc_size) {
+ new_alloc_size = FFMAX(ctx->auxiliary_info_size + size, ctx->auxiliary_info_alloc_size * 2);
+ if (av_reallocp(&ctx->auxiliary_info, new_alloc_size)) {
+ return AVERROR(ENOMEM);
+ }
+
+ ctx->auxiliary_info_alloc_size = new_alloc_size;
+ }
+
+ return 0;
+}
+
+static int auxiliary_info_write(MOVMuxCencContext* ctx,
+ const uint8_t *buf_in, int size)
+{
+ int ret;
+
+ ret = auxiliary_info_alloc_size(ctx, size);
+ if (ret) {
+ return ret;
+ }
+ memcpy(ctx->auxiliary_info + ctx->auxiliary_info_size, buf_in, size);
+ ctx->auxiliary_info_size += size;
+
+ return 0;
+}
+
+static int auxiliary_info_add_subsample(MOVMuxCencContext* ctx,
+ uint16_t clear_bytes, uint32_t encrypted_bytes)
+{
+ uint8_t* p;
+ int ret;
+
+ if (!ctx->use_subsamples) {
+ return 0;
+ }
+
+ ret = auxiliary_info_alloc_size(ctx, 6);
+ if (ret) {
+ return ret;
+ }
+
+ p = ctx->auxiliary_info + ctx->auxiliary_info_size;
+
+ AV_WB16(p, clear_bytes);
+ p += sizeof(uint16_t);
+
+ AV_WB32(p, encrypted_bytes);
+
+ ctx->auxiliary_info_size += 6;
+ ctx->subsample_count++;
+
+ return 0;
+}
+
+/**
+ * Encrypt the input buffer and write using avio_write
+ */
+static void mov_cenc_write_encrypted(MOVMuxCencContext* ctx, AVIOContext *pb,
+ const uint8_t *buf_in, int size)
+{
+ uint8_t chunk[4096];
+ const uint8_t* cur_pos = buf_in;
+ int size_left = size;
+ int cur_size;
+
+ while (size_left > 0) {
+ cur_size = FFMIN(size_left, sizeof(chunk));
+ av_aes_ctr_crypt(ctx->aes_ctr, chunk, cur_pos, cur_size);
+ avio_write(pb, chunk, cur_size);
+ cur_pos += cur_size;
+ size_left -= cur_size;
+ }
+}
+
+/**
+ * Start writing a packet
+ */
+static int mov_cenc_start_packet(MOVMuxCencContext* ctx)
+{
+ int ret;
+
+ /* write the iv */
+ ret = auxiliary_info_write(ctx, av_aes_ctr_get_iv(ctx->aes_ctr), AES_CTR_IV_SIZE);
+ if (ret) {
+ return ret;
+ }
+
+ if (!ctx->use_subsamples) {
+ return 0;
+ }
+
+ /* write a zero subsample count */
+ ctx->auxiliary_info_subsample_start = ctx->auxiliary_info_size;
+ ctx->subsample_count = 0;
+ ret = auxiliary_info_write(ctx, (uint8_t*)&ctx->subsample_count, sizeof(ctx->subsample_count));
+ if (ret) {
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * Finalize a packet
+ */
+static int mov_cenc_end_packet(MOVMuxCencContext* ctx)
+{
+ size_t new_alloc_size;
+
+ av_aes_ctr_increment_iv(ctx->aes_ctr);
+
+ if (!ctx->use_subsamples) {
+ ctx->auxiliary_info_entries++;
+ return 0;
+ }
+
+ /* add the auxiliary info entry size*/
+ if (ctx->auxiliary_info_entries >= ctx->auxiliary_info_sizes_alloc_size) {
+ new_alloc_size = ctx->auxiliary_info_entries * 2 + 1;
+ if (av_reallocp(&ctx->auxiliary_info_sizes, new_alloc_size)) {
+ return AVERROR(ENOMEM);
+ }
+
+ ctx->auxiliary_info_sizes_alloc_size = new_alloc_size;
+ }
+ ctx->auxiliary_info_sizes[ctx->auxiliary_info_entries] =
+ AES_CTR_IV_SIZE + ctx->auxiliary_info_size - ctx->auxiliary_info_subsample_start;
+ ctx->auxiliary_info_entries++;
+
+ /* update the subsample count*/
+ AV_WB16(ctx->auxiliary_info + ctx->auxiliary_info_subsample_start, ctx->subsample_count);
+
+ return 0;
+}
+
+int ff_mov_cenc_write_packet(MOVMuxCencContext* ctx, AVIOContext *pb,
+ const uint8_t *buf_in, int size)
+{
+ int ret;
+
+ ret = mov_cenc_start_packet(ctx);
+ if (ret) {
+ return ret;
+ }
+
+ ret = auxiliary_info_add_subsample(ctx, 0, size);
+ if (ret) {
+ return ret;
+ }
+
+ mov_cenc_write_encrypted(ctx, pb, buf_in, size);
+
+ ret = mov_cenc_end_packet(ctx);
+ if (ret) {
+ return ret;
+ }
+
+ return 0;
+}
+
+int ff_mov_cenc_avc_parse_nal_units(MOVMuxCencContext* ctx, AVIOContext *pb,
+ const uint8_t *buf_in, int size)
+{
+ const uint8_t *p = buf_in;
+ const uint8_t *end = p + size;
+ const uint8_t *nal_start, *nal_end;
+ int ret;
+
+ ret = mov_cenc_start_packet(ctx);
+ if (ret) {
+ return ret;
+ }
+
+ size = 0;
+ nal_start = ff_avc_find_startcode(p, end);
+ for (;;) {
+ while (nal_start < end && !*(nal_start++));
+ if (nal_start == end)
+ break;
+
+ nal_end = ff_avc_find_startcode(nal_start, end);
+
+ avio_wb32(pb, nal_end - nal_start);
+ avio_w8(pb, *nal_start);
+ mov_cenc_write_encrypted(ctx, pb, nal_start + 1, nal_end - nal_start - 1);
+
+ auxiliary_info_add_subsample(ctx, 5, nal_end - nal_start - 1);
+
+ size += 4 + nal_end - nal_start;
+ nal_start = nal_end;
+ }
+
+ ret = mov_cenc_end_packet(ctx);
+ if (ret) {
+ return ret;
+ }
+
+ return size;
+}
+
+int ff_mov_cenc_avc_write_nal_units(AVFormatContext *s, MOVMuxCencContext* ctx,
+ int nal_length_size, AVIOContext *pb, const uint8_t *buf_in, int size)
+{
+ int nalsize;
+ int ret;
+ int j;
+
+ ret = mov_cenc_start_packet(ctx);
+ if (ret) {
+ return ret;
+ }
+
+ while (size > 0) {
+ /* parse the nal size */
+ if (size < nal_length_size + 1) {
+ av_log(s, AV_LOG_ERROR, "CENC-AVC: remaining size %d smaller than nal length+type %d\n",
+ size, nal_length_size + 1);
+ return -1;
+ }
+
+ avio_write(pb, buf_in, nal_length_size + 1);
+
+ nalsize = 0;
+ for (j = 0; j < nal_length_size; j++) {
+ nalsize = (nalsize << 8) | *buf_in++;
+ }
+ size -= nal_length_size;
+
+ /* encrypt the nal body */
+ if (nalsize <= 0 || nalsize > size) {
+ av_log(s, AV_LOG_ERROR, "CENC-AVC: nal size %d remaining %d\n", nalsize, size);
+ return -1;
+ }
+
+ mov_cenc_write_encrypted(ctx, pb, buf_in + 1, nalsize - 1);
+ buf_in += nalsize;
+ size -= nalsize;
+
+ auxiliary_info_add_subsample(ctx, nal_length_size + 1, nalsize - 1);
+ }
+
+ ret = mov_cenc_end_packet(ctx);
+ if (ret) {
+ return ret;
+ }
+
+ return 0;
+}
+
+/* TODO: reuse this function from movenc.c */
+static int64_t update_size(AVIOContext *pb, int64_t pos)
+{
+ int64_t curpos = avio_tell(pb);
+ avio_seek(pb, pos, SEEK_SET);
+ avio_wb32(pb, curpos - pos); /* rewrite size */
+ avio_seek(pb, curpos, SEEK_SET);
+
+ return curpos - pos;
+}
+
+static int mov_cenc_write_senc_tag(MOVMuxCencContext* ctx, AVIOContext *pb,
+ int64_t* auxiliary_info_offset)
+{
+ int64_t pos = avio_tell(pb);
+
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "senc");
+ avio_wb32(pb, ctx->use_subsamples ? 0x02 : 0); /* version & flags */
+ avio_wb32(pb, ctx->auxiliary_info_entries); /* entry count */
+ *auxiliary_info_offset = avio_tell(pb);
+ avio_write(pb, ctx->auxiliary_info, ctx->auxiliary_info_size);
+ return update_size(pb, pos);
+}
+
+static int mov_cenc_write_saio_tag(AVIOContext *pb, int64_t auxiliary_info_offset)
+{
+ int64_t pos = avio_tell(pb);
+ uint8_t version;
+
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "saio");
+ version = auxiliary_info_offset > 0xffffffff ? 1 : 0;
+ avio_w8(pb, version);
+ avio_wb24(pb, 0); /* flags */
+ avio_wb32(pb, 1); /* entry count */
+ if (version) {
+ avio_wb64(pb, auxiliary_info_offset);
+ } else {
+ avio_wb32(pb, auxiliary_info_offset);
+ }
+ return update_size(pb, pos);
+}
+
+static int mov_cenc_write_saiz_tag(MOVMuxCencContext* ctx, AVIOContext *pb)
+{
+ int64_t pos = avio_tell(pb);
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "saiz");
+ avio_wb32(pb, 0); /* version & flags */
+ avio_w8(pb, ctx->use_subsamples ? 0 : AES_CTR_IV_SIZE); /* default size*/
+ avio_wb32(pb, ctx->auxiliary_info_entries); /* entry count */
+ if (ctx->use_subsamples) {
+ avio_write(pb, ctx->auxiliary_info_sizes, ctx->auxiliary_info_entries);
+ }
+ return update_size(pb, pos);
+}
+
+void ff_mov_cenc_write_stbl_atoms(MOVMuxCencContext* ctx, AVIOContext *pb)
+{
+ int64_t auxiliary_info_offset;
+
+ mov_cenc_write_senc_tag(ctx, pb, &auxiliary_info_offset);
+ mov_cenc_write_saio_tag(pb, auxiliary_info_offset);
+ mov_cenc_write_saiz_tag(ctx, pb);
+}
+
+static int mov_cenc_write_schi_tag(AVIOContext *pb, uint8_t* kid)
+{
+ int64_t pos = avio_tell(pb);
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "schi");
+
+ avio_wb32(pb, 32); /* size */
+ ffio_wfourcc(pb, "tenc");
+ avio_wb32(pb, 0); /* version & flags */
+ avio_wb24(pb, 1); /* is encrypted */
+ avio_w8(pb, AES_CTR_IV_SIZE); /* iv size */
+ avio_write(pb, kid, CENC_KID_SIZE);
+
+ return update_size(pb, pos);
+}
+
+int ff_mov_cenc_write_sinf_tag(MOVTrack* track, AVIOContext *pb, uint8_t* kid)
+{
+ int64_t pos = avio_tell(pb);
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "sinf");
+
+ /* frma */
+ avio_wb32(pb, 12); /* size */
+ ffio_wfourcc(pb, "frma");
+ avio_wl32(pb, track->tag);
+
+ /* schm */
+ avio_wb32(pb, 20); /* size */
+ ffio_wfourcc(pb, "schm");
+ avio_wb32(pb, 0); /* version & flags */
+ ffio_wfourcc(pb, "cenc"); /* scheme type*/
+ avio_wb32(pb, 0x10000); /* scheme version */
+
+ /* schi */
+ mov_cenc_write_schi_tag(pb, kid);
+
+ return update_size(pb, pos);
+}
+
+int ff_mov_cenc_init(MOVMuxCencContext* ctx, uint8_t* encryption_key,
+ int use_subsamples, int bitexact)
+{
+ int ret;
+
+ ctx->aes_ctr = av_aes_ctr_alloc();
+ if (!ctx->aes_ctr) {
+ return AVERROR(ENOMEM);
+ }
+
+ ret = av_aes_ctr_init(ctx->aes_ctr, encryption_key);
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (!bitexact) {
+ av_aes_ctr_set_random_iv(ctx->aes_ctr);
+ }
+
+ ctx->use_subsamples = use_subsamples;
+
+ return 0;
+}
+
+void ff_mov_cenc_free(MOVMuxCencContext* ctx)
+{
+ av_aes_ctr_free(ctx->aes_ctr);
+}
diff --git a/libavformat/movenccenc.h b/libavformat/movenccenc.h
new file mode 100644
index 0000000..6f9e70e
--- /dev/null
+++ b/libavformat/movenccenc.h
@@ -0,0 +1,86 @@
+/*
+ * MOV CENC (Common Encryption) writer
+ * Copyright (c) 2015 Eran Kornblau <erankor at gmail dot 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_MOVENCCENC_H
+#define AVFORMAT_MOVENCCENC_H
+
+#include "libavutil/aes_ctr.h"
+#include "avformat.h"
+#include "avio.h"
+
+#define CENC_KID_SIZE (16)
+
+struct MOVTrack;
+
+typedef struct {
+ struct AVAESCTR* aes_ctr;
+ uint8_t* auxiliary_info;
+ size_t auxiliary_info_size;
+ size_t auxiliary_info_alloc_size;
+ uint32_t auxiliary_info_entries;
+
+ /* subsample support */
+ int use_subsamples;
+ uint16_t subsample_count;
+ size_t auxiliary_info_subsample_start;
+ uint8_t* auxiliary_info_sizes;
+ size_t auxiliary_info_sizes_alloc_size;
+} MOVMuxCencContext;
+
+/**
+ * Initialize a CENC context
+ * @param key encryption key, must have a length of AES_CTR_KEY_SIZE
+ * @param use_subsamples when enabled parts of a packet can be encrypted, otherwise the whole packet is encrypted
+ */
+int ff_mov_cenc_init(MOVMuxCencContext* ctx, uint8_t* encryption_key, int use_subsamples, int bitexact);
+
+/**
+ * Free a CENC context
+ */
+void ff_mov_cenc_free(MOVMuxCencContext* ctx);
+
+/**
+ * Write a fully encrypted packet
+ */
+int ff_mov_cenc_write_packet(MOVMuxCencContext* ctx, AVIOContext *pb, const uint8_t *buf_in, int size);
+
+/**
+ * Parse AVC NAL units from annex B format, the nal size and type are written in the clear while the body is encrypted
+ */
+int ff_mov_cenc_avc_parse_nal_units(MOVMuxCencContext* ctx, AVIOContext *pb, const uint8_t *buf_in, int size);
+
+/**
+ * Write AVC NAL units that are in MP4 format, the nal size and type are written in the clear while the body is encrypted
+ */
+int ff_mov_cenc_avc_write_nal_units(AVFormatContext *s, MOVMuxCencContext* ctx, int nal_length_size,
+ AVIOContext *pb, const uint8_t *buf_in, int size);
+
+/**
+ * Write the cenc atoms that should reside inside stbl
+ */
+void ff_mov_cenc_write_stbl_atoms(MOVMuxCencContext* ctx, AVIOContext *pb);
+
+/**
+ * Write the sinf atom, contained inside stsd
+ */
+int ff_mov_cenc_write_sinf_tag(struct MOVTrack* track, AVIOContext *pb, uint8_t* kid);
+
+#endif /* AVFORMAT_MOVENCCENC_H */
diff --git a/libavformat/movenchint.c b/libavformat/movenchint.c
index 58ad9e4..964026e 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 e282218..a5c4f2e 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"
@@ -41,21 +42,37 @@
#define XING_TOC_COUNT 100
-typedef struct MP3DecContext {
+#define SAME_HEADER_MASK \
+ (0xffe00000 | (3 << 17) | (3 << 10) | (3 << 19))
+
+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;
+enum CheckRet {
+ CHECK_WRONG_HEADER = -1,
+ CHECK_SEEK_FAILED = -2,
+};
+
+static int check(AVIOContext *pb, int64_t pos, uint32_t *header);
+
/* mp3 read */
static int mp3_read_probe(AVProbeData *p)
{
int max_frames, first_frames = 0;
+ int whole_used = 0;
int frames, ret;
uint32_t header;
- uint8_t *buf, *buf0, *buf2, *end;
+ const uint8_t *buf, *buf0, *buf2, *end;
buf0 = p->buf;
end = p->buf + p->buf_size - sizeof(uint32_t);
@@ -67,7 +84,6 @@ static int mp3_read_probe(AVProbeData *p)
for(; buf < end; buf= buf2+1) {
buf2 = buf;
-
for(frames = 0; buf2 < end; frames++) {
MPADecodeHeader h;
@@ -78,39 +94,22 @@ static int mp3_read_probe(AVProbeData *p)
buf2 += h.frame_size;
}
max_frames = FFMAX(max_frames, frames);
- if(buf == buf0)
+ if(buf == buf0) {
first_frames= frames;
+ if (buf2 == end + sizeof(uint32_t))
+ whole_used = 1;
+ }
}
// 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>=7) 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(first_frames > 1 && whole_used) return 5;
+ 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
}
@@ -118,32 +117,35 @@ static void read_xing_toc(AVFormatContext *s, int64_t filesize, int64_t duration
{
int i;
MP3DecContext *mp3 = s->priv_data;
+ int fast_seek = s->flags & AVFMT_FLAG_FAST_SEEK;
+ int fill_index = (mp3->usetoc || fast_seek) && 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,
MPADecodeHeader *c, uint32_t spf)
{
#define LAST_BITS(k, n) ((k) & ((1 << (n)) - 1))
-#define MIDDLE_BITS(k, m, n) LAST_BITS((k) >> (m), ((n) - (m)))
+#define MIDDLE_BITS(k, m, n) LAST_BITS((k) >> (m), ((n) - (m) + 1))
uint16_t crc;
- uint32_t v, delays;
+ uint32_t v;
char version[10];
@@ -151,7 +153,9 @@ 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;
- static 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);
+ fsize = fsize >= avio_tell(s->pb) ? fsize - avio_tell(s->pb) : 0;
/* Check for Xing / Info tag */
avio_skip(s->pb, xing_offtbl[c->lsf == 1][c->nb_channels == 1]);
@@ -164,12 +168,24 @@ 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;
+ av_log(s, AV_LOG_WARNING,
+ "invalid concatenated file detected - using bitrate for duration\n");
+ } 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);
@@ -215,9 +231,25 @@ static void mp3_parse_info_tag(AVFormatContext *s, AVStream *st,
avio_r8(s->pb);
/* Encoder delays */
- delays = avio_rb24(s->pb);
- st->codecpar->initial_padding = delays >> 12;
- st->codecpar->trailing_padding = delays & ((1 << 12) - 1);
+ 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->start_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->start_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);
@@ -257,7 +289,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);
}
}
@@ -289,12 +321,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 */
@@ -303,17 +335,22 @@ 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->codecpar->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->codecpar->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;
+ int i;
+
+ s->metadata = s->internal->id3v2_meta;
+ s->internal->id3v2_meta = NULL;
st = avformat_new_stream(s, NULL);
if (!st)
@@ -321,17 +358,21 @@ static int mp3_read_header(AVFormatContext *s)
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->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 & AVIO_SEEKABLE_NORMAL)
+ mp3->filesize = avio_size(s->pb);
+
if (mp3_parse_vbr_tags(s, st, off) < 0)
avio_seek(s->pb, off, SEEK_SET);
@@ -339,6 +380,44 @@ static int mp3_read_header(AVFormatContext *s)
if (ret < 0)
return ret;
+ off = avio_tell(s->pb);
+ for (i = 0; i < 64 * 1024; i++) {
+ uint32_t header, header2;
+ int frame_size;
+ if (!(i&1023))
+ ffio_ensure_seekback(s->pb, i + 1024 + 4);
+ frame_size = check(s->pb, off + i, &header);
+ if (frame_size > 0) {
+ ret = avio_seek(s->pb, off, SEEK_SET);
+ if (ret < 0)
+ return ret;
+ ffio_ensure_seekback(s->pb, i + 1024 + frame_size + 4);
+ ret = check(s->pb, off + i + frame_size, &header2);
+ if (ret >= 0 &&
+ (header & SAME_HEADER_MASK) == (header2 & SAME_HEADER_MASK))
+ {
+ av_log(s, i > 0 ? AV_LOG_INFO : AV_LOG_VERBOSE, "Skipping %d bytes of junk at %"PRId64".\n", i, off);
+ ret = avio_seek(s->pb, off + i, SEEK_SET);
+ if (ret < 0)
+ return ret;
+ break;
+ } else if (ret == CHECK_SEEK_FAILED) {
+ av_log(s, AV_LOG_ERROR, "Invalid frame size (%d): Could not seek to %"PRId64".\n", frame_size, off + i + frame_size);
+ return AVERROR(EINVAL);
+ }
+ } else if (frame_size == CHECK_SEEK_FAILED) {
+ av_log(s, AV_LOG_ERROR, "Failed to read frame size: Could not seek to %"PRId64".\n", (int64_t) (i + 1024 + frame_size + 4));
+ return AVERROR(EINVAL);
+ }
+ ret = avio_seek(s->pb, off, SEEK_SET);
+ if (ret < 0)
+ return ret;
+ }
+
+ // the seek index is relative to the end of the xing vbr headers
+ for (i = 0; i < st->nb_index_entries; i++)
+ st->index_entries[i].pos += avio_tell(s->pb);
+
/* the parameters will be extracted from the compressed bitstream */
return 0;
}
@@ -347,112 +426,170 @@ 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 &&
- memcmp(&pkt->data[ret - ID3v1_TAG_SIZE], "TAG", 3) == 0)
- ret -= ID3v1_TAG_SIZE;
-
- /* note: we need to modify the packet size here to handle the last
- packet */
- pkt->size = ret;
return ret;
}
-#define SEEK_PACKETS 4
-#define SEEK_WINDOW (SEEK_PACKETS * MP3_PACKET_SIZE)
+#define SEEK_WINDOW 4096
-/* The toc entry can position to the wrong byte offset, try to pick
- * the closest frame by probing the data in a window of 4 packets.
- */
-
-static int check(AVIOContext *pb, int64_t pos, int64_t *out_pos)
+static int check(AVIOContext *pb, int64_t pos, uint32_t *ret_header)
{
- MPADecodeHeader mh = { 0 };
- int i;
- uint32_t header;
- int64_t off = 0;
-
-
- for (i = 0; i < SEEK_PACKETS; i++) {
- off = avio_seek(pb, pos + mh.frame_size, SEEK_SET);
- if (off < 0)
- break;
-
- header = avio_rb32(pb);
-
-
- if (avpriv_mpegaudio_decode_header(&mh, header))
- break;
- out_pos[i] = off;
- }
-
- return i;
+ int64_t ret = avio_seek(pb, pos, SEEK_SET);
+ uint8_t header_buf[4];
+ unsigned header;
+ MPADecodeHeader sd;
+ if (ret < 0)
+ return CHECK_SEEK_FAILED;
+
+ ret = avio_read(pb, &header_buf[0], 4);
+ /* We should always find four bytes for a valid mpa header. */
+ if (ret < 4)
+ return CHECK_SEEK_FAILED;
+
+ header = AV_RB32(&header_buf[0]);
+ if (ff_mpa_check_header(header) < 0)
+ return CHECK_WRONG_HEADER;
+ if (avpriv_mpegaudio_decode_header(&sd, header) == 1)
+ return CHECK_WRONG_HEADER;
+
+ if (ret_header)
+ *ret_header = header;
+ return sd.frame_size;
}
-static int reposition(AVFormatContext *s, int64_t pos)
+static int64_t mp3_sync(AVFormatContext *s, int64_t target_pos, int flags)
{
- int ret, best_valid = -1;
- int64_t p, best_pos = -1;
-
- for (p = FFMAX(pos - SEEK_WINDOW / 2, 0); p < pos + SEEK_WINDOW / 2; p++) {
- int64_t out_pos[SEEK_PACKETS];
- ret = check(s->pb, p, out_pos);
-
- if (best_valid < ret) {
- int i;
- for (i = 0; i < ret; i++) {
- if (llabs(best_pos - pos) > llabs(out_pos[i] - pos)) {
- best_pos = out_pos[i];
- best_valid = ret;
+ int dir = (flags&AVSEEK_FLAG_BACKWARD) ? -1 : 1;
+ int64_t best_pos;
+ int best_score, i, j;
+ int64_t ret;
+
+ avio_seek(s->pb, FFMAX(target_pos - SEEK_WINDOW, 0), SEEK_SET);
+ ret = avio_seek(s->pb, target_pos, SEEK_SET);
+ if (ret < 0)
+ return ret;
+
+#define MIN_VALID 3
+ best_pos = target_pos;
+ best_score = 999;
+ for(i=0; i<SEEK_WINDOW; i++) {
+ int64_t pos = target_pos + (dir > 0 ? i - SEEK_WINDOW/4 : -i);
+ int64_t candidate = -1;
+ int score = 999;
+
+ if (pos < 0)
+ continue;
+
+ for(j=0; j<MIN_VALID; j++) {
+ ret = check(s->pb, pos, NULL);
+ if(ret < 0) {
+ if (ret == CHECK_WRONG_HEADER) {
+ break;
+ } else if (ret == CHECK_SEEK_FAILED) {
+ av_log(s, AV_LOG_ERROR, "Could not seek to %"PRId64".\n", pos);
+ return AVERROR(EINVAL);
}
}
- if (best_pos == pos && best_valid == SEEK_PACKETS)
+ if ((target_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;
}
}
- if (best_valid <= 0)
- return AVERROR(ENOSYS);
-
- p = avio_seek(s->pb, best_pos, SEEK_SET);
- if (p < 0)
- return p;
-
- return 0;
+ return avio_seek(s->pb, best_pos, SEEK_SET);
}
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);
+ int64_t best_pos;
+ int fast_seek = s->flags & AVFMT_FLAG_FAST_SEEK;
+ int64_t filesize = mp3->header_filesize;
+
+ if (filesize <= 0) {
+ int64_t size = avio_size(s->pb);
+ if (size > 0 && size > s->internal->data_offset)
+ filesize = size - s->internal->data_offset;
+ }
- if (!mp3->xing_toc)
- return AVERROR(ENOSYS);
+ if (mp3->xing_toc && (mp3->usetoc || (fast_seek && !mp3->is_cbr))) {
+ int64_t ret = av_index_search_timestamp(st, timestamp, flags);
- if (ret < 0)
- return ret;
+ // NOTE: The MP3 TOC is not a precise lookup table. Accuracy is worse
+ // for bigger files.
+ av_log(s, AV_LOG_WARNING, "Using MP3 TOC to seek; may be imprecise.\n");
- ie = &st->index_entries[ret];
+ if (ret < 0)
+ return ret;
- ret = reposition(s, ie->pos);
- if (ret < 0)
- return ret;
+ ie = &st->index_entries[ret];
+ } else if (fast_seek && st->duration > 0 && filesize > 0) {
+ if (!mp3->is_cbr)
+ av_log(s, AV_LOG_WARNING, "Using scaling to seek VBR MP3; may be imprecise.\n");
- ff_update_cur_dts(s, st, ie->timestamp);
+ ie = &ie1;
+ timestamp = av_clip64(timestamp, 0, st->duration);
+ ie->timestamp = timestamp;
+ ie->pos = av_rescale(timestamp, filesize, st->duration) + s->internal->data_offset;
+ } else {
+ return -1; // generic index code
+ }
+
+ best_pos = mp3_sync(s, ie->pos, flags);
+ if (best_pos < 0)
+ return best_pos;
+ if (mp3->is_cbr && ie == &ie1 && mp3->frames) {
+ int frame_duration = av_rescale(st->duration, 1, mp3->frames);
+ ie1.timestamp = frame_duration * av_rescale(best_pos - s->internal->data_offset, mp3->frames, mp3->header_filesize);
+ }
+
+ ff_update_cur_dts(s, st, ie->timestamp);
return 0;
}
+static const AVOption options[] = {
+ { "usetoc", "use table of contents", offsetof(MP3DecContext, usetoc), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 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)"),
@@ -463,4 +600,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 d87d7be..8479e24 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);
@@ -110,6 +111,8 @@ typedef struct MP3Context {
uint64_t bag[XING_NUM_BAGS];
int initial_bitrate;
int has_variable_bitrate;
+ int delay;
+ int padding;
/* index of the audio stream */
int audio_stream_idx;
@@ -125,7 +128,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;
AVCodecParameters *par = s->streams[mp3->audio_stream_idx]->codecpar;
@@ -135,14 +138,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 & AVIO_SEEKABLE_NORMAL) || !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 +159,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 (par->channels) {
@@ -166,35 +168,36 @@ 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 - par->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);
+ ret = avpriv_mpegaudio_decode_header(&mpah, header);
+ av_assert0(ret >= 0);
+ 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 +207,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,25 +234,22 @@ 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
ffio_fill(dyn_ctx, 0, 8); // empty replaygain fields
avio_w8(dyn_ctx, 0); // unknown encoding flags
avio_w8(dyn_ctx, 0); // unknown abr/minimal bitrate
-
- // encoder delay
- if (par->initial_padding >= 1 << 12 ||
- par->trailing_padding >= 1 << 12) {
- av_log(s, AV_LOG_WARNING, "Too many samples of padding.\n");
- avio_wb24(dyn_ctx, 0);
- } else {
- avio_wb24(dyn_ctx, par->initial_padding << 12 | par->trailing_padding);
- }
+ avio_wb24(dyn_ctx, 0); // empty encoder delay/padding
avio_w8(dyn_ctx, 0); // misc
avio_w8(dyn_ctx, 0); // mp3gain
@@ -269,6 +267,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;
}
/*
@@ -289,7 +289,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;
@@ -305,26 +305,61 @@ 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 ret;
+ int av_unused base;
uint32_t h;
h = AV_RB32(pkt->data);
- ret = avpriv_mpegaudio_decode_header(&c, h);
+ ret = avpriv_mpegaudio_decode_header(&mpah, h);
if (ret >= 0) {
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 %08"PRIX32"...) "
+ "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) {
+ uint8_t *side_data = NULL;
+ int side_data_size = 0;
+
+ 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);
+
+ side_data = av_packet_get_side_data(pkt,
+ AV_PKT_DATA_SKIP_SAMPLES,
+ &side_data_size);
+ if (side_data && side_data_size >= 10) {
+ mp3->padding = FFMAX(AV_RL32(side_data + 4) + 528 + 1, 0);
+ if (!mp3->delay)
+ mp3->delay = FFMAX(AV_RL32(side_data) - 528 - 1, 0);
+ } else {
+ mp3->padding = 0;
+ }
}
}
@@ -337,7 +372,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)) {
@@ -398,6 +433,17 @@ static void mp3_update_xing(AVFormatContext *s)
}
}
+ /* write encoder delay/padding */
+ if (mp3->delay >= 1 << 12) {
+ mp3->delay = (1 << 12) - 1;
+ av_log(s, AV_LOG_WARNING, "Too many samples of initial padding.\n");
+ }
+ if (mp3->padding >= 1 << 12) {
+ mp3->padding = (1 << 12) - 1;
+ av_log(s, AV_LOG_WARNING, "Too many samples of trailing padding.\n");
+ }
+ AV_WB24(mp3->xing_frame + mp3->xing_offset + 141, (mp3->delay << 12) + mp3->padding);
+
AV_WB32(mp3->xing_frame + mp3->xing_offset + XING_SIZE - 8, mp3->audio_size);
AV_WB16(mp3->xing_frame + mp3->xing_offset + XING_SIZE - 4, mp3->audio_crc);
@@ -433,13 +479,24 @@ 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;
+}
+
static const AVOption options[] = {
{ "id3v2_version", "Select ID3v2 version to write. Currently 3 and 4 are supported.",
offsetof(MP3Context, id3v2_version), AV_OPT_TYPE_INT, {.i64 = 4}, 0, 4, AV_OPT_FLAG_ENCODING_PARAM},
{ "write_id3v1", "Enable ID3v1 writing. ID3v1 tags are written in UTF-8 which may not be supported by most software.",
- offsetof(MP3Context, write_id3v1), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
+ offsetof(MP3Context, write_id3v1), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
{ "write_xing", "Write the Xing header containing file duration.",
- offsetof(MP3Context, write_xing), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
+ offsetof(MP3Context, write_xing), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
{ NULL },
};
@@ -458,14 +515,13 @@ 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);
- pktl->pkt = *pkt;
- pktl->pkt.buf = av_buffer_ref(pkt->buf);
- if (!pktl->pkt.buf) {
+ if (!pktl || av_packet_ref(&pktl->pkt, pkt) < 0) {
av_freep(&pktl);
- return AVERROR(ENOMEM);
+ 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);
}
if (mp3->queue_end)
@@ -554,7 +610,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);
}
@@ -572,6 +628,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 5be50a7..af33374 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->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
st->codecpar->bits_per_coded_sample = 16;
- st->codecpar->extradata_size = 16;
- st->codecpar->extradata = av_mallocz(st->codecpar->extradata_size+AV_INPUT_BUFFER_PADDING_SIZE);
- avio_read(s->pb, st->codecpar->extradata, 16);
+ if (ff_get_extradata(s, st->codecpar, s->pb, 16) < 0)
+ return AVERROR(ENOMEM);
st->codecpar->sample_rate = mpc_rate[st->codecpar->extradata[2] & 3];
avpriv_set_pts_info(st, 32, MPC_FRAMESIZE, st->codecpar->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 c4027e5..f280faa 100644
--- a/libavformat/mpc8.c
+++ b/libavformat/mpc8.c
@@ -2,33 +2,32 @@
* 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
*/
-#include "libavcodec/bitstream.h"
+#include "libavcodec/get_bits.h"
#include "libavcodec/unary.h"
-
#include "apetag.h"
#include "avformat.h"
#include "internal.h"
#include "avio_internal.h"
/// Two-byte MPC tag
-#define MKMPCTAG(a, b) (a | (b << 8))
+#define MKMPCTAG(a, b) ((a) | ((b) << 8))
#define TAG_MPCK MKTAG('M','P','C','K')
@@ -56,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;
@@ -76,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)
@@ -92,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)
@@ -107,17 +106,17 @@ static int mpc8_probe(AVProbeData *p)
return 0;
}
-static inline int64_t gb_get_v(BitstreamContext *bc)
+static inline int64_t gb_get_v(GetBitContext *gb)
{
- int64_t v = 0;
+ uint64_t v = 0;
int bits = 0;
- while (bitstream_read_bit(bc) && bits < 64 - 7) {
+ while(get_bits1(gb) && bits < 64-7){
v <<= 7;
- v |= bitstream_read(bc, 7);
+ v |= get_bits(gb, 7);
bits += 7;
}
v <<= 7;
- v |= bitstream_read(bc, 7);
+ v |= get_bits(gb, 7);
return v;
}
@@ -137,8 +136,8 @@ 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;
- BitstreamContext bc;
+ int i, t, seekd, ret;
+ GetBitContext gb;
if (s->nb_streams == 0) {
av_log(s, AV_LOG_ERROR, "No stream added before parsing seek table\n");
@@ -151,28 +150,35 @@ 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 + AV_INPUT_BUFFER_PADDING_SIZE)))
return;
- avio_read(s->pb, buf, size);
- bitstream_init8(&bc, buf, size);
- size = gb_get_v(&bc);
+ 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, AV_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){
av_log(s, AV_LOG_ERROR, "Seek table is too big\n");
return;
}
- seekd = bitstream_read(&bc, 4);
+ seekd = get_bits(&gb, 4);
for(i = 0; i < 2; i++){
- pos = gb_get_v(&bc) + c->header_pos;
+ pos = gb_get_v(&gb) + c->header_pos;
ppos[1 - i] = pos;
av_add_index_entry(s->streams[0], pos, i, 0, 0, AVINDEX_KEYFRAME);
}
for(; i < size; i++){
- t = get_unary(&bc, 1, 33) << 12;
- t += bitstream_read(&bc, 12);
+ t = get_unary(&gb, 1, 33) << 12;
+ t += get_bits(&gb, 12);
if(t & 1)
t = -(t & ~1);
pos = (t >> 1) + ppos[0]*2 - ppos[1];
@@ -214,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);
@@ -242,9 +252,8 @@ static int mpc8_read_header(AVFormatContext *s)
st->codecpar->codec_id = AV_CODEC_ID_MUSEPACK8;
st->codecpar->bits_per_coded_sample = 16;
- st->codecpar->extradata_size = 2;
- st->codecpar->extradata = av_mallocz(st->codecpar->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
- avio_read(pb, st->codecpar->extradata, st->codecpar->extradata_size);
+ if (ff_get_extradata(s, st->codecpar, pb, 2) < 0)
+ return AVERROR(ENOMEM);
st->codecpar->channels = (st->codecpar->extradata[1] >> 4) + 1;
st->codecpar->sample_rate = mpc8_rate[st->codecpar->extradata[0] >> 5];
@@ -252,6 +261,8 @@ static int mpc8_read_header(AVFormatContext *s)
st->start_time = 0;
st->duration = c->samples / (1152 << (st->codecpar->extradata[1]&3)*2);
size -= avio_tell(pb) - pos;
+ if (size > 0)
+ avio_skip(pb, size);
if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
int64_t pos = avio_tell(s->pb);
@@ -268,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 0898353..50fe7a1 100644
--- a/libavformat/mpeg.c
+++ b/libavformat/mpeg.c
@@ -2,20 +2,20 @@
* MPEG-1/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,12 +23,20 @@
#include "internal.h"
#include "mpeg.h"
+#if CONFIG_VOBSUB_DEMUXER
+# include "subtitles.h"
+# include "libavutil/bprint.h"
+# include "libavutil/opt.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 &&
@@ -61,79 +69,88 @@ 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 > 6 + 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;
}
typedef struct MpegDemuxContext {
+ AVClass *class;
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];
+ char *sub_name;
+#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] = { 0 };
+ 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;
@@ -158,7 +175,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--;
@@ -194,7 +211,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) {
@@ -234,10 +253,10 @@ 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);
+ return FFERROR_REDO;
}
if (startcode == PACK_START_CODE)
@@ -249,21 +268,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);
@@ -273,7 +353,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;
@@ -281,6 +363,8 @@ redo:
len = avio_rb16(s->pb);
pts =
dts = AV_NOPTS_VALUE;
+ if (startcode != PRIVATE_STREAM_2)
+ {
/* stuffing */
for (;;) {
if (len < 1)
@@ -355,22 +439,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;
@@ -398,20 +471,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 */
@@ -422,7 +505,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;
@@ -445,9 +527,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];
@@ -457,11 +539,25 @@ 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 if (m->imkh_cctv && startcode == 0x1c0 && len > 80) {
+ codec_id = AV_CODEC_ID_PCM_ALAW;
+ request_probe = 50;
+ } else {
+ codec_id = AV_CODEC_ID_MP2;
+ if (m->imkh_cctv)
+ request_probe = 25;
+ }
} else if (startcode >= 0x80 && startcode <= 0x87) {
type = AVMEDIA_TYPE_AUDIO;
codec_id = AV_CODEC_ID_AC3;
@@ -472,7 +568,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 || startcode == 0xa1) {
+ 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;
@@ -486,23 +586,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 */
@@ -516,18 +599,35 @@ skip:
st->id = startcode;
st->codecpar->codec_type = type;
st->codecpar->codec_id = codec_id;
+ if ( st->codecpar->codec_id == AV_CODEC_ID_PCM_MULAW
+ || st->codecpar->codec_id == AV_CODEC_ID_PCM_ALAW) {
+ st->codecpar->channels = 1;
+ st->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
+ st->codecpar->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 (st->codecpar->codec_id == AV_CODEC_ID_MLP) {
+ if (len < 6)
+ goto skip;
+ avio_skip(s->pb, 6);
+ len -=6;
+ }
+ }
ret = av_get_packet(s->pb, pkt, len);
pkt->pts = pts;
pkt->dts = dts;
pkt->pos = dummy_pos;
pkt->stream_index = st->index;
- av_log(s, AV_LOG_TRACE, "%d: pts=%0.3f dts=%0.3f size=%d\n",
+
+ if (s->debug & FF_FDEBUG_TS)
+ av_log(s, AV_LOG_TRACE, "%d: pts=%0.3f dts=%0.3f size=%d\n",
pkt->stream_index, pkt->pts / 90000.0, pkt->dts / 90000.0,
pkt->size);
@@ -547,7 +647,8 @@ static int64_t mpegps_read_dts(AVFormatContext *s, int stream_index,
for (;;) {
len = mpegps_read_pes_header(s, &pos, &startcode, &pts, &dts);
if (len < 0) {
- av_log(s, AV_LOG_TRACE, "none (ret=%d)\n", len);
+ if (s->debug & FF_FDEBUG_TS)
+ av_log(s, AV_LOG_TRACE, "none (ret=%d)\n", len);
return AV_NOPTS_VALUE;
}
if (startcode == s->streams[stream_index]->id &&
@@ -556,7 +657,8 @@ static int64_t mpegps_read_dts(AVFormatContext *s, int stream_index,
}
avio_skip(s->pb, len);
}
- av_log(s, AV_LOG_TRACE, "pos=0x%"PRIx64" dts=0x%"PRIx64" %0.3f\n",
+ if (s->debug & FF_FDEBUG_TS)
+ av_log(s, AV_LOG_TRACE, "pos=0x%"PRIx64" dts=0x%"PRIx64" %0.3f\n",
pos, dts, dts / 90000.0);
*ppos = pos;
return dts;
@@ -572,3 +674,365 @@ 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;
+ size_t fname_len;
+ char *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;
+
+ if (!vobsub->sub_name) {
+ char *ext;
+ vobsub->sub_name = av_strdup(s->filename);
+ if (!vobsub->sub_name) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+
+ fname_len = strlen(vobsub->sub_name);
+ ext = vobsub->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, vobsub->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_whiteblacklists(vobsub->sub_ctx, s)) < 0)
+ goto end;
+
+ ret = avformat_open_input(&vobsub->sub_ctx, vobsub->sub_name, iformat, NULL);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "Unable to open %s as MPEG subtitles\n", vobsub->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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->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;
+ vobsub->q[i].keep_duplicates = 1;
+ ff_subtitles_queue_finalize(s, &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->codecpar->extradata = av_strdup(header_str);
+ sub_st->codecpar->extradata_size = header.len;
+ }
+ av_free(header_str);
+
+end:
+ 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 = { 0 };
+
+ 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) != s->streams[idx_pkt.stream_index]->id)
+ 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_packet_unref(&idx_pkt);
+ return 0;
+
+fail:
+ av_packet_unref(pkt);
+ av_packet_unref(&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;
+}
+
+static const AVOption options[] = {
+ { "sub_name", "URI for .sub file", offsetof(MpegDemuxContext, sub_name), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_DECODING_PARAM },
+ { NULL }
+};
+
+static const AVClass vobsub_demuxer_class = {
+ .class_name = "vobsub",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+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",
+ .priv_class = &vobsub_demuxer_class,
+};
+#endif
diff --git a/libavformat/mpeg.h b/libavformat/mpeg.h
index 7f7f830..617e36c 100644
--- a/libavformat/mpeg.h
+++ b/libavformat/mpeg.h
@@ -2,20 +2,20 @@
* MPEG-1/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 1507e0e..c77c3df 100644
--- a/libavformat/mpegenc.c
+++ b/libavformat/mpegenc.c
@@ -2,20 +2,20 @@
* MPEG-1/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;
@@ -79,7 +77,7 @@ typedef struct MpegMuxContext {
int is_dvd;
int64_t last_scr; /* current system clock */
- double vcd_padding_bitrate; // FIXME floats
+ int64_t vcd_padding_bitrate_num;
int64_t vcd_padding_bytes_written;
int preload;
@@ -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,10 +321,10 @@ 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 = AV_TIME_BASE*7/10;
s->vcd_padding_bytes_written = 0;
- s->vcd_padding_bitrate = 0;
+ s->vcd_padding_bitrate_num = 0;
s->audio_bound = 0;
s->video_bound = 0;
@@ -353,6 +350,15 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx)
switch (st->codecpar->codec_type) {
case AVMEDIA_TYPE_AUDIO:
+ if (!s->is_mpeg2 &&
+ (st->codecpar->codec_id == AV_CODEC_ID_AC3 ||
+ st->codecpar->codec_id == AV_CODEC_ID_DTS ||
+ st->codecpar->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->codecpar->codec_id));
if (st->codecpar->codec_id == AV_CODEC_ID_AC3) {
stream->id = ac3_id++;
} else if (st->codecpar->codec_id == AV_CODEC_ID_DTS) {
@@ -391,10 +397,16 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx)
stream->max_buffer_size = 6 * 1024 + props->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:
@@ -402,7 +414,9 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx)
stream->max_buffer_size = 16 * 1024;
break;
default:
- return -1;
+ av_log(ctx, AV_LOG_ERROR, "Invalid media type %s for output stream #%d\n",
+ av_get_media_type_string(st->codecpar->codec_type), i);
+ return AVERROR(EINVAL);
}
stream->fifo = av_fifo_alloc(16);
if (!stream->fifo)
@@ -434,16 +448,22 @@ 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) {
- double overhead_rate;
+ int64_t overhead_rate;
/* The VCD standard mandates that the mux_rate field is 3528
* (see standard p. IV-6).
@@ -463,12 +483,12 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx)
/* Add the header overhead to the data rate.
* 2279 data bytes per audio pack, 2294 data bytes per video pack */
- overhead_rate = ((audio_bitrate / 8.0) / 2279) * (2324 - 2279);
- overhead_rate += ((video_bitrate / 8.0) / 2294) * (2324 - 2294);
- overhead_rate *= 8;
+ overhead_rate = audio_bitrate * 2294LL * (2324 - 2279);
+ overhead_rate += video_bitrate * 2279LL * (2324 - 2294);
/* Add padding so that the full bitrate is 2324*75 bytes/sec */
- s->vcd_padding_bitrate = 2324 * 75 * 8 - (bitrate + overhead_rate);
+ s->vcd_padding_bitrate_num = (2324LL * 75 * 8 - bitrate) * 2279 * 2294 - overhead_rate;
+#define VCD_PADDING_BITRATE_DEN (2279 * 2294)
}
if (s->is_vcd || s->is_mpeg2)
@@ -498,12 +518,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);
}
@@ -521,12 +541,12 @@ static int get_vcd_padding_size(AVFormatContext *ctx, int64_t pts)
MpegMuxContext *s = ctx->priv_data;
int pad_bytes = 0;
- if (s->vcd_padding_bitrate > 0 && pts != AV_NOPTS_VALUE) {
+ if (s->vcd_padding_bitrate_num > 0 && pts != AV_NOPTS_VALUE) {
int64_t full_pad_bytes;
// FIXME: this is wrong
full_pad_bytes =
- (int64_t)((s->vcd_padding_bitrate * (pts / 90000.0)) / 8.0);
+ av_rescale(s->vcd_padding_bitrate_num, pts, 90000LL * 8 * VCD_PADDING_BITRATE_DEN);
pad_bytes = (int)(full_pad_bytes - s->vcd_padding_bytes_written);
if (pad_bytes < 0)
@@ -857,7 +877,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);
@@ -925,7 +945,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;
}
@@ -947,6 +967,7 @@ static int output_packet(AVFormatContext *ctx, int flush)
int best_i = -1;
int best_score = INT_MIN;
int ignore_constraints = 0;
+ int ignore_delay = 0;
int64_t scr = s->last_scr;
PacketDesc *timestamp_packet;
const int64_t max_delay = av_rescale(ctx->max_delay, 90000, AV_TIME_BASE);
@@ -957,7 +978,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,
@@ -967,14 +988,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)
+ if (next_pkt && next_pkt->dts - scr > max_delay && !ignore_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;
@@ -984,6 +1007,7 @@ retry:
if (best_i < 0) {
int64_t best_dts = INT64_MAX;
+ int has_premux = 0;
for (i = 0; i < ctx->nb_streams; i++) {
AVStream *st = ctx->streams[i];
@@ -991,32 +1015,40 @@ retry:
PacketDesc *pkt_desc = stream->predecode_packet;
if (pkt_desc && pkt_desc->dts < best_dts)
best_dts = pkt_desc->dts;
+ has_premux |= !!stream->premux_packet;
}
- av_log(ctx, AV_LOG_TRACE, "bumping scr, scr:%f, dts:%f\n",
- scr / 90000.0, best_dts / 90000.0);
- if (best_dts == INT64_MAX)
- return 0;
+ if (best_dts < INT64_MAX) {
+ av_log(ctx, AV_LOG_TRACE, "bumping scr, scr:%f, dts:%f\n",
+ scr / 90000.0, best_dts / 90000.0);
- if (scr >= best_dts + 1 && !ignore_constraints) {
+ if (scr >= best_dts + 1 && !ignore_constraints) {
+ av_log(ctx, AV_LOG_ERROR,
+ "packet too large, ignoring buffer limits to mux it\n");
+ ignore_constraints = 1;
+ }
+ scr = FFMAX(best_dts + 1, scr);
+ if (remove_decoded_packets(ctx, scr) < 0)
+ return -1;
+ } else if (has_premux && flush) {
av_log(ctx, AV_LOG_ERROR,
- "packet too large, ignoring buffer limits to mux it\n");
+ "delay too large, ignoring ...\n");
+ ignore_delay = 1;
ignore_constraints = 1;
- }
- scr = FFMAX(best_dts + 1, scr);
- if (remove_decoded_packets(ctx, scr) < 0)
- return -1;
+ } else
+ return 0;
+
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) {
@@ -1034,7 +1066,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);
}
@@ -1061,8 +1093,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;
@@ -1089,14 +1123,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_log(ctx, AV_LOG_TRACE, "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);
@@ -1104,6 +1146,8 @@ static int mpeg_mux_write_packet(AVFormatContext *ctx, AVPacket *pkt)
stream->next_packet = &stream->premux_packet;
*stream->next_packet =
pkt_desc = av_mallocz(sizeof(PacketDesc));
+ if (!pkt_desc)
+ return AVERROR(ENOMEM);
pkt_desc->pts = pts;
pkt_desc->dts = dts;
pkt_desc->unwritten_size =
@@ -1157,8 +1201,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;
}
@@ -1166,7 +1210,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 dc9339f..53cbcfb 100644
--- a/libavformat/mpegts.c
+++ b/libavformat/mpegts.c
@@ -2,35 +2,35 @@
* MPEG-2 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
*/
#include "libavutil/buffer.h"
#include "libavutil/crc.h"
+#include "libavutil/internal.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
#include "libavutil/dict.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
-
-#include "libavcodec/bitstream.h"
+#include "libavutil/avassert.h"
#include "libavcodec/bytestream.h"
+#include "libavcodec/get_bits.h"
#include "libavcodec/opus.h"
-
#include "avformat.h"
#include "mpegts.h"
#include "internal.h"
@@ -56,6 +56,7 @@
enum MpegTSFilterType {
MPEGTS_PES,
MPEGTS_SECTION,
+ MPEGTS_PCR,
};
typedef struct MpegTSFilter MpegTSFilter;
@@ -76,6 +77,8 @@ typedef struct MpegTSSectionFilter {
int section_index;
int section_h_size;
int last_ver;
+ unsigned crc;
+ unsigned last_crc;
uint8_t *section_buf;
unsigned int check_crc : 1;
unsigned int end_of_section_reached : 1;
@@ -87,6 +90,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;
@@ -99,6 +103,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 {
@@ -108,9 +115,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;
@@ -118,6 +127,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 */
@@ -129,6 +141,11 @@ struct MpegTSContext {
/** to detect seek */
int64_t last_pos;
+ int skip_changes;
+ int skip_clear;
+
+ int scan_all_pmts;
+
int resync_size;
/******************************************/
@@ -138,15 +155,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 synchronization.", offsetof(MpegTSContext, resync_size), AV_OPT_TYPE_INT, { .i64 = MAX_RESYNC_SIZE}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }
+ { "resync_size", "set 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_BOOL,
+ {.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_BOOL,
+ { .i64 = -1}, -1, 1, AV_OPT_FLAG_DECODING_PARAM },
+ {"skip_changes", "skip changing / adding streams / programs", offsetof(MpegTSContext, skip_changes), AV_OPT_TYPE_BOOL,
+ {.i64 = 0}, 0, 1, 0 },
+ {"skip_clear", "skip clearing programs", offsetof(MpegTSContext, skip_clear), AV_OPT_TYPE_BOOL,
+ {.i64 = 0}, 0, 1, 0 },
{ NULL },
};
@@ -159,10 +188,10 @@ static const AVClass mpegts_class = {
static const AVOption raw_options[] = {
MPEGTS_OPTIONS,
- { "compute_pcr", "Compute exact PCR for each transport stream packet.",
- offsetof(MpegTSContext, mpeg2ts_compute_pcr), AV_OPT_TYPE_INT,
+ { "compute_pcr", "compute exact PCR for each transport stream packet",
+ offsetof(MpegTSContext, mpeg2ts_compute_pcr), AV_OPT_TYPE_BOOL,
{ .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
- { "ts_packetsize", "Output option carrying the raw packet size.",
+ { "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 },
@@ -206,6 +235,7 @@ typedef struct PESContext {
int total_size;
int pes_header_size;
int extended_stream_id;
+ uint8_t stream_id;
int64_t pts, dts;
int64_t ts_packet_pos; /**< position of first TS packet of this PES packet */
uint8_t header[MAX_PES_HEADER_SIZE];
@@ -215,13 +245,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)
@@ -240,28 +299,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
@@ -339,24 +418,35 @@ 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 (tss->section_h_size >= 4)
+ tss->crc = AV_RB32(tss->section_buf + tss->section_h_size - 4);
+
+ 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);
+ if (crc_valid != 1)
+ tss->last_ver = -1;
+ }
}
}
-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_log(ts->stream, AV_LOG_TRACE, "Filter: pid=0x%x\n", pid);
+ av_log(ts->stream, AV_LOG_TRACE, "Filter: pid=0x%x type=%d\n", pid, type);
if (pid >= NB_PID_MAX || ts->pids[pid])
return NULL;
@@ -365,11 +455,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;
@@ -391,24 +496,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;
@@ -430,33 +531,32 @@ static void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter)
ts->pids[pid] = NULL;
}
-static int analyze(const uint8_t *buf, int size, int packet_size, int *index,
+static int analyze(const uint8_t *buf, int size, int packet_size,
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));
-
- for (x = i = 0; i < size - 3; i++) {
- if (buf[i] == 0x47 &&
- (!probe || (!(buf[i + 1] & 0x80) && (buf[i + 3] & 0x30)))) {
- stat[x]++;
- if (stat[x] > best_score) {
- best_score = stat[x];
- if (index)
- *index = x;
+ memset(stat, 0, packet_size * sizeof(*stat));
+
+ for (i = 0; i < size - 3; i++) {
+ if (buf[i] == 0x47) {
+ int pid = AV_RB16(buf+1) & 0x1FFF;
+ int asc = buf[i + 3] & 0x30;
+ if (!probe || pid == 0x1FFF || asc) {
+ int x = i % packet_size;
+ stat[x]++;
+ stat_all++;
+ if (stat[x] > best_score) {
+ best_score = stat[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 */
@@ -467,9 +567,9 @@ static int get_packet_size(const uint8_t *buf, int size)
if (size < (TS_FEC_PACKET_SIZE * 5 + 1))
return AVERROR_INVALIDDATA;
- score = analyze(buf, size, TS_PACKET_SIZE, NULL, 0);
- dvhs_score = analyze(buf, size, TS_DVHS_PACKET_SIZE, NULL, 0);
- fec_score = analyze(buf, size, TS_FEC_PACKET_SIZE, NULL, 0);
+ score = analyze(buf, size, TS_PACKET_SIZE, 0);
+ dvhs_score = analyze(buf, size, TS_DVHS_PACKET_SIZE, 0);
+ fec_score = analyze(buf, size, TS_FEC_PACKET_SIZE, 0);
av_log(NULL, AV_LOG_TRACE, "score: %d, dvhs_score: %d, fec_score: %d \n",
score, dvhs_score, fec_score);
@@ -491,6 +591,17 @@ typedef struct SectionHeader {
uint8_t last_sec_num;
} SectionHeader;
+static int skip_identical(const SectionHeader *h, MpegTSSectionFilter *tssf)
+{
+ if (h->version == tssf->last_ver && tssf->last_crc == tssf->crc)
+ return 1;
+
+ tssf->last_ver = h->version;
+ tssf->last_crc = tssf->crc;
+
+ return 0;
+}
+
static inline int get8(const uint8_t **pp, const uint8_t *p_end)
{
const uint8_t *p;
@@ -510,7 +621,7 @@ static inline int get16(const uint8_t **pp, const uint8_t *p_end)
int c;
p = *pp;
- if ((p + 1) >= p_end)
+ if (1 >= p_end - p)
return AVERROR_INVALIDDATA;
c = AV_RB16(p);
p += 2;
@@ -529,7 +640,7 @@ static char *getstr8(const uint8_t **pp, const uint8_t *p_end)
len = get8(&p, p_end);
if (len < 0)
return NULL;
- if ((p + len) > p_end)
+ if (len > p_end - p)
return NULL;
str = av_malloc(len + 1);
if (!str)
@@ -583,8 +694,14 @@ 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 },
+ { 0x1c, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
+ { 0x20, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
{ 0x21, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_JPEG2000 },
{ 0x24, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC },
{ 0x42, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_CAVS },
@@ -601,7 +718,16 @@ 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 },
+ { 0x92, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_HDMV_TEXT_SUBTITLE },
+ { 0 },
+};
+
+/* SCTE types */
+static const StreamType SCTE_types[] = {
+ { 0x86, AVMEDIA_TYPE_DATA, AV_CODEC_ID_SCTE_35 },
{ 0 },
};
@@ -619,12 +745,21 @@ static const StreamType REGD_types[] = {
{ MKTAG('D', 'T', 'S', '1'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS },
{ MKTAG('D', 'T', 'S', '2'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS },
{ MKTAG('D', 'T', 'S', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS },
+ { MKTAG('E', 'A', 'C', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3 },
{ 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('I', 'D', '3', ' '), AVMEDIA_TYPE_DATA, AV_CODEC_ID_TIMED_ID3 },
{ 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 */
@@ -641,8 +776,13 @@ static void mpegts_find_stream_type(AVStream *st,
{
for (; types->stream_type; types++)
if (stream_type == types->stream_type) {
- st->codecpar->codec_type = types->codec_type;
- st->codecpar->codec_id = types->codec_id;
+ if (st->codecpar->codec_type != types->codec_type ||
+ st->codecpar->codec_id != types->codec_id) {
+ st->codecpar->codec_type = types->codec_type;
+ st->codecpar->codec_id = types->codec_id;
+ st->internal->need_context_update = 1;
+ }
+ st->request_probe = 0;
return;
}
}
@@ -650,6 +790,15 @@ 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->codecpar->codec_type;
+ int old_codec_id = st->codecpar->codec_id;
+ int old_codec_tag = st->codecpar->codec_tag;
+
+ if (avcodec_is_open(st->internal->avctx)) {
+ av_log(pes->stream, AV_LOG_DEBUG, "cannot set stream info, internal codec is open\n");
+ return 0;
+ }
+
avpriv_set_pts_info(st, 33, 1, 90000);
st->priv_data = pes;
st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
@@ -665,7 +814,10 @@ static int mpegts_set_stream_info(AVStream *st, PESContext *pes,
st->codecpar->codec_tag = pes->stream_type;
mpegts_find_stream_type(st, pes->stream_type, ISO_types);
- if (prog_reg_desc == AV_RL32("HDMV") &&
+ if (pes->stream_type == 4 || pes->stream_type == 0x0f)
+ st->request_probe = 50;
+ if ((prog_reg_desc == AV_RL32("HDMV") ||
+ prog_reg_desc == AV_RL32("HDPR")) &&
st->codecpar->codec_id == AV_CODEC_ID_NONE) {
mpegts_find_stream_type(st, pes->stream_type, HDMV_types);
if (pes->stream_type == 0x83) {
@@ -695,12 +847,48 @@ static int mpegts_set_stream_info(AVStream *st, PESContext *pes,
}
if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
mpegts_find_stream_type(st, pes->stream_type, MISC_types);
+ if (st->codecpar->codec_id == AV_CODEC_ID_NONE) {
+ st->codecpar->codec_id = old_codec_id;
+ st->codecpar->codec_type = old_codec_type;
+ }
+ if ((st->codecpar->codec_id == AV_CODEC_ID_NONE ||
+ (st->request_probe > 0 && st->request_probe < AVPROBE_SCORE_STREAM_RETRY / 5)) &&
+ st->probe_packets > 0 &&
+ stream_type == STREAM_TYPE_PRIVATE_DATA) {
+ st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
+ st->codecpar->codec_id = AV_CODEC_ID_BIN_DATA;
+ st->request_probe = AVPROBE_SCORE_STREAM_RETRY / 5;
+ }
+
+ /* queue a context update if properties changed */
+ if (old_codec_type != st->codecpar->codec_type ||
+ old_codec_id != st->codecpar->codec_id ||
+ old_codec_tag != st->codecpar->codec_tag)
+ st->internal->need_context_update = 1;
return 0;
}
-static void new_pes_packet(PESContext *pes, AVPacket *pkt)
+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_data_packet(const uint8_t *buffer, int len, AVPacket *pkt)
+{
+ av_init_packet(pkt);
+ pkt->data = (uint8_t *)buffer;
+ pkt->size = len;
+}
+
+static int new_pes_packet(PESContext *pes, AVPacket *pkt)
{
+ char *sd;
+
av_init_packet(pkt);
pkt->buf = pes->buffer;
@@ -726,67 +914,82 @@ 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);
+
+ sd = av_packet_new_side_data(pkt, AV_PKT_DATA_MPEGTS_STREAM_ID, 1);
+ if (!sd)
+ return AVERROR(ENOMEM);
+ *sd = pes->stream_id;
+
+ return 0;
+}
+
+static uint64_t get_ts64(GetBitContext *gb, int bits)
+{
+ if (get_bits_left(gb) < bits)
+ return AV_NOPTS_VALUE;
+ return get_bits64(gb, bits);
}
static int read_sl_header(PESContext *pes, SLConfigDescr *sl,
const uint8_t *buf, int buf_size)
{
- BitstreamContext bc;
+ GetBitContext gb;
int au_start_flag = 0, au_end_flag = 0, ocr_flag = 0, idle_flag = 0;
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;
- bitstream_init8(&bc, buf, buf_size);
+ uint8_t buf_padded[128 + AV_INPUT_BUFFER_PADDING_SIZE];
+ int buf_padded_size = FFMIN(buf_size, sizeof(buf_padded) - AV_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 = bitstream_read_bit(&bc);
+ au_start_flag = get_bits1(&gb);
if (sl->use_au_end)
- au_end_flag = bitstream_read_bit(&bc);
+ au_end_flag = get_bits1(&gb);
if (!sl->use_au_start && !sl->use_au_end)
au_start_flag = au_end_flag = 1;
if (sl->ocr_len > 0)
- ocr_flag = bitstream_read_bit(&bc);
+ ocr_flag = get_bits1(&gb);
if (sl->use_idle)
- idle_flag = bitstream_read_bit(&bc);
+ idle_flag = get_bits1(&gb);
if (sl->use_padding)
- padding_flag = bitstream_read_bit(&bc);
+ padding_flag = get_bits1(&gb);
if (padding_flag)
- padding_bits = bitstream_read(&bc, 3);
+ padding_bits = get_bits(&gb, 3);
if (!idle_flag && (!padding_flag || padding_bits != 0)) {
if (sl->packet_seq_num_len)
- bitstream_skip(&bc, sl->packet_seq_num_len);
+ skip_bits_long(&gb, sl->packet_seq_num_len);
if (sl->degr_prior_len)
- if (bitstream_read_bit(&bc))
- bitstream_skip(&bc, sl->degr_prior_len);
+ if (get_bits1(&gb))
+ skip_bits(&gb, sl->degr_prior_len);
if (ocr_flag)
- bitstream_skip(&bc, sl->ocr_len);
+ skip_bits_long(&gb, sl->ocr_len);
if (au_start_flag) {
if (sl->use_rand_acc_pt)
- bitstream_read_bit(&bc);
+ get_bits1(&gb);
if (sl->au_seq_num_len > 0)
- bitstream_skip(&bc, sl->au_seq_num_len);
+ skip_bits_long(&gb, sl->au_seq_num_len);
if (sl->use_timestamps) {
- dts_flag = bitstream_read_bit(&bc);
- cts_flag = bitstream_read_bit(&bc);
+ dts_flag = get_bits1(&gb);
+ cts_flag = get_bits1(&gb);
}
}
if (sl->inst_bitrate_len)
- inst_bitrate_flag = bitstream_read_bit(&bc);
+ inst_bitrate_flag = get_bits1(&gb);
if (dts_flag == 1)
- dts = bitstream_read_63(&bc, sl->timestamp_len);
+ dts = get_ts64(&gb, sl->timestamp_len);
if (cts_flag == 1)
- cts = bitstream_read_63(&bc, sl->timestamp_len);
+ cts = get_ts64(&gb, sl->timestamp_len);
if (sl->au_len > 0)
- bitstream_skip(&bc, sl->au_len);
+ skip_bits_long(&gb, sl->au_len);
if (inst_bitrate_flag)
- bitstream_skip(&bc, sl->inst_bitrate_len);
+ skip_bits_long(&gb, sl->inst_bitrate_len);
}
if (dts != AV_NOPTS_VALUE)
@@ -797,7 +1000,7 @@ static int read_sl_header(PESContext *pes, SLConfigDescr *sl,
if (sl->timestamp_len && sl->timestamp_res)
avpriv_set_pts_info(pes->st, sl->timestamp_len, 1, sl->timestamp_res);
- return (bitstream_tell(&bc) + 7) >> 3;
+ return (get_bits_count(&gb) + 7) >> 3;
}
/* return non zero if a packet could be constructed */
@@ -808,18 +1011,21 @@ static int mpegts_push_data(MpegTSFilter *filter,
PESContext *pes = filter->u.pes_filter.opaque;
MpegTSContext *ts = pes->ts;
const uint8_t *p;
- int len, code;
+ int ret, len, code;
if (!ts->pkt)
return 0;
if (is_start) {
if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) {
- new_pes_packet(pes, ts->pkt);
+ ret = new_pes_packet(pes, ts->pkt);
+ if (ret < 0)
+ return ret;
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;
@@ -842,6 +1048,7 @@ static int mpegts_push_data(MpegTSFilter *filter,
code = pes->header[3] | 0x100;
av_log(pes->stream, AV_LOG_TRACE, "pid=%x pes_code=%#x\n", pes->pid,
code);
+ pes->stream_id = pes->header[3];
if ((pes->st && pes->st->discard == AVDISCARD_ALL &&
(!pes->sub_st ||
@@ -851,6 +1058,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);
@@ -875,14 +1085,15 @@ 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->codecpar->codec_id == AV_CODEC_ID_NONE) {
+ if (pes->st->codecpar->codec_id == AV_CODEC_ID_NONE && !pes->st->request_probe) {
av_log(pes->stream, AV_LOG_TRACE,
"pid=%x stream_type=%x probing\n",
pes->pid,
pes->stream_type);
- pes->st->codecpar->codec_id = AV_CODEC_ID_PROBE;
+ pes->st->request_probe = 1;
}
} else {
+ pes->pes_header_size = 6;
pes->state = MPEGTS_PAYLOAD;
pes->data_index = 0;
}
@@ -964,13 +1175,66 @@ 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->codecpar->codec_id == AV_CODEC_ID_DVB_TELETEXT
+ || pes->st->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE)
+ ) {
+ 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->codecpar->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->st->codecpar->codec_id == AV_CODEC_ID_DVB_TELETEXT &&
+ pes->dts > pcr + 3654 + 9000) {
+ pes->pts = pes->dts = pcr + 3654 + 9000;
+ } else if (pes->st->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE &&
+ pes->dts > pcr + 10*90000) { //10sec
+ 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);
+ ret = new_pes_packet(pes, ts->pkt);
+ if (ret < 0)
+ return ret;
pes->total_size = MAX_PES_PAYLOAD;
pes->buffer = av_buffer_alloc(pes->total_size +
AV_INPUT_BUFFER_PADDING_SIZE);
@@ -985,18 +1249,20 @@ 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;
+ ret = new_pes_packet(pes, ts->pkt);
+ if (ret < 0)
+ return ret;
+ }
}
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;
@@ -1040,6 +1306,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,
@@ -1153,6 +1420,14 @@ static int parse_MP4SLDescrTag(MP4DescrParseContext *d, int64_t off, int len)
if (!descr)
return AVERROR_INVALIDDATA;
+#define R8_CHECK_CLIP_MAX(dst, maxv) do { \
+ descr->sl.dst = avio_r8(&d->pb); \
+ if (descr->sl.dst > maxv) { \
+ descr->sl.dst = maxv; \
+ return AVERROR_INVALIDDATA; \
+ } \
+} while (0)
+
predefined = avio_r8(&d->pb);
if (!predefined) {
int lengths;
@@ -1165,21 +1440,17 @@ static int parse_MP4SLDescrTag(MP4DescrParseContext *d, int64_t off, int len)
descr->sl.use_idle = !!(flags & 0x02);
descr->sl.timestamp_res = avio_rb32(&d->pb);
avio_rb32(&d->pb);
- descr->sl.timestamp_len = avio_r8(&d->pb);
- descr->sl.ocr_len = avio_r8(&d->pb);
- descr->sl.au_len = avio_r8(&d->pb);
+ R8_CHECK_CLIP_MAX(timestamp_len, 63);
+ R8_CHECK_CLIP_MAX(ocr_len, 63);
+ R8_CHECK_CLIP_MAX(au_len, 31);
descr->sl.inst_bitrate_len = avio_r8(&d->pb);
lengths = avio_rb16(&d->pb);
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;
- if (descr->sl.timestamp_len >= 64 ||
- descr->sl.ocr_len >= 64 ||
- descr->sl.au_len >= 32) {
- return AVERROR_INVALIDDATA;
- }
- } else {
+ } else if (!d->predefined_SLConfigDescriptor_seen){
avpriv_report_missing_feature(d->s, "Predefined SLConfigDescriptor");
+ d->predefined_SLConfigDescriptor_seen = 1;
}
return 0;
}
@@ -1288,9 +1559,8 @@ static void m4sl_cb(MpegTSFilter *filter, const uint8_t *section,
return;
if (h.tid != M4OD_TID)
return;
- if (h.version == tssf->last_ver)
+ if (skip_identical(&h, tssf))
return;
- tssf->last_ver = h.version;
mp4_read_od(s, p, (unsigned) (p_end - p), mp4_descr, &mp4_descr_count,
MAX_MP4_DESCR_COUNT);
@@ -1303,7 +1573,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;
}
@@ -1326,12 +1596,35 @@ static void m4sl_cb(MpegTSFilter *filter, const uint8_t *section,
st->need_parsing = 0;
st->codecpar->codec_type = avcodec_get_type(st->codecpar->codec_id);
+ st->internal->need_context_update = 1;
}
}
for (i = 0; i < mp4_descr_count; i++)
av_free(mp4_descr[i].dec_config_descr);
}
+static void scte_data_cb(MpegTSFilter *filter, const uint8_t *section,
+ int section_len)
+{
+ AVProgram *prg = NULL;
+ MpegTSContext *ts = filter->u.section_filter.opaque;
+
+ int idx = ff_find_stream_index(ts->stream, filter->pid);
+ if (idx < 0)
+ return;
+
+ new_data_packet(section, section_len, ts->pkt);
+ ts->pkt->stream_index = idx;
+ prg = av_find_program_from_stream(ts->stream, NULL, idx);
+ if (prg && prg->pcr_pid != -1 && prg->discard != AVDISCARD_ALL) {
+ MpegTSFilter *f = ts->pids[prg->pcr_pid];
+ if (f && f->last_pcr != -1)
+ ts->pkt->pts = ts->pkt->dts = f->last_pcr/300;
+ }
+ ts->stop_parse = 1;
+
+}
+
static const uint8_t opus_coupled_stream_cnt[9] = {
1, 0, 1, 1, 2, 2, 2, 3, 3
};
@@ -1373,7 +1666,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
av_log(fc, AV_LOG_TRACE, "tag: 0x%02x len=%d\n", desc_tag, desc_len);
- if (st->codecpar->codec_id == AV_CODEC_ID_NONE &&
+ if ((st->codecpar->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);
@@ -1393,8 +1686,10 @@ 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->codecpar->codec_id == AV_CODEC_ID_AAC &&
- st->codecpar->extradata_size > 0)
+ st->codecpar->extradata_size > 0) {
st->need_parsing = 0;
+ st->internal->need_context_update = 1;
+ }
if (st->codecpar->codec_id == AV_CODEC_ID_MPEG4SYSTEMS)
mpegts_open_section_filter(ts, pid, m4sl_cb, ts, 1);
}
@@ -1403,7 +1698,9 @@ 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->codecpar->codec_id == AV_CODEC_ID_AAC_LATM &&
+ (st->codecpar->codec_id == AV_CODEC_ID_AAC_LATM ||
+ (st->request_probe == 0 && st->codecpar->codec_id == AV_CODEC_ID_NONE) ||
+ 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,
@@ -1411,46 +1708,117 @@ 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->codecpar->codec_id == AV_CODEC_ID_AAC &&
- st->codecpar->extradata_size > 0)
- st->need_parsing = 0;
+ st->codecpar->extradata_size > 0) {
+ st->request_probe = st->need_parsing = 0;
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->internal->need_context_update = 1;
+ }
}
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 */
+ av_assert0(language_count <= sizeof(language) / 4);
+
+ if (st->codecpar->extradata == NULL) {
+ if (ff_alloc_extradata(st->codecpar, language_count * 2)) {
+ return AVERROR(ENOMEM);
+ }
+ }
+
+ if (st->codecpar->extradata_size < language_count * 2)
+ return AVERROR_INVALIDDATA;
+
+ extradata = st->codecpar->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);
+ st->internal->need_context_update = 1;
+ }
+ }
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->codecpar->extradata) {
- if (st->codecpar->extradata_size == 4 &&
- memcmp(st->codecpar->extradata, *pp, 4))
- avpriv_request_sample(fc, "DVB sub with multiple IDs");
- } else {
- st->codecpar->extradata = av_malloc(4 + AV_INPUT_BUFFER_PADDING_SIZE);
- if (st->codecpar->extradata) {
- st->codecpar->extradata_size = 4;
- memcpy(st->codecpar->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 */
+ av_assert0(language_count <= sizeof(language) / 4);
+
+ if (st->codecpar->extradata == NULL) {
+ if (ff_alloc_extradata(st->codecpar, language_count * 5)) {
+ return AVERROR(ENOMEM);
+ }
+ }
+
+ if (st->codecpar->extradata_size < language_count * 5)
+ return AVERROR_INVALIDDATA;
+
+ extradata = st->codecpar->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);
+ st->internal->need_context_update = 1;
}
}
- *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) {
@@ -1478,8 +1846,23 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
case 0x05: /* registration descriptor */
st->codecpar->codec_tag = bytestream_get_le32(pp);
av_log(fc, AV_LOG_TRACE, "reg_desc=%.4s\n", (char *)&st->codecpar->codec_tag);
- if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
+ if (st->codecpar->codec_id == AV_CODEC_ID_NONE || st->request_probe > 0) {
mpegts_find_stream_type(st, st->codecpar->codec_tag, REGD_types);
+ if (st->codecpar->codec_tag == MKTAG('B', 'S', 'S', 'D'))
+ st->request_probe = 50;
+ }
+ 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->codecpar->codec_tag = bytestream_get_le32(pp);
+ if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
+ mpegts_find_stream_type(st, st->codecpar->codec_tag, METADATA_types);
+ }
break;
case 0x7f: /* DVB extension descriptor */
ext_desc_tag = get8(pp, desc_end);
@@ -1501,7 +1884,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
return AVERROR_INVALIDDATA;
if (channel_config_code <= 0x8) {
st->codecpar->extradata[9] = channels = channel_config_code ? channel_config_code : 2;
- st->codecpar->extradata[18] = channel_config_code ? (channels > 2) : 255;
+ st->codecpar->extradata[18] = channel_config_code ? (channels > 2) : /* Dual Mono */ 255;
st->codecpar->extradata[19] = opus_stream_cnt[channel_config_code];
st->codecpar->extradata[20] = opus_coupled_stream_cnt[channel_config_code];
memcpy(&st->codecpar->extradata[21], opus_channel_map[channels - 1], channels);
@@ -1509,6 +1892,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
avpriv_request_sample(fc, "Opus in MPEG-TS - channel_config_code > 0x8");
}
st->need_parsing = AVSTREAM_PARSE_FULL;
+ st->internal->need_context_update = 1;
}
}
break;
@@ -1519,6 +1903,12 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
return 0;
}
+static int is_pes_stream(int stream_type, uint32_t prog_reg_desc)
+{
+ return !(stream_type == 0x13 ||
+ (stream_type == 0x86 && prog_reg_desc == AV_RL32("CUEI")) );
+}
+
static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
{
MpegTSContext *ts = filter->u.section_filter.opaque;
@@ -1542,22 +1932,26 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
p = section;
if (parse_section_header(h, &p, p_end) < 0)
return;
- if (h->version == tssf->last_ver)
+ if (skip_identical(h, tssf))
return;
- tssf->last_ver = h->version;
- av_log(ts->stream, AV_LOG_TRACE, "sid=0x%x sec_num=%d/%d\n",
- h->id, h->sec_num, h->last_sec_num);
+ av_log(ts->stream, AV_LOG_TRACE, "sid=0x%x sec_num=%d/%d version=%d tid=%d\n",
+ h->id, h->sec_num, h->last_sec_num, h->version, h->tid);
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_log(ts->stream, AV_LOG_TRACE, "pcr_pid=0x%x\n", pcr_pid);
@@ -1594,7 +1988,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 (;;) {
@@ -1605,23 +2001,29 @@ 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;
- } else if (stream_type != 0x13) {
+ } else if (is_pes_stream(stream_type, prog_reg_desc)) {
if (ts->pids[pid])
mpegts_close_filter(ts, ts->pids[pid]); // wrongly added sdt filter probably
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 {
@@ -1630,8 +2032,14 @@ 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->codecpar->codec_type = AVMEDIA_TYPE_DATA;
+ if (stream_type == 0x86 && prog_reg_desc == AV_RL32("CUEI")) {
+ mpegts_find_stream_type(st, stream_type, SCTE_types);
+ mpegts_open_section_filter(ts, pid, scte_data_cb, ts, 1);
+ }
}
}
@@ -1643,15 +2051,15 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
add_pid_to_pmt(ts, h->id, pid);
- ff_program_add_stream_index(ts->stream, h->id, st->index);
+ av_program_add_stream_index(ts->stream, h->id, st->index);
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,
@@ -1660,7 +2068,7 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
if (pes && prog_reg_desc == AV_RL32("HDMV") &&
stream_type == 0x83 && pes->sub_st) {
- ff_program_add_stream_index(ts->stream, h->id,
+ av_program_add_stream_index(ts->stream, h->id,
pes->sub_st->index);
pes->sub_st->codecpar->codec_tag = st->codecpar->codec_tag;
}
@@ -1668,6 +2076,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);
@@ -1680,6 +2091,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_log(ts->stream, AV_LOG_TRACE, "PAT:\n");
hex_dump_debug(ts->stream, section, section_len);
@@ -1690,9 +2102,12 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
return;
if (h->tid != PAT_TID)
return;
- if (h->version == tssf->last_ver)
+ if (ts->skip_changes)
return;
- tssf->last_ver = h->version;
+
+ if (skip_identical(h, tssf))
+ return;
+ ts->stream->ts_id = h->id;
clear_programs(ts);
for (;;) {
@@ -1704,20 +2119,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_log(ts->stream, AV_LOG_TRACE, "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)
@@ -1738,9 +2177,10 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
return;
if (h->tid != SDT_TID)
return;
- if (h->version == tssf->last_ver)
+ if (ts->skip_changes)
+ return;
+ if (skip_identical(h, tssf))
return;
- tssf->last_ver = h->version;
onid = get16(&p, p_end);
if (onid < 0)
@@ -1768,7 +2208,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_log(ts->stream, AV_LOG_TRACE, "tag: 0x%02x len=%d\n",
@@ -1803,6 +2243,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)
{
@@ -1823,6 +2266,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 */
@@ -1843,7 +2287,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) {
@@ -1852,26 +2296,31 @@ 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) {
/* pointer field present */
len = *p++;
- if (p + len > p_end)
+ if (len > p_end - p)
return 0;
if (len && cc_ok) {
/* write remaining section bytes */
@@ -1892,31 +2341,99 @@ 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];
+ if (st->codecpar->codec_type >= 0)
+ types |= 1<<st->codecpar->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)
+static int mpegts_resync(AVFormatContext *s, int seekback, const uint8_t *current_packet)
{
MpegTSContext *ts = s->priv_data;
AVIOContext *pb = s->pb;
int c, i;
+ uint64_t pos = avio_tell(pb);
+
+ avio_seek(pb, -FFMIN(seekback, pos), SEEK_CUR);
+
+ //Special case for files like 01c56b0dc1.ts
+ if (current_packet[0] == 0x80 && current_packet[12] == 0x47) {
+ avio_seek(pb, 12, SEEK_CUR);
+ return 0;
+ }
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;
}
}
@@ -1940,8 +2457,8 @@ 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);
- if (mpegts_resync(s) < 0)
+
+ if (mpegts_resync(s, raw_packet_size, *data) < 0)
return AVERROR(EAGAIN);
else
continue;
@@ -1960,12 +2477,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 + AV_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;
@@ -1978,8 +2496,11 @@ static int handle_packets(MpegTSContext *ts, int nb_packets)
av_buffer_unref(&pes->buffer);
pes->data_index = 0;
pes->state = MPEGTS_SKIP; /* skip until pes header */
+ } else if (ts->pids[i]->type == MPEGTS_SECTION) {
+ ts->pids[i]->u.section_filter.last_ver = -1;
}
ts->pids[i]->last_cc = -1;
+ ts->pids[i]->last_pcr = -1;
}
}
}
@@ -1988,11 +2509,15 @@ static int handle_packets(MpegTSContext *ts, int nb_packets)
packet_num = 0;
memset(packet + TS_PACKET_SIZE, 0, AV_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;
@@ -2008,32 +2533,42 @@ 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;
+ if (!check_count)
+ return 0;
- 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_log(NULL, AV_LOG_TRACE, "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 , 1);
+ int dvhs_score = analyze(p->buf + TS_DVHS_PACKET_SIZE*i, TS_DVHS_PACKET_SIZE*left, TS_DVHS_PACKET_SIZE, 1);
+ int fec_score = analyze(p->buf + TS_FEC_PACKET_SIZE *i, TS_FEC_PACKET_SIZE *left, TS_FEC_PACKET_SIZE , 1);
+ score = FFMAX3(score, dvhs_score, fec_score);
+ sumscore += score;
+ maxscore = FFMAX(maxscore, score);
+ }
- /* we need a clear definition for the returned score otherwise
- * things will become messy sooner or later */
- if (score > fec_score && score > dvhs_score && score > 6)
- return AVPROBE_SCORE_MAX + score - CHECK_COUNT;
- else if (dvhs_score > score && dvhs_score > fec_score && dvhs_score > 6)
- return AVPROBE_SCORE_MAX + dvhs_score - CHECK_COUNT;
- else if (fec_score > 6)
- return AVPROBE_SCORE_MAX + fec_score - CHECK_COUNT;
- else
- return AVERROR_INVALIDDATA;
+ sumscore = sumscore * CHECK_COUNT / check_count;
+ maxscore = maxscore * CHECK_COUNT / CHECK_BLOCK;
+
+ ff_dlog(0, "TS score: %d %d\n", sumscore, maxscore);
+
+ if (check_count > CHECK_COUNT && sumscore > 6) {
+ return AVPROBE_SCORE_MAX + sumscore - CHECK_COUNT;
+ } else if (check_count >= CHECK_COUNT && sumscore > 6) {
+ return AVPROBE_SCORE_MAX/2 + sumscore - CHECK_COUNT;
+ } else if (check_count >= CHECK_COUNT && maxscore > 6) {
+ return AVPROBE_SCORE_MAX/2 + sumscore - CHECK_COUNT;
+ } else if (sumscore > 6) {
+ return 2;
+ } else {
+ return 0;
+ }
}
/* return the 90kHz PCR and the extension for the 27MHz PCR. return
@@ -2064,24 +2599,36 @@ 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 & AVIO_SEEKABLE_NORMAL) ? 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->internal->prefer_codec_framerate = 1;
- /* read the first 1024 bytes to get packet size */
+ if (ffio_ensure_seekback(pb, probesize) < 0)
+ av_log(s, AV_LOG_WARNING, "Failed to allocate buffers for seekback\n");
+
+ /* 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;
@@ -2089,15 +2636,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 & AVIO_SEEKABLE_NORMAL))
- 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;
@@ -2138,8 +2683,17 @@ static int mpegts_read_header(AVFormatContext *s)
packet_count[nb_pcrs] = nb_packets;
pcrs[nb_pcrs] = pcr_h * 300 + pcr_l;
nb_pcrs++;
- if (nb_pcrs >= 2)
- break;
+ if (nb_pcrs >= 2) {
+ if (pcrs[1] - pcrs[0] > 0) {
+ /* the difference needs to be positive to make sense for bitrate computation */
+ break;
+ } else {
+ av_log(ts->stream, AV_LOG_WARNING, "invalid pcr pair %"PRId64" >= %"PRId64"\n", pcrs[0], pcrs[1]);
+ pcrs[0] = pcrs[1];
+ packet_count[0] = packet_count[1];
+ nb_pcrs--;
+ }
+ }
} else {
finished_reading_packet(s, ts->raw_packet_size);
}
@@ -2150,14 +2704,14 @@ static int mpegts_read_header(AVFormatContext *s)
/* NOTE2: it is only the bitrate of the start of the stream */
ts->pcr_incr = (pcrs[1] - pcrs[0]) / (packet_count[1] - packet_count[0]);
ts->cur_pcr = pcrs[0] - ts->pcr_incr * packet_count[0];
- s->bit_rate = TS_PACKET_SIZE * 8 * 27e6 / ts->pcr_incr;
+ s->bit_rate = TS_PACKET_SIZE * 8 * 27000000LL / ts->pcr_incr;
st->codecpar->bit_rate = s->bit_rate;
st->start_time = ts->cur_pcr;
av_log(ts->stream, AV_LOG_TRACE, "start=%0.3f pcr=%0.3f incr=%d\n",
st->start_time / 1000000.0, pcrs[0] / 27e6, ts->pcr_incr);
}
- avio_seek(pb, pos, SEEK_SET);
+ seek_back(s, pb, pos);
return 0;
}
@@ -2220,12 +2774,15 @@ static int mpegts_read_packet(AVFormatContext *s, AVPacket *pkt)
ts->pkt = pkt;
ret = handle_packets(ts, 0);
if (ret < 0) {
+ av_packet_unref(ts->pkt);
/* flush pes data left */
for (i = 0; i < NB_PID_MAX; i++)
if (ts->pids[i] && ts->pids[i]->type == MPEGTS_PES) {
PESContext *pes = ts->pids[i]->u.pes_filter.opaque;
if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) {
- new_pes_packet(pes, pkt);
+ ret = new_pes_packet(pes, pkt);
+ if (ret < 0)
+ return ret;
pes->state = MPEGTS_SKIP;
ret = 0;
break;
@@ -2234,7 +2791,7 @@ static int mpegts_read_packet(AVFormatContext *s, AVPacket *pkt)
}
if (!ret && pkt->size < 0)
- ret = AVERROR(EINTR);
+ ret = AVERROR_INVALIDDATA;
return ret;
}
@@ -2256,7 +2813,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;
@@ -2264,74 +2821,70 @@ 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) {
+ if (mpegts_resync(s, TS_PACKET_SIZE, buf) < 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;
+ 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) {
+ int64_t dts = pkt.dts;
+ *ppos = pkt.pos;
+ av_packet_unref(&pkt);
+ return dts;
+ }
+ }
+ pos = pkt.pos;
+ av_packet_unref(&pkt);
}
- 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;
@@ -2342,22 +2895,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) {
@@ -2367,12 +2921,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);
@@ -2386,8 +2942,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,
};
@@ -2399,8 +2954,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 9f374c6..272e2be 100644
--- a/libavformat/mpegts.h
+++ b/libavformat/mpegts.h
@@ -2,20 +2,20 @@
* MPEG-2 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
*/
@@ -51,6 +51,7 @@
#define STREAM_TYPE_AUDIO_AAC 0x0f
#define STREAM_TYPE_AUDIO_AAC_LATM 0x11
#define STREAM_TYPE_VIDEO_MPEG4 0x10
+#define STREAM_TYPE_METADATA 0x15
#define STREAM_TYPE_VIDEO_H264 0x1b
#define STREAM_TYPE_VIDEO_HEVC 0x24
#define STREAM_TYPE_VIDEO_CAVS 0x42
@@ -58,14 +59,16 @@
#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
+#define STREAM_TYPE_AUDIO_EAC3 0x87
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 +108,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 12c406f..fdfa544 100644
--- a/libavformat/mpegtsenc.c
+++ b/libavformat/mpegtsenc.c
@@ -2,23 +2,24 @@
* MPEG-2 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"
@@ -45,6 +46,7 @@
typedef struct MpegTSSection {
int pid;
int cc;
+ int discontinuity;
void (*write_packet)(struct MpegTSSection *s, const uint8_t *packet);
void *opaque;
} MpegTSSection;
@@ -57,8 +59,20 @@ typedef struct MpegTSService {
int pcr_pid;
int pcr_packet_count;
int pcr_packet_period;
+ AVProgram *program;
} 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,
+ MPEGTS_SERVICE_TYPE_HEVC_DIGITAL_HDTV = 0x1F,
+};
typedef struct MpegTSWrite {
const AVClass *av_class;
MpegTSSection pat; /* MPEG-2 PAT table */
@@ -78,6 +92,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;
@@ -88,8 +103,18 @@ typedef struct MpegTSWrite {
int pcr_period;
#define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01
#define MPEGTS_FLAG_AAC_LATM 0x02
-#define MPEGTS_FLAG_SYSTEM_B 0x04
+#define MPEGTS_FLAG_PAT_PMT_AT_FRAMES 0x04
+#define MPEGTS_FLAG_SYSTEM_B 0x08
+#define MPEGTS_FLAG_DISCONT 0x10
int flags;
+ int copyts;
+ int tables_version;
+ double pat_period;
+ double sdt_period;
+ int64_t last_pat_ts;
+ int64_t last_sdt_ts;
+
+ int omit_video_pes_length;
} MpegTSWrite;
/* a PES packet header is generated every DEFAULT_PES_HEADER_FREQ packets */
@@ -130,6 +155,12 @@ static void mpegts_write_section(MpegTSSection *s, uint8_t *buf, int len)
*q++ = s->pid;
s->cc = s->cc + 1 & 0xf;
*q++ = 0x10 | s->cc;
+ if (s->discontinuity) {
+ q[-1] |= 0x20;
+ *q++ = 1;
+ *q++ = 0x80;
+ s->discontinuity = 0;
+ }
if (first)
*q++ = 0; /* 0 offset */
len1 = TS_PACKET_SIZE - (q - packet);
@@ -188,7 +219,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 */
@@ -200,14 +231,20 @@ typedef struct MpegTSWriteStream {
struct MpegTSService *service;
int pid; /* stream associated pid */
int cc;
+ int discontinuity;
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;
uint8_t *payload;
AVFormatContext *amux;
AVRational user_tb;
+
+ /* For Opus */
+ int opus_queued_samples;
+ int opus_pending_trim_start;
} MpegTSWriteStream;
static void mpegts_write_pat(AVFormatContext *s)
@@ -223,11 +260,33 @@ 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)
+/* NOTE: !str is accepted for an empty string */
+static void putstr8(uint8_t **q_ptr, const char *str, int write_len)
+{
+ uint8_t *q;
+ int len;
+
+ q = *q_ptr;
+ if (!str)
+ len = 0;
+ else
+ len = strlen(str);
+ if (write_len)
+ *q++ = len;
+ if (!str) {
+ *q_ptr = q;
+ return;
+ }
+ memcpy(q, str, len);
+ q += len;
+ *q_ptr = q;
+}
+
+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;
@@ -250,7 +309,21 @@ 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 (s->nb_programs) {
+ int k, found = 0;
+ AVProgram *program = service->program;
+
+ for (k = 0; k < program->nb_stream_indexes; k++)
+ if (program->stream_index[k] == i) {
+ found = 1;
+ break;
+ }
+
+ if (!found)
+ continue;
+ }
+
+ if (q - data > SECTION_LENGTH - 32) {
err = 1;
break;
}
@@ -274,9 +347,17 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
case AV_CODEC_ID_DIRAC:
stream_type = STREAM_TYPE_VIDEO_DIRAC;
break;
+ case AV_CODEC_ID_VC1:
+ stream_type = STREAM_TYPE_VIDEO_VC1;
+ break;
case AV_CODEC_ID_MP2:
case AV_CODEC_ID_MP3:
- stream_type = STREAM_TYPE_AUDIO_MPEG1;
+ if ( st->codecpar->sample_rate > 0
+ && st->codecpar->sample_rate < 32000) {
+ stream_type = STREAM_TYPE_AUDIO_MPEG2;
+ } else {
+ stream_type = STREAM_TYPE_AUDIO_MPEG1;
+ }
break;
case AV_CODEC_ID_AAC:
stream_type = (ts->flags & MPEGTS_FLAG_AAC_LATM)
@@ -291,10 +372,28 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
? STREAM_TYPE_PRIVATE_DATA
: STREAM_TYPE_AUDIO_AC3;
break;
+ case AV_CODEC_ID_EAC3:
+ stream_type = (ts->flags & MPEGTS_FLAG_SYSTEM_B)
+ ? STREAM_TYPE_PRIVATE_DATA
+ : STREAM_TYPE_AUDIO_EAC3;
+ 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;
+ case AV_CODEC_ID_OPUS:
+ stream_type = STREAM_TYPE_PRIVATE_DATA;
+ break;
+ case AV_CODEC_ID_TIMED_ID3:
+ stream_type = STREAM_TYPE_METADATA;
+ break;
default:
stream_type = STREAM_TYPE_PRIVATE_DATA;
break;
}
+
*q++ = stream_type;
put16(&q, 0xe000 | ts_st->pid);
desc_length_ptr = q;
@@ -303,10 +402,99 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
/* write optional descriptors here */
switch (st->codecpar->codec_type) {
case AVMEDIA_TYPE_AUDIO:
- if (st->codecpar->codec_id == AV_CODEC_ID_AC3 && (ts->flags & MPEGTS_FLAG_SYSTEM_B)) {
- *q++ = 0x6a; /* ETSI EN 300 468 AC-3 descriptor */
- *q++ = 1;
- *q++ = 0x00;
+ if (st->codecpar->codec_id==AV_CODEC_ID_AC3 && (ts->flags & MPEGTS_FLAG_SYSTEM_B)) {
+ *q++=0x6a; // AC3 descriptor see A038 DVB SI
+ *q++=1; // 1 byte, all flags sets to 0
+ *q++=0; // omit all fields...
+ }
+ if (st->codecpar->codec_id==AV_CODEC_ID_EAC3 && (ts->flags & MPEGTS_FLAG_SYSTEM_B)) {
+ *q++=0x7a; // EAC3 descriptor see A038 DVB SI
+ *q++=1; // 1 byte, all flags sets to 0
+ *q++=0; // omit all fields...
+ }
+ if (st->codecpar->codec_id==AV_CODEC_ID_S302M) {
+ *q++ = 0x05; /* MPEG-2 registration descriptor*/
+ *q++ = 4;
+ *q++ = 'B';
+ *q++ = 'S';
+ *q++ = 'S';
+ *q++ = 'D';
+ }
+ if (st->codecpar->codec_id==AV_CODEC_ID_OPUS) {
+ /* 6 bytes registration descriptor, 4 bytes Opus audio descriptor */
+ if (q - data > SECTION_LENGTH - 6 - 4) {
+ err = 1;
+ break;
+ }
+
+ *q++ = 0x05; /* MPEG-2 registration descriptor*/
+ *q++ = 4;
+ *q++ = 'O';
+ *q++ = 'p';
+ *q++ = 'u';
+ *q++ = 's';
+
+ *q++ = 0x7f; /* DVB extension descriptor */
+ *q++ = 2;
+ *q++ = 0x80;
+
+ if (st->codecpar->extradata && st->codecpar->extradata_size >= 19) {
+ if (st->codecpar->extradata[18] == 0 && st->codecpar->channels <= 2) {
+ /* RTP mapping family */
+ *q++ = st->codecpar->channels;
+ } else if (st->codecpar->extradata[18] == 1 && st->codecpar->channels <= 8 &&
+ st->codecpar->extradata_size >= 21 + st->codecpar->channels) {
+ static const uint8_t coupled_stream_counts[9] = {
+ 1, 0, 1, 1, 2, 2, 2, 3, 3
+ };
+ static const uint8_t channel_map_a[8][8] = {
+ {0},
+ {0, 1},
+ {0, 2, 1},
+ {0, 1, 2, 3},
+ {0, 4, 1, 2, 3},
+ {0, 4, 1, 2, 3, 5},
+ {0, 4, 1, 2, 3, 5, 6},
+ {0, 6, 1, 2, 3, 4, 5, 7},
+ };
+ static const uint8_t channel_map_b[8][8] = {
+ {0},
+ {0, 1},
+ {0, 1, 2},
+ {0, 1, 2, 3},
+ {0, 1, 2, 3, 4},
+ {0, 1, 2, 3, 4, 5},
+ {0, 1, 2, 3, 4, 5, 6},
+ {0, 1, 2, 3, 4, 5, 6, 7},
+ };
+ /* Vorbis mapping family */
+
+ if (st->codecpar->extradata[19] == st->codecpar->channels - coupled_stream_counts[st->codecpar->channels] &&
+ st->codecpar->extradata[20] == coupled_stream_counts[st->codecpar->channels] &&
+ memcmp(&st->codecpar->extradata[21], channel_map_a[st->codecpar->channels-1], st->codecpar->channels) == 0) {
+ *q++ = st->codecpar->channels;
+ } else if (st->codecpar->channels >= 2 && st->codecpar->extradata[19] == st->codecpar->channels &&
+ st->codecpar->extradata[20] == 0 &&
+ memcmp(&st->codecpar->extradata[21], channel_map_b[st->codecpar->channels-1], st->codecpar->channels) == 0) {
+ *q++ = st->codecpar->channels | 0x80;
+ } else {
+ /* Unsupported, could write an extended descriptor here */
+ av_log(s, AV_LOG_ERROR, "Unsupported Opus Vorbis-style channel mapping");
+ *q++ = 0xff;
+ }
+ } else {
+ /* Unsupported */
+ av_log(s, AV_LOG_ERROR, "Unsupported Opus channel mapping for family %d", st->codecpar->extradata[18]);
+ *q++ = 0xff;
+ }
+ } else if (st->codecpar->channels <= 2) {
+ /* Assume RTP mapping family */
+ *q++ = st->codecpar->channels;
+ } else {
+ /* Unsupported */
+ av_log(s, AV_LOG_ERROR, "Unsupported Opus channel mapping");
+ *q++ = 0xff;
+ }
}
if (lang) {
@@ -349,26 +537,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->codecpar->extradata_size == 4) {
- memcpy(q, st->codecpar->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->codecpar->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->codecpar->extradata_size - extradata_copied >= 5) {
+ *q++ = st->codecpar->extradata[extradata_copied + 4]; /* subtitling_type */
+ memcpy(q, st->codecpar->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->codecpar->extradata_size == 4) && (extradata_copied == 0)) {
+ /* support of old 4-byte extradata format */
+ memcpy(q, st->codecpar->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->codecpar->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->codecpar->extradata_size - 1 > extradata_copied) {
+ memcpy(q, st->codecpar->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;
@@ -380,6 +624,33 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
*q++ = 'r';
*q++ = 'a';
*q++ = 'c';
+ } else if (stream_type == STREAM_TYPE_VIDEO_VC1) {
+ *q++ = 0x05; /*MPEG-2 registration descriptor*/
+ *q++ = 4;
+ *q++ = 'V';
+ *q++ = 'C';
+ *q++ = '-';
+ *q++ = '1';
+ }
+ break;
+ case AVMEDIA_TYPE_DATA:
+ if (st->codecpar->codec_id == AV_CODEC_ID_SMPTE_KLV) {
+ *q++ = 0x05; /* MPEG-2 registration descriptor */
+ *q++ = 4;
+ *q++ = 'K';
+ *q++ = 'L';
+ *q++ = 'V';
+ *q++ = 'A';
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_TIMED_ID3) {
+ const char *tag = "ID3 ";
+ *q++ = 0x26; /* metadata descriptor */
+ *q++ = 13;
+ put16(&q, 0xffff); /* metadata application format */
+ putstr8(&q, tag, 0);
+ *q++ = 0xff; /* metadata format */
+ putstr8(&q, tag, 0);
+ *q++ = 0; /* metadata service ID */
+ *q++ = 0xF; /* metadata_locator_record_flag|MPEG_carriage_flags|reserved */
}
break;
}
@@ -395,25 +666,9 @@ 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);
-}
-
-/* NOTE: str == NULL is accepted for an empty string */
-static void putstr8(uint8_t **q_ptr, const char *str)
-{
- uint8_t *q;
- int len;
-
- q = *q_ptr;
- if (!str)
- len = 0;
- else
- len = strlen(str);
- *q++ = len;
- memcpy(q, str, len);
- q += len;
- *q_ptr = q;
+ return 0;
}
static void mpegts_write_sdt(AVFormatContext *s)
@@ -439,9 +694,9 @@ static void mpegts_write_sdt(AVFormatContext *s)
*q++ = 0x48;
desc_len_ptr = q;
q++;
- *q++ = 0x01; /* digital television service */
- putstr8(&q, service->provider_name);
- putstr8(&q, service->name);
+ *q++ = ts->service_type;
+ putstr8(&q, service->provider_name, 1);
+ putstr8(&q, service->name, 1);
desc_len_ptr[0] = q - desc_len_ptr - 1;
/* fill descriptor length */
@@ -450,7 +705,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);
}
@@ -468,14 +723,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)
@@ -503,7 +761,7 @@ static void section_write_packet(MpegTSSection *s, const uint8_t *packet)
avio_write(ctx->pb, packet, TS_PACKET_SIZE);
}
-static int mpegts_write_header(AVFormatContext *s)
+static int mpegts_init(AVFormatContext *s)
{
MpegTSWrite *ts = s->priv_data;
MpegTSWriteStream *ts_st;
@@ -524,43 +782,70 @@ static int mpegts_write_header(AVFormatContext *s)
ts->tsid = ts->transport_stream_id;
ts->onid = ts->original_network_id;
- /* allocate a single DVB service */
- title = av_dict_get(s->metadata, "service_name", NULL, 0);
- if (!title)
- title = av_dict_get(s->metadata, "title", NULL, 0);
- service_name = title ? title->value : DEFAULT_SERVICE_NAME;
- provider = av_dict_get(s->metadata, "service_provider", NULL, 0);
- provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME;
- service = mpegts_add_service(ts, ts->service_id,
- provider_name, service_name);
-
- if (!service)
- return AVERROR(ENOMEM);
+ if (!s->nb_programs) {
+ /* allocate a single DVB service */
+ title = av_dict_get(s->metadata, "service_name", NULL, 0);
+ if (!title)
+ title = av_dict_get(s->metadata, "title", NULL, 0);
+ service_name = title ? title->value : DEFAULT_SERVICE_NAME;
+ provider = av_dict_get(s->metadata, "service_provider", NULL, 0);
+ provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME;
+ service = mpegts_add_service(ts, ts->service_id,
+ provider_name, service_name);
+
+ if (!service)
+ return AVERROR(ENOMEM);
+
+ service->pmt.write_packet = section_write_packet;
+ service->pmt.opaque = s;
+ service->pmt.cc = 15;
+ service->pmt.discontinuity= ts->flags & MPEGTS_FLAG_DISCONT;
+ } else {
+ for (i = 0; i < s->nb_programs; i++) {
+ AVProgram *program = s->programs[i];
+ title = av_dict_get(program->metadata, "service_name", NULL, 0);
+ if (!title)
+ title = av_dict_get(program->metadata, "title", NULL, 0);
+ service_name = title ? title->value : DEFAULT_SERVICE_NAME;
+ provider = av_dict_get(program->metadata, "service_provider", NULL, 0);
+ provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME;
+ service = mpegts_add_service(ts, program->id,
+ provider_name, service_name);
+
+ if (!service)
+ return AVERROR(ENOMEM);
- service->pmt.write_packet = section_write_packet;
- service->pmt.opaque = s;
- service->pmt.cc = 15;
+ service->pmt.write_packet = section_write_packet;
+ service->pmt.opaque = s;
+ service->pmt.cc = 15;
+ service->pmt.discontinuity= ts->flags & MPEGTS_FLAG_DISCONT;
+ service->program = program;
+ }
+ }
ts->pat.pid = PAT_PID;
/* Initialize at 15 so that it wraps and is equal to 0 for the
* first packet we write. */
ts->pat.cc = 15;
+ ts->pat.discontinuity= ts->flags & MPEGTS_FLAG_DISCONT;
ts->pat.write_packet = section_write_packet;
ts->pat.opaque = s;
ts->sdt.pid = SDT_PID;
ts->sdt.cc = 15;
+ ts->sdt.discontinuity= ts->flags & MPEGTS_FLAG_DISCONT;
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 */
for (i = 0; i < s->nb_streams; i++) {
+ AVProgram *program;
st = s->streams[i];
ts_st = av_mallocz(sizeof(MpegTSWriteStream));
@@ -578,6 +863,17 @@ static int mpegts_write_header(AVFormatContext *s)
ret = AVERROR(ENOMEM);
goto fail;
}
+
+ program = av_find_program_from_stream(s, NULL, i);
+ if (program) {
+ for (j = 0; j < ts->nb_services; j++) {
+ if (ts->services[j]->program == program) {
+ service = ts->services[j];
+ break;
+ }
+ }
+ }
+
ts_st->service = service;
/* MPEG pid values < 16 are reserved. Applications which set st->id in
* this range are assigned a calculated pid. */
@@ -608,6 +904,7 @@ static int mpegts_write_header(AVFormatContext *s)
ts_st->payload_dts = AV_NOPTS_VALUE;
ts_st->first_pts_check = 1;
ts_st->cc = 15;
+ ts_st->discontinuity = ts->flags & MPEGTS_FLAG_DISCONT;
/* update PCR pid by using the first video stream */
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
service->pcr_pid == 0x1fff) {
@@ -641,9 +938,12 @@ static int mpegts_write_header(AVFormatContext *s)
if (ret < 0)
goto fail;
}
+ if (st->codecpar->codec_id == AV_CODEC_ID_OPUS) {
+ ts_st->opus_pending_trim_start = st->codecpar->initial_padding * 48000 / st->codecpar->sample_rate;
+ }
}
- 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) {
@@ -654,16 +954,17 @@ static int mpegts_write_header(AVFormatContext *s)
ts_st = pcr_st->priv_data;
if (ts->mux_rate > 1) {
- service->pcr_packet_period = (ts->mux_rate * ts->pcr_period) /
+ service->pcr_packet_period = (int64_t)ts->mux_rate * ts->pcr_period /
(TS_PACKET_SIZE * 8 * 1000);
- ts->sdt_packet_period = (ts->mux_rate * SDT_RETRANS_TIME) /
+ ts->sdt_packet_period = (int64_t)ts->mux_rate * SDT_RETRANS_TIME /
(TS_PACKET_SIZE * 8 * 1000);
- ts->pat_packet_period = (ts->mux_rate * PAT_RETRANS_TIME) /
+ ts->pat_packet_period = (int64_t)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->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
@@ -682,6 +983,18 @@ 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;
+ }
+
+ ts->last_pat_ts = AV_NOPTS_VALUE;
+ ts->last_sdt_ts = AV_NOPTS_VALUE;
+ // The user specified a period, use only it
+ if (ts->pat_period < INT_MAX/2) {
+ ts->pat_packet_period = INT_MAX;
+ }
+ if (ts->sdt_period < INT_MAX/2) {
+ ts->sdt_packet_period = INT_MAX;
}
// output a PCR as soon as possible
@@ -706,40 +1019,35 @@ static int mpegts_write_header(AVFormatContext *s)
}
}
- avio_flush(s->pb);
-
return 0;
fail:
- av_free(service);
- av_free(pids);
- for (i = 0; i < s->nb_streams; i++) {
- st = s->streams[i];
- ts_st = st->priv_data;
- if (ts_st) {
- av_freep(&ts_st->payload);
- if (ts_st->amux) {
- avformat_free_context(ts_st->amux);
- ts_st->amux = NULL;
- }
- }
- av_freep(&st->priv_data);
- }
+ av_freep(&pids);
return ret;
}
/* send SDT, PAT and PMT tables regularly */
-static void retransmit_si_info(AVFormatContext *s)
+static void retransmit_si_info(AVFormatContext *s, int force_pat, int64_t dts)
{
MpegTSWrite *ts = s->priv_data;
int i;
- if (++ts->sdt_packet_count == ts->sdt_packet_period) {
+ if (++ts->sdt_packet_count == ts->sdt_packet_period ||
+ (dts != AV_NOPTS_VALUE && ts->last_sdt_ts == AV_NOPTS_VALUE) ||
+ (dts != AV_NOPTS_VALUE && dts - ts->last_sdt_ts >= ts->sdt_period*90000.0)
+ ) {
ts->sdt_packet_count = 0;
+ if (dts != AV_NOPTS_VALUE)
+ ts->last_sdt_ts = FFMAX(dts, ts->last_sdt_ts);
mpegts_write_sdt(s);
}
- if (++ts->pat_packet_count == ts->pat_packet_period) {
+ if (++ts->pat_packet_count == ts->pat_packet_period ||
+ (dts != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE) ||
+ (dts != AV_NOPTS_VALUE && dts - ts->last_pat_ts >= ts->pat_period*90000.0) ||
+ force_pat) {
ts->pat_packet_count = 0;
+ if (dts != AV_NOPTS_VALUE)
+ ts->last_pat_ts = FFMAX(dts, ts->last_pat_ts);
mpegts_write_pat(s);
for (i = 0; i < ts->nb_services; i++)
mpegts_write_pmt(s, ts->services[i]);
@@ -792,6 +1100,10 @@ static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st)
/* Continuity Count field does not increment (see 13818-1 section 2.4.3.3) */
*q++ = TS_PACKET_SIZE - 5; /* Adaptation Field Length */
*q++ = 0x10; /* Adaptation flags: PCR present */
+ if (ts_st->discontinuity) {
+ q[-1] |= 0x80;
+ ts_st->discontinuity = 0;
+ }
/* PCR coded into 6 bytes */
q += write_pcr_bits(q, get_pcr(ts, s->pb));
@@ -820,7 +1132,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
@@ -836,7 +1148,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;
}
@@ -855,20 +1167,27 @@ static uint8_t *get_ts_payload_start(uint8_t *pkt)
* NOTE: 'payload' contains a complete PES payload. */
static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
const uint8_t *payload, int payload_size,
- int64_t pts, int64_t dts, int key)
+ int64_t pts, int64_t dts, int key, int stream_id)
{
MpegTSWriteStream *ts_st = st->priv_data;
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->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key;
+
+ av_assert0(ts_st->payload != buf || st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO);
+ if (ts->flags & MPEGTS_FLAG_PAT_PMT_AT_FRAMES && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ force_pat = 1;
+ }
is_start = 1;
while (payload_size > 0) {
- retransmit_si_info(s);
+ retransmit_si_info(s, force_pat, dts);
+ force_pat = 0;
write_pcr = 0;
if (ts_st->pid == ts_st->service->pcr_pid) {
@@ -902,6 +1221,11 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
*q++ = ts_st->pid;
ts_st->cc = ts_st->cc + 1 & 0xf;
*q++ = 0x10 | ts_st->cc; // payload indicator + CC
+ if (ts_st->discontinuity) {
+ set_af_flag(buf, 0x80);
+ q = get_ts_payload_start(buf);
+ ts_st->discontinuity = 0;
+ }
if (key && is_start && pts != AV_NOPTS_VALUE) {
// set Random Access for key frames
if (ts_st->pid == ts_st->service->pcr_pid)
@@ -924,11 +1248,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->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (st->codecpar->codec_id == AV_CODEC_ID_DIRAC)
*q++ = 0xfd;
@@ -939,10 +1265,27 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
st->codecpar->codec_id == AV_CODEC_ID_MP3 ||
st->codecpar->codec_id == AV_CODEC_ID_AAC)) {
*q++ = 0xc0;
+ } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
+ st->codecpar->codec_id == AV_CODEC_ID_AC3 &&
+ ts->m2ts_mode) {
+ *q++ = 0xfd;
+ } else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA &&
+ st->codecpar->codec_id == AV_CODEC_ID_TIMED_ID3) {
+ *q++ = 0xbd;
+ } else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
+ *q++ = stream_id != -1 ? stream_id : 0xfc;
+
+ if (stream_id == 0xbd) /* asynchronous KLV */
+ pts = dts = AV_NOPTS_VALUE;
} else {
*q++ = 0xbd;
- if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
- private_code = 0x20;
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+ if (st->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
+ is_dvb_subtitle = 1;
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_DVB_TELETEXT) {
+ is_dvb_teletext = 1;
+ }
+ }
}
header_len = 0;
flags = 0;
@@ -965,16 +1308,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->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
+ st->codecpar->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->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ len = 0;
+ }
*q++ = len >> 8;
*q++ = len;
val = 0x80;
- /* data alignment indicator is required for subtitle data */
- if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
+ /* data alignment indicator is required for subtitle and data streams */
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE || st->codecpar->codec_type == AVMEDIA_TYPE_DATA)
val |= 0x04;
*q++ = val;
*q++ = flags;
@@ -995,8 +1359,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->codecpar->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 */
@@ -1027,13 +1411,104 @@ 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 && AV_RB24(pkt->data) != 0x000001) {
+ 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 %08"PRIX32, 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 && AV_RB24(pkt->data) != 0x000001) {
+ 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 %08"PRIX32, AV_RB32(pkt->data));
+ av_log(s, AV_LOG_WARNING, "\n");
+ }
+ return 0;
+}
+
+/* Based on GStreamer's gst-plugins-base/ext/ogg/gstoggstream.c
+ * Released under the LGPL v2.1+, written by
+ * Vincent Penquerc'h <vincent.penquerch@collabora.co.uk>
+ */
+static int opus_get_packet_samples(AVFormatContext *s, AVPacket *pkt)
+{
+ static const int durations[32] = {
+ 480, 960, 1920, 2880, /* Silk NB */
+ 480, 960, 1920, 2880, /* Silk MB */
+ 480, 960, 1920, 2880, /* Silk WB */
+ 480, 960, /* Hybrid SWB */
+ 480, 960, /* Hybrid FB */
+ 120, 240, 480, 960, /* CELT NB */
+ 120, 240, 480, 960, /* CELT NB */
+ 120, 240, 480, 960, /* CELT NB */
+ 120, 240, 480, 960, /* CELT NB */
+ };
+ int toc, frame_duration, nframes, duration;
+
+ if (pkt->size < 1)
+ return 0;
+
+ toc = pkt->data[0];
+
+ frame_duration = durations[toc >> 3];
+ switch (toc & 3) {
+ case 0:
+ nframes = 1;
+ break;
+ case 1:
+ nframes = 2;
+ break;
+ case 2:
+ nframes = 2;
+ break;
+ case 3:
+ if (pkt->size < 2)
+ return 0;
+ nframes = pkt->data[1] & 63;
+ break;
+ }
+
+ duration = nframes * frame_duration;
+ if (duration > 5760) {
+ av_log(s, AV_LOG_WARNING,
+ "Opus packet duration > 120 ms, invalid");
+ return 0;
+ }
+
+ return duration;
}
static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
@@ -1044,8 +1519,18 @@ 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;
+ int opus_samples = 0;
+ int side_data_size;
+ char *side_data = NULL;
+ int stream_id = -1;
+
+ side_data = av_packet_get_side_data(pkt,
+ AV_PKT_DATA_MPEGTS_STREAM_ID,
+ &side_data_size);
+ if (side_data)
+ stream_id = side_data[0];
if (ts->reemit_pat_pmt) {
av_log(s, AV_LOG_WARNING,
@@ -1060,13 +1545,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;
@@ -1074,29 +1561,35 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
if (st->codecpar->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->codecpar->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->codecpar->extradata) > 1)
+ extradd = 0;
do {
p = avpriv_find_start_code(p, buf_end, &state);
- av_log(s, AV_LOG_TRACE, "nal %"PRIu32"\n", state & 0x1f);
+ av_log(s, AV_LOG_TRACE, "nal %"PRId32"\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->codecpar->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->codecpar->codec_id == AV_CODEC_ID_AAC) {
if (pkt->size < 2) {
@@ -1110,12 +1603,12 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
if (!ts_st->amux) {
av_log(s, AV_LOG_ERROR, "AAC bitstream not in ADTS format "
"and extradata missing\n");
- return AVERROR_INVALIDDATA;
- }
-
+ } else {
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)
@@ -1129,33 +1622,150 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
size = avio_close_dyn_buf(ts_st->amux->pb, &data);
ts_st->amux->pb = NULL;
buf = data;
+ }
+ }
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_HEVC) {
+ const uint8_t *p = buf, *buf_end = p + size;
+ uint32_t state = -1;
+ int extradd = (pkt->flags & AV_PKT_FLAG_KEY) ? st->codecpar->extradata_size : 0;
+ int ret = check_hevc_startcode(s, st, pkt);
+ if (ret < 0)
+ return ret;
+
+ if (extradd && AV_RB24(st->codecpar->extradata) > 1)
+ extradd = 0;
+
+ do {
+ p = avpriv_find_start_code(p, buf_end, &state);
+ av_log(s, AV_LOG_TRACE, "nal %"PRId32"\n", (state & 0x7e)>>1);
+ if ((state & 0x7e) == 2*32)
+ extradd = 0;
+ } while (p < buf_end && (state & 0x7e) != 2*35 &&
+ (state & 0x7e) >= 2*32);
+
+ if ((state & 0x7e) < 2*16 && (state & 0x7e) >= 2*24)
+ extradd = 0;
+ if ((state & 0x7e) != 2*35) { // AUD NAL
+ data = av_malloc(pkt->size + 7 + extradd);
+ if (!data)
+ return AVERROR(ENOMEM);
+ memcpy(data + 7, st->codecpar->extradata, extradd);
+ memcpy(data + 7 + extradd, pkt->data, pkt->size);
+ AV_WB32(data, 0x00000001);
+ data[4] = 2*35;
+ data[5] = 1;
+ data[6] = 0x50; // any slice type (0x4) + rbsp stop one bit
+ buf = data;
+ size = pkt->size + 7 + extradd;
+ }
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_OPUS) {
+ if (pkt->size < 2) {
+ av_log(s, AV_LOG_ERROR, "Opus packet too short\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* Add Opus control header */
+ if ((AV_RB16(pkt->data) >> 5) != 0x3ff) {
+ uint8_t *side_data;
+ int side_data_size;
+ int i, n;
+ int ctrl_header_size;
+ int trim_start = 0, trim_end = 0;
+
+ opus_samples = opus_get_packet_samples(s, pkt);
+
+ side_data = av_packet_get_side_data(pkt,
+ AV_PKT_DATA_SKIP_SAMPLES,
+ &side_data_size);
+
+ if (side_data && side_data_size >= 10) {
+ trim_end = AV_RL32(side_data + 4) * 48000 / st->codecpar->sample_rate;
+ }
+
+ ctrl_header_size = pkt->size + 2 + pkt->size / 255 + 1;
+ if (ts_st->opus_pending_trim_start)
+ ctrl_header_size += 2;
+ if (trim_end)
+ ctrl_header_size += 2;
+
+ data = av_malloc(ctrl_header_size);
+ if (!data)
+ return AVERROR(ENOMEM);
+
+ data[0] = 0x7f;
+ data[1] = 0xe0;
+ if (ts_st->opus_pending_trim_start)
+ data[1] |= 0x10;
+ if (trim_end)
+ data[1] |= 0x08;
+
+ n = pkt->size;
+ i = 2;
+ do {
+ data[i] = FFMIN(n, 255);
+ n -= 255;
+ i++;
+ } while (n >= 0);
+
+ av_assert0(2 + pkt->size / 255 + 1 == i);
+
+ if (ts_st->opus_pending_trim_start) {
+ trim_start = FFMIN(ts_st->opus_pending_trim_start, opus_samples);
+ AV_WB16(data + i, trim_start);
+ i += 2;
+ ts_st->opus_pending_trim_start -= trim_start;
+ }
+ if (trim_end) {
+ trim_end = FFMIN(trim_end, opus_samples - trim_start);
+ AV_WB16(data + i, trim_end);
+ i += 2;
+ }
+
+ memcpy(data + i, pkt->data, pkt->size);
+ buf = data;
+ size = ctrl_header_size;
+ } else {
+ /* TODO: Can we get TS formatted data here? If so we will
+ * need to count the samples of that too! */
+ av_log(s, AV_LOG_WARNING, "Got MPEG-TS formatted Opus data, unhandled");
}
}
- if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) {
- // for video and subtitle, write a single pes packet
- mpegts_write_pes(s, st, buf, size, pts, dts,
- pkt->flags & AV_PKT_FLAG_KEY);
- av_free(data);
- return 0;
+ if (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, stream_id);
+ ts_st2->payload_size = 0;
+ }
+ }
}
- if (ts_st->payload_size + size > ts->pes_payload_size ||
+ if (ts_st->payload_size && (ts_st->payload_size + size > ts->pes_payload_size ||
(dts != AV_NOPTS_VALUE && ts_st->payload_dts != AV_NOPTS_VALUE &&
av_compare_ts(dts - ts_st->payload_dts, st->time_base,
- s->max_delay, AV_TIME_BASE_Q) >= 0)) {
- 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;
- }
+ s->max_delay, AV_TIME_BASE_Q) >= 0) ||
+ ts_st->opus_queued_samples + opus_samples >= 5760 /* 120ms */)) {
+ 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, stream_id);
+ ts_st->payload_size = 0;
+ ts_st->opus_queued_samples = 0;
+ }
+
+ if (st->codecpar->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, stream_id);
+ ts_st->opus_queued_samples = 0;
+ av_free(data);
+ return 0;
}
if (!ts_st->payload_size) {
@@ -1166,6 +1776,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
memcpy(ts_st->payload + ts_st->payload_size, buf, size);
ts_st->payload_size += size;
+ ts_st->opus_queued_samples += opus_samples;
av_free(data);
@@ -1183,11 +1794,11 @@ static void mpegts_write_flush(AVFormatContext *s)
if (ts_st->payload_size > 0) {
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_flags & AV_PKT_FLAG_KEY, -1);
ts_st->payload_size = 0;
+ ts_st->opus_queued_samples = 0;
}
}
- avio_flush(s->pb);
}
static int mpegts_write_packet(AVFormatContext *s, AVPacket *pkt)
@@ -1202,20 +1813,27 @@ static int mpegts_write_packet(AVFormatContext *s, AVPacket *pkt)
static int mpegts_write_end(AVFormatContext *s)
{
+ if (s->pb)
+ mpegts_write_flush(s);
+
+ return 0;
+}
+
+static void mpegts_deinit(AVFormatContext *s)
+{
MpegTSWrite *ts = s->priv_data;
MpegTSService *service;
int i;
- if (s->pb)
- mpegts_write_flush(s);
-
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MpegTSWriteStream *ts_st = st->priv_data;
- av_freep(&ts_st->payload);
- if (ts_st->amux) {
- avformat_free_context(ts_st->amux);
- ts_st->amux = NULL;
+ if (ts_st) {
+ av_freep(&ts_st->payload);
+ if (ts_st->amux) {
+ avformat_free_context(ts_st->amux);
+ ts_st->amux = NULL;
+ }
}
}
@@ -1223,11 +1841,31 @@ 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;
+static int mpegts_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt)
+{
+ int ret = 1;
+ AVStream *st = s->streams[pkt->stream_index];
+
+ if (st->codecpar->codec_id == AV_CODEC_ID_H264) {
+ if (pkt->size >= 5 && AV_RB32(pkt->data) != 0x0000001 &&
+ (AV_RB24(pkt->data) != 0x000001 ||
+ (st->codecpar->extradata_size > 0 &&
+ st->codecpar->extradata[0] == 1)))
+ ret = ff_stream_add_bitstream_filter(st, "h264_mp4toannexb", NULL);
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_HEVC) {
+ if (pkt->size >= 5 && AV_RB32(pkt->data) != 0x0000001 &&
+ (AV_RB24(pkt->data) != 0x000001 ||
+ (st->codecpar->extradata_size > 0 &&
+ st->codecpar->extradata[0] == 1)))
+ ret = ff_stream_add_bitstream_filter(st, "hevc_mp4toannexb", NULL);
+ }
+
+ return ret;
}
static const AVOption options[] = {
@@ -1240,15 +1878,42 @@ 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" },
+ { "hevc_digital_hdtv", "HEVC Digital Television Service.",
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_HEVC_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.",
- offsetof(MpegTSWrite, m2ts_mode), AV_OPT_TYPE_INT,
- { .i64 = -1 }, -1, 1, AV_OPT_FLAG_ENCODING_PARAM},
+ { .i64 = 0x0100 }, 0x0010, 0x0f00, AV_OPT_FLAG_ENCODING_PARAM },
+ { "mpegts_m2ts_mode", "Enable m2ts mode.",
+ offsetof(MpegTSWrite, m2ts_mode), AV_OPT_TYPE_BOOL,
+ { .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 },
@@ -1264,16 +1929,37 @@ static const AVOption options[] = {
{ "latm", "Use LATM packetization for AAC",
0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_AAC_LATM }, 0, INT_MAX,
AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" },
+ { "pat_pmt_at_frames", "Reemit PAT and PMT at each video frame",
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_PAT_PMT_AT_FRAMES}, 0, INT_MAX,
+ AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" },
{ "system_b", "Conform to System B (DVB) instead of System A (ATSC)",
0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_SYSTEM_B }, 0, INT_MAX,
AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" },
+ { "initial_discontinuity", "Mark initial packets as discontinuous",
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_DISCONT }, 0, INT_MAX,
+ AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" },
// backward compatibility
{ "resend_headers", "Reemit PAT/PMT before writing the next packet",
offsetof(MpegTSWrite, reemit_pat_pmt), AV_OPT_TYPE_INT,
{ .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
- { "pcr_period", "PCR retransmission time",
+ { "mpegts_copyts", "don't offset dts/pts",
+ offsetof(MpegTSWrite, copyts), AV_OPT_TYPE_BOOL,
+ { .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_BOOL,
+ { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
+ { "pcr_period", "PCR retransmission time in milliseconds",
offsetof(MpegTSWrite, pcr_period), AV_OPT_TYPE_INT,
{ .i64 = PCR_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "pat_period", "PAT/PMT retransmission time limit in seconds",
+ offsetof(MpegTSWrite, pat_period), AV_OPT_TYPE_DOUBLE,
+ { .dbl = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "sdt_period", "SDT retransmission time limit in seconds",
+ offsetof(MpegTSWrite, sdt_period), AV_OPT_TYPE_DOUBLE,
+ { .dbl = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
{ NULL },
};
@@ -1292,9 +1978,11 @@ AVOutputFormat ff_mpegts_muxer = {
.priv_data_size = sizeof(MpegTSWrite),
.audio_codec = AV_CODEC_ID_MP2,
.video_codec = AV_CODEC_ID_MPEG2VIDEO,
- .write_header = mpegts_write_header,
+ .init = mpegts_init,
.write_packet = mpegts_write_packet,
.write_trailer = mpegts_write_end,
+ .deinit = mpegts_deinit,
+ .check_bitstream = mpegts_check_bitstream,
.flags = AVFMT_ALLOW_FLUSH | AVFMT_VARIABLE_FPS,
.priv_class = &mpegts_muxer_class,
};
diff --git a/libavformat/mpegvideodec.c b/libavformat/mpegvideodec.c
index 96b95a5..aca9621 100644
--- a/libavformat/mpegvideodec.c
+++ b/libavformat/mpegvideodec.c
@@ -3,26 +3,29 @@
* 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"
+#include "libavcodec/internal.h"
+
#define SEQ_START_CODE 0x000001b3
#define GOP_START_CODE 0x000001b8
#define PICTURE_START_CODE 0x00000100
@@ -34,24 +37,54 @@
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;
+ const uint8_t *ptr = p->buf, *end = ptr + p->buf_size;
+ uint32_t last = 0;
+ int j;
- for(i=0; i<p->buf_size; i++){
- code = (code<<8) + p->buf[i];
+ while (ptr < end) {
+ ptr = avpriv_find_start_code(ptr, end, &code);
if ((code & 0xffffff00) == 0x100) {
switch(code){
- case SEQ_START_CODE: seq++; break;
+ case SEQ_START_CODE:
+ if (!(ptr[3 + 1 + 2] & 0x20))
+ break;
+ j = -1;
+ if (ptr[j + 8] & 2)
+ j+= 64;
+ if (j >= end - ptr)
+ break;
+ if (ptr[j + 8] & 1)
+ j+= 64;
+ if (j >= end - ptr)
+ break;
+ if (AV_RB24(ptr + 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 1a78c32..3904ccb 100644
--- a/libavformat/mpjpeg.c
+++ b/libavformat/mpjpeg.c
@@ -2,44 +2,51 @@
* 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)
{
- avio_printf(s->pb, "--%s\n", BOUNDARY_TAG);
+ MPJPEGContext *mpj = s->priv_data;
+ avio_printf(s->pb, "--%s\r\n", mpj->boundary_tag);
avio_flush(s->pb);
return 0;
}
static int mpjpeg_write_packet(AVFormatContext *s, AVPacket *pkt)
{
- avio_printf(s->pb,
- "Content-length: %i\n"
- "Content-type: image/jpeg\n\n",
+ MPJPEGContext *mpj = s->priv_data;
+ avio_printf(s->pb, "Content-type: image/jpeg\r\n");
+ avio_printf(s->pb, "Content-length: %d\r\n\r\n",
pkt->size);
avio_write(s->pb, pkt->data, pkt->size);
- avio_printf(s->pb, "\n--%s\n", BOUNDARY_TAG);
+ avio_printf(s->pb, "\r\n--%s\r\n", mpj->boundary_tag);
return 0;
}
@@ -48,15 +55,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/mpjpegdec.c b/libavformat/mpjpegdec.c
index 844aa87..83aa70d 100644
--- a/libavformat/mpjpegdec.c
+++ b/libavformat/mpjpegdec.c
@@ -2,34 +2,56 @@
* Multipart JPEG format
* Copyright (c) 2015 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 "libavutil/avstring.h"
+#include "libavutil/opt.h"
#include "avformat.h"
#include "internal.h"
+#include "avio_internal.h"
-static int get_line(AVIOContext *pb, char *line, int line_size)
+
+
+typedef struct MPJPEGDemuxContext {
+ const AVClass *class;
+ char *boundary;
+ char *searchstr;
+ int searchstr_len;
+ int strict_mime_boundary;
+} MPJPEGDemuxContext;
+
+
+static void trim_right(char *p)
{
- int i = ff_get_line(pb, line, line_size);
+ char *end;
- if (i > 1 && line[i - 2] == '\r')
- line[i - 2] = '\0';
+ if (!p || !*p)
+ return;
+
+ end = p + strlen(p);
+ while (end > p && av_isspace(*(end-1)))
+ *(--end) = '\0';
+}
+
+static int get_line(AVIOContext *pb, char *line, int line_size)
+{
+ ff_get_line(pb, line, line_size);
if (pb->error)
return pb->error;
@@ -37,20 +59,33 @@ static int get_line(AVIOContext *pb, char *line, int line_size)
if (pb->eof_reached)
return AVERROR_EOF;
+ trim_right(line);
return 0;
}
+
+
static int split_tag_value(char **tag, char **value, char *line)
{
char *p = line;
+ int foundData = 0;
+
+ *tag = NULL;
+ *value = NULL;
- while (*p != '\0' && *p != ':')
+
+ while (*p != '\0' && *p != ':') {
+ if (!av_isspace(*p)) {
+ foundData = 1;
+ }
p++;
+ }
if (*p != ':')
- return AVERROR_INVALIDDATA;
+ return foundData ? AVERROR_INVALIDDATA : 0;
*p = '\0';
*tag = line;
+ trim_right(*tag);
p++;
@@ -58,49 +93,38 @@ static int split_tag_value(char **tag, char **value, char *line)
p++;
*value = p;
+ trim_right(*value);
return 0;
}
-static int check_content_type(char *line)
-{
- char *tag, *value;
- int ret = split_tag_value(&tag, &value, line);
-
- if (ret < 0)
- return ret;
-
- if (av_strcasecmp(tag, "Content-type") ||
- av_strcasecmp(value, "image/jpeg"))
- return AVERROR_INVALIDDATA;
+static int parse_multipart_header(AVIOContext *pb,
+ int* size,
+ const char* expected_boundary,
+ void *log_ctx);
+static int mpjpeg_read_close(AVFormatContext *s)
+{
+ MPJPEGDemuxContext *mpjpeg = s->priv_data;
+ av_freep(&mpjpeg->boundary);
+ av_freep(&mpjpeg->searchstr);
return 0;
}
static int mpjpeg_read_probe(AVProbeData *p)
{
AVIOContext *pb;
- char line[128] = { 0 };
int ret = 0;
+ int size = 0;
if (p->buf_size < 2 || p->buf[0] != '-' || p->buf[1] != '-')
return 0;
pb = avio_alloc_context(p->buf, p->buf_size, 0, NULL, NULL, NULL, NULL);
if (!pb)
- return AVERROR(ENOMEM);
-
- while (!pb->eof_reached) {
- ret = get_line(pb, line, sizeof(line));
- if (ret < 0)
- break;
+ return 0;
- ret = check_content_type(line);
- if (!ret) {
- ret = AVPROBE_SCORE_MAX;
- break;
- }
- }
+ ret = (parse_multipart_header(pb, &size, "--", NULL) >= 0) ? AVPROBE_SCORE_MAX : 0;
avio_context_free(&pb);
@@ -110,14 +134,15 @@ static int mpjpeg_read_probe(AVProbeData *p)
static int mpjpeg_read_header(AVFormatContext *s)
{
AVStream *st;
- char boundary[70 + 2 + 1];
+ char boundary[70 + 2 + 1] = {0};
int64_t pos = avio_tell(s->pb);
int ret;
-
- ret = get_line(s->pb, boundary, sizeof(boundary));
- if (ret < 0)
- return ret;
+ do {
+ ret = get_line(s->pb, boundary, sizeof(boundary));
+ if (ret < 0)
+ return ret;
+ } while (!boundary[0]);
if (strncmp(boundary, "--", 2))
return AVERROR_INVALIDDATA;
@@ -147,35 +172,51 @@ static int parse_content_length(const char *value)
return val;
}
-static int parse_multipart_header(AVFormatContext *s)
+static int parse_multipart_header(AVIOContext *pb,
+ int* size,
+ const char* expected_boundary,
+ void *log_ctx)
{
char line[128];
int found_content_type = 0;
- int ret, size = -1;
+ int ret;
+
+ *size = -1;
// get the CRLF as empty string
- ret = get_line(s->pb, line, sizeof(line));
+ ret = get_line(pb, line, sizeof(line));
if (ret < 0)
return ret;
/* some implementation do not provide the required
* initial CRLF (see rfc1341 7.2.1)
*/
- if (!line[0]) {
- ret = get_line(s->pb, line, sizeof(line));
+ while (!line[0]) {
+ ret = get_line(pb, line, sizeof(line));
if (ret < 0)
return ret;
}
- if (strncmp(line, "--", 2))
+ if (!av_strstart(line, expected_boundary, NULL)) {
+ if (log_ctx)
+ av_log(log_ctx,
+ AV_LOG_ERROR,
+ "Expected boundary '%s' not found, instead found a line of %"SIZE_SPECIFIER" bytes\n",
+ expected_boundary,
+ strlen(line));
+
return AVERROR_INVALIDDATA;
+ }
- while (!s->pb->eof_reached) {
+ while (!pb->eof_reached) {
char *tag, *value;
- ret = get_line(s->pb, line, sizeof(line));
- if (ret < 0)
+ ret = get_line(pb, line, sizeof(line));
+ if (ret < 0) {
+ if (ret == AVERROR_EOF)
+ break;
return ret;
+ }
if (line[0] == '\0')
break;
@@ -183,50 +224,182 @@ static int parse_multipart_header(AVFormatContext *s)
ret = split_tag_value(&tag, &value, line);
if (ret < 0)
return ret;
+ if (value==NULL || tag==NULL)
+ break;
if (!av_strcasecmp(tag, "Content-type")) {
if (av_strcasecmp(value, "image/jpeg")) {
- av_log(s, AV_LOG_ERROR,
- "Unexpected %s : %s\n",
- tag, value);
+ if (log_ctx)
+ av_log(log_ctx, AV_LOG_ERROR,
+ "Unexpected %s : %s\n",
+ tag, value);
return AVERROR_INVALIDDATA;
} else
found_content_type = 1;
} else if (!av_strcasecmp(tag, "Content-Length")) {
- size = parse_content_length(value);
- if (size < 0)
- return size;
+ *size = parse_content_length(value);
+ if ( *size < 0 )
+ if (log_ctx)
+ av_log(log_ctx, AV_LOG_WARNING,
+ "Invalid Content-Length value : %s\n",
+ value);
}
}
- if (!found_content_type || size < 0) {
- return AVERROR_INVALIDDATA;
+ return found_content_type ? 0 : AVERROR_INVALIDDATA;
+}
+
+
+static char* mpjpeg_get_boundary(AVIOContext* pb)
+{
+ uint8_t *mime_type = NULL;
+ const char *start;
+ const char *end;
+ uint8_t *res = NULL;
+ int len;
+
+ /* get MIME type, and skip to the first parameter */
+ av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type);
+ start = mime_type;
+ while (start != NULL && *start != '\0') {
+ start = strchr(start, ';');
+ if (!start)
+ break;
+
+ start = start+1;
+
+ while (av_isspace(*start))
+ start++;
+
+ if (!av_stristart(start, "boundary=", &start)) {
+ end = strchr(start, ';');
+ if (end)
+ len = end - start - 1;
+ else
+ len = strlen(start);
+
+ /* some endpoints may enclose the boundary
+ in Content-Type in quotes */
+ if ( len>2 && *start == '"' && start[len-1] == '"' ) {
+ start++;
+ len -= 2;
+ }
+ res = av_strndup(start, len);
+ break;
+ }
}
- return size;
+ av_freep(&mime_type);
+ return res;
}
+
static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt)
{
+ int size;
int ret;
- int size = parse_multipart_header(s);
- if (size < 0)
- return size;
+ MPJPEGDemuxContext *mpjpeg = s->priv_data;
+ if (mpjpeg->boundary == NULL) {
+ uint8_t* boundary = NULL;
+ if (mpjpeg->strict_mime_boundary) {
+ boundary = mpjpeg_get_boundary(s->pb);
+ }
+ if (boundary != NULL) {
+ mpjpeg->boundary = boundary;
+ mpjpeg->searchstr = av_asprintf( "\r\n%s\r\n", boundary );
+ } else {
+ mpjpeg->boundary = av_strdup("--");
+ mpjpeg->searchstr = av_strdup("\r\n--");
+ }
+ if (!mpjpeg->boundary || !mpjpeg->searchstr) {
+ av_freep(&mpjpeg->boundary);
+ av_freep(&mpjpeg->searchstr);
+ return AVERROR(ENOMEM);
+ }
+ mpjpeg->searchstr_len = strlen(mpjpeg->searchstr);
+ }
+
+ ret = parse_multipart_header(s->pb, &size, mpjpeg->boundary, s);
+
- ret = av_get_packet(s->pb, pkt, size);
if (ret < 0)
return ret;
- return 0;
+ if (size > 0) {
+ /* size has been provided to us in MIME header */
+ ret = av_get_packet(s->pb, pkt, size);
+ } else {
+ /* no size was given -- we read until the next boundary or end-of-file */
+ int remaining = 0, len;
+
+ const int read_chunk = 2048;
+ av_init_packet(pkt);
+ pkt->data = NULL;
+ pkt->size = 0;
+ pkt->pos = avio_tell(s->pb);
+
+ /* we may need to return as much as all we've read back to the buffer */
+ ffio_ensure_seekback(s->pb, read_chunk);
+
+ while ((ret = av_append_packet(s->pb, pkt, read_chunk - remaining)) >= 0) {
+ /* scan the new data */
+ char *start;
+
+ len = ret + remaining;
+ start = pkt->data + pkt->size - len;
+ do {
+ if (!memcmp(start, mpjpeg->searchstr, mpjpeg->searchstr_len)) {
+ // got the boundary! rewind the stream
+ avio_seek(s->pb, -len, SEEK_CUR);
+ pkt->size -= len;
+ return pkt->size;
+ }
+ len--;
+ start++;
+ } while (len >= mpjpeg->searchstr_len);
+ remaining = len;
+ }
+
+ /* error or EOF occurred */
+ if (ret == AVERROR_EOF) {
+ ret = pkt->size > 0 ? pkt->size : AVERROR_EOF;
+ } else {
+ av_packet_unref(pkt);
+ }
+ }
+
+ return ret;
}
+#define OFFSET(x) offsetof(MPJPEGDemuxContext, x)
+
+#define DEC AV_OPT_FLAG_DECODING_PARAM
+const AVOption mpjpeg_options[] = {
+ { "strict_mime_boundary", "require MIME boundaries match", OFFSET(strict_mime_boundary), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DEC },
+ { NULL }
+};
+
+
+static const AVClass mpjpeg_demuxer_class = {
+ .class_name = "MPJPEG demuxer",
+ .item_name = av_default_item_name,
+ .option = mpjpeg_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVInputFormat ff_mpjpeg_demuxer = {
.name = "mpjpeg",
.long_name = NULL_IF_CONFIG_SMALL("MIME multipart JPEG"),
.mime_type = "multipart/x-mixed-replace",
.extensions = "mjpg",
+ .priv_data_size = sizeof(MPJPEGDemuxContext),
.read_probe = mpjpeg_read_probe,
.read_header = mpjpeg_read_header,
.read_packet = mpjpeg_read_packet,
+ .read_close = mpjpeg_read_close,
+ .priv_class = &mpjpeg_demuxer_class,
+ .flags = AVFMT_NOTIMESTAMPS,
};
+
+
diff --git a/libavformat/mpl2dec.c b/libavformat/mpl2dec.c
new file mode 100644
index 0000000..dfcdf5a
--- /dev/null
+++ b/libavformat/mpl2dec.c
@@ -0,0 +1,154 @@
+/*
+ * 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 "libavutil/intreadwrite.h"
+
+#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;
+
+ if (AV_RB24(ptr) == 0xefbbbf)
+ ptr += 3;
+
+ 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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->codec_id = AV_CODEC_ID_MPL2;
+
+ if (avio_rb24(s->pb) != 0xefbbbf)
+ avio_seek(s->pb, -3, SEEK_CUR);
+
+ 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(s, &mpl2->q);
+ return res;
+}
+
+static int mpl2_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ MPL2Context *mpl2 = s->priv_data;
+ return ff_subtitles_queue_read_packet(&mpl2->q, pkt);
+}
+
+static int mpl2_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ MPL2Context *mpl2 = s->priv_data;
+ return ff_subtitles_queue_seek(&mpl2->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int mpl2_read_close(AVFormatContext *s)
+{
+ MPL2Context *mpl2 = s->priv_data;
+ ff_subtitles_queue_clean(&mpl2->q);
+ return 0;
+}
+
+AVInputFormat ff_mpl2_demuxer = {
+ .name = "mpl2",
+ .long_name = NULL_IF_CONFIG_SMALL("MPL2 subtitles"),
+ .priv_data_size = sizeof(MPL2Context),
+ .read_probe = mpl2_probe,
+ .read_header = mpl2_read_header,
+ .read_packet = mpl2_read_packet,
+ .read_seek2 = mpl2_read_seek,
+ .read_close = mpl2_read_close,
+ .extensions = "txt,mpl2",
+};
diff --git a/libavformat/mpsubdec.c b/libavformat/mpsubdec.c
new file mode 100644
index 0000000..1236efa
--- /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;
+ int multiplier = 100;
+ double current_pts = 0;
+
+ av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+ while (!avio_feof(s->pb)) {
+ char line[1024];
+ double 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;
+ } else if (sscanf(line, "%lf %lf", &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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->codec_id = AV_CODEC_ID_TEXT;
+
+ ff_subtitles_queue_finalize(s, &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/msf.c b/libavformat/msf.c
new file mode 100644
index 0000000..6bd18f2
--- /dev/null
+++ b/libavformat/msf.c
@@ -0,0 +1,110 @@
+/*
+ * MSF demuxer
+ * Copyright (c) 2015 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 msf_probe(AVProbeData *p)
+{
+ if (memcmp(p->buf, "MSF", 3))
+ return 0;
+
+ if (AV_RB32(p->buf+8) <= 0)
+ return 0;
+
+ if (AV_RB32(p->buf+16) <= 0)
+ return 0;
+
+ if (AV_RB32(p->buf+4) > 16)
+ return AVPROBE_SCORE_MAX / 5; //unsupported / unknown codec
+
+ return AVPROBE_SCORE_MAX / 3 * 2;
+}
+
+static int msf_read_header(AVFormatContext *s)
+{
+ unsigned codec, size;
+ AVStream *st;
+ int ret;
+
+ avio_skip(s->pb, 4);
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ codec = avio_rb32(s->pb);
+ st->codecpar->channels = avio_rb32(s->pb);
+ if (st->codecpar->channels <= 0 || st->codecpar->channels >= INT_MAX / 1024)
+ return AVERROR_INVALIDDATA;
+ size = avio_rb32(s->pb);
+ st->codecpar->sample_rate = avio_rb32(s->pb);
+ if (st->codecpar->sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+ // avio_rb32(s->pb); /* byte flags with encoder info */
+ switch (codec) {
+ case 0: st->codecpar->codec_id = AV_CODEC_ID_PCM_S16BE; break;
+ case 1: st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; break;
+ case 3: st->codecpar->block_align = 16 * st->codecpar->channels;
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_PSX; break;
+ case 4:
+ case 5:
+ case 6: st->codecpar->block_align = (codec == 4 ? 96 : codec == 5 ? 152 : 192) * st->codecpar->channels;
+ ret = ff_alloc_extradata(st->codecpar, 14);
+ if (ret < 0)
+ return ret;
+ memset(st->codecpar->extradata, 0, st->codecpar->extradata_size);
+ AV_WL16(st->codecpar->extradata, 1); /* version */
+ AV_WL16(st->codecpar->extradata+2, 2048 * st->codecpar->channels); /* unknown size */
+ AV_WL16(st->codecpar->extradata+6, codec == 4 ? 1 : 0); /* joint stereo */
+ AV_WL16(st->codecpar->extradata+8, codec == 4 ? 1 : 0); /* joint stereo (repeat?) */
+ AV_WL16(st->codecpar->extradata+10, 1);
+ st->codecpar->codec_id = AV_CODEC_ID_ATRAC3; break;
+ case 7: st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
+ st->codecpar->codec_id = AV_CODEC_ID_MP3; break;
+ default:
+ avpriv_request_sample(s, "Codec %d", codec);
+ return AVERROR_PATCHWELCOME;
+ }
+ st->duration = av_get_audio_frame_duration2(st->codecpar, size);
+ avio_skip(s->pb, 0x40 - avio_tell(s->pb));
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ return 0;
+}
+
+static int msf_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+
+ return av_get_packet(s->pb, pkt, par->block_align ? par->block_align : 1024 * par->channels);
+}
+
+AVInputFormat ff_msf_demuxer = {
+ .name = "msf",
+ .long_name = NULL_IF_CONFIG_SMALL("Sony PS3 MSF"),
+ .read_probe = msf_probe,
+ .read_header = msf_read_header,
+ .read_packet = msf_read_packet,
+ .extensions = "msf",
+};
diff --git a/libavformat/msnwc_tcp.c b/libavformat/msnwc_tcp.c
index 6b9589d..3c73ac7 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
*/
@@ -90,10 +90,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 AVERROR_INVALIDDATA;
}
diff --git a/libavformat/mtaf.c b/libavformat/mtaf.c
new file mode 100644
index 0000000..b25c2aa
--- /dev/null
+++ b/libavformat/mtaf.c
@@ -0,0 +1,81 @@
+/*
+ * MTAF demuxer
+ * Copyright (c) 2016 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"
+
+static int mtaf_probe(AVProbeData *p)
+{
+ if (p->buf_size < 0x44)
+ return 0;
+
+ if (AV_RL32(p->buf) != MKTAG('M','T','A','F') ||
+ AV_RL32(p->buf + 0x40) != MKTAG('H','E','A','D'))
+ return 0;
+
+ return AVPROBE_SCORE_MAX;
+}
+
+static int mtaf_read_header(AVFormatContext *s)
+{
+ int stream_count;
+ AVStream *st;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avio_skip(s->pb, 0x5c);
+ st->duration = avio_rl32(s->pb);
+ avio_skip(s->pb, 1);
+ stream_count = avio_r8(s->pb);
+ if (!stream_count)
+ return AVERROR_INVALIDDATA;
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_MTAF;
+ st->codecpar->channels = 2 * stream_count;
+ st->codecpar->sample_rate = 48000;
+ st->codecpar->block_align = 0x110 * st->codecpar->channels / 2;
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ avio_seek(s->pb, 0x800, SEEK_SET);
+
+ return 0;
+}
+
+static int mtaf_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+
+ return av_get_packet(s->pb, pkt, par->block_align);
+}
+
+AVInputFormat ff_mtaf_demuxer = {
+ .name = "mtaf",
+ .long_name = NULL_IF_CONFIG_SMALL("Konami PS2 MTAF"),
+ .read_probe = mtaf_probe,
+ .read_header = mtaf_read_header,
+ .read_packet = mtaf_read_packet,
+ .extensions = "mtaf",
+};
diff --git a/libavformat/mtv.c b/libavformat/mtv.c
index fdf13bb..dcf4aa4 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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = AV_CODEC_ID_MP3;
st->codecpar->bit_rate = mtv->audio_br;
diff --git a/libavformat/musx.c b/libavformat/musx.c
new file mode 100644
index 0000000..aff6c31
--- /dev/null
+++ b/libavformat/musx.c
@@ -0,0 +1,177 @@
+/*
+ * MUSX demuxer
+ * Copyright (c) 2016 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/avassert.h"
+#include "avformat.h"
+#include "internal.h"
+
+static int musx_probe(AVProbeData *p)
+{
+ if (memcmp(p->buf, "MUSX", 4))
+ return 0;
+
+ return AVPROBE_SCORE_MAX / 5 * 2;
+}
+
+static int musx_read_header(AVFormatContext *s)
+{
+ unsigned type, version, coding, offset;
+ AVStream *st;
+
+ avio_skip(s->pb, 8);
+ version = avio_rl32(s->pb);
+ if (version != 10 &&
+ version != 6 &&
+ version != 5 &&
+ version != 4 &&
+ version != 201) {
+ avpriv_request_sample(s, "Unsupported version: %d", version);
+ return AVERROR_PATCHWELCOME;
+ }
+ avio_skip(s->pb, 4);
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ if (version == 201) {
+ avio_skip(s->pb, 8);
+ offset = avio_rl32(s->pb);
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_PSX;
+ st->codecpar->channels = 2;
+ st->codecpar->sample_rate = 32000;
+ st->codecpar->block_align = 0x80 * st->codecpar->channels;
+ } else if (version == 10) {
+ type = avio_rl32(s->pb);
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ offset = 0x800;
+ switch (type) {
+ case MKTAG('P', 'S', '3', '_'):
+ st->codecpar->channels = 2;
+ st->codecpar->sample_rate = 44100;
+ avio_skip(s->pb, 44);
+ coding = avio_rl32(s->pb);
+ if (coding == MKTAG('D', 'A', 'T', '4') ||
+ coding == MKTAG('D', 'A', 'T', '8')) {
+ avio_skip(s->pb, 4);
+ st->codecpar->channels = avio_rl32(s->pb);
+ if (st->codecpar->channels <= 0 ||
+ st->codecpar->channels > INT_MAX / 0x20)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->sample_rate = avio_rl32(s->pb);
+ }
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_DAT4;
+ st->codecpar->block_align = 0x20 * st->codecpar->channels;
+ break;
+ case MKTAG('W', 'I', 'I', '_'):
+ avio_skip(s->pb, 44);
+ coding = avio_rl32(s->pb);
+ if (coding != MKTAG('D', 'A', 'T', '4') &&
+ coding != MKTAG('D', 'A', 'T', '8')) {
+ avpriv_request_sample(s, "Unsupported coding: %X", coding);
+ return AVERROR_PATCHWELCOME;
+ }
+ avio_skip(s->pb, 4);
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_DAT4;
+ st->codecpar->channels = avio_rl32(s->pb);
+ if (st->codecpar->channels <= 0 ||
+ st->codecpar->channels > INT_MAX / 0x20)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->sample_rate = avio_rl32(s->pb);
+ st->codecpar->block_align = 0x20 * st->codecpar->channels;
+ break;
+ case MKTAG('X', 'E', '_', '_'):
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_DAT4;
+ st->codecpar->channels = 2;
+ st->codecpar->sample_rate = 32000;
+ st->codecpar->block_align = 0x20 * st->codecpar->channels;
+ break;
+ case MKTAG('P', 'S', 'P', '_'):
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_PSX;
+ st->codecpar->channels = 2;
+ st->codecpar->sample_rate = 32768;
+ st->codecpar->block_align = 0x80 * st->codecpar->channels;
+ break;
+ case MKTAG('P', 'S', '2', '_'):
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_PSX;
+ st->codecpar->channels = 2;
+ st->codecpar->sample_rate = 32000;
+ st->codecpar->block_align = 0x80 * st->codecpar->channels;
+ break;
+ default:
+ avpriv_request_sample(s, "Unsupported type: %X", type);
+ return AVERROR_PATCHWELCOME;
+ }
+ } else if (version == 6 || version == 5 || version == 4) {
+ type = avio_rl32(s->pb);
+ avio_skip(s->pb, 20);
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->channels = 2;
+ switch (type) {
+ case MKTAG('G', 'C', '_', '_'):
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_DAT4;
+ st->codecpar->block_align = 0x20 * st->codecpar->channels;
+ st->codecpar->sample_rate = 32000;
+ offset = avio_rb32(s->pb);
+ break;
+ case MKTAG('P', 'S', '2', '_'):
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_PSX;
+ st->codecpar->block_align = 0x80 * st->codecpar->channels;
+ st->codecpar->sample_rate = 32000;
+ offset = avio_rl32(s->pb);
+ break;
+ case MKTAG('X', 'B', '_', '_'):
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_DAT4;
+ st->codecpar->block_align = 0x20 * st->codecpar->channels;
+ st->codecpar->sample_rate = 44100;
+ offset = avio_rl32(s->pb);
+ break;
+ default:
+ avpriv_request_sample(s, "Unsupported type: %X", type);
+ return AVERROR_PATCHWELCOME;
+ }
+ } else {
+ av_assert0(0);
+ }
+
+ avio_seek(s->pb, offset, SEEK_SET);
+
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ return 0;
+}
+
+static int musx_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+
+ return av_get_packet(s->pb, pkt, par->block_align);
+}
+
+AVInputFormat ff_musx_demuxer = {
+ .name = "musx",
+ .long_name = NULL_IF_CONFIG_SMALL("Eurocom MUSX"),
+ .read_probe = musx_probe,
+ .read_header = musx_read_header,
+ .read_packet = musx_read_packet,
+ .extensions = "musx",
+};
diff --git a/libavformat/mux.c b/libavformat/mux.c
index a85c3c7..53ad46d 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,8 +27,10 @@
#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"
#include "libavutil/avstring.h"
#include "libavutil/internal.h"
#include "libavutil/mathematics.h"
@@ -42,20 +44,166 @@
#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(FFFrac *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(FFFrac *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;
+}
+
+enum AVChromaLocation ff_choose_chroma_location(AVFormatContext *s, AVStream *st)
+{
+ AVCodecParameters *par = st->codecpar;
+ const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(par->format);
+
+ if (par->chroma_location != AVCHROMA_LOC_UNSPECIFIED)
+ return par->chroma_location;
+
+ if (pix_desc) {
+ if (pix_desc->log2_chroma_h == 0) {
+ return AVCHROMA_LOC_TOPLEFT;
+ } else if (pix_desc->log2_chroma_w == 1 && pix_desc->log2_chroma_h == 1) {
+ if (par->field_order == AV_FIELD_UNKNOWN || par->field_order == AV_FIELD_PROGRESSIVE) {
+ switch (par->codec_id) {
+ case AV_CODEC_ID_MJPEG:
+ case AV_CODEC_ID_MPEG1VIDEO: return AVCHROMA_LOC_CENTER;
+ }
+ }
+ if (par->field_order == AV_FIELD_UNKNOWN || par->field_order != AV_FIELD_PROGRESSIVE) {
+ switch (par->codec_id) {
+ case AV_CODEC_ID_MPEG2VIDEO: return AVCHROMA_LOC_LEFT;
+ }
+ }
+ }
+ }
+
+ return AVCHROMA_LOC_UNSPECIFIED;
+
+}
+
+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
@@ -78,7 +226,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,23 +240,42 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options)
AVCodecParameters *par = NULL;
AVOutputFormat *of = s->oformat;
const AVCodecDescriptor *desc;
+ 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 && FF_API_LAVF_AVCTX
+#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS
- if (s->nb_streams && s->streams[0]->codec->flags & AV_CODEC_FLAG_BITEXACT)
- s->flags |= AVFMT_FLAG_BITEXACT;
+ if (s->nb_streams && s->streams[0]->codec->flags & AV_CODEC_FLAG_BITEXACT) {
+ if (!(s->flags & AVFMT_FLAG_BITEXACT)) {
+#if FF_API_LAVF_BITEXACT
+ av_log(s, AV_LOG_WARNING,
+ "Setting the AVFormatContext to bitexact mode, because "
+ "the AVCodecContext is in that mode. This behavior will "
+ "change in the future. To keep the current behavior, set "
+ "AVFormatContext.flags |= AVFMT_FLAG_BITEXACT.\n");
+ s->flags |= AVFMT_FLAG_BITEXACT;
+#else
+ av_log(s, AV_LOG_WARNING,
+ "The AVFormatContext is not in set to bitexact mode, only "
+ "the AVCodecContext. If this is not intended, set "
+ "AVFormatContext.flags |= AVFMT_FLAG_BITEXACT.\n");
+#endif
+ }
+ }
FF_ENABLE_DEPRECATION_WARNINGS
#endif
// 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;
}
@@ -168,18 +335,18 @@ FF_ENABLE_DEPRECATION_WARNINGS
ret = AVERROR(EINVAL);
goto fail;
}
-
- if (av_cmp_q(st->sample_aspect_ratio,
- par->sample_aspect_ratio)) {
+ if (av_cmp_q(st->sample_aspect_ratio, par->sample_aspect_ratio)
+ && fabs(av_q2d(st->sample_aspect_ratio) - av_q2d(par->sample_aspect_ratio)) > 0.004*av_q2d(st->sample_aspect_ratio)
+ ) {
if (st->sample_aspect_ratio.num != 0 &&
st->sample_aspect_ratio.den != 0 &&
- par->sample_aspect_ratio.den != 0 &&
+ par->sample_aspect_ratio.num != 0 &&
par->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,
- par->sample_aspect_ratio.num,
- par->sample_aspect_ratio.den);
+ "(%d/%d) and encoder layer (%d/%d)\n",
+ st->sample_aspect_ratio.num, st->sample_aspect_ratio.den,
+ par->sample_aspect_ratio.num,
+ par->sample_aspect_ratio.den);
ret = AVERROR(EINVAL);
goto fail;
}
@@ -192,21 +359,21 @@ FF_ENABLE_DEPRECATION_WARNINGS
st->internal->reorder = 1;
if (of->codec_tag) {
- if (par->codec_tag &&
- par->codec_id == AV_CODEC_ID_RAWVIDEO &&
- !av_codec_get_tag(of->codec_tag, par->codec_id) &&
- !validate_codec_tag(s, st)) {
+ if ( par->codec_tag
+ && par->codec_id == AV_CODEC_ID_RAWVIDEO
+ && ( av_codec_get_tag(of->codec_tag, par->codec_id) == 0
+ || av_codec_get_tag(of->codec_tag, par->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
par->codec_tag = 0;
}
if (par->codec_tag) {
if (!validate_codec_tag(s, st)) {
- char tagbuf[32];
- av_get_codec_tag_string(tagbuf, sizeof(tagbuf), par->codec_tag);
+ const uint32_t otag = av_codec_get_tag(s->oformat->codec_tag, par->codec_id);
av_log(s, AV_LOG_ERROR,
- "Tag %s/0x%08"PRIx32" incompatible with output codec id '%d'\n",
- tagbuf, par->codec_tag, par->codec_id);
+ "Tag %s incompatible with output codec id '%d' (%s)\n",
+ av_fourcc2str(par->codec_tag), par->codec_id, av_fourcc2str(otag));
ret = AVERROR_INVALIDDATA;
goto fail;
}
@@ -227,7 +394,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;
}
}
@@ -235,6 +402,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) {
@@ -242,6 +415,15 @@ FF_ENABLE_DEPRECATION_WARNINGS
*options = tmp;
}
+ if (s->oformat->init) {
+ if ((ret = s->oformat->init(s)) < 0) {
+ if (s->oformat->deinit)
+ s->oformat->deinit(s);
+ return ret;
+ }
+ return ret == 0;
+ }
+
return 0;
fail:
@@ -249,43 +431,158 @@ fail:
return ret;
}
-int avformat_write_header(AVFormatContext *s, AVDictionary **options)
+static int init_pts(AVFormatContext *s)
{
- int ret = 0;
+ int i;
+ AVStream *st;
- if (ret = init_muxer(s, options))
- return ret;
+ /* init PTS generation */
+ for (i = 0; i < s->nb_streams; i++) {
+ int64_t den = AV_NOPTS_VALUE;
+ st = s->streams[i];
+
+ switch (st->codecpar->codec_type) {
+ case AVMEDIA_TYPE_AUDIO:
+ den = (int64_t)st->time_base.num * st->codecpar->sample_rate;
+ break;
+ case AVMEDIA_TYPE_VIDEO:
+ den = (int64_t)st->time_base.num * st->time_base.den;
+ break;
+ default:
+ break;
+ }
+
+ if (!st->priv_pts)
+ st->priv_pts = av_mallocz(sizeof(*st->priv_pts));
+ if (!st->priv_pts)
+ return AVERROR(ENOMEM);
+
+ if (den != AV_NOPTS_VALUE) {
+ if (den <= 0)
+ return AVERROR_INVALIDDATA;
+ frac_init(st->priv_pts, 0, 0, den);
+ }
+ }
+
+ return 0;
+}
+
+static void flush_if_needed(AVFormatContext *s)
+{
+ if (s->pb && s->pb->error >= 0) {
+ if (s->flush_packets == 1 || s->flags & AVFMT_FLAG_FLUSH_PACKETS)
+ avio_flush(s->pb);
+ else if (s->flush_packets && !(s->oformat->flags & AVFMT_NOFILE))
+ avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT);
+ }
+}
+
+static int write_header_internal(AVFormatContext *s)
+{
if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb)
avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_HEADER);
if (s->oformat->write_header) {
- ret = s->oformat->write_header(s);
+ int ret = s->oformat->write_header(s);
+ if (ret >= 0 && s->pb && s->pb->error < 0)
+ ret = s->pb->error;
+ s->internal->write_header_ret = ret;
if (ret < 0)
return ret;
+ flush_if_needed(s);
}
+ s->internal->header_written = 1;
if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb)
avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_UNKNOWN);
+ return 0;
+}
+
+int avformat_init_output(AVFormatContext *s, AVDictionary **options)
+{
+ int ret = 0;
- if (s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO) {
- if (s->oformat->flags & (AVFMT_TS_NEGATIVE | AVFMT_NOTIMESTAMPS)) {
- s->avoid_negative_ts = 0;
- } else
- s->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE;
+ if ((ret = init_muxer(s, options)) < 0)
+ return ret;
+
+ s->internal->initialized = 1;
+ s->internal->streams_initialized = ret;
+
+ if (s->oformat->init && ret) {
+ 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
+ s->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE;
+ }
+
+ return AVSTREAM_INIT_IN_INIT_OUTPUT;
}
- return 0;
+ return AVSTREAM_INIT_IN_WRITE_HEADER;
}
+int avformat_write_header(AVFormatContext *s, AVDictionary **options)
+{
+ int ret = 0;
+ int already_initialized = s->internal->initialized;
+ int streams_already_initialized = s->internal->streams_initialized;
+
+ if (!already_initialized)
+ if ((ret = avformat_init_output(s, options)) < 0)
+ return ret;
+
+ if (!(s->oformat->check_bitstream && s->flags & AVFMT_FLAG_AUTO_BSF)) {
+ ret = write_header_internal(s);
+ if (ret < 0)
+ goto fail;
+ }
+
+ if (!s->internal->streams_initialized) {
+ if ((ret = init_pts(s)) < 0)
+ goto fail;
+
+ 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
+ s->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE;
+ }
+ }
+
+ return streams_already_initialized;
+
+fail:
+ if (s->oformat->deinit)
+ s->oformat->deinit(s);
+ return ret;
+}
+
+#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))
+
+
#if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS
//FIXME merge with compute_pkt_fields
-static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt)
+static int compute_muxer_pkt_fields(AVFormatContext *s, AVStream *st, AVPacket *pkt)
{
- int delay = FFMAX(st->codec->has_b_frames, !!st->codec->max_b_frames);
+ int delay = FFMAX(st->codecpar->video_delay, st->internal->avctx->max_b_frames > 0);
int num, den, i;
+ int frame_size;
if (!s->internal->missing_ts_warning &&
!(s->oformat->flags & AVFMT_NOTIMESTAMPS) &&
+ (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC) || (st->disposition & AV_DISPOSITION_TIMED_THUMBNAILS)) &&
(pkt->pts == AV_NOPTS_VALUE || pkt->dts == AV_NOPTS_VALUE)) {
av_log(s, AV_LOG_WARNING,
"Timestamps are unset in a packet for stream %d. "
@@ -294,11 +591,15 @@ static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt)
s->internal->missing_ts_warning = 1;
}
- av_log(s, AV_LOG_TRACE, "compute_pkt_fields2: pts:%" PRId64 " dts:%" PRId64 " cur_dts:%" PRId64 " b:%d size:%d st:%d\n",
- pkt->pts, pkt->dts, st->cur_dts, delay, pkt->size, pkt->stream_index);
+ if (s->debug & FF_FDEBUG_TS)
+ av_log(s, AV_LOG_TRACE, "compute_muxer_pkt_fields: 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->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
+ av_log(s, AV_LOG_WARNING, "Packet with invalid duration %"PRId64" in stream %d\n",
+ pkt->duration, pkt->stream_index);
+ pkt->duration = 0;
+ }
/* duration field */
if (pkt->duration == 0) {
@@ -311,6 +612,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->priv_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;
@@ -324,75 +637,168 @@ 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->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE &&
+ st->codecpar->codec_type != AVMEDIA_TYPE_DATA &&
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_log(s, AV_LOG_TRACE, "av_write_frame: pts2:%"PRId64" dts2:%"PRId64"\n",
- pkt->pts, pkt->dts);
- st->cur_dts = pkt->dts;
+ if (s->debug & FF_FDEBUG_TS)
+ av_log(s, AV_LOG_TRACE, "av_write_frame: pts2:%s dts2:%s\n",
+ av_ts2str(pkt->pts), av_ts2str(pkt->dts));
+ st->cur_dts = pkt->dts;
+ st->priv_pts->val = pkt->dts;
+
+ /* update pts */
+ switch (st->codecpar->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->priv_pts->num != st->priv_pts->den >> 1 || st->priv_pts->val)) {
+ frac_add(st->priv_pts, (int64_t)st->time_base.den * frame_size);
+ }
+ break;
+ case AVMEDIA_TYPE_VIDEO:
+ frac_add(st->priv_pts, (int64_t)st->time_base.den * st->time_base.num);
+ break;
+ }
return 0;
}
FF_ENABLE_DEPRECATION_WARNINGS
#endif
-/*
+/**
+ * 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;
+ int64_t pts_backup, dts_backup;
+
+ pts_backup = pkt->pts;
+ dts_backup = pkt->dts;
+
// If the timestamp offsetting below is adjusted, adjust
// ff_interleaved_peek similarly.
+ 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;
+ int64_t ts = s->internal->avoid_negative_ts_use_pts ? pkt->pts : pkt->dts;
- 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;
+ if (s->internal->offset == AV_NOPTS_VALUE && ts != AV_NOPTS_VALUE &&
+ (ts < 0 || s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_MAKE_ZERO)) {
+ s->internal->offset = -ts;
+ 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;
- 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"
- "Try -max_interleave_delta 0 as a possible workaround.\n",
- pkt->dts, pkt->stream_index);
+ if (s->internal->avoid_negative_ts_use_pts) {
+ if (pkt->pts != AV_NOPTS_VALUE && pkt->pts < 0) {
+ av_log(s, AV_LOG_WARNING, "failed to avoid negative "
+ "pts %s in stream %d.\n"
+ "Try -avoid_negative_ts 1 as a possible workaround.\n",
+ av_ts2str(pkt->pts),
+ pkt->stream_index
+ );
+ }
+ } else {
+ 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 %s in stream %d.\n"
+ "Try -max_interleave_delta 0 as a possible workaround.\n",
+ av_ts2str(pkt->dts),
+ pkt->stream_index
+ );
+ }
}
}
- ret = s->oformat->write_packet(s, pkt);
+
+#if FF_API_LAVF_MERGE_SD
+FF_DISABLE_DEPRECATION_WARNINGS
+ did_split = av_packet_split_side_data(pkt);
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+ if (!s->internal->header_written) {
+ ret = s->internal->write_header_ret ? s->internal->write_header_ret : write_header_internal(s);
+ if (ret < 0)
+ goto fail;
+ }
+
+ 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->pb && ret >= 0) {
- if (s->flags & AVFMT_FLAG_FLUSH_PACKETS)
- avio_flush(s->pb);
+ flush_if_needed(s);
if (s->pb->error < 0)
ret = s->pb->error;
}
+fail:
+#if FF_API_LAVF_MERGE_SD
+FF_DISABLE_DEPRECATION_WARNINGS
+ if (did_split)
+ av_packet_merge_side_data(pkt);
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+ if (ret < 0) {
+ pkt->pts = pts_backup;
+ pkt->dts = dts_backup;
+ }
+
return ret;
}
@@ -467,6 +873,57 @@ static int prepare_input_packet(AVFormatContext *s, AVPacket *pkt)
return 0;
}
+static int do_packet_auto_bsf(AVFormatContext *s, AVPacket *pkt) {
+ AVStream *st = s->streams[pkt->stream_index];
+ int i, ret;
+
+ if (!(s->flags & AVFMT_FLAG_AUTO_BSF))
+ return 1;
+
+ if (s->oformat->check_bitstream) {
+ if (!st->internal->bitstream_checked) {
+ if ((ret = s->oformat->check_bitstream(s, pkt)) < 0)
+ return ret;
+ else if (ret == 1)
+ st->internal->bitstream_checked = 1;
+ }
+ }
+
+#if FF_API_LAVF_MERGE_SD
+FF_DISABLE_DEPRECATION_WARNINGS
+ if (st->internal->nb_bsfcs) {
+ ret = av_packet_split_side_data(pkt);
+ if (ret < 0)
+ av_log(s, AV_LOG_WARNING, "Failed to split side data before bitstream filter\n");
+ }
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+ for (i = 0; i < st->internal->nb_bsfcs; i++) {
+ AVBSFContext *ctx = st->internal->bsfcs[i];
+ // TODO: when any bitstream filter requires flushing at EOF, we'll need to
+ // flush each stream's BSF chain on write_trailer.
+ if ((ret = av_bsf_send_packet(ctx, pkt)) < 0) {
+ av_log(ctx, AV_LOG_ERROR,
+ "Failed to send packet to filter %s for stream %d\n",
+ ctx->filter->name, pkt->stream_index);
+ return ret;
+ }
+ // TODO: when any automatically-added bitstream filter is generating multiple
+ // output packets for a single input one, we'll need to call this in a loop
+ // and write each output packet.
+ if ((ret = av_bsf_receive_packet(ctx, pkt)) < 0) {
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
+ return 0;
+ av_log(ctx, AV_LOG_ERROR,
+ "Failed to send packet to filter %s for stream %d\n",
+ ctx->filter->name, pkt->stream_index);
+ return ret;
+ }
+ }
+ return 1;
+}
+
int av_write_frame(AVFormatContext *s, AVPacket *pkt)
{
int ret;
@@ -476,55 +933,107 @@ 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) {
+ if (!s->internal->header_written) {
+ ret = s->internal->write_header_ret ? s->internal->write_header_ret : write_header_internal(s);
+ if (ret < 0)
+ return ret;
+ }
+ ret = s->oformat->write_packet(s, NULL);
+ flush_if_needed(s);
+ if (ret >= 0 && s->pb && s->pb->error < 0)
+ ret = s->pb->error;
+ return ret;
+ }
return 1;
}
+ ret = do_packet_auto_bsf(s, pkt);
+ if (ret <= 0)
+ return ret;
+
#if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX
- ret = compute_pkt_fields2(s, s->streams[pkt->stream_index], pkt);
+ ret = compute_muxer_pkt_fields(s, s->streams[pkt->stream_index], pkt);
if (ret < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))
return ret;
#endif
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)
return AVERROR(ENOMEM);
-
- if ((ret = av_packet_ref(&this_pktl->pkt, pkt)) < 0) {
- av_free(this_pktl);
- return ret;
+ if ((pkt->flags & AV_PKT_FLAG_UNCODED_FRAME)) {
+ av_assert0(pkt->size == UNCODED_FRAME_PACKET_SIZE);
+ av_assert0(((AVFrame *)pkt->data)->buf);
+ this_pktl->pkt = *pkt;
+ pkt->buf = NULL;
+ pkt->side_data = NULL;
+ pkt->side_data_elems = 0;
+ } else {
+ if ((ret = av_packet_ref(&this_pktl->pkt, 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->codecpar->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:
@@ -546,6 +1055,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->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) != (st2->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))) {
+ int64_t ts = av_rescale_q(pkt ->dts, st ->time_base, AV_TIME_BASE_Q) - s->audio_preload*(st ->codecpar->codec_type == AVMEDIA_TYPE_AUDIO);
+ int64_t ts2= av_rescale_q(next->dts, st2->time_base, AV_TIME_BASE_Q) - s->audio_preload*(st2->codecpar->codec_type == AVMEDIA_TYPE_AUDIO);
+ if (ts == ts2) {
+ ts= ( pkt ->dts* st->time_base.num*AV_TIME_BASE - s->audio_preload*(int64_t)(st ->codecpar->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->codecpar->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;
@@ -557,14 +1076,33 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out,
{
AVPacketList *pktl;
int stream_count = 0;
+ int noninterleaved_count = 0;
int i, ret;
+ int eof = flush;
if (pkt) {
if ((ret = ff_interleave_add_packet(s, pkt, interleave_compare_dts)) < 0)
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]->codecpar->codec_type != AVMEDIA_TYPE_ATTACHMENT &&
+ s->streams[i]->codecpar->codec_id != AV_CODEC_ID_VP8 &&
+ s->streams[i]->codecpar->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,
@@ -582,7 +1120,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) {
@@ -592,23 +1129,60 @@ 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 (s->internal->packet_buffer &&
+ eof &&
+ (s->flags & AVFMT_FLAG_SHORTEST) &&
+ s->internal->shortest_end == AV_NOPTS_VALUE) {
+ AVPacket *top_pkt = &s->internal->packet_buffer->pkt;
+
+ s->internal->shortest_end = av_rescale_q(top_pkt->dts,
+ s->streams[top_pkt->stream_index]->time_base,
+ AV_TIME_BASE_Q);
+ }
- if (stream_count && (s->internal->nb_interleaved_streams == stream_count || flush)) {
+ if (s->internal->shortest_end != AV_NOPTS_VALUE) {
+ while (s->internal->packet_buffer) {
+ AVPacket *top_pkt = &s->internal->packet_buffer->pkt;
+ AVStream *st;
+ int64_t top_dts = av_rescale_q(top_pkt->dts,
+ s->streams[top_pkt->stream_index]->time_base,
+ AV_TIME_BASE_Q);
+
+ if (s->internal->shortest_end + 1 >= top_dts)
+ break;
+
+ pktl = s->internal->packet_buffer;
+ st = s->streams[pktl->pkt.stream_index];
+
+ s->internal->packet_buffer = pktl->next;
+ if (!s->internal->packet_buffer)
+ s->internal->packet_buffer_end = NULL;
+
+ if (st->last_in_packet_buffer == pktl)
+ st->last_in_packet_buffer = NULL;
+
+ av_packet_unref(&pktl->pkt);
+ av_freep(&pktl);
+ flush = 0;
+ }
+ }
+
+ 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);
@@ -623,10 +1197,13 @@ int ff_interleaved_peek(AVFormatContext *s, int stream,
while (pktl) {
if (pktl->pkt.stream_index == stream) {
*pkt = pktl->pkt;
- if (add_offset && s->internal->offset != AV_NOPTS_VALUE) {
- int64_t offset = av_rescale_q(s->internal->offset,
- s->internal->offset_timebase,
- s->streams[stream]->time_base);
+ if (add_offset) {
+ AVStream *st = s->streams[pkt->stream_index];
+ int64_t offset = st->mux_ts_offset;
+
+ if (s->output_ts_offset)
+ 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)
@@ -668,12 +1245,20 @@ int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt)
goto fail;
if (pkt) {
-#if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX
AVStream *st = s->streams[pkt->stream_index];
- av_log(s, AV_LOG_TRACE, "av_interleaved_write_frame size:%d dts:%" PRId64 " pts:%" PRId64 "\n",
- pkt->size, pkt->dts, pkt->pts);
- if ((ret = compute_pkt_fields2(s, st, pkt)) < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))
+ ret = do_packet_auto_bsf(s, pkt);
+ if (ret == 0)
+ return 0;
+ else if (ret < 0)
+ goto fail;
+
+ if (s->debug & FF_FDEBUG_TS)
+ av_log(s, AV_LOG_TRACE, "av_interleaved_write_frame size:%d dts:%s pts:%s\n",
+ pkt->size, av_ts2str(pkt->dts), av_ts2str(pkt->pts));
+
+#if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX
+ if ((ret = compute_muxer_pkt_fields(s, st, pkt)) < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))
goto fail;
#endif
@@ -705,6 +1290,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);
@@ -718,7 +1305,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;
@@ -731,17 +1318,38 @@ int av_write_trailer(AVFormatContext *s)
if (ret < 0)
goto fail;
+ if(s->pb && s->pb->error)
+ goto fail;
}
- if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb)
- avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_TRAILER);
- if (s->oformat->write_trailer)
+ if (!s->internal->header_written) {
+ ret = s->internal->write_header_ret ? s->internal->write_header_ret : write_header_internal(s);
+ if (ret < 0)
+ goto fail;
+ }
+
+fail:
+ if (s->internal->header_written && s->oformat->write_trailer) {
+ if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb)
+ avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_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);
+ if (s->oformat->deinit)
+ s->oformat->deinit(s);
-fail:
+ s->internal->header_written =
+ s->internal->initialized =
+ s->internal->streams_initialized = 0;
+
+ 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);
@@ -752,10 +1360,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;
@@ -767,5 +1385,63 @@ 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->side_data = local_pkt.side_data;
+ pkt->side_data_elems = local_pkt.side_data_elems;
+ 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 = frame->pkt_duration;
+ 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 52c5355..f7aa4cb 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);
@@ -306,6 +317,10 @@ static int mv_read_header(AVFormatContext *avctx)
ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
ast->nb_frames = vst->nb_frames;
ast->codecpar->sample_rate = avio_rb32(pb);
+ if (ast->codecpar->sample_rate <= 0) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid sample rate %d\n", ast->codecpar->sample_rate);
+ return AVERROR_INVALIDDATA;
+ }
avpriv_set_pts_info(ast, 33, 1, ast->codecpar->sample_rate);
if (set_channels(avctx, ast, avio_rb32(pb)) < 0)
return AVERROR_INVALIDDATA;
@@ -327,6 +342,8 @@ static int mv_read_header(AVFormatContext *avctx)
uint32_t pos = avio_rb32(pb);
uint32_t asize = avio_rb32(pb);
uint32_t vsize = avio_rb32(pb);
+ if (avio_feof(pb))
+ return AVERROR_INVALIDDATA;
avio_skip(pb, 8);
av_add_index_entry(ast, pos, timestamp, asize, 0, AVINDEX_KEYFRAME);
av_add_index_entry(vst, pos + asize, i, vsize, 0, AVINDEX_KEYFRAME);
@@ -335,7 +352,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 +363,8 @@ static int mv_read_header(AVFormatContext *avctx)
if (!ast)
return AVERROR(ENOMEM);
ast->codecpar->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->codecpar->bits_per_coded_sample == 16) {
@@ -371,7 +390,8 @@ static int mv_read_header(AVFormatContext *avctx)
if (!vst)
return AVERROR(ENOMEM);
vst->codecpar->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)
@@ -394,7 +414,7 @@ static int mv_read_packet(AVFormatContext *avctx, AVPacket *pkt)
AVStream *st = avctx->streams[mv->stream_index];
const AVIndexEntry *index;
int frame = mv->frame[mv->stream_index];
- int ret;
+ int64_t ret;
uint64_t pos;
if (frame < st->nb_index_entries) {
diff --git a/libavformat/mvi.c b/libavformat/mvi.c
index 1874074..9f90faf 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->codecpar->extradata_size = 2;
- vst->codecpar->extradata = av_mallocz(2 + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (ff_alloc_extradata(vst->codecpar, 2))
+ return AVERROR(ENOMEM);
version = avio_r8(pb);
vst->codecpar->extradata[0] = avio_r8(pb);
diff --git a/libavformat/mxf.c b/libavformat/mxf.c
index 0b7e9823..bfc3218 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 },
};
@@ -40,13 +42,26 @@ const MXFCodecUL ff_mxf_codec_uls[] = {
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x20,0x02,0x03 }, 14, AV_CODEC_ID_MPEG4 }, /* XDCAM proxy_pal030926.mxf */
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x01,0x02,0x00 }, 13, AV_CODEC_ID_DVVIDEO }, /* DV25 IEC PAL */
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x07,0x04,0x01,0x02,0x02,0x03,0x01,0x01,0x00 }, 14, AV_CODEC_ID_JPEG2000 }, /* JPEG 2000 code stream */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x04,0x01,0x00,0x00 }, 14, AV_CODEC_ID_VC1 }, /* VC1 SP@LL */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x04,0x02,0x00,0x00 }, 14, AV_CODEC_ID_VC1 }, /* VC1 SP@ML */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x04,0x03,0x00,0x00 }, 14, AV_CODEC_ID_VC1 }, /* VC1 MP@LL */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x04,0x04,0x00,0x00 }, 14, AV_CODEC_ID_VC1 }, /* VC1 MP@ML */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x04,0x05,0x00,0x00 }, 14, AV_CODEC_ID_VC1 }, /* VC1 MP@HL */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x04,0x06,0x00,0x00 }, 14, AV_CODEC_ID_VC1 }, /* VC1 AP@L0 */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x04,0x07,0x00,0x00 }, 14, AV_CODEC_ID_VC1 }, /* VC1 AP@L1 */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x04,0x08,0x00,0x00 }, 14, AV_CODEC_ID_VC1 }, /* VC1 AP@L2 */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x04,0x09,0x00,0x00 }, 14, AV_CODEC_ID_VC1 }, /* VC1 AP@L3 */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x04,0x0A,0x00,0x00 }, 14, AV_CODEC_ID_VC1 }, /* VC1 AP@L4 */
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x01,0x7F,0x00,0x00,0x00 }, 13, AV_CODEC_ID_RAWVIDEO }, /* uncompressed */
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x01,0x01,0x02,0x01,0x00 }, 15, AV_CODEC_ID_RAWVIDEO }, /* uncompressed 422 8-bit */
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x71,0x00,0x00,0x00 }, 13, AV_CODEC_ID_DNXHD }, /* SMPTE VC-3/DNxHD */
{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x03,0x02,0x00,0x00 }, 14, AV_CODEC_ID_DNXHD }, /* SMPTE VC-3/DNxHD */
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,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 */
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0E,0x04,0x02,0x01,0x02,0x11,0x00,0x00 }, 14, AV_CODEC_ID_PRORES }, /* Avid MC7 ProRes */
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0D,0x04,0x01,0x02,0x02,0x03,0x06,0x00,0x00 }, 14, AV_CODEC_ID_PRORES }, /* Apple ProRes */
/* SoundEssenceCompression */
{ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x03,0x04,0x02,0x02,0x02,0x03,0x03,0x01,0x00 }, 14, AV_CODEC_ID_AAC }, /* MPEG-2 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 */
@@ -65,6 +80,11 @@ const MXFCodecUL ff_mxf_pixel_format_uls[] = {
{ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_PIX_FMT_NONE },
};
+const MXFCodecUL ff_mxf_codec_tag_uls[] = {
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0E,0x04,0x03,0x01,0x01,0x03,0x01,0x00 }, 15, MKTAG('A', 'V', 'u', 'p') }, /* Avid 1:1 */
+ { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, 0 },
+};
+
static const struct {
enum AVPixelFormat pix_fmt;
const char data[16];
@@ -91,6 +111,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 2443207..f3db1f9 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,16 @@ enum MXFMetadataSetType {
IndexTableSegment,
EssenceContainerData,
TypeBottom,// add metadata type before this
+ EssenceGroup,
+ TaggedValue,
};
enum MXFFrameLayout {
- FullFrame = 0,
- MixedFields,
- OneField,
- SegmentedFrame,
- SeparateFields
+ FullFrame = 0,
+ SeparateFields,
+ OneField,
+ MixedFields,
+ SegmentedFrame,
};
typedef struct KLVPacket {
@@ -67,7 +70,7 @@ typedef struct MXFCodecUL {
int id;
} MXFCodecUL;
-typedef struct MXFSamplesPerFrame {
+typedef struct {
struct AVRational time_base;
int samples_per_frame[6];
} MXFSamplesPerFrame;
@@ -75,6 +78,7 @@ typedef struct MXFSamplesPerFrame {
extern const MXFCodecUL ff_mxf_data_definition_uls[];
extern const MXFCodecUL ff_mxf_codec_uls[];
extern const MXFCodecUL ff_mxf_pixel_format_uls[];
+extern const MXFCodecUL ff_mxf_codec_tag_uls[];
int ff_mxf_decode_pixel_layout(const char pixel_layout[16], enum AVPixelFormat *pix_fmt);
const MXFSamplesPerFrame *ff_mxf_get_samples_per_frame(AVFormatContext *s, AVRational time_base);
@@ -99,24 +103,24 @@ const MXFSamplesPerFrame *ff_mxf_get_samples_per_frame(AVFormatContext *s, AVRat
"0x%02x,0x%02x,0x%02x,0x%02x," \
"0x%02x,0x%02x,0x%02x,0x%02x," \
"0x%02x,0x%02x,0x%02x,0x%02x ", \
- s, \
- (x)[0], (x)[1], (x)[2], (x)[3], \
- (x)[4], (x)[5], (x)[6], (x)[7], \
- (x)[8], (x)[9], (x)[10], (x)[11], \
- (x)[12], (x)[13], (x)[14], (x)[15]); \
+ s, UID_ARG(x)); \
av_log(pc, AV_LOG_INFO, \
"%s " \
"%02x.%02x.%02x.%02x." \
"%02x.%02x.%02x.%02x." \
"%02x.%02x.%02x.%02x." \
"%02x.%02x.%02x.%02x\n", \
- s, \
- (x)[0], (x)[1], (x)[2], (x)[3], \
- (x)[4], (x)[5], (x)[6], (x)[7], \
- (x)[8], (x)[9], (x)[10], (x)[11], \
- (x)[12], (x)[13], (x)[14], (x)[15])
+ s, UID_ARG(x))
#else
-#define PRINT_KEY(pc, s, x)
+#define PRINT_KEY(pc, s, x) do { if(0) \
+ av_log(pc, AV_LOG_VERBOSE, \
+ "%s " \
+ "0x%02x,0x%02x,0x%02x,0x%02x," \
+ "0x%02x,0x%02x,0x%02x,0x%02x," \
+ "0x%02x,0x%02x,0x%02x,0x%02x," \
+ "0x%02x,0x%02x,0x%02x,0x%02x ", \
+ s, UID_ARG(x)); \
+ }while(0)
#endif
#endif /* AVFORMAT_MXF_H */
diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
index cf4931e..118e3e4 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,12 @@
#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/parseutils.h"
+#include "libavutil/timecode.h"
#include "avformat.h"
#include "internal.h"
#include "mxf.h"
@@ -69,7 +73,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 +90,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 {
@@ -97,6 +102,7 @@ typedef struct MXFCryptoContext {
typedef struct MXFStructuralComponent {
UID uid;
enum MXFMetadataSetType type;
+ UID source_package_ul;
UID source_package_uid;
UID data_definition_ul;
int64_t duration;
@@ -117,9 +123,40 @@ 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;
+ char *name;
+ char *value;
+} MXFTaggedValue;
+
+typedef struct {
+ UID uid;
+ enum MXFMetadataSetType type;
MXFSequence *sequence; /* mandatory, and only one */
UID sequence_ref;
int track_id;
+ char *name;
uint8_t track_number[4];
AVRational edit_rate;
int intra_only;
@@ -132,16 +169,20 @@ typedef struct MXFDescriptor {
enum MXFMetadataSetType type;
UID essence_container_ul;
UID essence_codec_ul;
+ UID codec_ul;
AVRational sample_rate;
AVRational aspect_ratio;
int width;
int height; /* Field height, not frame height */
int frame_layout; /* See MXFFrameLayout enum */
-#define MXF_TFF 1
-#define MXF_BFF 2
+ int video_line_map[2];
+#define MXF_FIELD_DOMINANCE_DEFAULT 0
+#define MXF_FIELD_DOMINANCE_FF 1 /* coded first, displayed first */
+#define MXF_FIELD_DOMINANCE_FL 2 /* coded first, displayed last */
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 +213,14 @@ 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;
+ UID *comment_refs;
+ int comment_count;
} MXFPackage;
typedef struct MXFMetadataSet {
@@ -193,6 +238,7 @@ typedef struct MXFIndexTable {
int nb_segments;
MXFIndexTableSegment **segments; /* sorted by IndexStartPosition */
AVIndexEntry *fake_index; /* used for calling ff_index_search_timestamp() */
+ int8_t *offsets; /* temporal offsets for display order to stored order conversion */
} MXFIndexTable;
typedef struct MXFContext {
@@ -207,7 +253,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,10 +282,13 @@ 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 };
static const uint8_t mxf_avid_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0e,0x04,0x03,0x01 };
+static const uint8_t mxf_canopus_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x0a,0x0e,0x0f,0x03,0x01 };
static const uint8_t mxf_system_item_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x03,0x01,0x04 };
static const uint8_t mxf_klv_key[] = { 0x06,0x0e,0x2b,0x34 };
/* complete keys to match */
@@ -249,9 +297,54 @@ 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 };
+static const uint8_t mxf_jp2k_rsiz[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x01,0x00 };
+static const uint8_t mxf_indirect_value_utf16le[] = { 0x4c,0x00,0x02,0x10,0x01,0x00,0x00,0x00,0x00,0x06,0x0e,0x2b,0x34,0x01,0x04,0x01,0x01 };
+static const uint8_t mxf_indirect_value_utf16be[] = { 0x42,0x01,0x10,0x02,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x2b,0x34,0x01,0x04,0x01,0x01 };
#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);
+ av_freep(&((MXFPackage *)*ctx)->comment_refs);
+ break;
+ case TaggedValue:
+ av_freep(&((MXFTaggedValue *)*ctx)->name);
+ av_freep(&((MXFTaggedValue *)*ctx)->value);
+ break;
+ case Track:
+ av_freep(&((MXFTrack *)*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 +363,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;
@@ -298,7 +391,7 @@ static int mxf_get_stream_index(AVFormatContext *s, KLVPacket *klv)
for (i = 0; i < s->nb_streams; i++) {
MXFTrack *track = s->streams[i]->priv_data;
/* SMPTE 379M 7.3 */
- if (!memcmp(klv->key + sizeof(mxf_essence_element_key), track->track_number, sizeof(track->track_number)))
+ if (track && !memcmp(klv->key + sizeof(mxf_essence_element_key), track->track_number, sizeof(track->track_number)))
return i;
}
/* return 0 if only one stream, for OP Atom files with 0 as track number */
@@ -407,12 +500,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 || item_num < 0) {
+ 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 +519,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 +544,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 +574,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 +620,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 +637,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 +665,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 +678,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;
@@ -598,20 +699,63 @@ static int mxf_read_cryptographic_context(void *arg, AVIOContext *pb, int tag, i
return 0;
}
+static int mxf_read_strong_ref_array(AVIOContext *pb, UID **refs, int *count)
+{
+ *count = avio_rb32(pb);
+ *refs = av_calloc(*count, sizeof(UID));
+ if (!*refs) {
+ *count = 0;
+ return AVERROR(ENOMEM);
+ }
+ avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
+ avio_read(pb, (uint8_t *)*refs, *count * sizeof(UID));
+ return 0;
+}
+
+static inline int mxf_read_utf16_string(AVIOContext *pb, int size, char** str, int be)
+{
+ int ret;
+ size_t buf_size;
+
+ if (size < 0 || size > INT_MAX/2)
+ return AVERROR(EINVAL);
+
+ buf_size = size + size / 2 + 1;
+ *str = av_malloc(buf_size);
+ if (!*str)
+ return AVERROR(ENOMEM);
+
+ if (be)
+ ret = avio_get_str16be(pb, size, *str, buf_size);
+ else
+ ret = avio_get_str16le(pb, size, *str, buf_size);
+
+ if (ret < 0) {
+ av_freep(str);
+ return ret;
+ }
+
+ return ret;
+}
+
+#define READ_STR16(type, big_endian) \
+static int mxf_read_utf16 ## type ##_string(AVIOContext *pb, int size, char** str) \
+{ \
+return mxf_read_utf16_string(pb, size, str, big_endian); \
+}
+READ_STR16(be, 1)
+READ_STR16(le, 0)
+#undef READ_STR16
+
static int mxf_read_content_storage(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
{
MXFContext *mxf = arg;
switch (tag) {
case 0x1901:
- mxf->packages_count = avio_rb32(pb);
- if (mxf->packages_count >= UINT_MAX / sizeof(UID))
- return AVERROR_INVALIDDATA;
- mxf->packages_refs = av_malloc(mxf->packages_count * sizeof(UID));
- if (!mxf->packages_refs)
- return AVERROR(ENOMEM);
- avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
- avio_read(pb, (uint8_t *)mxf->packages_refs, mxf->packages_count * sizeof(UID));
- break;
+ if (mxf->packages_refs)
+ av_log(mxf->fc, AV_LOG_VERBOSE, "Multiple packages_refs\n");
+ av_free(mxf->packages_refs);
+ return mxf_read_strong_ref_array(pb, &mxf->packages_refs, &mxf->packages_count);
}
return 0;
}
@@ -628,7 +772,7 @@ static int mxf_read_source_clip(void *arg, AVIOContext *pb, int tag, int size, U
break;
case 0x1101:
/* UMID, only get last 16 bytes */
- avio_skip(pb, 16);
+ avio_read(pb, source_clip->source_package_ul, 16);
avio_read(pb, source_clip->source_package_uid, 16);
break;
case 0x1102:
@@ -638,19 +782,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;
@@ -666,6 +820,9 @@ static int mxf_read_track(void *arg, AVIOContext *pb, int tag, int size, UID uid
case 0x4804:
avio_read(pb, track->track_number, 4);
break;
+ case 0x4802:
+ mxf_read_utf16be_string(pb, size, &track->name);
+ break;
case 0x4b01:
track->edit_rate.num = avio_rb32(pb);
track->edit_rate.den = avio_rb32(pb);
@@ -691,41 +848,46 @@ static int mxf_read_sequence(void *arg, AVIOContext *pb, int tag, int size, UID
sequence->origin = avio_r8(pb);
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)
- 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));
+ return mxf_read_strong_ref_array(pb, &sequence->structural_components_refs,
+ &sequence->structural_components_count);
+ }
+ return 0;
+}
+
+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:
+ return mxf_read_strong_ref_array(pb, &essence_group->structural_components_refs,
+ &essence_group->structural_components_count);
}
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_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));
- 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;
+ return mxf_read_strong_ref_array(pb, &package->tracks_refs,
+ &package->tracks_count);
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_utf16be_string(pb, size, &package->name);
+ case 0x4406:
+ return mxf_read_strong_ref_array(pb, &package->comment_refs,
+ &package->comment_count);
}
return 0;
}
@@ -735,31 +897,22 @@ 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);
+ if(segment->nb_index_entries && length < 11)
+ return AVERROR_INVALIDDATA;
- 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);
}
for (i = 0; i < segment->nb_index_entries; i++) {
+ if(avio_feof(pb))
+ return AVERROR_INVALIDDATA;
segment->temporal_offset_entries[i] = avio_r8(pb);
avio_r8(pb); /* KeyFrameOffset */
segment->flag_entries[i] = avio_r8(pb);
@@ -809,7 +962,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 +972,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,21 +982,21 @@ 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;
+ int entry_count, entry_size;
+
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));
- 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));
+ return mxf_read_strong_ref_array(pb, &descriptor->sub_descriptors_refs,
+ &descriptor->sub_descriptors_count);
+ case 0x3002: /* ContainerDuration */
+ descriptor->duration = avio_rb64(pb);
break;
case 0x3004:
avio_read(pb, descriptor->essence_container_ul, 16);
break;
+ case 0x3005:
+ avio_read(pb, descriptor->codec_ul, 16);
+ break;
case 0x3006:
descriptor->linked_track_id = avio_rb32(pb);
break;
@@ -858,6 +1012,21 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int
case 0x320C:
descriptor->frame_layout = avio_r8(pb);
break;
+ case 0x320D:
+ entry_count = avio_rb32(pb);
+ entry_size = avio_rb32(pb);
+ if (entry_size == 4) {
+ if (entry_count > 0)
+ descriptor->video_line_map[0] = avio_rb32(pb);
+ else
+ descriptor->video_line_map[0] = 0;
+ if (entry_count > 1)
+ descriptor->video_line_map[1] = avio_rb32(pb);
+ else
+ descriptor->video_line_map[1] = 0;
+ } else
+ av_log(NULL, AV_LOG_WARNING, "VideoLineMap element size %d currently not supported\n", entry_size);
+ break;
case 0x320E:
descriptor->aspect_ratio.num = avio_rb32(pb);
descriptor->aspect_ratio.den = avio_rb32(pb);
@@ -893,19 +1062,57 @@ 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 + AV_INPUT_BUFFER_PADDING_SIZE);
+ descriptor->extradata = av_malloc(size);
if (!descriptor->extradata)
return AVERROR(ENOMEM);
descriptor->extradata_size = size;
avio_read(pb, descriptor->extradata, size);
}
+ if (IS_KLV_KEY(uid, mxf_jp2k_rsiz)) {
+ uint32_t rsiz = avio_rb16(pb);
+ if (rsiz == FF_PROFILE_JPEG2000_DCINEMA_2K ||
+ rsiz == FF_PROFILE_JPEG2000_DCINEMA_4K)
+ descriptor->pix_fmt = AV_PIX_FMT_XYZ12;
+ }
break;
}
return 0;
}
+static int mxf_read_indirect_value(void *arg, AVIOContext *pb, int size)
+{
+ MXFTaggedValue *tagged_value = arg;
+ uint8_t key[17];
+
+ if (size <= 17)
+ return 0;
+
+ avio_read(pb, key, 17);
+ /* TODO: handle other types of of indirect values */
+ if (memcmp(key, mxf_indirect_value_utf16le, 17) == 0) {
+ return mxf_read_utf16le_string(pb, size - 17, &tagged_value->value);
+ } else if (memcmp(key, mxf_indirect_value_utf16be, 17) == 0) {
+ return mxf_read_utf16be_string(pb, size - 17, &tagged_value->value);
+ }
+ return 0;
+}
+
+static int mxf_read_tagged_value(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
+{
+ MXFTaggedValue *tagged_value = arg;
+ switch (tag){
+ case 0x5001:
+ return mxf_read_utf16be_string(pb, size, &tagged_value->name);
+ case 0x5003:
+ return mxf_read_indirect_value(tagged_value, pb, size);
+ }
+ return 0;
+}
+
/*
* Match an uid independently of the version byte and up to len common bytes
* Returns: boolean
@@ -947,9 +1154,16 @@ static void *mxf_resolve_strong_ref(MXFContext *mxf, UID *strong_ref, enum MXFMe
static const MXFCodecUL mxf_picture_essence_container_uls[] = {
// video 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,0x07,0x0d,0x01,0x03,0x01,0x02,0x0c,0x01,0x00 }, 14, AV_CODEC_ID_JPEG2000 },
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x10,0x60,0x01 }, 14, AV_CODEC_ID_H264 }, /* H.264 frame wrapped */
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x02,0x0d,0x01,0x03,0x01,0x02,0x12,0x01,0x00 }, 14, AV_CODEC_ID_VC1 }, /* VC-1 frame wrapped */
+ { { 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,0x01,0x04,0x01 }, 14, AV_CODEC_ID_MPEG2VIDEO }, /* Type D-10 mapping of 40Mbps 525/60-I */
{ { 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,0x04,0x01,0x01,0x0a,0x0e,0x0f,0x03,0x01,0x02,0x20,0x01,0x01 }, 15, AV_CODEC_ID_HQ_HQA },
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0a,0x0e,0x0f,0x03,0x01,0x02,0x20,0x02,0x01 }, 15, AV_CODEC_ID_HQX },
+ { { 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 },
};
@@ -966,6 +1180,15 @@ static const MXFCodecUL mxf_intra_only_picture_essence_coding_uls[] = {
{ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE },
};
+/* actual coded width for AVC-Intra to allow selecting correct SPS/PPS */
+static const MXFCodecUL mxf_intra_only_picture_coded_width[] = {
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x01 }, 16, 1440 },
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x02 }, 16, 1440 },
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x03 }, 16, 1440 },
+ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x01,0x32,0x21,0x04 }, 16, 1440 },
+ { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, 0 },
+};
+
static const MXFCodecUL mxf_sound_essence_container_uls[] = {
// sound essence container uls
{ { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x06,0x01,0x00 }, 14, AV_CODEC_ID_PCM_S16LE }, /* BWF Frame wrapped */
@@ -978,7 +1201,7 @@ 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 * const mxf_data_essence_descriptor[] = {
@@ -999,9 +1222,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);
@@ -1156,6 +1378,7 @@ static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_ta
{
int i, j, x;
int8_t max_temporal_offset = -128;
+ uint8_t *flags;
/* first compute how many entries we have */
for (i = 0; i < index_table->nb_segments; i++) {
@@ -1173,15 +1396,13 @@ 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))) ||
+ !(index_table->offsets = av_calloc(index_table->nb_ptses, sizeof(int8_t))) ||
+ !(flags = av_calloc(index_table->nb_ptses, sizeof(uint8_t)))) {
av_freep(&index_table->ptses);
+ av_freep(&index_table->fake_index);
+ av_freep(&index_table->offsets);
return AVERROR(ENOMEM);
}
@@ -1239,8 +1460,7 @@ static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_ta
break;
}
- index_table->fake_index[x].timestamp = x;
- index_table->fake_index[x].flags = !(s->flag_entries[j] & 0x30) ? AVINDEX_KEYFRAME : 0;
+ flags[x] = !(s->flag_entries[j] & 0x30) ? AVINDEX_KEYFRAME : 0;
if (index < 0 || index >= index_table->nb_ptses) {
av_log(mxf->fc, AV_LOG_ERROR,
@@ -1249,11 +1469,20 @@ static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_ta
continue;
}
+ index_table->offsets[x] = offset;
index_table->ptses[index] = x;
max_temporal_offset = FFMAX(max_temporal_offset, offset);
}
}
+ /* calculate the fake index table in display order */
+ for (x = 0; x < index_table->nb_ptses; x++) {
+ index_table->fake_index[x].timestamp = x;
+ if (index_table->ptses[x] != AV_NOPTS_VALUE)
+ index_table->fake_index[index_table->ptses[x]].flags = flags[x];
+ }
+ av_freep(&flags);
+
index_table->first_dts = -max_temporal_offset;
return 0;
@@ -1267,6 +1496,14 @@ static int mxf_compute_index_tables(MXFContext *mxf)
{
int i, j, k, ret, nb_sorted_segments;
MXFIndexTableSegment **sorted_segments = NULL;
+ AVStream *st = NULL;
+
+ for (i = 0; i < mxf->fc->nb_streams; i++) {
+ if (mxf->fc->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_DATA)
+ continue;
+ st = mxf->fc->streams[i];
+ break;
+ }
if ((ret = mxf_get_sorted_table_segments(mxf, &nb_sorted_segments, &sorted_segments)) ||
nb_sorted_segments <= 0) {
@@ -1336,7 +1573,7 @@ static int mxf_compute_index_tables(MXFContext *mxf)
av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i segment %i has zero IndexDuration and there's more than one segment\n",
t->index_sid, k);
- if (mxf->fc->nb_streams <= 0) {
+ if (!st) {
av_log(mxf->fc, AV_LOG_WARNING, "no streams?\n");
break;
}
@@ -1344,7 +1581,7 @@ static int mxf_compute_index_tables(MXFContext *mxf)
/* assume the first stream's duration is reasonable
* leave index_duration = 0 on further segments in case we have any (unlikely)
*/
- t->segments[k]->index_duration = mxf->fc->streams[0]->duration;
+ t->segments[k]->index_duration = st->duration;
break;
}
}
@@ -1355,18 +1592,314 @@ 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_package_comments(MXFContext *mxf, AVDictionary **pm, MXFPackage *package)
+{
+ MXFTaggedValue *tag;
+ int size, i;
+ char *key = NULL;
+
+ for (i = 0; i < package->comment_count; i++) {
+ tag = mxf_resolve_strong_ref(mxf, &package->comment_refs[i], TaggedValue);
+ if (!tag || !tag->name || !tag->value)
+ continue;
+
+ size = strlen(tag->name) + 8 + 1;
+ key = av_mallocz(size);
+ if (!key)
+ return AVERROR(ENOMEM);
+
+ snprintf(key, size, "comment_%s", tag->name);
+ av_dict_set(pm, key, tag->value, AV_DICT_DONT_STRDUP_KEY);
+ }
+ return 0;
+}
+
+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;
+ }
+
+ if (physical_track->edit_rate.num <= 0 ||
+ physical_track->edit_rate.den <= 0) {
+ av_log(mxf->fc, AV_LOG_WARNING,
+ "Invalid edit rate (%d/%d) found on structural"
+ " component #%d, defaulting to 25/1\n",
+ physical_track->edit_rate.num,
+ physical_track->edit_rate.den, i);
+ physical_track->edit_rate = (AVRational){25, 1};
+ }
+
+ 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_add_metadata_stream(MXFContext *mxf, MXFTrack *track)
+{
+ MXFStructuralComponent *component = NULL;
+ const MXFCodecUL *codec_ul = NULL;
+ MXFPackage tmp_package;
+ AVStream *st;
+ int j;
+
+ for (j = 0; j < track->sequence->structural_components_count; j++) {
+ component = mxf_resolve_sourceclip(mxf, &track->sequence->structural_components_refs[j]);
+ if (!component)
+ continue;
+ break;
+ }
+ if (!component)
+ return 0;
+
+ st = avformat_new_stream(mxf->fc, NULL);
+ if (!st) {
+ av_log(mxf->fc, AV_LOG_ERROR, "could not allocate metadata stream\n");
+ return AVERROR(ENOMEM);
+ }
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
+ st->codecpar->codec_id = AV_CODEC_ID_NONE;
+ st->id = track->track_id;
+
+ memcpy(&tmp_package.package_ul, component->source_package_ul, 16);
+ memcpy(&tmp_package.package_uid, component->source_package_uid, 16);
+ mxf_add_umid_metadata(&st->metadata, "file_package_umid", &tmp_package);
+ if (track->name && track->name[0])
+ av_dict_set(&st->metadata, "track_name", track->name, 0);
+
+ codec_ul = mxf_get_codec_ul(ff_mxf_data_definition_uls, &track->sequence->data_definition_ul);
+ av_dict_set(&st->metadata, "data_type", av_get_media_type_string(codec_ul->id), 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_log(mxf->fc, AV_LOG_TRACE, "metadata sets count %d\n", mxf->metadata_sets_count);
@@ -1380,6 +1913,11 @@ 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);
+ mxf_parse_package_comments(mxf, &mxf->fc->metadata, material_package);
+
for (i = 0; i < material_package->tracks_count; i++) {
MXFPackage *source_package = NULL;
MXFTrack *material_track = NULL;
@@ -1387,41 +1925,60 @@ 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, only finds first valid source clip */
+ if(material_track->sequence->structural_components_count > 1)
+ av_log(mxf->fc, AV_LOG_WARNING, "material track %d: has %d components\n",
+ material_track->track_id, material_track->sequence->structural_components_count);
+
+ 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_log(mxf->fc, AV_LOG_TRACE, "material track %d: no corresponding source package found\n", material_track->track_id);
- break;
+ continue;
}
for (k = 0; k < source_package->tracks_count; k++) {
if (!(temp_track = mxf_resolve_strong_ref(mxf, &source_package->tracks_refs[k], Track))) {
@@ -1438,9 +1995,14 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
av_log(mxf->fc, AV_LOG_ERROR, "material track %d: no corresponding source track found\n", material_track->track_id);
break;
}
+ if(source_track && component)
+ break;
}
- if (!source_track || !component)
+ if (!source_track || !component || !source_package) {
+ if((ret = mxf_add_metadata_stream(mxf, material_track)))
+ goto fail_and_free;
continue;
+ }
if (!(source_track->sequence = mxf_resolve_strong_ref(mxf, &source_track->sequence_ref, Sequence))) {
av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track sequence strong ref\n");
@@ -1461,9 +2023,19 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
ret = AVERROR(ENOMEM);
goto fail_and_free;
}
- st->id = source_track->track_id;
+ st->id = material_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 +2058,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->codecpar->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 +2080,29 @@ 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->codecpar->codec_id = codec_ul->id;
+ st->codecpar->codec_id = (enum AVCodecID)codec_ul->id;
+ if (st->codecpar->codec_id == AV_CODEC_ID_NONE) {
+ codec_ul = mxf_get_codec_ul(ff_mxf_codec_uls, &descriptor->codec_ul);
+ st->codecpar->codec_id = (enum AVCodecID)codec_ul->id;
+ }
+
+ av_log(mxf->fc, AV_LOG_VERBOSE, "%s: Universal Label: ",
+ avcodec_get_name(st->codecpar->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);
+ if (material_track->name && material_track->name[0])
+ av_dict_set(&st->metadata, "track_name", material_track->name, 0);
+
+ mxf_parse_physical_source_package(mxf, source_track, st);
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
source_track->intra_only = mxf_is_intra_only(descriptor);
@@ -1534,81 +2110,103 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
st->codecpar->codec_id = container_ul->id;
st->codecpar->width = descriptor->width;
- /* Field height, not frame height */
- st->codecpar->height = descriptor->height;
+ st->codecpar->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");
- break;
case FullFrame:
st->codecpar->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 SegmentedFrame:
+ st->codecpar->field_order = AV_FIELD_PROGRESSIVE;
case SeparateFields:
- case MixedFields:
- switch (descriptor->field_dominance) {
- case MXF_TFF:
- st->codecpar->field_order = AV_FIELD_TT;
- break;
- case MXF_BFF:
- st->codecpar->field_order = AV_FIELD_BB;
- break;
- default:
- avpriv_request_sample(mxf->fc,
- "Field dominance %d support",
- descriptor->field_dominance);
- break;
+ av_log(mxf->fc, AV_LOG_DEBUG, "video_line_map: (%d, %d), field_dominance: %d\n",
+ descriptor->video_line_map[0], descriptor->video_line_map[1],
+ descriptor->field_dominance);
+ if ((descriptor->video_line_map[0] > 0) && (descriptor->video_line_map[1] > 0)) {
+ /* Detect coded field order from VideoLineMap:
+ * (even, even) => bottom field coded first
+ * (even, odd) => top field coded first
+ * (odd, even) => top field coded first
+ * (odd, odd) => bottom field coded first
+ */
+ if ((descriptor->video_line_map[0] + descriptor->video_line_map[1]) % 2) {
+ switch (descriptor->field_dominance) {
+ case MXF_FIELD_DOMINANCE_DEFAULT:
+ case MXF_FIELD_DOMINANCE_FF:
+ st->codecpar->field_order = AV_FIELD_TT;
+ break;
+ case MXF_FIELD_DOMINANCE_FL:
+ st->codecpar->field_order = AV_FIELD_TB;
+ break;
+ default:
+ avpriv_request_sample(mxf->fc,
+ "Field dominance %d support",
+ descriptor->field_dominance);
+ }
+ } else {
+ switch (descriptor->field_dominance) {
+ case MXF_FIELD_DOMINANCE_DEFAULT:
+ case MXF_FIELD_DOMINANCE_FF:
+ st->codecpar->field_order = AV_FIELD_BB;
+ break;
+ case MXF_FIELD_DOMINANCE_FL:
+ st->codecpar->field_order = AV_FIELD_BT;
+ break;
+ default:
+ avpriv_request_sample(mxf->fc,
+ "Field dominance %d support",
+ descriptor->field_dominance);
+ }
+ }
}
/* Turn field height into frame height. */
st->codecpar->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->codecpar->codec_id == AV_CODEC_ID_RAWVIDEO) {
st->codecpar->format = descriptor->pix_fmt;
if (st->codecpar->format == AV_PIX_FMT_NONE) {
pix_fmt_ul = mxf_get_codec_ul(ff_mxf_pixel_format_uls,
&descriptor->essence_codec_ul);
- st->codecpar->format = pix_fmt_ul->id;
- if (st->codecpar->format == 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 */
- if (descriptor->horiz_subsampling == 2 &&
- descriptor->vert_subsampling == 1 &&
- descriptor->component_depth == 8) {
- st->codecpar->format = AV_PIX_FMT_UYVY422;
+ st->codecpar->format = (enum AVPixelFormat)pix_fmt_ul->id;
+ if (st->codecpar->format== AV_PIX_FMT_NONE) {
+ st->codecpar->codec_tag = mxf_get_codec_ul(ff_mxf_codec_tag_uls,
+ &descriptor->essence_codec_ul)->id;
+ if (!st->codecpar->codec_tag) {
+ /* support files created before RP224v10 by defaulting to UYVY422
+ if subsampling is 4:2:2 and component depth is 8-bit */
+ if (descriptor->horiz_subsampling == 2 &&
+ descriptor->vert_subsampling == 1 &&
+ descriptor->component_depth == 8) {
+ st->codecpar->format = AV_PIX_FMT_UYVY422;
+ }
}
}
}
}
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->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
container_ul = mxf_get_codec_ul(mxf_sound_essence_container_uls, essence_container_ul);
- if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
- st->codecpar->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->codecpar->codec_id == AV_CODEC_ID_NONE || (st->codecpar->codec_id == AV_CODEC_ID_PCM_ALAW && (enum AVCodecID)container_ul->id != AV_CODEC_ID_NONE))
+ st->codecpar->codec_id = (enum AVCodecID)container_ul->id;
st->codecpar->channels = descriptor->channels;
st->codecpar->bits_per_coded_sample = descriptor->bits_per_sample;
@@ -1653,12 +2251,14 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
}
}
if (descriptor->extradata) {
- st->codecpar->extradata = av_mallocz(descriptor->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (st->codecpar->extradata) {
+ if (!ff_alloc_extradata(st->codecpar, descriptor->extradata_size)) {
memcpy(st->codecpar->extradata, descriptor->extradata, descriptor->extradata_size);
- st->codecpar->extradata_size = descriptor->extradata_size;
}
} else if (st->codecpar->codec_id == AV_CODEC_ID_H264) {
+ int coded_width = mxf_get_codec_ul(mxf_intra_only_picture_coded_width,
+ &descriptor->essence_codec_ul)->id;
+ if (coded_width)
+ st->codecpar->width = coded_width;
ret = ff_generate_avci_extradata(st);
if (ret < 0)
return ret;
@@ -1674,46 +2274,7 @@ 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)
+static int64_t mxf_timestamp_to_int64(uint64_t timestamp)
{
struct tm time = { 0 };
time.tm_year = (timestamp >> 48) - 1900;
@@ -1732,17 +2293,11 @@ static int mxf_timestamp_to_str(uint64_t timestamp, char **str)
time.tm_min = av_clip(time.tm_min, 0, 59);
time.tm_sec = av_clip(time.tm_sec, 0, 59);
- *str = av_mallocz(32);
- if (!*str)
- return AVERROR(ENOMEM);
- if (!strftime(*str, 32, "%Y-%m-%d %H:%M:%S", &time))
- (*str)[0] = '\0';
-
- return 0;
+ return (int64_t)av_timegm(&time) * 1000000;
}
#define SET_STR_METADATA(pb, name, str) do { \
- if ((ret = mxf_read_utf16_string(pb, size, &str)) < 0) \
+ if ((ret = mxf_read_utf16be_string(pb, size, &str)) < 0) \
return ret; \
av_dict_set(&s->metadata, name, str, AV_DICT_DONT_STRDUP_VAL); \
} while (0)
@@ -1756,9 +2311,8 @@ static int mxf_timestamp_to_str(uint64_t timestamp, char **str)
#define SET_TS_METADATA(pb, name, var, str) do { \
var = avio_rb64(pb); \
- if ((ret = mxf_timestamp_to_str(var, &str)) < 0) \
+ if ((ret = avpriv_dict_set_timestamp(&s->metadata, name, mxf_timestamp_to_int64(var)) < 0)) \
return ret; \
- av_dict_set(&s->metadata, name, str, AV_DICT_DONT_STRDUP_VAL); \
} while (0)
static int mxf_read_identification_metadata(void *arg, AVIOContext *pb, int tag, int size, UID _uid, int64_t klv_offset)
@@ -1798,9 +2352,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,17 +2377,19 @@ 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,0x3f,0x00 }, mxf_read_tagged_value, sizeof(MXFTaggedValue), TaggedValue },
{ { 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 */
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x28,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* CDCI */
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x29,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* RGBA */
- { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x51,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* MPEG 2 Video */
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x48,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* Wave */
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x47,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* AES3 */
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x51,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* MPEG2VideoDescriptor */
@@ -1829,11 +2397,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 +2426,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 +2450,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 +2477,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_log(mxf->fc, AV_LOG_TRACE, "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 +2584,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_log(mxf->fc, AV_LOG_TRACE, "no last partition\n");
+ if (!mxf->footer_partition) {
+ av_log(mxf->fc, AV_LOG_TRACE, "no FooterPartition\n");
return 0;
}
- av_log(mxf->fc, AV_LOG_TRACE, "seeking to last partition\n");
+ av_log(mxf->fc, AV_LOG_TRACE, "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 & AVIO_SEEKABLE_NORMAL)) {
- 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 +2625,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 +2642,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,44 +2665,27 @@ 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 */
return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id < AV_CODEC_ID_PCM_S24DAUD;
}
+static AVStream* mxf_get_opatom_stream(MXFContext *mxf)
+{
+ int i;
+
+ if (mxf->op != OPAtom)
+ return NULL;
+
+ for (i = 0; i < mxf->fc->nb_streams; i++) {
+ if (mxf->fc->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_DATA)
+ continue;
+ return mxf->fc->streams[i];
+ }
+ return NULL;
+}
+
/**
* Deal with the case where for some audio atoms EditUnitByteCount is
* very small (2, 4..). In those cases we should read more than one
@@ -2054,13 +2697,13 @@ static void mxf_handle_small_eubc(AVFormatContext *s)
/* assuming non-OPAtom == frame wrapped
* no sane writer would wrap 2 byte PCM packets with 20 byte headers.. */
- if (mxf->op != OPAtom)
+ AVStream *st = mxf_get_opatom_stream(mxf);
+ if (!st)
return;
/* expect PCM with exactly one index table segment and a small (< 32) EUBC */
- if (s->nb_streams != 1 ||
- s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO ||
- !is_pcm(s->streams[0]->codecpar->codec_id) ||
+ if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO ||
+ !is_pcm(st->codecpar->codec_id) ||
mxf->nb_index_tables != 1 ||
mxf->index_tables[0].nb_segments != 1 ||
mxf->index_tables[0].segments[0]->edit_unit_byte_count >= 32)
@@ -2073,20 +2716,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;
+
+ st = mxf_get_opatom_stream(mxf);
+ if (!st)
+ return 0;
+
+ /* TODO: support raw video without an index if they exist */
+ if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO || !is_pcm(st->codecpar->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;
+ }
+
+ segment->type = IndexTableSegment;
+ /* stream will be treated as small EditUnitByteCount */
+ segment->edit_unit_byte_count = (av_get_bits_per_sample(st->codecpar->codec_id) * st->codecpar->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 & AVIO_SEEKABLE_NORMAL))
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 +2813,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 +2845,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 +2864,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 +2899,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,39 +2910,22 @@ 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",
- UID_ARG(klv.key));
}
}
- if (!metadata->read)
+ if (!metadata->read) {
+ av_log(s, AV_LOG_VERBOSE, "Dark key " PRIxUID "\n",
+ UID_ARG(klv.key));
avio_skip(s->pb, klv.length);
+ }
}
/* 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 +2934,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 +2946,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 +2973,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 +2993,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 +3031,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 +3052,37 @@ static int mxf_set_audio_pts(MXFContext *mxf, AVCodecParameters *par,
pkt->pts = track->sample_count;
- if (par->channels <= 0 || par->channels * bits_per_sample < 8)
- return AVERROR_INVALIDDATA;
+ if ( par->channels <= 0
+ || bits_per_sample <= 0
+ || par->channels * (int64_t)bits_per_sample < 8)
+ return AVERROR(EINVAL);
+ track->sample_count += pkt->size / (par->channels * (int64_t)bits_per_sample / 8);
+ return 0;
+}
- track->sample_count += pkt->size / (par->channels * bits_per_sample / 8);
+static int mxf_set_pts(MXFContext *mxf, AVStream *st, AVPacket *pkt, int64_t next_ofs)
+{
+ AVCodecParameters *par = st->codecpar;
+ MXFTrack *track = st->priv_data;
+
+ if (par->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) {
+ pkt->dts = mxf->current_edit_unit + t->first_dts;
+ pkt->pts = t->ptses[mxf->current_edit_unit];
+ } else if (track && track->intra_only) {
+ /* intra-only -> PTS = EditUnit.
+ * 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 (par->codec_type == AVMEDIA_TYPE_AUDIO) {
+ int ret = mxf_set_audio_pts(mxf, par, pkt);
+ if (ret < 0)
+ return ret;
+ }
return 0;
}
@@ -2339,9 +3092,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_log(s, AV_LOG_TRACE, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset);
if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) {
@@ -2353,12 +3104,11 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
return 0;
}
if (IS_KLV_KEY(klv.key, mxf_essence_element_key) ||
+ IS_KLV_KEY(klv.key, mxf_canopus_essence_element_key) ||
IS_KLV_KEY(klv.key, mxf_avid_essence_element_key)) {
int index = mxf_get_stream_index(s, &klv);
int64_t next_ofs, next_klv;
AVStream *st;
- MXFTrack *track;
- AVCodecParameters *par;
if (index < 0) {
av_log(s, AV_LOG_ERROR,
@@ -2368,7 +3118,6 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
}
st = s->streams[index];
- track = st->priv_data;
if (s->streams[index]->discard == AVDISCARD_ALL)
goto skip;
@@ -2377,11 +3126,10 @@ 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?"
+ "OPAtom misinterpreted as OP1a? "
"KLV for edit unit %i extending into "
"next edit unit",
mxf->current_edit_unit);
@@ -2404,28 +3152,9 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
pkt->stream_index = index;
pkt->pos = klv.offset;
- par = s->streams[index]->codecpar;
-
- if (par->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) {
- 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) */
- pkt->pts = mxf->current_edit_unit;
- }
- } else if (par->codec_type == AVMEDIA_TYPE_AUDIO) {
- ret = mxf_set_audio_pts(mxf, par, pkt);
- if (ret < 0)
- return ret;
- }
+ ret = mxf_set_pts(mxf, st, pkt, next_ofs);
+ if (ret < 0)
+ return ret;
/* seek for truncated packets */
avio_seek(s->pb, next_klv, SEEK_SET);
@@ -2435,7 +3164,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)
@@ -2450,9 +3179,13 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
if (mxf->op != OPAtom)
return mxf_read_packet_old(s, pkt);
+ // If we have no streams then we basically are at EOF
+ st = mxf_get_opatom_stream(mxf);
+ if (!st)
+ return AVERROR_EOF;
+
/* OPAtom - clip wrapped demuxing */
/* NOTE: mxf_read_header() makes sure nb_index_tables > 0 for OPAtom */
- st = s->streams[0];
t = &mxf->index_tables[0];
if (mxf->current_edit_unit >= st->duration)
@@ -2479,31 +3212,23 @@ 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;
+ pkt->stream_index = st->index;
- if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && t->ptses &&
- mxf->current_edit_unit >= 0 && mxf->current_edit_unit < t->nb_ptses) {
- pkt->dts = mxf->current_edit_unit + t->first_dts;
- pkt->pts = t->ptses[mxf->current_edit_unit];
- } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
- int ret = mxf_set_audio_pts(mxf, st->codecpar, pkt);
- if (ret < 0)
- return ret;
- }
+ ret = mxf_set_pts(mxf, st, pkt, next_pos);
+ if (ret < 0)
+ return ret;
mxf->current_edit_unit += edit_units;
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 +3237,20 @@ 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[i].offsets);
+ }
}
av_freep(&mxf->index_tables);
@@ -2553,18 +3258,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;
}
@@ -2580,6 +3294,9 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
MXFIndexTable *t;
MXFTrack *source_track = st->priv_data;
+ if(st->codecpar->codec_type == AVMEDIA_TYPE_DATA)
+ return 0;
+
/* if audio then truncate sample_time to EditRate */
if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
sample_time = av_rescale_q(sample_time, st->time_base,
@@ -2606,16 +3323,28 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
sample_time = FFMAX(sample_time, 0);
if (t->fake_index) {
+ /* The first frames may not be keyframes in presentation order, so
+ * we have to advance the target to be able to find the first
+ * keyframe backwards... */
+ if (!(flags & AVSEEK_FLAG_ANY) &&
+ (flags & AVSEEK_FLAG_BACKWARD) &&
+ t->ptses[0] != AV_NOPTS_VALUE &&
+ sample_time < t->ptses[0] &&
+ (t->fake_index[t->ptses[0]].flags & AVINDEX_KEYFRAME))
+ sample_time = t->ptses[0];
+
/* behave as if we have a proper index */
if ((sample_time = ff_index_search_timestamp(t->fake_index, t->nb_ptses, sample_time, flags)) < 0)
return sample_time;
+ /* get the stored order index from the display order index */
+ sample_time += t->offsets[sample_time];
} else {
/* no IndexEntryArray (one or more CBR segments)
* make sure we don't seek past the end */
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);
@@ -2642,6 +3371,7 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
AVInputFormat ff_mxf_demuxer = {
.name = "mxf",
.long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format)"),
+ .flags = AVFMT_SEEK_TO_PTS,
.priv_data_size = sizeof(MXFContext),
.read_probe = mxf_probe,
.read_header = mxf_read_header,
diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c
index eb53dbf..035e65e 100644
--- a/libavformat/mxfenc.c
+++ b/libavformat/mxfenc.c
@@ -3,29 +3,35 @@
* 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
*/
/*
+ * signal_standard, color_siting, store_user_comments, sample rate and klv_fill_key version
+ * fixes sponsored by NOA GmbH
+ */
+
+/*
* References
* SMPTE 336M KLV Data Encoding Protocol Using Key-Length-Value
* 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 +40,26 @@
#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/pixdesc.h"
#include "libavutil/time_internal.h"
#include "libavcodec/bytestream.h"
+#include "libavcodec/dnxhddata.h"
+#include "libavcodec/dv_profile.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 +83,15 @@ 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 color_siting;
+ int signal_standard;
+ int h_chroma_sub_sample;
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 {
@@ -81,13 +101,58 @@ typedef struct MXFContainerEssenceEntry {
void (*write_desc)(AVFormatContext *, AVStream *);
} MXFContainerEssenceEntry;
+enum ULIndex {
+ INDEX_MPEG2 = 0,
+ INDEX_AES3,
+ INDEX_WAV,
+ INDEX_D10_625_50_50_VIDEO,
+ INDEX_D10_625_50_50_AUDIO,
+ INDEX_D10_525_60_50_VIDEO,
+ INDEX_D10_525_60_50_AUDIO,
+ INDEX_D10_625_50_40_VIDEO,
+ INDEX_D10_625_50_40_AUDIO,
+ INDEX_D10_525_60_40_VIDEO,
+ INDEX_D10_525_60_40_AUDIO,
+ INDEX_D10_625_50_30_VIDEO,
+ INDEX_D10_625_50_30_AUDIO,
+ INDEX_D10_525_60_30_VIDEO,
+ INDEX_D10_525_60_30_AUDIO,
+ INDEX_DV,
+ INDEX_DV25_525_60,
+ INDEX_DV25_625_50,
+ INDEX_DV25_525_60_IEC,
+ INDEX_DV25_625_50_IEC,
+ INDEX_DV50_525_60,
+ INDEX_DV50_625_50,
+ INDEX_DV100_1080_60,
+ INDEX_DV100_1080_50,
+ INDEX_DV100_720_60,
+ INDEX_DV100_720_50,
+ INDEX_DNXHD_1080p_10bit_HIGH,
+ INDEX_DNXHD_1080p_8bit_MEDIUM,
+ INDEX_DNXHD_1080p_8bit_HIGH,
+ INDEX_DNXHD_1080i_10bit_HIGH,
+ INDEX_DNXHD_1080i_8bit_MEDIUM,
+ INDEX_DNXHD_1080i_8bit_HIGH,
+ INDEX_DNXHD_720p_10bit,
+ INDEX_DNXHD_720p_8bit_HIGH,
+ INDEX_DNXHD_720p_8bit_MEDIUM,
+ INDEX_DNXHD_720p_8bit_LOW,
+ INDEX_JPEG2000,
+ INDEX_H264,
+};
+
static const struct {
enum AVCodecID id;
- int index;
+ enum ULIndex index;
} mxf_essence_mappings[] = {
- { AV_CODEC_ID_MPEG2VIDEO, 0 },
- { AV_CODEC_ID_PCM_S24LE, 1 },
- { AV_CODEC_ID_PCM_S16LE, 1 },
+ { AV_CODEC_ID_MPEG2VIDEO, INDEX_MPEG2 },
+ { AV_CODEC_ID_PCM_S24LE, INDEX_AES3 },
+ { AV_CODEC_ID_PCM_S16LE, INDEX_AES3 },
+ { AV_CODEC_ID_DVVIDEO, INDEX_DV },
+ { AV_CODEC_ID_DNXHD, INDEX_DNXHD_1080p_10bit_HIGH },
+ { AV_CODEC_ID_JPEG2000, INDEX_JPEG2000 },
+ { AV_CODEC_ID_H264, INDEX_H264 },
{ AV_CODEC_ID_NONE }
};
@@ -164,6 +229,124 @@ 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 },
+
+ // IEC DV25 525/60
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x01,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 },
+ // IEC DV25 625/50
+ { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x02,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,0x01,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 +354,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 +368,18 @@ 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;
+ int signal_standard;
+ uint32_t tagged_value_count;
+ AVRational audio_edit_rate;
+ int store_user_comments;
} MXFContext;
static const uint8_t uuid_base[] = { 0xAD,0xAB,0x44,0x24,0x2f,0x25,0x4d,0xc7,0x92,0xff,0x29,0xbd };
@@ -201,13 +389,14 @@ 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 };
static const uint8_t random_index_pack_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x11,0x01,0x00 };
static const uint8_t header_open_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x02,0x01,0x00 }; // OpenIncomplete
static const uint8_t header_closed_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x02,0x04,0x00 }; // ClosedComplete
-static const uint8_t klv_fill_key[] = { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x03,0x01,0x02,0x10,0x01,0x00,0x00,0x00 };
+static const uint8_t klv_fill_key[] = { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x01,0x02,0x10,0x01,0x00,0x00,0x00 };
static const uint8_t body_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x03,0x04,0x00 }; // ClosedComplete
/**
@@ -218,6 +407,7 @@ static const uint8_t multiple_desc_ul[] = { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x
/**
* SMPTE RP210 http://www.smpte-ra.org/mdd/index.html
+ * https://smpte-ra.org/sites/default/files/Labels.xml
*/
static const MXFLocalTagPair mxf_local_tag_batch[] = {
// preface set
@@ -246,6 +436,7 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = {
{ 0x4401, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x15,0x10,0x00,0x00,0x00,0x00}}, /* Package UID */
{ 0x4405, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x10,0x01,0x03,0x00,0x00}}, /* Package Creation Date */
{ 0x4404, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x10,0x02,0x05,0x00,0x00}}, /* Package Modified Date */
+ { 0x4402, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x01,0x03,0x03,0x02,0x01,0x00,0x00,0x00}}, /* Package Name */
{ 0x4403, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x06,0x05,0x00,0x00}}, /* Tracks Strong reference array */
{ 0x4701, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x02,0x03,0x00,0x00}}, /* Descriptor */
// Track
@@ -270,6 +461,7 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = {
{ 0x3F01, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x06,0x01,0x01,0x04,0x06,0x0B,0x00,0x00}}, /* Sub Descriptors reference array */
{ 0x3006, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x06,0x01,0x01,0x03,0x05,0x00,0x00,0x00}}, /* Linked Track ID */
{ 0x3001, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x06,0x01,0x01,0x00,0x00,0x00,0x00}}, /* SampleRate */
+ { 0x3002, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x06,0x01,0x02,0x00,0x00,0x00,0x00}}, /* ContainerDuration */
{ 0x3004, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x01,0x02,0x00,0x00}}, /* Essence Container */
// Generic Picture Essence Descriptor
{ 0x320C, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x03,0x01,0x04,0x00,0x00,0x00}}, /* Frame Layout */
@@ -278,11 +470,15 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = {
{ 0x3202, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x02,0x01,0x00,0x00,0x00}}, /* Stored Height */
{ 0x3209, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x01,0x0C,0x00,0x00,0x00}}, /* Display Width */
{ 0x3208, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x01,0x0B,0x00,0x00,0x00}}, /* Display Height */
+ { 0x320B, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x01,0x0E,0x00,0x00,0x00}}, /* Presentation Y offset */
{ 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) */
+ { 0x3215, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x05,0x01,0x13,0x00,0x00,0x00,0x00}}, /* Signal Standard */
// 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 */
+ { 0x3303, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x01,0x06,0x00,0x00,0x00}}, /* Color Siting */
// Generic Sound Essence Descriptor
{ 0x3D02, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x04,0x02,0x03,0x01,0x04,0x00,0x00,0x00}}, /* Locked/Unlocked */
{ 0x3D03, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x03,0x01,0x01,0x01,0x00,0x00}}, /* Audio sampling rate */
@@ -306,6 +502,12 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = {
{ 0x3D0A, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x03,0x02,0x01,0x00,0x00,0x00}}, /* Block Align */
};
+static const MXFLocalTagPair mxf_user_comments_local_tag[] = {
+ { 0x4406, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x02,0x01,0x02,0x0C,0x00,0x00,0x00}}, /* User Comments */
+ { 0x5001, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x02,0x01,0x02,0x09,0x01,0x00,0x00}}, /* Name */
+ { 0x5003, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x02,0x01,0x02,0x0A,0x01,0x00,0x00}}, /* Value */
+};
+
static void mxf_write_uuid(AVIOContext *pb, enum MXFMetadataSetType type, int value)
{
avio_write(pb, uuid_base, 12);
@@ -363,6 +565,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
*/
@@ -377,10 +585,12 @@ static int mxf_get_essence_container_ul_index(enum AVCodecID id)
static void mxf_write_primer_pack(AVFormatContext *s)
{
+ MXFContext *mxf = s->priv_data;
AVIOContext *pb = s->pb;
int local_tag_number, i = 0;
local_tag_number = FF_ARRAY_ELEMS(mxf_local_tag_batch);
+ local_tag_number += mxf->store_user_comments * FF_ARRAY_ELEMS(mxf_user_comments_local_tag);
avio_write(pb, primer_pack_key, 16);
klv_encode_ber_length(pb, local_tag_number * 18 + 8);
@@ -388,10 +598,15 @@ static void mxf_write_primer_pack(AVFormatContext *s)
avio_wb32(pb, local_tag_number); // local_tag num
avio_wb32(pb, 18); // item size, always 18 according to the specs
- for (i = 0; i < local_tag_number; i++) {
+ for (i = 0; i < FF_ARRAY_ELEMS(mxf_local_tag_batch); i++) {
avio_wb16(pb, mxf_local_tag_batch[i].local_tag);
avio_write(pb, mxf_local_tag_batch[i].uid, 16);
}
+ if (mxf->store_user_comments)
+ for (i = 0; i < FF_ARRAY_ELEMS(mxf_user_comments_local_tag); i++) {
+ avio_wb16(pb, mxf_user_comments_local_tag[i].local_tag);
+ avio_write(pb, mxf_user_comments_local_tag[i].uid, 16);
+ }
}
static void mxf_write_local_tag(AVIOContext *pb, int size, int tag)
@@ -427,18 +642,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 +670,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 +696,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
@@ -486,22 +711,69 @@ static void mxf_write_preface(AVFormatContext *s)
}
/*
- * Write a local tag containing an ascii string as utf-16
+ * Returns the length of the UTF-16 string, in 16-bit characters, that would result
+ * from decoding the utf-8 string.
+ */
+static uint64_t mxf_utf16len(const char *utf8_str)
+{
+ const uint8_t *q = utf8_str;
+ uint64_t size = 0;
+ while (*q) {
+ uint32_t ch;
+ GET_UTF8(ch, *q++, goto invalid;)
+ if (ch < 0x10000)
+ size++;
+ else
+ size += 2;
+ continue;
+invalid:
+ av_log(NULL, AV_LOG_ERROR, "Invalid UTF8 sequence in mxf_utf16len\n\n");
+ }
+ size += 1;
+ return size;
+}
+
+/*
+ * Returns the calculated length a local tag containing an utf-8 string as utf-16
+ */
+static int mxf_utf16_local_tag_length(const char *utf8_str)
+{
+ uint64_t size;
+
+ if (!utf8_str)
+ return 0;
+
+ size = mxf_utf16len(utf8_str);
+ if (size >= UINT16_MAX/2) {
+ av_log(NULL, AV_LOG_ERROR, "utf16 local tag size %"PRIx64" invalid (too large), ignoring\n", size);
+ return 0;
+ }
+
+ return 4 + size * 2;
+}
+
+/*
+ * Write a local tag containing an utf-8 string as utf-16
*/
static void mxf_write_local_tag_utf16(AVIOContext *pb, int tag, const char *value)
{
- int i, size = strlen(value);
+ uint64_t size = mxf_utf16len(value);
+
+ if (size >= UINT16_MAX/2) {
+ av_log(NULL, AV_LOG_ERROR, "utf16 local tag size %"PRIx64" invalid (too large), ignoring\n", size);
+ return;
+ }
+
mxf_write_local_tag(pb, size*2, tag);
- for (i = 0; i < size; i++)
- avio_wb16(pb, value[i]);
+ avio_put_str16be(pb, value);
}
static void mxf_write_identification(AVFormatContext *s)
{
MXFContext *mxf = s->priv_data;
AVIOContext *pb = s->pb;
- const char *company = "Libav";
- const char *product = "OP1a Muxer";
+ const char *company = "FFmpeg";
+ const char *product = s->oformat != &ff_mxf_opatom_muxer ? "OP1a Muxer" : "OPAtom Muxer";
const char *version;
int length;
@@ -510,7 +782,9 @@ static void mxf_write_identification(AVFormatContext *s)
version = s->flags & AVFMT_FLAG_BITEXACT ?
"0.0.0" : AV_STRINGIFY(LIBAVFORMAT_VERSION);
- length = 84 + (strlen(company)+strlen(product)+strlen(version))*2; // utf-16
+ length = 72 + mxf_utf16_local_tag_length(company) +
+ mxf_utf16_local_tag_length(product) +
+ mxf_utf16_local_tag_length(version);
klv_encode_ber_length(pb, length);
// write uid
@@ -521,7 +795,6 @@ static void mxf_write_identification(AVFormatContext *s)
// write generation uid
mxf_write_local_tag(pb, 16, 0x3C09);
mxf_write_uuid(pb, Identification, 1);
-
mxf_write_local_tag_utf16(pb, 0x3C01, company); // Company Name
mxf_write_local_tag_utf16(pb, 0x3C02, product); // Product Name
mxf_write_local_tag_utf16(pb, 0x3C04, version); // Version String
@@ -586,9 +859,16 @@ static void mxf_write_track(AVFormatContext *s, AVStream *st, enum MXFMetadataSe
else
avio_write(pb, sc->track_essence_element_key + 12, 4);
+ // write edit rate
mxf_write_local_tag(pb, 8, 0x4B01);
- avio_wb32(pb, mxf->time_base.den);
- avio_wb32(pb, mxf->time_base.num);
+
+ if (st == mxf->timecode_track && s->oformat == &ff_mxf_opatom_muxer) {
+ avio_wb32(pb, mxf->tc.rate.num);
+ avio_wb32(pb, mxf->tc.rate.den);
+ } else {
+ avio_wb32(pb, mxf->time_base.den);
+ avio_wb32(pb, mxf->time_base.num);
+ }
// write origin
mxf_write_local_tag(pb, 8, 0x4B02);
@@ -617,7 +897,12 @@ static void mxf_write_common_fields(AVFormatContext *s, AVStream *st)
// write duration
mxf_write_local_tag(pb, 8, 0x0202);
- avio_wb64(pb, mxf->duration);
+
+ if (st != mxf->timecode_track && s->oformat == &ff_mxf_opatom_muxer && st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ avio_wb64(pb, mxf->body_offset / mxf->edit_unit_byte_count);
+ } else {
+ avio_wb64(pb, mxf->duration);
+ }
}
static void mxf_write_sequence(AVFormatContext *s, AVStream *st, enum MXFMetadataSetType type)
@@ -665,7 +950,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 +958,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)
@@ -765,8 +1050,19 @@ static void mxf_write_generic_desc(AVFormatContext *s, AVStream *st, const UID k
avio_wb32(pb, st->index+2);
mxf_write_local_tag(pb, 8, 0x3001);
- avio_wb32(pb, mxf->time_base.den);
- avio_wb32(pb, mxf->time_base.num);
+ if (s->oformat == &ff_mxf_d10_muxer) {
+ avio_wb32(pb, mxf->time_base.den);
+ avio_wb32(pb, mxf->time_base.num);
+ } else {
+ if (st->codecpar->codec_id == AV_CODEC_ID_PCM_S16LE ||
+ st->codecpar->codec_id == AV_CODEC_ID_PCM_S24LE) {
+ avio_wb32(pb, st->codecpar->sample_rate);
+ avio_wb32(pb, 1);
+ } else {
+ avio_wb32(pb, mxf->time_base.den);
+ avio_wb32(pb, mxf->time_base.num);
+ }
+ }
mxf_write_local_tag(pb, 16, 0x3004);
avio_write(pb, mxf_essence_container_uls[sc->index].container_ul, 16);
@@ -785,8 +1081,13 @@ static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID ke
int stored_height = (st->codecpar->height+15)/16*16;
int display_height;
int f1, f2;
+ unsigned desc_size = size+8+8+8+8+8+8+8+5+16+4+12+20+5;
+ if (sc->interlaced && sc->field_dominance)
+ desc_size += 5;
+ if (sc->signal_standard)
+ 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->codecpar->width);
@@ -807,13 +1108,26 @@ static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID ke
mxf_write_local_tag(pb, 4, 0x3208);
avio_wb32(pb, display_height>>sc->interlaced);
+ // presentation Y offset
+ mxf_write_local_tag(pb, 4, 0x320B);
+ avio_wb32(pb, (st->codecpar->height - display_height)>>sc->interlaced);
+
// 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);
- avio_wb32(pb, 2);
+ avio_wb32(pb, sc->h_chroma_sub_sample);
+
+ // color siting
+ mxf_write_local_tag(pb, 1, 0x3303);
+ avio_w8(pb, sc->color_siting);
+
+ if (sc->signal_standard) {
+ mxf_write_local_tag(pb, 1, 0x3215);
+ avio_w8(pb, sc->signal_standard);
+ }
// frame layout
mxf_write_local_tag(pb, 1, 0x320C);
@@ -821,9 +1135,9 @@ static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID ke
// video line map
switch (st->codecpar->height) {
- case 576: f1 = 23; f2 = 336; break;
+ case 576: f1 = 23; f2 = st->codecpar->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->codecpar->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;
@@ -835,12 +1149,12 @@ static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID ke
f1 *= 2;
}
- mxf_write_local_tag(pb, 12+sc->interlaced*4, 0x320D);
- avio_wb32(pb, sc->interlaced ? 2 : 1);
+
+ mxf_write_local_tag(pb, 16, 0x320D);
+ avio_wb32(pb, 2);
avio_wb32(pb, 4);
avio_wb32(pb, f1);
- if (sc->interlaced)
- avio_wb32(pb, f2);
+ avio_wb32(pb, f2);
mxf_write_local_tag(pb, 8, 0x320E);
avio_wb32(pb, sc->aspect_ratio.num);
@@ -848,6 +1162,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,26 +1178,42 @@ 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->codecpar->profile<<4) | st->codecpar->level;
- mxf_write_cdci_common(s, st, mxf_mpegvideo_descriptor_key, 8+5);
+ if (st->codecpar->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->codecpar->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->codecpar->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->codecpar->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;
+ int duration_size = 0;
+
+ if (s->oformat == &ff_mxf_opatom_muxer)
+ duration_size = 12;
- mxf_write_generic_desc(s, st, key, size+5+12+8+8);
+ mxf_write_generic_desc(s, st, key, size+duration_size+5+12+8+8);
+
+ if (duration_size > 0) {
+ mxf_write_local_tag(pb, 8, 0x3002);
+ avio_wb64(pb, mxf->body_offset / mxf->edit_unit_byte_count);
+ }
// audio locked
mxf_write_local_tag(pb, 1, 0x3D02);
@@ -889,7 +1225,19 @@ 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->codecpar->channels);
+ if (mxf->channel_count == -1) {
+ if (show_warnings && (s->oformat == &ff_mxf_d10_muxer) && (st->codecpar->channels != 4) && (st->codecpar->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->codecpar->channels);
+ } else if (s->oformat == &ff_mxf_d10_muxer) {
+ if (show_warnings && (mxf->channel_count < st->codecpar->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 {
+ avio_wb32(pb, st->codecpar->channels);
+ }
mxf_write_local_tag(pb, 4, 0x3D01);
avio_wb32(pb, av_get_bits_per_sample(st->codecpar->codec_id));
@@ -924,26 +1272,79 @@ static void mxf_write_generic_sound_desc(AVFormatContext *s, AVStream *st)
mxf_write_generic_sound_common(s, st, mxf_generic_sound_descriptor_key, 0);
}
-static void mxf_write_package(AVFormatContext *s, enum MXFMetadataSetType type)
+static const uint8_t mxf_indirect_value_utf16le[] = { 0x4c,0x00,0x02,0x10,0x01,0x00,0x00,0x00,0x00,0x06,0x0e,0x2b,0x34,0x01,0x04,0x01,0x01 };
+
+static int mxf_write_tagged_value(AVFormatContext *s, const char* name, const char* value)
+{
+ MXFContext *mxf = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int name_size = mxf_utf16_local_tag_length(name);
+ int indirect_value_size = 13 + mxf_utf16_local_tag_length(value);
+
+ if (!name_size || indirect_value_size == 13)
+ return 1;
+
+ mxf_write_metadata_key(pb, 0x013f00);
+ klv_encode_ber_length(pb, 24 + name_size + indirect_value_size);
+
+ // write instance UID
+ mxf_write_local_tag(pb, 16, 0x3C0A);
+ mxf_write_uuid(pb, TaggedValue, mxf->tagged_value_count);
+
+ // write name
+ mxf_write_local_tag_utf16(pb, 0x5001, name); // Name
+
+ // write indirect value
+ mxf_write_local_tag(pb, indirect_value_size, 0x5003);
+ avio_write(pb, mxf_indirect_value_utf16le, 17);
+ avio_put_str16le(pb, value);
+
+ mxf->tagged_value_count++;
+ return 0;
+}
+
+static int mxf_write_user_comments(AVFormatContext *s, const AVDictionary *m)
+{
+ MXFContext *mxf = s->priv_data;
+ AVDictionaryEntry *t = NULL;
+ int count = 0;
+
+ while ((t = av_dict_get(m, "comment_", t, AV_DICT_IGNORE_SUFFIX))) {
+ if (mxf->tagged_value_count >= UINT16_MAX) {
+ av_log(s, AV_LOG_ERROR, "too many tagged values, ignoring remaining\n");
+ return count;
+ }
+
+ if (mxf_write_tagged_value(s, t->key + 8, t->value) == 0)
+ count++;
+ }
+ return count;
+}
+
+static void mxf_write_package(AVFormatContext *s, enum MXFMetadataSetType type, const char *package_name)
{
MXFContext *mxf = s->priv_data;
AVIOContext *pb = s->pb;
int i, track_count = s->nb_streams+1;
+ int name_size = mxf_utf16_local_tag_length(package_name);
+ int user_comment_count = 0;
if (type == MaterialPackage) {
+ if (mxf->store_user_comments)
+ user_comment_count = mxf_write_user_comments(s, s->metadata);
mxf_write_metadata_key(pb, 0x013600);
PRINT_KEY(s, "Material Package key", pb->buf_ptr - 16);
- klv_encode_ber_length(pb, 92 + 16*track_count);
+ klv_encode_ber_length(pb, 92 + name_size + (16*track_count) + (16*user_comment_count) + 12LL*mxf->store_user_comments);
} else {
mxf_write_metadata_key(pb, 0x013700);
PRINT_KEY(s, "Source Package key", pb->buf_ptr - 16);
- klv_encode_ber_length(pb, 112 + 16*track_count); // 20 bytes length for descriptor reference
+ klv_encode_ber_length(pb, 112 + name_size + (16*track_count) + 12LL*mxf->store_user_comments); // 20 bytes length for descriptor reference
}
// write uid
mxf_write_local_tag(pb, 16, 0x3C0A);
mxf_write_uuid(pb, type, 0);
- av_log(s,AV_LOG_DEBUG, "package type:%d\n", type);
+ av_log(s, AV_LOG_DEBUG, "package type:%d\n", type);
PRINT_KEY(s, "package uid", pb->buf_ptr - 16);
// write package umid
@@ -951,6 +1352,10 @@ static void mxf_write_package(AVFormatContext *s, enum MXFMetadataSetType type)
mxf_write_umid(s, type == SourcePackage);
PRINT_KEY(s, "package umid second part", pb->buf_ptr - 16);
+ // package name
+ if (name_size)
+ mxf_write_local_tag_utf16(pb, 0x4402, package_name);
+
// package creation date
mxf_write_local_tag(pb, 8, 0x4405);
avio_wb64(pb, mxf->timestamp);
@@ -967,6 +1372,14 @@ static void mxf_write_package(AVFormatContext *s, enum MXFMetadataSetType type)
for (i = 0; i < s->nb_streams; i++)
mxf_write_uuid(pb, type == MaterialPackage ? Track : Track + TypeBottom, i);
+ // write user comment refs
+ if (mxf->store_user_comments) {
+ mxf_write_local_tag(pb, user_comment_count*16 + 8, 0x4406);
+ mxf_write_refs_count(pb, user_comment_count);
+ for (i = 0; i < user_comment_count; i++)
+ mxf_write_uuid(pb, TaggedValue, mxf->tagged_value_count - user_comment_count + i);
+ }
+
// write multiple descriptor reference
if (type == SourcePackage) {
mxf_write_local_tag(pb, 16, 0x4701);
@@ -1019,11 +1432,33 @@ static int mxf_write_essence_container_data(AVFormatContext *s)
static int mxf_write_header_metadata_sets(AVFormatContext *s)
{
+ const char *material_package_name = NULL;
+ const char *file_package_name = NULL;
+ AVDictionaryEntry *entry = NULL;
+ AVStream *st = NULL;
+ int i;
+
+ if (entry = av_dict_get(s->metadata, "material_package_name", NULL, 0))
+ material_package_name = entry->value;
+
+ if (entry = av_dict_get(s->metadata, "file_package_name", NULL, 0)) {
+ file_package_name = entry->value;
+ } else {
+ /* check if any of the streams contain a file_package_name */
+ for (i = 0; i < s->nb_streams; i++) {
+ st = s->streams[i];
+ if (entry = av_dict_get(st->metadata, "file_package_name", NULL, 0)) {
+ file_package_name = entry->value;
+ break;
+ }
+ }
+ }
+
mxf_write_preface(s);
mxf_write_identification(s);
mxf_write_content_storage(s);
- mxf_write_package(s, MaterialPackage);
- mxf_write_package(s, SourcePackage);
+ mxf_write_package(s, MaterialPackage, material_package_name);
+ mxf_write_package(s, SourcePackage, file_package_name);
mxf_write_essence_container_data(s);
return 0;
}
@@ -1177,9 +1612,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 +1640,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 +1650,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 +1664,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 +1682,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 +1716,271 @@ 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;
+ if ((sc->interlaced = avpriv_dnxhd_get_interlaced(cid)) < 0)
+ return AVERROR_INVALIDDATA;
+
+ switch (cid) {
+ case 1235:
+ sc->index = INDEX_DNXHD_1080p_10bit_HIGH;
+ sc->component_depth = 10;
+ break;
+ case 1237:
+ sc->index = INDEX_DNXHD_1080p_8bit_MEDIUM;
+ break;
+ case 1238:
+ sc->index = INDEX_DNXHD_1080p_8bit_HIGH;
+ break;
+ case 1241:
+ sc->index = INDEX_DNXHD_1080i_10bit_HIGH;
+ sc->component_depth = 10;
+ break;
+ case 1242:
+ sc->index = INDEX_DNXHD_1080i_8bit_MEDIUM;
+ break;
+ case 1243:
+ sc->index = INDEX_DNXHD_1080i_8bit_HIGH;
+ break;
+ case 1250:
+ sc->index = INDEX_DNXHD_720p_10bit;
+ sc->component_depth = 10;
+ break;
+ case 1251:
+ sc->index = INDEX_DNXHD_720p_8bit_HIGH;
+ break;
+ case 1252:
+ sc->index = INDEX_DNXHD_720p_8bit_MEDIUM;
+ break;
+ case 1253:
+ sc->index = INDEX_DNXHD_720p_8bit_LOW;
+ 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->codecpar->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->codecpar->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;
+ const AVDVProfile *profile;
+
+ if (mxf->header_written)
+ return 1;
+
+ // Check for minimal frame size
+ if (pkt->size < 120000)
+ return -1;
+
+ profile = av_dv_frame_profile(NULL, pkt->data, pkt->size);
+
+ 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 ((vsc_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 = INDEX_DV100_720_50 + 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 = INDEX_DV100_1080_50 + pal;
+ frame_size = pal ? 576000 : 480000;
+ break;
+ case 0x04: // DV50
+ ul_index = INDEX_DV50_525_60 + pal;
+ frame_size = pal ? 288000 : 240000;
+ break;
+ default: // DV25
+ if (profile && profile->pix_fmt == AV_PIX_FMT_YUV420P && pal) {
+ ul_index = INDEX_DV25_525_60_IEC + pal;
+ frame_size = pal ? 144000 : 120000;
+ break;
+ }
+ ul_index = INDEX_DV25_525_60 + pal;
+ frame_size = pal ? 144000 : 120000;
+ }
+
+ sc->index = ul_index;
+ 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->codecpar->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->codecpar->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,0x31,0x11,0x01 }, 0, 66, 0 }, // AVC Baseline, Unconstrained Coding
+ {{ 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 H264_NAL_SPS:
+ st->codecpar->profile = buf[1];
+ e->flags |= 0x40;
+ break;
+ case H264_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->codecpar->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->codecpar->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 +2018,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 +2029,8 @@ static int mxf_parse_mpeg2_frame(AVFormatContext *s, AVStream *st,
st->codecpar->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 +2039,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 +2095,41 @@ 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_init_timecode(AVFormatContext *s, AVStream *st, AVRational rate)
+{
+ MXFContext *mxf = s->priv_data;
+ AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
+ if (!tcr)
+ tcr = av_dict_get(st->metadata, "timecode", NULL, 0);
+
+ if (tcr)
+ return av_timecode_init_from_string(&mxf->tc, rate, tcr->value, s);
+ else
+ return av_timecode_init(&mxf->tc, rate, 0, 0, s);
}
static int mxf_write_header(AVFormatContext *s)
{
MXFContext *mxf = s->priv_data;
- int i;
+ int i, ret;
uint8_t present[FF_ARRAY_ELEMS(mxf_essence_container_uls)] = {0};
- const int *samples_per_frame = NULL;
- AVDictionaryEntry *t;
+ const MXFSamplesPerFrame *spf = NULL;
int64_t timestamp = 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;
+ }
+
+ if (!av_dict_get(s->metadata, "comment_", NULL, AV_DICT_IGNORE_SUFFIX))
+ mxf->store_user_comments = 0;
+
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MXFStreamContext *sc = av_mallocz(sizeof(*sc));
@@ -1430,47 +2137,76 @@ static int mxf_write_header(AVFormatContext *s)
return AVERROR(ENOMEM);
st->priv_data = sc;
+ if (((i == 0) ^ (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) && s->oformat != &ff_mxf_opatom_muxer) {
+ 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->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
- if (i != 0) {
- av_log(s, AV_LOG_ERROR, "video stream must be first track\n");
- return -1;
- }
+ const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(st->codecpar->format);
// 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;
+ sc->h_chroma_sub_sample = 2;
+ sc->color_siting = 0xFF;
+
+ if (pix_desc) {
+ sc->component_depth = pix_desc->comp[0].depth;
+ sc->h_chroma_sub_sample = 1 << pix_desc->log2_chroma_w;
+ }
+ switch (ff_choose_chroma_location(s, st)) {
+ case AVCHROMA_LOC_TOPLEFT: sc->color_siting = 0; break;
+ case AVCHROMA_LOC_LEFT: sc->color_siting = 6; break;
+ case AVCHROMA_LOC_TOP: sc->color_siting = 1; break;
+ case AVCHROMA_LOC_CENTER: sc->color_siting = 3; break;
}
+
+ 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((ret = mxf_init_timecode(s, st, rate)) < 0)
+ return ret;
+
+ sc->video_bit_rate = st->codecpar->bit_rate;
if (s->oformat == &ff_mxf_d10_muxer) {
- if (st->codecpar->bit_rate == 50000000)
- if (mxf->time_base.den == 25) sc->index = 3;
- else sc->index = 5;
- else if (st->codecpar->bit_rate == 40000000)
- if (mxf->time_base.den == 25) sc->index = 7;
- else sc->index = 9;
- else if (st->codecpar->bit_rate == 30000000)
- if (mxf->time_base.den == 25) sc->index = 11;
- else sc->index = 13;
- else {
+ if (st->codecpar->codec_id != AV_CODEC_ID_MPEG2VIDEO) {
+ av_log(s, AV_LOG_ERROR, "error MXF D-10 only support MPEG-2 Video\n");
+ return AVERROR(EINVAL);
+ }
+ if ((sc->video_bit_rate == 50000000) && (mxf->time_base.den == 25)) {
+ sc->index = INDEX_D10_625_50_50_VIDEO;
+ } else if ((sc->video_bit_rate == 49999840 || sc->video_bit_rate == 50000000) && (mxf->time_base.den != 25)) {
+ sc->index = INDEX_D10_525_60_50_VIDEO;
+ } else if (sc->video_bit_rate == 40000000) {
+ if (mxf->time_base.den == 25) sc->index = INDEX_D10_625_50_40_VIDEO;
+ else sc->index = INDEX_D10_525_60_40_VIDEO;
+ } else if (sc->video_bit_rate == 30000000) {
+ if (mxf->time_base.den == 25) sc->index = INDEX_D10_625_50_30_VIDEO;
+ else sc->index = INDEX_D10_525_60_30_VIDEO;
+ } 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->codecpar->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);
+
+ sc->signal_standard = 1;
}
+ if (mxf->signal_standard >= 0)
+ sc->signal_standard = mxf->signal_standard;
} else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
if (st->codecpar->sample_rate != 48000) {
av_log(s, AV_LOG_ERROR, "only 48khz is implemented\n");
@@ -1487,8 +2223,35 @@ static int mxf_write_header(AVFormatContext *s)
av_log(s, AV_LOG_ERROR, "MXF D-10 only support 16 or 24 bits le audio\n");
}
sc->index = ((MXFStreamContext*)s->streams[0]->priv_data)->index + 1;
- } else
- mxf->slice_count = 1;
+ } else if (s->oformat == &ff_mxf_opatom_muxer) {
+ AVRational tbc = av_inv_q(mxf->audio_edit_rate);
+
+ if (st->codecpar->codec_id != AV_CODEC_ID_PCM_S16LE &&
+ st->codecpar->codec_id != AV_CODEC_ID_PCM_S24LE) {
+ av_log(s, AV_LOG_ERROR, "Only pcm_s16le and pcm_s24le audio codecs are implemented\n");
+ return AVERROR_PATCHWELCOME;
+ }
+ if (st->codecpar->channels != 1) {
+ av_log(s, AV_LOG_ERROR, "MXF OPAtom only supports single channel audio\n");
+ return AVERROR(EINVAL);
+ }
+
+ spf = ff_mxf_get_samples_per_frame(s, tbc);
+ if (!spf) {
+ av_log(s, AV_LOG_ERROR, "Unsupported timecode frame rate %d/%d\n", tbc.den, tbc.num);
+ return AVERROR(EINVAL);
+ }
+
+ mxf->time_base = st->time_base;
+ if((ret = mxf_init_timecode(s, st, av_inv_q(spf->time_base))) < 0)
+ return ret;
+
+ mxf->timecode_base = (tbc.den + tbc.num/2) / tbc.num;
+ mxf->edit_unit_byte_count = (av_get_bits_per_sample(st->codecpar->codec_id) * st->codecpar->channels) >> 3;
+ sc->index = INDEX_WAV;
+ } else {
+ mxf->slice_count = 1;
+ }
}
if (!sc->index) {
@@ -1511,7 +2274,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,12 +2285,13 @@ 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))
- timestamp = ff_iso8601_to_unix_time(t->value);
- if (timestamp)
+ if (ff_parse_creation_time_metadata(s, &timestamp, 1) > 0)
mxf->timestamp = mxf_parse_timestamp(timestamp);
mxf->duration = -1;
@@ -1539,10 +2303,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 +2315,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 +2322,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 +2331,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 +2343,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 +2360,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->codecpar->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 +2377,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 +2417,57 @@ 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 +2491,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->codecpar->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->codecpar->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->codecpar->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)
@@ -1733,6 +2546,10 @@ static int mxf_write_packet(AVFormatContext *s, AVPacket *pkt)
}
mxf->edit_units_count++;
} else if (!mxf->edit_unit_byte_count && st->index == 1) {
+ if (!mxf->edit_units_count) {
+ av_log(s, AV_LOG_ERROR, "No packets in first stream\n");
+ return AVERROR_PATCHWELCOME;
+ }
mxf->index_entries[mxf->edit_units_count-1].slice_offset =
mxf->body_offset - mxf->index_entries[mxf->edit_units_count-1].offset;
}
@@ -1765,7 +2582,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);
@@ -1786,18 +2603,25 @@ static int mxf_write_footer(AVFormatContext *s)
{
MXFContext *mxf = s->priv_data;
AVIOContext *pb = s->pb;
- int err;
+ int err = 0;
+
+ if (!mxf->header_written ||
+ (s->oformat == &ff_mxf_opatom_muxer && !mxf->body_partition_offset)) {
+ /* reason could be invalid options/not supported codec/out of memory */
+ err = AVERROR_UNKNOWN;
+ goto end;
+ }
mxf->duration = mxf->last_indexed_edit_unit + mxf->edit_units_count;
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;
+ goto end;
} else {
if ((err = mxf_write_partition(s, 0, 2, footer_partition_key, 0)) < 0)
- return err;
+ goto end;
mxf_write_klv_fill(s);
mxf_write_index_table_segment(s);
}
@@ -1806,18 +2630,26 @@ static int mxf_write_footer(AVFormatContext *s)
mxf_write_random_index_pack(s);
if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ 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)
+ goto end;
+ }
+
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;
+ goto end;
mxf_write_klv_fill(s);
mxf_write_index_table_segment(s);
} else {
if ((err = mxf_write_partition(s, 0, 0, header_closed_partition_key, 1)) < 0)
- return err;
+ goto end;
}
}
+end:
ff_audio_interleave_close(s);
av_freep(&mxf->index_entries);
@@ -1827,7 +2659,7 @@ static int mxf_write_footer(AVFormatContext *s)
mxf_free(s);
- return 0;
+ return err < 0 ? err : 0;
}
static int mxf_interleave_get_packet(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush)
@@ -1900,6 +2732,70 @@ static int mxf_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int
mxf_interleave_get_packet, mxf_compare_timestamps);
}
+#define MXF_COMMON_OPTIONS \
+ { "signal_standard", "Force/set Sigal Standard",\
+ offsetof(MXFContext, signal_standard), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},\
+ { "bt601", "ITU-R BT.601 and BT.656, also SMPTE 125M (525 and 625 line interlaced)",\
+ 0, AV_OPT_TYPE_CONST, {.i64 = 1}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},\
+ { "bt1358", "ITU-R BT.1358 and ITU-R BT.799-3, also SMPTE 293M (525 and 625 line progressive)",\
+ 0, AV_OPT_TYPE_CONST, {.i64 = 2}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},\
+ { "smpte347m", "SMPTE 347M (540 Mbps mappings)",\
+ 0, AV_OPT_TYPE_CONST, {.i64 = 3}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},\
+ { "smpte274m", "SMPTE 274M (1125 line)",\
+ 0, AV_OPT_TYPE_CONST, {.i64 = 4}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},\
+ { "smpte296m", "SMPTE 296M (750 line progressive)",\
+ 0, AV_OPT_TYPE_CONST, {.i64 = 5}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},\
+ { "smpte349m", "SMPTE 349M (1485 Mbps mappings)",\
+ 0, AV_OPT_TYPE_CONST, {.i64 = 6}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},\
+ { "smpte428", "SMPTE 428-1 DCDM",\
+ 0, AV_OPT_TYPE_CONST, {.i64 = 7}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},
+
+
+
+static const AVOption mxf_options[] = {
+ MXF_COMMON_OPTIONS
+ { "store_user_comments", "",
+ offsetof(MXFContext, store_user_comments), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
+ { NULL },
+};
+
+static const AVClass mxf_muxer_class = {
+ .class_name = "MXF muxer",
+ .item_name = av_default_item_name,
+ .option = mxf_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+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},
+ MXF_COMMON_OPTIONS
+ { "store_user_comments", "",
+ offsetof(MXFContext, store_user_comments), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, 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,
+};
+
+static const AVOption opatom_options[] = {
+ { "mxf_audio_edit_rate", "Audio edit rate for timecode",
+ offsetof(MXFContext, audio_edit_rate), AV_OPT_TYPE_RATIONAL, {.dbl=25}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ MXF_COMMON_OPTIONS
+ { NULL },
+};
+
+static const AVClass mxf_opatom_muxer_class = {
+ .class_name = "MXF-OPAtom muxer",
+ .item_name = av_default_item_name,
+ .option = opatom_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVOutputFormat ff_mxf_muxer = {
.name = "mxf",
.long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format)"),
@@ -1913,6 +2809,7 @@ AVOutputFormat ff_mxf_muxer = {
.write_trailer = mxf_write_footer,
.flags = AVFMT_NOTIMESTAMPS,
.interleave_packet = mxf_interleave,
+ .priv_class = &mxf_muxer_class,
};
AVOutputFormat ff_mxf_d10_muxer = {
@@ -1927,4 +2824,21 @@ 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,
+ .priv_class = &mxf_opatom_muxer_class,
};
diff --git a/libavformat/mxg.c b/libavformat/mxg.c
index 245afb4..6fbf99c 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 +
- AV_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 +
+ AV_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);
@@ -175,7 +177,7 @@ static int mxg_read_packet(AVFormatContext *s, AVPacket *pkt)
if (mxg->soi_ptr - mxg->buffer > mxg->cache_size) {
if (mxg->cache_size > 0) {
- memcpy(mxg->buffer, mxg->buffer_ptr, mxg->cache_size);
+ memmove(mxg->buffer, mxg->buffer_ptr, mxg->cache_size);
}
mxg->buffer_ptr = mxg->buffer;
diff --git a/libavformat/ncdec.c b/libavformat/ncdec.c
index d726a2e..8cadcc7 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 2c34b4a..b3987a4 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
*/
@@ -23,16 +23,21 @@
#include "tls.h"
#include "url.h"
#include "libavcodec/internal.h"
+#include "libavutil/avutil.h"
#include "libavutil/mem.h"
+#include "libavutil/time.h"
-void ff_tls_init(void)
+int ff_tls_init(void)
{
#if CONFIG_TLS_OPENSSL_PROTOCOL
- ff_openssl_init();
+ int ret;
+ if ((ret = ff_openssl_init()) < 0)
+ return ret;
#endif
#if CONFIG_TLS_GNUTLS_PROTOCOL
ff_gnutls_init();
#endif
+ return 0;
}
void ff_tls_deinit(void)
@@ -70,10 +75,30 @@ int ff_network_wait_fd(int fd, int write)
int ev = write ? POLLOUT : POLLIN;
struct pollfd p = { .fd = fd, .events = ev, .revents = 0 };
int ret;
- ret = poll(&p, 1, 100);
+ ret = poll(&p, 1, POLLING_TIME);
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
@@ -129,12 +154,12 @@ 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);
if (ret < 0)
- return AVERROR(errno);
+ return ff_neterrno();
return ret;
}
@@ -149,8 +174,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
}
#ifdef SO_NOSIGPIPE
@@ -160,13 +187,14 @@ int ff_socket(int af, int type, int proto)
return fd;
}
-int ff_listen_bind(int fd, const struct sockaddr *addr,
- socklen_t addrlen, int timeout, URLContext *h)
+int ff_listen(int fd, const struct sockaddr *addr,
+ socklen_t addrlen)
{
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();
@@ -174,6 +202,13 @@ int ff_listen_bind(int fd, const struct sockaddr *addr,
ret = listen(fd, 1);
if (ret)
return ff_neterrno();
+ return ret;
+}
+
+int ff_accept(int fd, int timeout, URLContext *h)
+{
+ int ret;
+ struct pollfd lp = { fd, POLLIN, 0 };
ret = ff_poll_interrupt(&lp, 1, timeout, &h->interrupt_callback);
if (ret < 0)
@@ -182,10 +217,21 @@ int ff_listen_bind(int fd, const struct sockaddr *addr,
ret = accept(fd, NULL, NULL);
if (ret < 0)
return ff_neterrno();
+ if (ff_socket_nonblock(ret, 1) < 0)
+ av_log(NULL, AV_LOG_DEBUG, "ff_socket_nonblock failed\n");
- closesocket(fd);
+ return ret;
+}
- ff_socket_nonblock(ret, 1);
+int ff_listen_bind(int fd, const struct sockaddr *addr,
+ socklen_t addrlen, int timeout, URLContext *h)
+{
+ int ret;
+ if ((ret = ff_listen(fd, addr, addrlen)) < 0)
+ return ret;
+ if ((ret = ff_accept(fd, timeout, h)) < 0)
+ return ret;
+ closesocket(fd);
return ret;
}
@@ -197,7 +243,8 @@ int ff_listen_connect(int fd, const struct sockaddr *addr,
int ret;
socklen_t optlen;
- ff_socket_nonblock(fd, 1);
+ if (ff_socket_nonblock(fd, 1) < 0)
+ av_log(NULL, AV_LOG_DEBUG, "ff_socket_nonblock failed\n");
while ((ret = connect(fd, addr, addrlen))) {
ret = ff_neterrno();
diff --git a/libavformat/network.h b/libavformat/network.h
index 09cee58..f83c796 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
@@ -234,6 +255,26 @@ int ff_listen_bind(int fd, const struct sockaddr *addr,
URLContext *h);
/**
+ * Bind to a file descriptor to an address without accepting connections.
+ * @param fd First argument of bind().
+ * @param addr Second argument of bind().
+ * @param addrlen Third argument of bind().
+ * @return 0 on success or an AVERROR on failure.
+ */
+int ff_listen(int fd, const struct sockaddr *addr, socklen_t addrlen);
+
+/**
+ * Poll for a single connection on the passed file descriptor.
+ * @param fd The listening socket file descriptor.
+ * @param timeout Polling timeout in milliseconds.
+ * @param h URLContext providing interrupt check
+ * callback and logging context.
+ * @return A non-blocking file descriptor on success
+ * or an AVERROR on failure.
+ */
+int ff_accept(int fd, int timeout, URLContext *h);
+
+/**
* Connect to a file descriptor and poll for result.
*
* @param fd First argument of connect(),
diff --git a/libavformat/nistspheredec.c b/libavformat/nistspheredec.c
new file mode 100644
index 0000000..55f22eb
--- /dev/null
+++ b/libavformat/nistspheredec.c
@@ -0,0 +1,138 @@
+/*
+ * 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[256], 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->codecpar->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->codecpar->bits_per_coded_sample)
+ st->codecpar->bits_per_coded_sample = bps << 3;
+
+ if (!av_strcasecmp(coding, "pcm")) {
+ if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
+ st->codecpar->codec_id = ff_get_pcm_codec_id(st->codecpar->bits_per_coded_sample,
+ 0, be, 0xFFFF);
+ } else if (!av_strcasecmp(coding, "alaw")) {
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_ALAW;
+ } else if (!av_strcasecmp(coding, "ulaw") ||
+ !av_strcasecmp(coding, "mu-law")) {
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_MULAW;
+ } else if (!av_strncasecmp(coding, "pcm,embedded-shorten", 20)) {
+ st->codecpar->codec_id = AV_CODEC_ID_SHORTEN;
+ if (ff_alloc_extradata(st->codecpar, 1))
+ st->codecpar->extradata[0] = 1;
+ } else {
+ avpriv_request_sample(s, "coding %s", coding);
+ }
+
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ st->codecpar->block_align = st->codecpar->bits_per_coded_sample * st->codecpar->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 %u", &st->codecpar->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, "mu-law")) {
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_MULAW;
+ } 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 %d", &bps);
+ } else if (!memcmp(buffer, "sample_rate", 11)) {
+ sscanf(buffer, "%*s %*s %d", &st->codecpar->sample_rate);
+ } else if (!memcmp(buffer, "sample_sig_bits", 15)) {
+ sscanf(buffer, "%*s %*s %d", &st->codecpar->bits_per_coded_sample);
+ } else {
+ char key[32], value[32];
+ if (sscanf(buffer, "%31s %*s %31s", key, value) == 2) {
+ 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/nsvdec.c b/libavformat/nsvdec.c
index c91d2a1..d8ce656 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;
};
@@ -191,6 +188,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') },
@@ -204,6 +202,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 },
@@ -221,7 +220,7 @@ static int nsv_resync(AVFormatContext *s)
int i;
for (i = 0; i < NSV_MAX_RESYNC; i++) {
- if (pb->eof_reached) {
+ if (avio_feof(pb)) {
av_log(s, AV_LOG_TRACE, "NSV EOF\n");
nsv->state = NSV_UNSYNC;
return -1;
@@ -285,7 +284,7 @@ static int nsv_parse_NSVf_header(AVFormatContext *s)
table_entries_used = avio_rl32(pb);
av_log(s, AV_LOG_TRACE, "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_log(s, AV_LOG_TRACE, "NSV got header; filepos %"PRId64"\n", avio_tell(pb));
@@ -322,7 +321,7 @@ static int nsv_parse_NSVf_header(AVFormatContext *s)
}
av_free(strings);
}
- if (pb->eof_reached)
+ if (avio_feof(pb))
return -1;
av_log(s, AV_LOG_TRACE, "NSV got infos; filepos %"PRId64"\n", avio_tell(pb));
@@ -332,16 +331,19 @@ static int nsv_parse_NSVf_header(AVFormatContext *s)
nsv->index_entries = table_entries_used;
if((unsigned)table_entries_used >= UINT_MAX / sizeof(uint32_t))
return -1;
- nsv->nsvs_file_offset = av_malloc((unsigned)table_entries_used * sizeof(uint32_t));
+ nsv->nsvs_file_offset = av_malloc_array((unsigned)table_entries_used, sizeof(uint32_t));
if (!nsv->nsvs_file_offset)
return AVERROR(ENOMEM);
- for(i=0;i<table_entries_used;i++)
+ for(i=0;i<table_entries_used;i++) {
+ if (avio_feof(pb))
+ return AVERROR_INVALIDDATA;
nsv->nsvs_file_offset[i] = avio_rl32(pb) + size;
+ }
if(table_entries > table_entries_used &&
avio_rl32(pb) == MKTAG('T','O','C','2')) {
- nsv->nsvs_timestamps = av_malloc((unsigned)table_entries_used*sizeof(uint32_t));
+ nsv->nsvs_timestamps = av_malloc_array((unsigned)table_entries_used, sizeof(uint32_t));
if (!nsv->nsvs_timestamps)
return AVERROR(ENOMEM);
for(i=0;i<table_entries_used;i++) {
@@ -354,7 +356,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;
@@ -398,8 +400,7 @@ static int nsv_parse_NSVs_header(AVFormatContext *s)
nsv->avsync = avio_rl16(pb);
nsv->framerate = framerate;
- av_log(s, AV_LOG_TRACE, "NSV NSVs vsize %"PRIu16"x%"PRIu16"\n",
- vwidth, vheight);
+ av_log(s, AV_LOG_TRACE, "NSV NSVs vsize %dx%d\n", vwidth, vheight);
/* XXX change to ap != NULL ? */
if (s->nb_streams == 0) { /* streams not yet published, let's do that */
@@ -520,12 +521,13 @@ static int nsv_read_chunk(AVFormatContext *s, int fill_header)
uint32_t vsize;
uint16_t asize;
uint16_t auxsize;
+ int ret;
if (nsv->ahead[0].data || nsv->ahead[1].data)
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++)
@@ -544,8 +546,7 @@ null_chunk_retry:
asize = avio_rl16(pb);
vsize = (vsize << 4) | (auxcount >> 4);
auxcount &= 0x0f;
- av_log(s, AV_LOG_TRACE, "NSV CHUNK %"PRIu8" aux, %"PRIu32" bytes video, %"PRIu16" bytes audio\n",
- auxcount, vsize, asize);
+ av_log(s, AV_LOG_TRACE, "NSV CHUNK %d aux, %"PRIu32" bytes video, %d bytes audio\n", auxcount, vsize, asize);
/* skip aux stuff */
for (i = 0; i < auxcount; i++) {
uint32_t av_unused auxtag;
@@ -555,7 +556,7 @@ null_chunk_retry:
vsize -= auxsize + sizeof(uint16_t) + sizeof(uint32_t); /* that's becoming brain-dead */
}
- if (pb->eof_reached)
+ if (avio_feof(pb))
return -1;
if (!vsize && !asize) {
nsv->state = NSV_UNSYNC;
@@ -571,13 +572,13 @@ 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 */
for (i = 0; i < FFMIN(8, vsize); i++)
- av_log(s, AV_LOG_TRACE, "NSV video: [%d] = %02"PRIx8"\n",
- i, pkt->data[i]);
+ av_log(s, AV_LOG_TRACE, "NSV video: [%d] = %02x\n", i, pkt->data[i]);
}
if(st[NSV_ST_VIDEO])
((NSVStream*)st[NSV_ST_VIDEO]->priv_data)->frame_offset++;
@@ -597,12 +598,11 @@ null_chunk_retry:
if (!channels || !samplerate)
return AVERROR_INVALIDDATA;
asize-=4;
- av_log(s, AV_LOG_TRACE, "NSV RAWAUDIO: bps %"PRIu8", nchan %"PRIu8", srate %"PRIu16"\n",
- bps, channels, samplerate);
+ av_log(s, AV_LOG_TRACE, "NSV RAWAUDIO: bps %d, nchan %d, srate %d\n", bps, channels, samplerate);
if (fill_header) {
st[NSV_ST_AUDIO]->need_parsing = AVSTREAM_PARSE_NONE; /* we know everything */
if (bps != 16) {
- av_log(s, AV_LOG_TRACE, "NSV AUDIO bit/sample != 16 (%"PRIu8")!!!\n", bps);
+ av_log(s, AV_LOG_TRACE, "NSV AUDIO bit/sample != 16 (%d)!!!\n", bps);
}
bps /= channels; // ???
if (bps == 8)
@@ -611,11 +611,11 @@ null_chunk_retry:
channels = 1;
st[NSV_ST_AUDIO]->codecpar->channels = channels;
st[NSV_ST_AUDIO]->codecpar->sample_rate = samplerate;
- av_log(s, AV_LOG_TRACE, "NSV RAWAUDIO: bps %"PRIu8", nchan %"PRIu8", srate %"PRIu16"\n",
- bps, channels, samplerate);
+ av_log(s, AV_LOG_TRACE, "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] ) {
@@ -623,8 +623,7 @@ null_chunk_retry:
pkt->dts = (((NSVStream*)st[NSV_ST_VIDEO]->priv_data)->frame_offset-1);
pkt->dts *= (int64_t)1000 * nsv->framerate.den;
pkt->dts += (int64_t)nsv->avsync * nsv->framerate.num;
- av_log(s, AV_LOG_TRACE, "NSV AUDIO: sync:%"PRId16", dts:%"PRId64,
- nsv->avsync, pkt->dts);
+ av_log(s, AV_LOG_TRACE, "NSV AUDIO: sync:%d, dts:%"PRId64, nsv->avsync, pkt->dts);
}
nst->frame_offset++;
}
@@ -693,11 +692,8 @@ static int nsv_read_close(AVFormatContext *s)
static int nsv_probe(AVProbeData *p)
{
- int i;
- int score;
- int vsize, asize, auxcount;
- score = 0;
- av_log(NULL, AV_LOG_TRACE, "nsv_probe(), buf_size %d\n", p->buf_size);
+ int i, score = 0;
+
/* check file header */
/* streamed files might not have any header */
if (p->buf[0] == 'N' && p->buf[1] == 'S' &&
@@ -708,19 +704,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 0da5940..fd293d7 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_WRAPPED_AVFRAME,
.write_packet = null_write_packet,
- .flags = AVFMT_NOFILE | AVFMT_NOTIMESTAMPS,
+ .flags = AVFMT_VARIABLE_FPS | AVFMT_NOFILE | AVFMT_NOTIMESTAMPS,
};
diff --git a/libavformat/nut.c b/libavformat/nut.c
index ec43a3f..592fe4d 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,9 @@
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_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 +39,32 @@ 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_HEVC, MKTAG('H', 'E', 'V', 'C') },
+ { 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,56 @@ 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 , 9 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG( 9 , 11 , '3', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 9 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG( 9 , 10 , '3', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 9 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG( 9 , 0 , '3', 'Y') },
+ { 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') },
@@ -116,6 +143,8 @@ const AVCodecTag ff_nut_video_tags[] = {
{ AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 0, 10) },
{ AV_CODEC_ID_RAWVIDEO, MKTAG(10, 0, '4', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '1', 0, 12) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(12, 0, '1', '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', '4', 11, 16) },
@@ -125,41 +154,89 @@ 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('G', '4', 0, 8) },
+
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '4', 00 , 10 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 00 , '4', 'G') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '4', 00 , 12 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 00 , '4', 'G') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '4', 00 , 16 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 00 , '4', 'G') },
+
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('X', 'Y', 'Z' , 36 ) },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG(36 , 'Z' , 'Y', 'X') },
+
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('P', 'A', 'L', 8 ) },
+
+ { 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_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_MP3, MKTAG('M', 'P', '3', ' ') },
- { AV_CODEC_ID_NONE, 0 }
+ { AV_CODEC_ID_OPUS, MKTAG('O', 'p', 'u', 's') },
+ { 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)
@@ -175,19 +252,21 @@ 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;
}
-int ff_nut_sp_pos_cmp(const Syncpoint *a, const Syncpoint *b)
+int ff_nut_sp_pos_cmp(const void *a, const void *b)
{
- return ((a->pos - b->pos) >> 32) - ((b->pos - a->pos) >> 32);
+ const Syncpoint *va = a, *vb = b;
+ return ((va->pos - vb->pos) >> 32) - ((vb->pos - va->pos) >> 32);
}
-int ff_nut_sp_pts_cmp(const Syncpoint *a, const Syncpoint *b)
+int ff_nut_sp_pts_cmp(const void *a, const void *b)
{
- return ((a->ts - b->ts) >> 32) - ((b->ts - a->ts) >> 32);
+ const Syncpoint *va = a, *vb = b;
+ return ((va->ts - vb->ts) >> 32) - ((vb->ts - va->ts) >> 32);
}
int ff_nut_add_sp(NUTContext *nut, int64_t pos, int64_t back_ptr, int64_t ts)
@@ -201,11 +280,12 @@ 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;
- av_tree_insert(&nut->syncpoints, sp,
- (int (*)(void *, const void *)) ff_nut_sp_pos_cmp, &node);
+ av_tree_insert(&nut->syncpoints, sp, ff_nut_sp_pos_cmp, &node);
if (node) {
av_free(sp);
av_free(node);
@@ -222,8 +302,10 @@ static int enu_free(void *opaque, void *elem)
void ff_nut_free_sp(NUTContext *nut)
{
- av_tree_enumerate(nut->syncpoints, NULL, NULL, enu_free);
- av_tree_destroy(nut->syncpoints);
+ if (nut->syncpoints) {
+ av_tree_enumerate(nut->syncpoints, NULL, NULL, enu_free);
+ av_tree_destroy(nut->syncpoints);
+ }
}
const Dispositions ff_nut_dispositions[] = {
diff --git a/libavformat/nut.h b/libavformat/nut.h
index 52ce8fc..a4409ee 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 {
@@ -100,18 +102,25 @@ typedef struct NUTContext {
unsigned int max_distance;
unsigned int time_base_count;
int64_t last_syncpoint_pos;
+ int64_t last_resync_pos;
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[];
@@ -123,8 +132,8 @@ typedef struct Dispositions {
void ff_nut_reset_ts(NUTContext *nut, AVRational time_base, int64_t val);
int64_t ff_lsb2full(StreamContext *stream, int64_t lsb);
-int ff_nut_sp_pos_cmp(const Syncpoint *a, const Syncpoint *b);
-int ff_nut_sp_pts_cmp(const Syncpoint *a, const Syncpoint *b);
+int ff_nut_sp_pos_cmp(const void *a, const void *b);
+int ff_nut_sp_pts_cmp(const void *a, const void *b);
int ff_nut_add_sp(NUTContext *nut, int64_t pos, int64_t back_ptr, int64_t ts);
void ff_nut_free_sp(NUTContext *nut);
diff --git a/libavformat/nutdec.c b/libavformat/nutdec.c
index 55c2171..27440c8 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);
@@ -43,11 +47,15 @@ static int get_str(AVIOContext *bc, char *string, unsigned int maxlen)
while (len > maxlen) {
avio_r8(bc);
len--;
+ if (bc->eof_reached)
+ len = maxlen;
}
if (maxlen)
string[FFMIN(len, maxlen - 1)] = 0;
+ if (bc->eof_reached)
+ return AVERROR_EOF;
if (maxlen == len)
return -1;
else
@@ -72,8 +80,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;
+ }
}
static int get_packetheader(NUTContext *nut, AVIOContext *bc,
@@ -104,7 +114,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;
@@ -142,11 +152,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;
@@ -157,7 +167,8 @@ static int nut_probe(AVProbeData *p)
tmp = ffio_read_varlen(bc); \
if (!(check)) { \
av_log(s, AV_LOG_ERROR, "Error " #dst " is (%"PRId64")\n", tmp); \
- return AVERROR_INVALIDDATA; \
+ ret = AVERROR_INVALIDDATA; \
+ goto fail; \
} \
dst = tmp; \
} while (0)
@@ -169,8 +180,11 @@ static int skip_reserved(AVIOContext *bc, int64_t pos)
avio_seek(bc, pos, SEEK_CUR);
return AVERROR_INVALIDDATA;
} else {
- while (pos--)
+ while (pos--) {
+ if (bc->eof_reached)
+ return AVERROR_INVALIDDATA;
avio_r8(bc);
+ }
return 0;
}
}
@@ -181,19 +195,21 @@ static int decode_main_header(NUTContext *nut)
AVIOContext *bc = s->pb;
uint64_t tmp, end;
unsigned int stream_count;
- int i, j, count;
+ int i, j, count, ret;
int tmp_stream, tmp_mul, tmp_pts, tmp_size, tmp_res, tmp_head_idx;
end = get_packetheader(nut, bc, 1, MAIN_STARTCODE);
end += avio_tell(bc);
nut->version = ffio_read_varlen(bc);
- if (nut->version < NUT_MIN_VERSION &&
+ if (nut->version < NUT_MIN_VERSION ||
nut->version > NUT_MAX_VERSION) {
av_log(s, AV_LOG_ERROR, "Version %d not supported.\n",
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);
@@ -204,7 +220,7 @@ 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));
+ nut->time_base = av_malloc_array(nut->time_base_count, sizeof(AVRational));
if (!nut->time_base)
return AVERROR(ENOMEM);
@@ -215,7 +231,8 @@ static int decode_main_header(NUTContext *nut)
av_log(s, AV_LOG_ERROR, "invalid time base %d/%d\n",
nut->time_base[i].num,
nut->time_base[i].den);
- return AVERROR_INVALIDDATA;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
}
}
tmp_pts = 0;
@@ -249,17 +266,25 @@ static int decode_main_header(NUTContext *nut)
if (tmp_fields > 7)
tmp_head_idx = ffio_read_varlen(bc);
- while (tmp_fields-- > 8)
+ while (tmp_fields-- > 8) {
+ if (bc->eof_reached) {
+ av_log(s, AV_LOG_ERROR, "reached EOF while decoding main header\n");
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
ffio_read_varlen(bc);
+ }
- if (count == 0 || i + count > 256) {
+ if (count <= 0 || count > 256 - (i <= 'N') - i) {
av_log(s, AV_LOG_ERROR, "illegal count %d at %d\n", count, i);
- return AVERROR_INVALIDDATA;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
}
if (tmp_stream >= stream_count) {
av_log(s, AV_LOG_ERROR, "illegal stream number %d >= %d\n",
tmp_stream, stream_count);
- return AVERROR_INVALIDDATA;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
}
for (j = 0; j < count; j++, i++) {
@@ -277,7 +302,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;
@@ -290,35 +315,48 @@ static int decode_main_header(NUTContext *nut)
av_log(s, AV_LOG_ERROR,
"invalid elision header %d : %d > %d\n",
i, nut->header_len[i], rem);
- return AVERROR_INVALIDDATA;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
}
rem -= nut->header_len[i];
hdr = av_malloc(nut->header_len[i]);
- if (!hdr)
- return AVERROR(ENOMEM);
+ if (!hdr) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
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);
}
if (skip_reserved(bc, end) || ffio_get_checksum(bc)) {
av_log(s, AV_LOG_ERROR, "main header checksum mismatch\n");
- return AVERROR_INVALIDDATA;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
}
- nut->stream = av_mallocz(sizeof(StreamContext) * stream_count);
- if (!nut->stream)
- return AVERROR(ENOMEM);
+ nut->stream = av_calloc(stream_count, sizeof(StreamContext));
+ if (!nut->stream) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
for (i = 0; i < stream_count; i++)
avformat_new_stream(s, NULL);
return 0;
+fail:
+ av_freep(&nut->time_base);
+ for (i = 1; i < nut->header_count; i++) {
+ av_freep(&nut->header[i]);
+ }
+ nut->header_count = 0;
+ return ret;
}
static int decode_stream_header(NUTContext *nut)
@@ -326,9 +364,9 @@ static int decode_stream_header(NUTContext *nut)
AVFormatContext *s = nut->avf;
AVIOContext *bc = s->pb;
StreamContext *stc;
- int class, stream_id;
+ int class, stream_id, ret;
uint64_t tmp, end;
- AVStream *st;
+ AVStream *st = NULL;
end = get_packetheader(nut, bc, 1, STREAM_STARTCODE);
end += avio_tell(bc);
@@ -348,6 +386,7 @@ static int decode_stream_header(NUTContext *nut)
st->codecpar->codec_id = av_codec_get_id((const AVCodecTag * const []) {
ff_nut_video_tags,
ff_codec_bmp_tags,
+ ff_codec_movvideo_tags,
0
},
tmp);
@@ -357,6 +396,7 @@ static int decode_stream_header(NUTContext *nut)
st->codecpar->codec_id = av_codec_get_id((const AVCodecTag * const []) {
ff_nut_audio_tags,
ff_codec_wav_tags,
+ ff_nut_audio_extra_tags,
0
},
tmp);
@@ -382,15 +422,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->codecpar->video_delay = stc->decode_delay;
ffio_read_varlen(bc); // stream flags
GET_V(st->codecpar->extradata_size, tmp < (1 << 30));
if (st->codecpar->extradata_size) {
- st->codecpar->extradata = av_mallocz(st->codecpar->extradata_size +
- AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
+ if (ff_get_extradata(s, st->codecpar, bc, st->codecpar->extradata_size) < 0)
return AVERROR(ENOMEM);
- avio_read(bc, st->codecpar->extradata, st->codecpar->extradata_size);
}
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
@@ -401,7 +439,8 @@ static int decode_stream_header(NUTContext *nut)
if ((!st->sample_aspect_ratio.num) != (!st->sample_aspect_ratio.den)) {
av_log(s, AV_LOG_ERROR, "invalid aspect ratio %d/%d\n",
st->sample_aspect_ratio.num, st->sample_aspect_ratio.den);
- return AVERROR_INVALIDDATA;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
}
ffio_read_varlen(bc); /* csp type */
} else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
@@ -412,12 +451,19 @@ static int decode_stream_header(NUTContext *nut)
if (skip_reserved(bc, end) || ffio_get_checksum(bc)) {
av_log(s, AV_LOG_ERROR,
"stream header %d checksum mismatch\n", stream_id);
- return AVERROR_INVALIDDATA;
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
}
stc->time_base = &nut->time_base[stc->time_base_id];
avpriv_set_pts_info(s->streams[stream_id], 63, stc->time_base->num,
stc->time_base->den);
return 0;
+fail:
+ if (st && st->codecpar) {
+ av_freep(&st->codecpar->extradata);
+ st->codecpar->extradata_size = 0;
+ }
+ return ret;
}
static void set_disposition_bits(AVFormatContext *avf, char *value,
@@ -441,7 +487,7 @@ static int decode_info_header(NUTContext *nut)
AVIOContext *bc = s->pb;
uint64_t tmp, chapter_start, chapter_len;
unsigned int stream_id_plus1, count;
- int chapter_id, i;
+ int chapter_id, i, ret = 0;
int64_t value, end;
char name[256], str_value[1024], type_str[256];
const char *type;
@@ -483,15 +529,25 @@ static int decode_info_header(NUTContext *nut)
}
for (i = 0; i < count; i++) {
- get_str(bc, name, sizeof(name));
+ ret = get_str(bc, name, sizeof(name));
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "get_str failed while decoding info header\n");
+ return ret;
+ }
value = get_s(bc);
+ str_value[0] = 0;
+
if (value == -1) {
type = "UTF-8";
- get_str(bc, str_value, sizeof(str_value));
+ ret = get_str(bc, str_value, sizeof(str_value));
} else if (value == -2) {
- get_str(bc, type_str, sizeof(type_str));
+ ret = get_str(bc, type_str, sizeof(type_str));
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "get_str failed while decoding info header\n");
+ return ret;
+ }
type = type_str;
- get_str(bc, str_value, sizeof(str_value));
+ ret = get_str(bc, str_value, sizeof(str_value));
} else if (value == -3) {
type = "s";
value = get_s(bc);
@@ -505,6 +561,11 @@ static int decode_info_header(NUTContext *nut)
type = "v";
}
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "get_str failed while decoding info header\n");
+ return ret;
+ }
+
if (stream_id_plus1 > s->nb_streams) {
av_log(s, AV_LOG_WARNING,
"invalid stream id %d for info packet\n",
@@ -517,6 +578,15 @@ 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 < 0 || st->r_frame_rate.num < 0)
+ 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)
@@ -530,14 +600,16 @@ static int decode_info_header(NUTContext *nut)
av_log(s, AV_LOG_ERROR, "info header checksum mismatch\n");
return AVERROR_INVALIDDATA;
}
- return 0;
+fail:
+ return FFMIN(ret, 0);
}
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;
@@ -548,7 +620,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);
@@ -566,8 +638,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;
@@ -575,6 +647,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;
@@ -582,24 +667,36 @@ static int find_and_decode_index(NUTContext *nut)
uint64_t tmp, end;
int i, j, syncpoint_count;
int64_t filesize = avio_size(bc);
- int64_t *syncpoints;
- int8_t *has_keyframe;
+ int64_t *syncpoints = NULL;
+ uint64_t max_pts;
+ int8_t *has_keyframe = NULL;
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_WARNING, "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;
@@ -623,13 +720,17 @@ 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--)
has_keyframe[n++] = flag;
has_keyframe[n++] = !flag;
} else {
+ if (x <= 1) {
+ av_log(s, AV_LOG_ERROR, "index: x %"PRIu64" is invalid\n", x);
+ goto fail;
+ }
while (x != 1) {
if (n >= syncpoint_count + 1) {
av_log(s, AV_LOG_ERROR, "index overflow B\n");
@@ -643,7 +744,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);
@@ -744,7 +845,7 @@ static int nut_read_header(AVFormatContext *s)
find_and_decode_index(nut);
avio_seek(bc, orig_pos, SEEK_SET);
}
- assert(nut->next_startcode == SYNCPOINT_STARTCODE);
+ av_assert0(nut->next_startcode == SYNCPOINT_STARTCODE);
ff_metadata_conv_ctx(s, NULL, ff_nut_metadata_conv);
@@ -756,13 +857,138 @@ fail:
return AVERROR_INVALIDDATA;
}
+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, ret;
+
+ 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;
+ ret = get_str(bc, name, sizeof(name));
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "get_str failed while reading sm data\n");
+ return ret;
+ }
+ value = get_s(bc);
+
+ if (value == -1) {
+ ret = get_str(bc, str_value, sizeof(str_value));
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "get_str failed while reading sm data\n");
+ return ret;
+ }
+ 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;
+
+ ret = get_str(bc, type_str, sizeof(type_str));
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "get_str failed while reading sm data\n");
+ return ret;
+ }
+ value_len = ffio_read_varlen(bc);
+ if (value_len < 0 || value_len >= maxpos - avio_tell(bc))
+ 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);
+ }
+
+ if (avio_tell(bc) >= maxpos)
+ return AVERROR_INVALIDDATA;
+
+ return 0;
+}
+
static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id,
uint8_t *header_idx, int frame_code)
{
AVFormatContext *s = nut->avf;
AVIOContext *bc = s->pb;
StreamContext *stc;
- int size, flags, size_mul, pts_delta, i, reserved_count;
+ int size, flags, size_mul, pts_delta, i, reserved_count, ret;
uint64_t tmp;
if (!(nut->flags & NUT_PIPE) &&
@@ -795,7 +1021,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)
@@ -806,8 +1032,13 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id,
*header_idx = ffio_read_varlen(bc);
if (flags & FLAG_RESERVED)
reserved_count = ffio_read_varlen(bc);
- for (i = 0; i < reserved_count; i++)
+ for (i = 0; i < reserved_count; i++) {
+ if (bc->eof_reached) {
+ av_log(s, AV_LOG_ERROR, "reached EOF while decoding frame header\n");
+ return AVERROR_INVALIDDATA;
+ }
ffio_read_varlen(bc);
+ }
if (*header_idx >= (unsigned)nut->header_count) {
av_log(s, AV_LOG_ERROR, "header_idx invalid\n");
@@ -830,6 +1061,8 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id,
stc->last_flags = flags;
return size;
+fail:
+ return ret;
}
static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code)
@@ -867,7 +1100,27 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code)
if (nut->header[header_idx])
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) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ if (read_sm_data(s, bc, pkt, 1, pkt->pos + size) < 0) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ 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)
+ goto fail;
+ }
+ av_shrink_packet(pkt, nut->header_len[header_idx] + ret);
pkt->stream_index = stream_id;
if (stc->last_flags & FLAG_KEY)
@@ -875,6 +1128,9 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code)
pkt->pts = pts;
return 0;
+fail:
+ av_packet_unref(pkt);
+ return ret;
}
static int nut_read_packet(AVFormatContext *s, AVPacket *pkt)
@@ -893,7 +1149,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;
@@ -925,7 +1181,8 @@ static int nut_read_packet(AVFormatContext *s, AVPacket *pkt)
default:
resync:
av_log(s, AV_LOG_DEBUG, "syncing from %"PRId64"\n", pos);
- tmp = find_any_startcode(bc, nut->last_syncpoint_pos + 1);
+ tmp = find_any_startcode(bc, FFMAX(nut->last_syncpoint_pos, nut->last_resync_pos) + 1);
+ nut->last_resync_pos = avio_tell(bc);
if (tmp == 0)
return AVERROR_INVALIDDATA;
av_log(s, AV_LOG_DEBUG, "sync\n");
@@ -947,21 +1204,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,
@@ -982,13 +1236,14 @@ 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;
ts = st->index_entries[index].timestamp;
} else {
- av_tree_find(nut->syncpoints, &dummy,
- (int (*)(void *, const void *)) ff_nut_sp_pts_cmp,
+ av_tree_find(nut->syncpoints, &dummy, ff_nut_sp_pts_cmp,
(void **) next_node);
av_log(s, AV_LOG_DEBUG, "%"PRIu64"-%"PRIu64" %"PRId64"-%"PRId64"\n",
next_node[0]->pos, next_node[1]->pos, next_node[0]->ts,
@@ -997,12 +1252,13 @@ static int read_seek(AVFormatContext *s, int stream_index,
next_node[1]->pos, next_node[1]->pos,
next_node[0]->ts, next_node[1]->ts,
AVSEEK_FLAG_BACKWARD, &ts, nut_read_timestamp);
+ if (pos < 0)
+ return pos;
if (!(flags & AVSEEK_FLAG_BACKWARD)) {
dummy.pos = pos + 16;
next_node[1] = &nopts_sp;
- av_tree_find(nut->syncpoints, &dummy,
- (int (*)(void *, const void *)) ff_nut_sp_pos_cmp,
+ av_tree_find(nut->syncpoints, &dummy, ff_nut_sp_pos_cmp,
(void **) next_node);
pos2 = ff_gen_search(s, -2, dummy.pos, next_node[0]->pos,
next_node[1]->pos, next_node[1]->pos,
@@ -1013,28 +1269,31 @@ static int read_seek(AVFormatContext *s, int stream_index,
// FIXME dir but I think it does not matter
}
dummy.pos = pos;
- sp = av_tree_find(nut->syncpoints, &dummy,
- (int (*)(void *, const void *)) ff_nut_sp_pos_cmp,
+ sp = av_tree_find(nut->syncpoints, &dummy, 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);
pos = find_startcode(s->pb, SYNCPOINT_STARTCODE, pos2);
avio_seek(s->pb, pos, SEEK_SET);
+ nut->last_syncpoint_pos = pos;
av_log(NULL, AV_LOG_DEBUG, "SP: %"PRId64"\n", pos);
if (pos2 > pos || pos2 + 15 < pos)
av_log(NULL, AV_LOG_ERROR, "no syncpoint at backptr pos\n");
for (i = 0; i < s->nb_streams; i++)
nut->stream[i].skip_until_key_frame = 1;
+ nut->last_resync_pos = 0;
+
return 0;
}
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 57179bb..a92ff55 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(AVCodecParameters *p, 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);
@@ -169,11 +168,23 @@ static void build_frame_code(AVFormatContext *s)
for (stream_id = 0; stream_id < s->nb_streams; stream_id++) {
int start2 = start + (end - start) * stream_id / s->nb_streams;
int end2 = start + (end - start) * (stream_id + 1) / s->nb_streams;
- AVCodecParameters *par = s->streams[stream_id]->codecpar;
- const AVCodecDescriptor *desc = avcodec_descriptor_get(par->codec_id);
- int is_audio = par->codec_type == AVMEDIA_TYPE_AUDIO;
+ AVCodecParameters *par = s->streams[stream_id]->codecpar;
+ int is_audio = par->codec_type == AVMEDIA_TYPE_AUDIO;
int intra_only = /*codec->intra_only || */ is_audio;
int pred_count;
+ int frame_size = 0;
+
+ if (par->codec_type == AVMEDIA_TYPE_AUDIO) {
+ frame_size = av_get_audio_frame_duration2(par, 0);
+ if (par->codec_id == AV_CODEC_ID_VORBIS && !frame_size)
+ frame_size = 64;
+ } else {
+ AVRational f = av_div_q(av_inv_q(s->streams[stream_id]->avg_frame_rate), *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,6 +200,7 @@ static void build_frame_code(AVFormatContext *s)
}
key_frame = intra_only;
+#if 1
if (is_audio) {
int frame_bytes;
int pts;
@@ -200,27 +212,29 @@ static void build_frame_code(AVFormatContext *s)
frame_bytes = frame_size * (int64_t)par->bit_rate / (8 * par->sample_rate);
}
- 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, par, 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 (par->video_delay) {
pred_count = 5;
pred_table[0] = -2;
pred_table[1] = -1;
@@ -241,6 +255,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;
@@ -255,15 +271,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;
@@ -274,7 +288,7 @@ static void put_tt(NUTContext *nut, AVRational *time_base, AVIOContext *bc,
*/
static void put_str(AVIOContext *bc, const char *string)
{
- int len = strlen(string);
+ size_t len = strlen(string);
ff_put_v(bc, len);
avio_write(bc, string, len);
@@ -316,6 +330,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);
@@ -334,17 +350,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;
@@ -373,22 +384,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++) {
@@ -396,7 +399,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);
}
@@ -405,32 +408,18 @@ static int write_streamheader(AVFormatContext *avctx, AVIOContext *bc,
{
NUTContext *nut = avctx->priv_data;
AVCodecParameters *par = st->codecpar;
- const AVCodecDescriptor *desc = avcodec_descriptor_get(par->codec_id);
- unsigned codec_tag = av_codec_get_tag(ff_nut_codec_tags, par->codec_id);
ff_put_v(bc, i);
switch (par->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 (av_codec_get_id(ff_nut_codec_tags, par->codec_tag) == par->codec_id ||
- !codec_tag || par->codec_id == AV_CODEC_ID_RAWVIDEO)
- codec_tag = par->codec_tag;
-
- if (codec_tag) {
- avio_wl32(bc, codec_tag);
+ if (par->codec_tag) {
+ avio_wl32(bc, par->codec_tag);
} else {
av_log(avctx, AV_LOG_ERROR, "No codec tag defined for stream %d\n", i);
return AVERROR(EINVAL);
@@ -439,7 +428,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, par->video_delay);
avio_w8(bc, 0); /* flags: 0x1 - fixed_fps, 0x2 - index_present */
ff_put_v(bc, par->extradata_size);
@@ -490,6 +479,7 @@ static int write_globalinfo(NUTContext *nut, AVIOContext *bc)
if (ret < 0)
return ret;
+ ff_standardize_creation_time(s);
while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX)))
count += add_info(dyn_bc, t->key, t->value);
@@ -506,20 +496,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->codecpar->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->avg_frame_rate.num, st->avg_frame_rate.den);
+ count += add_info(dyn_bc, "r_frame_rate", buf);
+ }
dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf);
if (count) {
@@ -565,6 +566,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, 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;
@@ -633,8 +686,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, "
@@ -643,12 +696,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);
@@ -659,8 +711,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->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->sample_rate) {
+ time_base = (AVRational) {1, st->codecpar->sample_rate};
+ } else {
+ time_base = ff_choose_timebase(s, st, 48000);
+ }
avpriv_set_pts_info(st, 64, time_base.num, time_base.den);
@@ -697,7 +754,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);
@@ -705,9 +762,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;
}
@@ -725,6 +783,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)
@@ -757,24 +817,165 @@ 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:
+ case AV_PKT_DATA_QUALITY_STATS:
+ // 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))
@@ -783,15 +984,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++) {
@@ -802,21 +1002,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,
- (int (*)(void *, const 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,
@@ -824,10 +1026,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)
@@ -860,10 +1077,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;
@@ -871,9 +1088,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;
}
@@ -892,9 +1108,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;
@@ -907,27 +1121,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,
@@ -935,24 +1146,53 @@ 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 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);
+ }
+
+ return 0;
+}
+
+static void nut_write_deinit(AVFormatContext *s)
+{
+ NUTContext *nut = s->priv_data;
+ int i;
+
ff_nut_free_sp(nut);
+ if (nut->stream)
+ 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);
-
- return 0;
}
#define OFFSET(x) offsetof(NUTContext, x)
@@ -962,6 +1202,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_BOOL, {.i64 = 1}, 0, 1, E, },
{ NULL },
};
@@ -984,6 +1225,7 @@ AVOutputFormat ff_nut_muxer = {
.write_header = nut_write_header,
.write_packet = nut_write_packet,
.write_trailer = nut_write_trailer,
+ .deinit = nut_write_deinit,
.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS,
.codec_tag = ff_nut_codec_tags,
.priv_class = &class,
diff --git a/libavformat/nuv.c b/libavformat/nuv.c
index 492f68f..9bdea4a 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
*/
@@ -66,14 +66,14 @@ static int nuv_probe(AVProbeData *p)
* @param myth set if this is a MythTVVideo format file
* @return 0 or AVERROR code
*/
-static int get_codec_data(AVIOContext *pb, AVStream *vst,
+static int get_codec_data(AVFormatContext *s, AVIOContext *pb, AVStream *vst,
AVStream *ast, int myth)
{
nuv_frametype frametype;
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->codecpar->extradata);
vst->codecpar->extradata_size = 0;
}
- vst->codecpar->extradata = av_malloc(size);
- if (!vst->codecpar->extradata)
+ if (ff_get_extradata(NULL, vst->codecpar, pb, size) < 0)
return AVERROR(ENOMEM);
- vst->codecpar->extradata_size = size;
- avio_read(pb, vst->codecpar->extradata, size);
size = 0;
if (!myth)
return 0;
@@ -117,6 +114,10 @@ static int get_codec_data(AVIOContext *pb, AVStream *vst,
ast->codecpar->codec_tag = avio_rl32(pb);
ast->codecpar->sample_rate = avio_rl32(pb);
+ if (ast->codecpar->sample_rate <= 0) {
+ av_log(s, AV_LOG_ERROR, "Invalid sample rate %d\n", ast->codecpar->sample_rate);
+ return AVERROR_INVALIDDATA;
+ }
ast->codecpar->bits_per_coded_sample = avio_rl32(pb);
ast->codecpar->channels = avio_rl32(pb);
ast->codecpar->channel_layout = 0;
@@ -209,6 +210,9 @@ static int nuv_header(AVFormatContext *s)
vst->codecpar->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
@@ -232,7 +236,7 @@ static int nuv_header(AVFormatContext *s)
} else
ctx->a_id = -1;
- if ((ret = get_codec_data(pb, vst, ast, is_mythtv)) < 0)
+ if ((ret = get_codec_data(s, pb, vst, ast, is_mythtv)) < 0)
return ret;
ctx->rtjpg_video = vst && vst->codecpar->codec_id == AV_CODEC_ID_NUV;
@@ -250,7 +254,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);
@@ -276,10 +280,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);
@@ -317,6 +320,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"),
@@ -324,5 +402,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 5965a97..97ad1a2 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"
@@ -40,6 +41,7 @@
static const struct ogg_codec * const ogg_codecs[] = {
&ff_skeleton_codec,
+ &ff_daala_codec,
&ff_dirac_codec,
&ff_speex_codec,
&ff_vorbis_codec,
@@ -57,6 +59,25 @@ 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);
+static int ogg_restore(AVFormatContext *s);
+
+static void free_stream(AVFormatContext *s, int i)
+{
+ struct ogg *ogg = s->priv_data;
+ struct ogg_stream *stream = &ogg->streams[i];
+
+ av_freep(&stream->buf);
+ if (stream->codec &&
+ stream->codec->cleanup) {
+ stream->codec->cleanup(s, i);
+ }
+
+ av_freep(&stream->private);
+ av_freep(&stream->new_metadata);
+}
+
//FIXME We could avoid some structure duplication
static int ogg_save(AVFormatContext *s)
{
@@ -64,8 +85,11 @@ static int ogg_save(AVFormatContext *s)
struct ogg_state *ost =
av_malloc(sizeof(*ost) + (ogg->nstreams - 1) * sizeof(*ogg->streams));
int i;
+ int ret = 0;
+
if (!ost)
return AVERROR(ENOMEM);
+
ost->pos = avio_tell(s->pb);
ost->curidx = ogg->curidx;
ost->next = ogg->state;
@@ -75,15 +99,23 @@ static int ogg_save(AVFormatContext *s)
for (i = 0; i < ogg->nstreams; i++) {
struct ogg_stream *os = ogg->streams + i;
os->buf = av_mallocz(os->bufsize + AV_INPUT_BUFFER_PADDING_SIZE);
- memcpy(os->buf, ost->streams[i].buf, os->bufpos);
+ if (os->buf)
+ memcpy(os->buf, ost->streams[i].buf, os->bufpos);
+ else
+ ret = AVERROR(ENOMEM);
+ os->new_metadata = NULL;
+ os->new_metadata_size = 0;
}
ogg->state = ost;
- return 0;
+ if (ret < 0)
+ ogg_restore(s);
+
+ return ret;
}
-static int ogg_restore(AVFormatContext *s, int discard)
+static int ogg_restore(AVFormatContext *s)
{
struct ogg *ogg = s->priv_data;
AVIOContext *bc = s->pb;
@@ -95,12 +127,15 @@ static int ogg_restore(AVFormatContext *s, int discard)
ogg->state = ost->next;
- if (!discard) {
-
- for (i = 0; i < ogg->nstreams; i++)
- av_free(ogg->streams[i].buf);
+ for (i = 0; i < ogg->nstreams; i++) {
+ av_freep(&ogg->streams[i].buf);
+ if (i >= ost->nstreams || !ost->streams[i].private) {
+ free_stream(s, i);
+ }
+ }
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,
@@ -110,16 +145,17 @@ static int ogg_restore(AVFormatContext *s, int discard)
} else
memcpy(ogg->streams, ost->streams,
ost->nstreams * sizeof(*ogg->streams));
- }
av_free(ost);
return 0;
}
-static int ogg_reset(struct ogg *ogg)
+static int ogg_reset(AVFormatContext *s)
{
+ struct ogg *ogg = s->priv_data;
int i;
+ int64_t start_pos = avio_tell(s->pb);
for (i = 0; i < ogg->nstreams; i++) {
struct ogg_stream *os = ogg->streams + i;
@@ -134,8 +170,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;
@@ -153,38 +197,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 & AVIO_SEEKABLE_NORMAL) {
+ uint8_t magic[8];
+ int64_t pos = avio_tell(s->pb);
+ avio_skip(s->pb, nsegs);
+ avio_read(s->pb, magic, sizeof(magic));
+ avio_seek(s->pb, pos, SEEK_SET);
+ codec = ogg_find_codec(magic, sizeof(magic));
+ if (!codec) {
+ av_log(s, AV_LOG_ERROR, "Cannot identify new stream\n");
+ return AVERROR_INVALIDDATA;
+ }
+ for (i = 0; i < ogg->nstreams; i++) {
+ if (ogg->streams[i].codec == codec)
+ break;
+ }
+ if (i >= ogg->nstreams)
+ return ogg_new_stream(s, serial);
+ } else if (ogg->nstreams != 1) {
+ avpriv_report_missing_feature(s, "Changing stream parameters in multistream ogg");
+ return AVERROR_PATCHWELCOME;
+ }
- os = av_realloc(ogg->streams, ogg->nstreams * sizeof(*ogg->streams));
+ os = &ogg->streams[i];
- if (!os)
- return AVERROR(ENOMEM);
+ os->serial = serial;
+ return i;
- ogg->streams = os;
+#if 0
+ buf = os->buf;
+ bufsize = os->bufsize;
+ codec = os->codec;
+
+ if (!ogg->state || ogg->state->streams[i].private != os->private)
+ av_freep(&ogg->streams[i].private);
+
+ /* Set Ogg stream settings similar to what is done in ogg_new_stream(). We
+ * also re-use the ogg_stream allocated buffer */
+ memset(os, 0, sizeof(*os));
+ os->serial = serial;
+ os->bufsize = bufsize;
+ os->buf = buf;
+ os->header = -1;
+ os->codec = codec;
+
+ return i;
+#endif
+}
+
+static int ogg_new_stream(AVFormatContext *s, uint32_t serial)
+{
+ struct ogg *ogg = s->priv_data;
+ int idx = ogg->nstreams;
+ AVStream *st;
+ struct ogg_stream *os;
+ size_t size;
- memset(ogg->streams + idx, 0, sizeof(*ogg->streams));
+ if (ogg->state) {
+ av_log(s, AV_LOG_ERROR, "New streams are not supposed to be added "
+ "in between Ogg context save/restore operations.\n");
+ return AVERROR_BUG;
+ }
- os = ogg->streams + idx;
+ /* Allocate and init a new Ogg Stream */
+ if (av_size_mult(ogg->nstreams + 1, sizeof(*ogg->streams), &size) < 0 ||
+ !(os = av_realloc(ogg->streams, size)))
+ return AVERROR(ENOMEM);
+ ogg->streams = os;
+ os = ogg->streams + idx;
+ memset(os, 0, sizeof(*os));
os->serial = serial;
os->bufsize = DECODER_BUFFER_SIZE;
os->buf = av_malloc(os->bufsize + AV_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;
}
@@ -209,7 +320,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;
@@ -234,9 +355,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 & AVIO_SEEKABLE_NORMAL) && 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;
@@ -247,8 +374,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);
@@ -258,28 +387,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) {
@@ -299,8 +419,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;
@@ -332,13 +458,20 @@ static int ogg_read_page(AVFormatContext *s, int *str)
os->flags = flags;
memset(os->buf + os->bufpos, 0, AV_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;
@@ -348,6 +481,8 @@ static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize,
int segp = 0, psize = 0;
av_log(s, AV_LOG_TRACE, "ogg_packet: curidx=%i\n", ogg->curidx);
+ if (sid)
+ *sid = -1;
do {
idx = ogg->curidx;
@@ -398,8 +533,6 @@ static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize,
}
} while (!complete);
- av_log(s, AV_LOG_TRACE, "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,
@@ -443,8 +576,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)
@@ -453,6 +586,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;
}
@@ -471,43 +606,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_log(s, AV_LOG_TRACE, "found headers\n");
-
- return 0;
-}
-
static int ogg_get_length(AVFormatContext *s)
{
struct ogg *ogg = s->priv_data;
int i, ret;
int64_t size, end;
+ int streams_left=0;
if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL))
return 0;
@@ -525,18 +629,48 @@ static int ogg_get_length(AVFormatContext *s)
if (ret < 0)
return ret;
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_restore(s);
+
+ ret = ogg_save(s);
+ if (ret < 0)
+ return ret;
+
+ 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 (s->streams[i]->duration == AV_NOPTS_VALUE)
+ continue;
+ 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);
return 0;
}
@@ -547,14 +681,12 @@ static int ogg_read_close(AVFormatContext *s)
int i;
for (i = 0; i < ogg->nstreams; i++) {
- av_free(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);
+ free_stream(s, i);
}
- av_free(ogg->streams);
+
+ ogg->nstreams = 0;
+
+ av_freep(&ogg->streams);
return 0;
}
@@ -562,17 +694,38 @@ 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_log(s, AV_LOG_TRACE, "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;
+ av_freep(&ogg->streams[i].private);
+ } 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
ret = ogg_get_length(s);
@@ -581,7 +734,6 @@ static int ogg_read_header(AVFormatContext *s)
return ret;
}
- //fill the extradata in the per codec callbacks
return 0;
}
@@ -615,14 +767,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]->codecpar->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 {
@@ -636,6 +814,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;
@@ -654,7 +833,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_packet_unref(pkt);
+ return AVERROR(ENOMEM);
}
static int64_t ogg_read_timestamp(AVFormatContext *s, int stream_index,
@@ -663,22 +867,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 an 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;
}
@@ -689,6 +909,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]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
@@ -696,6 +921,7 @@ static int ogg_read_seek(AVFormatContext *s, int stream_index,
os->keyframe_seek = 1;
ret = ff_seek_frame_binary(s, stream_index, timestamp, flags);
+ ogg_reset(s);
os = ogg->streams + stream_index;
if (ret < 0)
os->keyframe_seek = 0;
@@ -720,5 +946,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 807ac4a..4a2b6dd 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,9 +111,10 @@ 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_daala_codec;
extern const struct ogg_codec ff_dirac_codec;
extern const struct ogg_codec ff_flac_codec;
extern const struct ogg_codec ff_ogm_audio_codec;
@@ -155,6 +162,11 @@ ogg_gptopts (AVFormatContext * s, int i, uint64_t gp, int64_t *dts)
if (dts)
*dts = pts;
}
+ if (pts > INT64_MAX && pts != AV_NOPTS_VALUE) {
+ // The return type is unsigned, we thus cannot return negative pts
+ av_log(s, AV_LOG_ERROR, "invalid pts %"PRId64"\n", pts);
+ pts = AV_NOPTS_VALUE;
+ }
return pts;
}
diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c
index 2fef74a..10c4eda 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
*/
@@ -54,6 +54,8 @@ typedef struct OGGStreamContext {
int kfgshift;
int64_t last_kf_pts;
int vrev;
+ /* for VP8 granule */
+ int isvp8;
int eos;
unsigned page_count; ///< number of page buffered
OGGPage page; ///< current page
@@ -80,6 +82,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 +91,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,11 +146,19 @@ 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))) ||
+ (oggstream->isvp8 && !((granule >> 3) & 0x07ffffff));
+}
+
static int64_t ogg_granule_to_timestamp(OGGStreamContext *oggstream, int64_t granule)
{
if (oggstream->kfgshift)
return (granule>>oggstream->kfgshift) +
(granule & ((1<<oggstream->kfgshift)-1));
+ else if (oggstream->isvp8)
+ return granule >> 32;
else
return granule;
}
@@ -186,7 +198,7 @@ static int ogg_buffer_page(AVFormatContext *s, OGGStreamContext *oggstream)
return AVERROR(ENOMEM);
l->page = oggstream->page;
- oggstream->page.start_granule = oggstream->page.granule;
+ oggstream->page.start_granule = ogg_granule_to_timestamp(oggstream, oggstream->page.granule);
oggstream->page_count++;
ogg_reset_cur_page(oggstream);
@@ -212,9 +224,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
- if (st->codecpar->codec_id == AV_CODEC_ID_THEORA && !header &&
- ogg_granule_to_timestamp(oggstream, granule) >
- ogg_granule_to_timestamp(oggstream, oggstream->last_granule) + 1) {
+ // For theora and VP8, 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->codecpar->codec_id == AV_CODEC_ID_THEORA || st->codecpar->codec_id == AV_CODEC_ID_VP8) && !header &&
+ (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;
@@ -248,18 +265,21 @@ static int ogg_buffer_data(AVFormatContext *s, AVStream *st,
if (i == total_segments)
page->granule = granule;
- if (!header) {
+ {
AVStream *st = s->streams[page->stream_index];
int64_t start = av_rescale_q(page->start_granule, st->time_base,
AV_TIME_BASE_Q);
- int64_t next = av_rescale_q(page->granule, st->time_base,
- AV_TIME_BASE_Q);
+ int64_t next = av_rescale_q(ogg_granule_to_timestamp(oggstream, page->granule),
+ st->time_base, AV_TIME_BASE_Q);
- if (page->segments_count == 255 ||
- (ogg->pref_size > 0 && page->size >= ogg->pref_size) ||
- (ogg->pref_duration > 0 && next - start >= ogg->pref_duration)) {
+ if (page->segments_count == 255) {
ogg_buffer_page(s, oggstream);
+ } else if (!header) {
+ if ((ogg->pref_size > 0 && page->size >= ogg->pref_size) ||
+ (ogg->pref_duration > 0 && next - start >= ogg->pref_duration)) {
+ ogg_buffer_page(s, oggstream);
+ }
}
}
}
@@ -270,16 +290,18 @@ static int ogg_buffer_data(AVFormatContext *s, AVStream *st,
return 0;
}
-static uint8_t *ogg_write_vorbiscomment(int offset, int bitexact,
+static uint8_t *ogg_write_vorbiscomment(int64_t offset, int bitexact,
int *header_len, AVDictionary **m, int framing_bit)
{
- const char *vendor = bitexact ? "Libav" : LIBAVFORMAT_IDENT;
- int size;
+ const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT;
+ int64_t size;
uint8_t *p, *p0;
ff_metadata_conv(m, ff_vorbiscomment_metadata_conv, NULL);
size = offset + ff_vorbiscomment_length(*m, vendor) + framing_bit;
+ if (size > INT_MAX)
+ return NULL;
p = av_mallocz(size);
if (!p)
return NULL;
@@ -339,7 +361,7 @@ static int ogg_build_speex_headers(AVCodecParameters *par,
uint8_t *p;
if (par->extradata_size < SPEEX_HEADER_SIZE)
- return -1;
+ return AVERROR_INVALIDDATA;
// first packet: Speex header
p = av_mallocz(SPEEX_HEADER_SIZE);
@@ -368,7 +390,7 @@ static int ogg_build_opus_headers(AVCodecParameters *par,
uint8_t *p;
if (par->extradata_size < OPUS_HEADER_SIZE)
- return -1;
+ return AVERROR_INVALIDDATA;
/* first packet: Opus header */
p = av_mallocz(par->extradata_size);
@@ -388,6 +410,57 @@ static int ogg_build_opus_headers(AVCodecParameters *par,
return 0;
}
+#define VP8_HEADER_SIZE 26
+
+static int ogg_build_vp8_headers(AVFormatContext *s, AVStream *st,
+ OGGStreamContext *oggstream, int bitexact)
+{
+ AVCodecParameters *par = st->codecpar;
+ uint8_t *p;
+
+ /* first packet: VP8 header */
+ p = av_mallocz(VP8_HEADER_SIZE);
+ if (!p)
+ return AVERROR(ENOMEM);
+ oggstream->header[0] = p;
+ oggstream->header_len[0] = VP8_HEADER_SIZE;
+ bytestream_put_byte(&p, 0x4f); // HDRID
+ bytestream_put_buffer(&p, "VP80", 4); // Identifier
+ bytestream_put_byte(&p, 1); // HDRTYP
+ bytestream_put_byte(&p, 1); // VMAJ
+ bytestream_put_byte(&p, 0); // VMIN
+ bytestream_put_be16(&p, par->width);
+ bytestream_put_be16(&p, par->height);
+ bytestream_put_be24(&p, par->sample_aspect_ratio.num);
+ bytestream_put_be24(&p, par->sample_aspect_ratio.den);
+ if (st->r_frame_rate.num > 0 && st->r_frame_rate.den > 0) {
+ // OggVP8 requires pts to increase by 1 per visible frame, so use the least common
+ // multiple framerate if available.
+ av_log(s, AV_LOG_DEBUG, "Changing time base from %d/%d to %d/%d\n",
+ st->time_base.num, st->time_base.den,
+ st->r_frame_rate.den, st->r_frame_rate.num);
+ avpriv_set_pts_info(st, 64, st->r_frame_rate.den, st->r_frame_rate.num);
+ }
+ bytestream_put_be32(&p, st->time_base.den);
+ bytestream_put_be32(&p, st->time_base.num);
+
+ /* optional second packet: VorbisComment */
+ if (av_dict_get(st->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) {
+ p = ogg_write_vorbiscomment(7, bitexact, &oggstream->header_len[1], &st->metadata, 0);
+ if (!p)
+ return AVERROR(ENOMEM);
+ oggstream->header[1] = p;
+ bytestream_put_byte(&p, 0x4f); // HDRID
+ bytestream_put_buffer(&p, "VP80", 4); // Identifier
+ bytestream_put_byte(&p, 2); // HDRTYP
+ bytestream_put_byte(&p, 0x20);
+ }
+
+ oggstream->isvp8 = 1;
+
+ return 0;
+}
+
static void ogg_write_pages(AVFormatContext *s, int flush)
{
OGGContext *ogg = s->priv_data;
@@ -410,10 +483,10 @@ static void ogg_write_pages(AVFormatContext *s, int flush)
ogg->page_list = p;
}
-static int ogg_write_header(AVFormatContext *s)
+static int ogg_init(AVFormatContext *s)
{
OGGContext *ogg = s->priv_data;
- OGGStreamContext *oggstream;
+ OGGStreamContext *oggstream = NULL;
int i, j;
if (ogg->pref_size)
@@ -423,29 +496,33 @@ static int ogg_write_header(AVFormatContext *s)
AVStream *st = s->streams[i];
unsigned serial_num = i + ogg->serial_offset;
- if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
if (st->codecpar->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->codecpar->sample_rate);
+ }
if (st->codecpar->codec_id != AV_CODEC_ID_VORBIS &&
st->codecpar->codec_id != AV_CODEC_ID_THEORA &&
st->codecpar->codec_id != AV_CODEC_ID_SPEEX &&
st->codecpar->codec_id != AV_CODEC_ID_FLAC &&
- st->codecpar->codec_id != AV_CODEC_ID_OPUS) {
+ st->codecpar->codec_id != AV_CODEC_ID_OPUS &&
+ st->codecpar->codec_id != AV_CODEC_ID_VP8) {
av_log(s, AV_LOG_ERROR, "Unsupported codec id in stream %d\n", i);
- return -1;
+ return AVERROR(EINVAL);
}
- if (!st->codecpar->extradata || !st->codecpar->extradata_size) {
+ if ((!st->codecpar->extradata || !st->codecpar->extradata_size) &&
+ st->codecpar->codec_id != AV_CODEC_ID_VP8) {
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))
@@ -459,11 +536,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->codecpar->codec_id == AV_CODEC_ID_FLAC) {
int err = ogg_build_flac_headers(st->codecpar, 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);
@@ -472,7 +551,7 @@ static int ogg_write_header(AVFormatContext *s)
} else if (st->codecpar->codec_id == AV_CODEC_ID_SPEEX) {
int err = ogg_build_speex_headers(st->codecpar, 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);
@@ -481,12 +560,20 @@ static int ogg_write_header(AVFormatContext *s)
} else if (st->codecpar->codec_id == AV_CODEC_ID_OPUS) {
int err = ogg_build_opus_headers(st->codecpar, 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);
return err;
}
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_VP8) {
+ int err = ogg_build_vp8_headers(s, st, oggstream,
+ s->flags & AVFMT_FLAG_BITEXACT);
+ if (err) {
+ av_log(s, AV_LOG_ERROR, "Error writing VP8 headers\n");
+ av_freep(&st->priv_data);
+ return err;
+ }
} else {
uint8_t *p;
const char *cstr = st->codecpar->codec_id == AV_CODEC_ID_VORBIS ? "vorbis" : "theora";
@@ -495,14 +582,14 @@ static int ogg_write_header(AVFormatContext *s)
if (avpriv_split_xiph_headers(st->codecpar->extradata, st->codecpar->extradata_size,
st->codecpar->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)
@@ -512,6 +599,14 @@ static int ogg_write_header(AVFormatContext *s)
bytestream_put_buffer(&p, cstr, 6);
if (st->codecpar->codec_id == AV_CODEC_ID_THEORA) {
+ int den = AV_RB32(oggstream->header[0] + 22), num = AV_RB32(oggstream->header[0] + 26);
+ /* Make sure to use time base stored in the Theora stream header to write
+ correct timestamps */
+ if (st->time_base.num != num || st->time_base.den != den) {
+ av_log(s, AV_LOG_DEBUG, "Changing time base from %d/%d to %d/%d\n",
+ st->time_base.num, st->time_base.den, num, den);
+ avpriv_set_pts_info(st, 64, num, den);
+ }
/** KFGSHIFT is the width of the less significant section of the granule position
The less significant section is the frame count since the last keyframe */
oggstream->kfgshift = ((oggstream->header[0][40]&3)<<3)|(oggstream->header[0][41]>>5);
@@ -522,8 +617,16 @@ static int ogg_write_header(AVFormatContext *s)
}
}
+ return 0;
+}
+
+static int ogg_write_header(AVFormatContext *s)
+{
+ OGGStreamContext *oggstream = NULL;
+ int i, j;
+
for (j = 0; j < s->nb_streams; j++) {
- OGGStreamContext *oggstream = s->streams[j]->priv_data;
+ oggstream = s->streams[j]->priv_data;
ogg_buffer_data(s, s->streams[j], oggstream->header[0],
oggstream->header_len[0], 0, 1);
oggstream->page.flags |= 2; // bos
@@ -531,7 +634,7 @@ static int ogg_write_header(AVFormatContext *s)
}
for (j = 0; j < s->nb_streams; j++) {
AVStream *st = s->streams[j];
- OGGStreamContext *oggstream = st->priv_data;
+ oggstream = st->priv_data;
for (i = 1; i < 3; i++) {
if (oggstream->header_len[i])
ogg_buffer_data(s, st, oggstream->header[i],
@@ -571,7 +674,18 @@ static int ogg_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
av_rescale_q(st->codecpar->initial_padding,
(AVRational){ 1, st->codecpar->sample_rate },
st->time_base);
- else
+ else if (st->codecpar->codec_id == AV_CODEC_ID_VP8) {
+ int64_t pts, invcnt, dist;
+ int visible;
+
+ visible = (pkt->data[0] >> 4) & 1;
+ pts = pkt->pts + pkt->duration;
+ invcnt = (oggstream->last_granule >> 30) & 3;
+ invcnt = visible ? 3 : (invcnt == 3 ? 0 : invcnt + 1);
+ dist = (pkt->flags & AV_PKT_FLAG_KEY) ? 0 : ((oggstream->last_granule >> 3) & 0x07ffffff) + 1;
+
+ granule = (pts << 32) | (invcnt << 30) | (dist << 3);
+ } else
granule = pkt->pts + pkt->duration;
if (oggstream->page.start_granule == AV_NOPTS_VALUE)
@@ -602,7 +716,7 @@ static int ogg_write_packet(AVFormatContext *s, AVPacket *pkt)
}
ogg_write_pages(s, 2);
- return 0;
+ return 1;
}
static int ogg_write_trailer(AVFormatContext *s)
@@ -619,59 +733,103 @@ static int ogg_write_trailer(AVFormatContext *s)
ogg_write_pages(s, 1);
+ return 0;
+}
+
+static void ogg_free(AVFormatContext *s)
+{
+ int i;
+
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
OGGStreamContext *oggstream = st->priv_data;
+ if (!oggstream)
+ continue;
if (st->codecpar->codec_id == AV_CODEC_ID_FLAC ||
st->codecpar->codec_id == AV_CODEC_ID_SPEEX ||
- st->codecpar->codec_id == AV_CODEC_ID_OPUS) {
- av_free(oggstream->header[0]);
+ st->codecpar->codec_id == AV_CODEC_ID_OPUS ||
+ st->codecpar->codec_id == AV_CODEC_ID_VP8) {
+ av_freep(&oggstream->header[0]);
}
av_freep(&oggstream->header[1]);
av_freep(&st->priv_data);
}
- return 0;
}
#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"
+#if !CONFIG_OGV_MUXER
+ ",ogv"
+#endif
+#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,
.video_codec = AV_CODEC_ID_THEORA,
+ .init = ogg_init,
.write_header = ogg_write_header,
.write_packet = ogg_write_packet,
.write_trailer = ogg_write_trailer,
- .flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH,
+ .deinit = ogg_free,
+ .flags = AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH,
.priv_class = &ogg_muxer_class,
};
#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"),
.mime_type = "audio/ogg",
.extensions = "oga",
.priv_data_size = sizeof(OGGContext),
- .audio_codec = CONFIG_LIBVORBIS_ENCODER ?
- AV_CODEC_ID_VORBIS : AV_CODEC_ID_FLAC,
+ .audio_codec = AV_CODEC_ID_FLAC,
+ .init = ogg_init,
.write_header = ogg_write_header,
.write_packet = ogg_write_packet,
.write_trailer = ogg_write_trailer,
+ .deinit = ogg_free,
.flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH,
.priv_class = &oga_muxer_class,
};
#endif
+#if CONFIG_OGV_MUXER
+OGG_CLASS(ogv, Ogg video)
+AVOutputFormat ff_ogv_muxer = {
+ .name = "ogv",
+ .long_name = NULL_IF_CONFIG_SMALL("Ogg Video"),
+ .mime_type = "video/ogg",
+ .extensions = "ogv",
+ .priv_data_size = sizeof(OGGContext),
+ .audio_codec = CONFIG_LIBVORBIS_ENCODER ?
+ AV_CODEC_ID_VORBIS : AV_CODEC_ID_FLAC,
+ .video_codec = CONFIG_LIBTHEORA_ENCODER ?
+ AV_CODEC_ID_THEORA : AV_CODEC_ID_VP8,
+ .init = ogg_init,
+ .write_header = ogg_write_header,
+ .write_packet = ogg_write_packet,
+ .write_trailer = ogg_write_trailer,
+ .deinit = ogg_free,
+ .flags = AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH,
+ .priv_class = &ogv_muxer_class,
+};
+#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"),
@@ -679,16 +837,18 @@ AVOutputFormat ff_spx_muxer = {
.extensions = "spx",
.priv_data_size = sizeof(OGGContext),
.audio_codec = AV_CODEC_ID_SPEEX,
+ .init = ogg_init,
.write_header = ogg_write_header,
.write_packet = ogg_write_packet,
.write_trailer = ogg_write_trailer,
+ .deinit = ogg_free,
.flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH,
.priv_class = &spx_muxer_class,
};
#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"),
@@ -696,9 +856,11 @@ AVOutputFormat ff_opus_muxer = {
.extensions = "opus",
.priv_data_size = sizeof(OGGContext),
.audio_codec = AV_CODEC_ID_OPUS,
+ .init = ogg_init,
.write_header = ogg_write_header,
.write_packet = ogg_write_packet,
.write_trailer = ogg_write_trailer,
+ .deinit = ogg_free,
.flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH,
.priv_class = &opus_muxer_class,
};
diff --git a/libavformat/oggparsecelt.c b/libavformat/oggparsecelt.c
index 291a6b5..9c438a0 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) +
- AV_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->codecpar, 2 * sizeof(uint32_t)) < 0) {
av_free(priv);
return AVERROR(ENOMEM);
}
@@ -61,20 +59,22 @@ 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->codecpar->extradata);
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = AV_CODEC_ID_CELT;
st->codecpar->sample_rate = sample_rate;
st->codecpar->channels = nb_channels;
- st->codecpar->extradata = extradata;
- st->codecpar->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;
+
+ if (os->private) {
+ av_free(priv);
+ priv = os->private;
+ }
os->private = priv;
- AV_WL32(extradata + 0, overlap);
- AV_WL32(extradata + 4, version);
+ priv->extra_headers_left = 1 + extra_headers;
+
+ AV_WL32(st->codecpar->extradata + 0, overlap);
+ AV_WL32(st->codecpar->extradata + 4, version);
return 1;
} else if (priv && priv->extra_headers_left) {
/* Extra headers (vorbiscomment) */
diff --git a/libavformat/oggparsedaala.c b/libavformat/oggparsedaala.c
new file mode 100644
index 0000000..a373b41
--- /dev/null
+++ b/libavformat/oggparsedaala.c
@@ -0,0 +1,257 @@
+/*
+ * Ogg Daala parser
+ * Copyright (C) 2015 Rostislav Pehlivanov <atomnuker gmail com>
+ * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara 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 <stdlib.h>
+#include "libavcodec/bytestream.h"
+#include "avformat.h"
+#include "internal.h"
+#include "oggdec.h"
+
+struct DaalaPixFmtMap {
+ enum AVPixelFormat ffmpeg_fmt;
+ int depth;
+ int planes;
+ int xdec[4];
+ int ydec[4];
+};
+
+/* Currently supported formats only */
+static const struct DaalaPixFmtMap list_fmts[] = {
+ { AV_PIX_FMT_YUV420P, 8, 3, {0, 1, 1, 0}, {0, 1, 1, 0} },
+ { AV_PIX_FMT_YUV444P, 8, 3, {0, 0, 0, 0}, {0, 0, 0, 0} }
+};
+
+typedef struct DaalaInfoHeader {
+ int init_d;
+ int fpr;
+ int gpshift;
+ int gpmask;
+ int version_maj;
+ int version_min;
+ int version_sub;
+ int frame_duration;
+ int keyframe_granule_shift;
+ struct DaalaPixFmtMap format;
+} DaalaInfoHeader;
+
+static inline int daala_match_pix_fmt(struct DaalaPixFmtMap *fmt)
+{
+ int i, j;
+ for (i = 0; i < FF_ARRAY_ELEMS(list_fmts); i++) {
+ int match = 0;
+ if (fmt->depth != list_fmts[i].depth)
+ continue;
+ if (fmt->planes != list_fmts[i].planes)
+ continue;
+ for (j = 0; j < fmt->planes; j++) {
+ if (fmt->xdec[j] != list_fmts[i].xdec[j])
+ continue;
+ if (fmt->ydec[j] != list_fmts[i].ydec[j])
+ continue;
+ match++;
+ }
+ if (match == fmt->planes)
+ return list_fmts[i].ffmpeg_fmt;
+ }
+ return -1;
+}
+
+static int daala_header(AVFormatContext *s, int idx)
+{
+ int i, err;
+ uint8_t *cdp;
+ GetByteContext gb;
+ AVRational timebase;
+ struct ogg *ogg = s->priv_data;
+ struct ogg_stream *os = ogg->streams + idx;
+ AVStream *st = s->streams[idx];
+ int cds = st->codecpar->extradata_size + os->psize + 2;
+ DaalaInfoHeader *hdr = os->private;
+
+ if (!(os->buf[os->pstart] & 0x80))
+ return 0;
+
+ if (!hdr) {
+ hdr = av_mallocz(sizeof(*hdr));
+ if (!hdr)
+ return AVERROR(ENOMEM);
+ os->private = hdr;
+ }
+
+ switch (os->buf[os->pstart]) {
+ case 0x80:
+ bytestream2_init(&gb, os->buf + os->pstart, os->psize);
+ bytestream2_skip(&gb, ff_daala_codec.magicsize);
+
+ hdr->version_maj = bytestream2_get_byte(&gb);
+ hdr->version_min = bytestream2_get_byte(&gb);
+ hdr->version_sub = bytestream2_get_byte(&gb);
+
+ st->codecpar->width = bytestream2_get_ne32(&gb);
+ st->codecpar->height = bytestream2_get_ne32(&gb);
+
+ st->sample_aspect_ratio.num = bytestream2_get_ne32(&gb);
+ st->sample_aspect_ratio.den = bytestream2_get_ne32(&gb);
+
+ timebase.num = bytestream2_get_ne32(&gb);
+ timebase.den = bytestream2_get_ne32(&gb);
+ if (timebase.num < 0 && timebase.den < 0) {
+ av_log(s, AV_LOG_WARNING, "Invalid timebase, assuming 30 FPS\n");
+ timebase.num = 1;
+ timebase.den = 30;
+ }
+ avpriv_set_pts_info(st, 64, timebase.den, timebase.num);
+
+ hdr->frame_duration = bytestream2_get_ne32(&gb);
+ hdr->gpshift = bytestream2_get_byte(&gb);
+ if (hdr->gpshift >= 32) {
+ av_log(s, AV_LOG_ERROR, "Too large gpshift %d (>= 32).\n",
+ hdr->gpshift);
+ hdr->gpshift = 0;
+ return AVERROR_INVALIDDATA;
+ }
+ hdr->gpmask = (1U << hdr->gpshift) - 1;
+
+ hdr->format.depth = 8 + 2*(bytestream2_get_byte(&gb)-1);
+
+ hdr->fpr = bytestream2_get_byte(&gb);
+
+ hdr->format.planes = bytestream2_get_byte(&gb);
+ if (hdr->format.planes > 4) {
+ av_log(s, AV_LOG_ERROR,
+ "Invalid number of planes %d in daala pixel format map.\n",
+ hdr->format.planes);
+ return AVERROR_INVALIDDATA;
+ }
+ for (i = 0; i < hdr->format.planes; i++) {
+ hdr->format.xdec[i] = bytestream2_get_byte(&gb);
+ hdr->format.ydec[i] = bytestream2_get_byte(&gb);
+ }
+
+ if ((st->codecpar->format = daala_match_pix_fmt(&hdr->format)) < 0)
+ av_log(s, AV_LOG_ERROR, "Unsupported pixel format - %i %i\n",
+ hdr->format.depth, hdr->format.planes);
+
+ st->codecpar->codec_id = AV_CODEC_ID_DAALA;
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->need_parsing = AVSTREAM_PARSE_HEADERS;
+
+ hdr->init_d = 1;
+ break;
+ case 0x81:
+ if (!hdr->init_d)
+ return AVERROR_INVALIDDATA;
+ ff_vorbis_stream_comment(s, st,
+ os->buf + os->pstart + ff_daala_codec.magicsize,
+ os->psize - ff_daala_codec.magicsize);
+ break;
+ case 0x82:
+ if (!hdr->init_d)
+ return AVERROR_INVALIDDATA;
+ break;
+ default:
+ av_log(s, AV_LOG_ERROR, "Unknown header type %X\n", os->buf[os->pstart]);
+ return AVERROR_INVALIDDATA;
+ break;
+ }
+
+ if ((err = av_reallocp(&st->codecpar->extradata,
+ cds + AV_INPUT_BUFFER_PADDING_SIZE)) < 0) {
+ st->codecpar->extradata_size = 0;
+ return err;
+ }
+
+ memset(st->codecpar->extradata + cds, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+ cdp = st->codecpar->extradata + st->codecpar->extradata_size;
+ *cdp++ = os->psize >> 8;
+ *cdp++ = os->psize & 0xff;
+ memcpy(cdp, os->buf + os->pstart, os->psize);
+ st->codecpar->extradata_size = cds;
+
+ return 1;
+}
+
+static uint64_t daala_gptopts(AVFormatContext *ctx, int idx, uint64_t gp,
+ int64_t *dts)
+{
+ uint64_t iframe, pframe;
+ struct ogg *ogg = ctx->priv_data;
+ struct ogg_stream *os = ogg->streams + idx;
+ DaalaInfoHeader *hdr = os->private;
+
+ if (!hdr)
+ return AV_NOPTS_VALUE;
+
+ iframe = gp >> hdr->gpshift;
+ pframe = gp & hdr->gpmask;
+
+ if (!pframe)
+ os->pflags |= AV_PKT_FLAG_KEY;
+
+ if (dts)
+ *dts = iframe + pframe;
+
+ return iframe + pframe;
+}
+
+static int daala_packet(AVFormatContext *s, int idx)
+{
+ int seg, duration = 1;
+ struct ogg *ogg = s->priv_data;
+ struct ogg_stream *os = ogg->streams + idx;
+
+ /*
+ * 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)) {
+ for (seg = os->segp; seg < os->nsegs; seg++)
+ if (os->segments[seg] < 255)
+ duration++;
+
+ os->lastpts = os->lastdts = daala_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 != AV_NOPTS_VALUE)
+ 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_daala_codec = {
+ .name = "Daala",
+ .magic = "\200daala",
+ .magicsize = 6,
+ .header = daala_header,
+ .packet = daala_packet,
+ .gptopts = daala_gptopts,
+ .granule_is_start = 1,
+ .nb_header = 3,
+};
diff --git a/libavformat/oggparsedirac.c b/libavformat/oggparsedirac.c
index b0a3796..74b9ba4 100644
--- a/libavformat/oggparsedirac.c
+++ b/libavformat/oggparsedirac.c
@@ -1,23 +1,24 @@
/*
* 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
*/
+#include "libavutil/imgutils.h"
#include "libavutil/intreadwrite.h"
#include "libavcodec/dirac.h"
#include "avformat.h"
@@ -36,7 +37,7 @@ static int dirac_header(AVFormatContext *s, int idx)
if (st->codecpar->codec_id == AV_CODEC_ID_DIRAC)
return 0;
- ret = av_dirac_parse_sequence_header(&dsh, os->buf + os->pstart + 13, (os->psize - 13) * 8, s);
+ ret = av_dirac_parse_sequence_header(&dsh, os->buf + os->pstart + 13, (os->psize - 13), s);
if (ret < 0)
return ret;
@@ -51,12 +52,13 @@ static int dirac_header(AVFormatContext *s, int idx)
st->codecpar->color_space = dsh->colorspace;
st->codecpar->profile = dsh->profile;
st->codecpar->level = dsh->level;
+ if (av_image_check_sar(st->codecpar->width, st->codecpar->height, dsh->sample_aspect_ratio) >= 0)
+ st->sample_aspect_ratio = dsh->sample_aspect_ratio;
// Dirac in Ogg always stores timestamps as though the video were interlaced
avpriv_set_pts_info(st, 64, dsh->framerate.den, 2 * dsh->framerate.num);
av_freep(&dsh);
-
return 1;
}
diff --git a/libavformat/oggparseflac.c b/libavformat/oggparseflac.c
index 0946a3b..b5f1416 100644
--- a/libavformat/oggparseflac.c
+++ b/libavformat/oggparseflac.c
@@ -1,28 +1,26 @@
/*
* 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
*/
#include <stdlib.h>
-
-#include "libavcodec/bitstream.h"
+#include "libavcodec/get_bits.h"
#include "libavcodec/flac.h"
-
#include "avformat.h"
#include "internal.h"
#include "oggdec.h"
@@ -35,38 +33,37 @@ flac_header (AVFormatContext * s, int idx)
struct ogg *ogg = s->priv_data;
struct ogg_stream *os = ogg->streams + idx;
AVStream *st = s->streams[idx];
- BitstreamContext bc;
+ GetBitContext gb;
int mdt;
if (os->buf[os->pstart] == 0xff)
return 0;
- bitstream_init8(&bc, os->buf + os->pstart, os->psize);
- bitstream_skip(&bc, 1); /* metadata_last */
- mdt = bitstream_read(&bc, 7);
+ init_get_bits(&gb, os->buf + os->pstart, os->psize*8);
+ skip_bits1(&gb); /* metadata_last */
+ mdt = get_bits(&gb, 7);
if (mdt == OGG_FLAC_METADATA_TYPE_STREAMINFO) {
uint8_t *streaminfo_start = os->buf + os->pstart + 5 + 4 + 4 + 4;
uint32_t samplerate;
- bitstream_skip(&bc, 4 * 8); /* "FLAC" */
- if (bitstream_read(&bc, 8) != 1) /* unsupported major version */
+ skip_bits_long(&gb, 4*8); /* "FLAC" */
+ if(get_bits(&gb, 8) != 1) /* unsupported major version */
return -1;
- bitstream_skip(&bc, 8 + 16); /* minor version + header count */
- bitstream_skip(&bc, 4 * 8); /* "fLaC" */
+ skip_bits_long(&gb, 8 + 16); /* minor version + header count */
+ skip_bits_long(&gb, 4*8); /* "fLaC" */
/* METADATA_BLOCK_HEADER */
- if (bitstream_read(&bc, 32) != FLAC_STREAMINFO_SIZE)
+ if (get_bits_long(&gb, 32) != FLAC_STREAMINFO_SIZE)
return -1;
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = AV_CODEC_ID_FLAC;
st->need_parsing = AVSTREAM_PARSE_HEADERS;
- st->codecpar->extradata =
- av_malloc(FLAC_STREAMINFO_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
- memcpy(st->codecpar->extradata, streaminfo_start, FLAC_STREAMINFO_SIZE);
- st->codecpar->extradata_size = FLAC_STREAMINFO_SIZE;
+ if (ff_alloc_extradata(st->codecpar, FLAC_STREAMINFO_SIZE) < 0)
+ return AVERROR(ENOMEM);
+ memcpy(st->codecpar->extradata, streaminfo_start, st->codecpar->extradata_size);
samplerate = AV_RB24(st->codecpar->extradata + 10) >> 4;
if (!samplerate)
@@ -83,11 +80,49 @@ 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);
+ AVCodecContext *avctx;
+ int size, ret;
+ uint8_t *data;
+
+ if (!parser)
+ return -1;
+
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = AV_CODEC_ID_FLAC;
- return 0;
+ avctx = avcodec_alloc_context3(NULL);
+ if (!avctx) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ ret = avcodec_parameters_to_context(avctx, st->codecpar);
+ if (ret < 0)
+ goto fail;
+
+ parser->flags = PARSER_FLAG_COMPLETE_FRAMES;
+ av_parser_parse2(parser, avctx,
+ &data, &size, os->buf + os->pstart, os->psize,
+ AV_NOPTS_VALUE, AV_NOPTS_VALUE, -1);
+
+ av_parser_close(parser);
+
+ if (avctx->sample_rate) {
+ avpriv_set_pts_info(st, 64, 1, avctx->sample_rate);
+ avcodec_free_context(&avctx);
+ return 0;
+ }
+
+ avcodec_free_context(&avctx);
+ return 1;
+fail:
+ av_parser_close(parser);
+ avcodec_free_context(&avctx);
+ return ret;
}
const struct ogg_codec ff_flac_codec = {
diff --git a/libavformat/oggparseogm.c b/libavformat/oggparseogm.c
index 6278465..e7a501b 100644
--- a/libavformat/oggparseogm.c
+++ b/libavformat/oggparseogm.c
@@ -24,6 +24,7 @@
#include <stdlib.h>
+#include "libavutil/avassert.h"
#include "libavutil/intreadwrite.h"
#include "libavcodec/bytestream.h"
@@ -42,6 +43,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))
@@ -57,6 +59,8 @@ ogm_header(AVFormatContext *s, int idx)
tag = bytestream2_get_le32(&p);
st->codecpar->codec_id = ff_codec_get_id(ff_codec_bmp_tags, tag);
st->codecpar->codec_tag = tag;
+ if (st->codecpar->codec_id == AV_CODEC_ID_MPEG4)
+ st->need_parsing = AVSTREAM_PARSE_HEADERS;
} else if (bytestream2_peek_byte(&p) == 't') {
st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
st->codecpar->codec_id = AV_CODEC_ID_TEXT;
@@ -70,11 +74,13 @@ ogm_header(AVFormatContext *s, int idx)
acid[4] = 0;
cid = strtol(acid, NULL, 16);
st->codecpar->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->codecpar->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) {
@@ -95,6 +101,19 @@ ogm_header(AVFormatContext *s, int idx)
st->codecpar->bit_rate = bytestream2_get_le32(&p) * 8;
st->codecpar->sample_rate = spu * 10000000 / time_unit;
avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+ if (size >= 56 && st->codecpar->codec_id == AV_CODEC_ID_AAC) {
+ bytestream2_skip(&p, 4);
+ size -= 4;
+ }
+ if (size > 52) {
+ av_assert0(AV_INPUT_BUFFER_PADDING_SIZE <= 52);
+ size -= 52;
+ if (bytestream2_get_bytes_left(&p) < size)
+ return AVERROR_INVALIDDATA;
+ if (ff_alloc_extradata(st->codecpar, size) < 0)
+ return AVERROR(ENOMEM);
+ bytestream2_get_buffer(&p, st->codecpar->extradata, st->codecpar->extradata_size);
+ }
}
} else if (bytestream2_peek_byte(&p) == 3) {
bytestream2_skip(&p, 7);
@@ -119,15 +138,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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->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->codecpar->width = AV_RL32(p + 176);
st->codecpar->height = AV_RL32(p + 180);
} else if(t == 0x05589f81){
+ if (os->psize < 136)
+ return AVERROR_INVALIDDATA;
+
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = ff_codec_get_id(ff_codec_wav_tags, AV_RL16(p + 124));
st->codecpar->channels = AV_RL16(p + 126);
diff --git a/libavformat/oggparseopus.c b/libavformat/oggparseopus.c
index f523143..f45ad84 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,33 +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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = AV_CODEC_ID_OPUS;
st->codecpar->channels = AV_RL8(packet + 9);
- priv->pre_skip = AV_RL16(packet + 10);
+ priv->pre_skip = AV_RL16(packet + 10);
st->codecpar->initial_padding = 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 + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!extradata)
+ if (ff_alloc_extradata(st->codecpar, os->psize))
return AVERROR(ENOMEM);
- memcpy(extradata, packet, os->psize);
- st->codecpar->extradata = extradata;
- st->codecpar->extradata_size = os->psize;
+ memcpy(st->codecpar->extradata, packet, os->psize);
st->codecpar->sample_rate = 48000;
+ st->codecpar->seek_preroll = av_rescale(OPUS_SEEK_PREROLL_MS,
+ st->codecpar->sample_rate, 1000);
avpriv_set_pts_info(st, 64, 1, 48000);
priv->need_comments = 1;
return 1;
@@ -84,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;
@@ -91,26 +113,47 @@ 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;
+ if (os->granule > (1LL << 62)) {
+ av_log(avf, AV_LOG_ERROR, "Unsupported huge granule pos %"PRId64 "\n", os->granule);
+ 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;
@@ -123,10 +166,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;
- avpriv_report_missing_feature(avf,
- "Last packet truncated to %u since end trim support",
- os->pduration);
- return AVERROR_PATCHWELCOME;
+ os->end_trimming = skip;
+ av_log(avf, AV_LOG_DEBUG,
+ "Last packet was truncated to %d due to end trimming.\n",
+ os->pduration);
}
}
@@ -139,6 +182,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 bcbfe73..532fa6a 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->codecpar->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 2430cd7..27fc992 100644
--- a/libavformat/oggparsespeex.c
+++ b/libavformat/oggparsespeex.c
@@ -62,7 +62,16 @@ static int speex_header(AVFormatContext *s, int idx) {
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->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->codecpar->sample_rate = AV_RL32(p + 36);
+ if (st->codecpar->sample_rate <= 0) {
+ av_log(s, AV_LOG_ERROR, "Invalid sample rate %d\n", st->codecpar->sample_rate);
+ return AVERROR_INVALIDDATA;
+ }
st->codecpar->channels = AV_RL32(p + 48);
if (st->codecpar->channels < 1 || st->codecpar->channels > 2) {
av_log(s, AV_LOG_ERROR, "invalid channel count. Speex must be mono or stereo.\n");
@@ -73,12 +82,18 @@ static int speex_header(AVFormatContext *s, int idx) {
spxp->packet_size = AV_RL32(p + 56);
frames_per_packet = AV_RL32(p + 64);
+ if (spxp->packet_size < 0 ||
+ frames_per_packet < 0 ||
+ spxp->packet_size * (int64_t)frames_per_packet > INT32_MAX / 256) {
+ av_log(s, AV_LOG_ERROR, "invalid packet_size, frames_per_packet %d %d\n", spxp->packet_size, frames_per_packet);
+ spxp->packet_size = 0;
+ return AVERROR_INVALIDDATA;
+ }
if (frames_per_packet)
spxp->packet_size *= frames_per_packet;
- st->codecpar->extradata_size = os->psize;
- st->codecpar->extradata = av_malloc(st->codecpar->extradata_size
- + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (ff_alloc_extradata(st->codecpar, os->psize) < 0)
+ return AVERROR(ENOMEM);
memcpy(st->codecpar->extradata, p, st->codecpar->extradata_size);
avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
diff --git a/libavformat/oggparsetheora.c b/libavformat/oggparsetheora.c
index d9992fa..b14f9f0 100644
--- a/libavformat/oggparsetheora.c
+++ b/libavformat/oggparsetheora.c
@@ -23,11 +23,8 @@
**/
#include <stdlib.h>
-
#include "libavutil/bswap.h"
-
-#include "libavcodec/bitstream.h"
-
+#include "libavcodec/get_bits.h"
#include "avformat.h"
#include "internal.h"
#include "oggdec.h"
@@ -60,41 +57,41 @@ static int theora_header(AVFormatContext *s, int idx)
switch (os->buf[os->pstart]) {
case 0x80: {
- BitstreamContext bc;
+ GetBitContext gb;
AVRational timebase;
- bitstream_init8(&bc, os->buf + os->pstart, os->psize);
+ init_get_bits(&gb, os->buf + os->pstart, os->psize * 8);
/* 0x80"theora" */
- bitstream_skip(&bc, 7 * 8);
+ skip_bits_long(&gb, 7 * 8);
- thp->version = bitstream_read(&bc, 24);
+ thp->version = get_bits_long(&gb, 24);
if (thp->version < 0x030100) {
av_log(s, AV_LOG_ERROR,
"Too old or unsupported Theora (%x)\n", thp->version);
return AVERROR(ENOSYS);
}
- st->codecpar->width = bitstream_read(&bc, 16) << 4;
- st->codecpar->height = bitstream_read(&bc, 16) << 4;
+ st->codecpar->width = get_bits(&gb, 16) << 4;
+ st->codecpar->height = get_bits(&gb, 16) << 4;
if (thp->version >= 0x030400)
- bitstream_skip(&bc, 100);
+ skip_bits(&gb, 100);
if (thp->version >= 0x030200) {
- int width = bitstream_read(&bc, 24);
- int height = bitstream_read(&bc, 24);
+ int width = get_bits_long(&gb, 24);
+ int height = get_bits_long(&gb, 24);
if (width <= st->codecpar->width && width > st->codecpar->width - 16 &&
height <= st->codecpar->height && height > st->codecpar->height - 16) {
st->codecpar->width = width;
st->codecpar->height = height;
}
- bitstream_skip(&bc, 16);
+ skip_bits(&gb, 16);
}
- timebase.den = bitstream_read(&bc, 32);
- timebase.num = bitstream_read(&bc, 32);
+ timebase.den = get_bits_long(&gb, 32);
+ timebase.num = get_bits_long(&gb, 32);
if (!(timebase.num > 0 && timebase.den > 0)) {
av_log(s, AV_LOG_WARNING, "Invalid time base in theora stream, assuming 25 FPS\n");
timebase.num = 1;
@@ -102,16 +99,16 @@ static int theora_header(AVFormatContext *s, int idx)
}
avpriv_set_pts_info(st, 64, timebase.num, timebase.den);
- st->sample_aspect_ratio.num = bitstream_read(&bc, 24);
- st->sample_aspect_ratio.den = bitstream_read(&bc, 24);
+ st->sample_aspect_ratio.num = get_bits_long(&gb, 24);
+ st->sample_aspect_ratio.den = get_bits_long(&gb, 24);
if (thp->version >= 0x030200)
- bitstream_skip(&bc, 38);
+ skip_bits_long(&gb, 38);
if (thp->version >= 0x304000)
- bitstream_skip(&bc, 2);
+ skip_bits(&gb, 2);
- thp->gpshift = bitstream_read(&bc, 5);
- thp->gpmask = (1 << thp->gpshift) - 1;
+ thp->gpshift = get_bits(&gb, 5);
+ thp->gpmask = (1U << thp->gpshift) - 1;
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->codec_id = AV_CODEC_ID_THEORA;
@@ -125,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;
}
@@ -133,6 +131,8 @@ static int theora_header(AVFormatContext *s, int idx)
st->codecpar->extradata_size = 0;
return err;
}
+ memset(st->codecpar->extradata + cds, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+
cdp = st->codecpar->extradata + st->codecpar->extradata_size;
*cdp++ = os->psize >> 8;
*cdp++ = os->psize & 0xff;
@@ -168,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 > 0)
+ 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 95fe082..65b1998 100644
--- a/libavformat/oggparsevorbis.c
+++ b/libavformat/oggparsevorbis.c
@@ -153,18 +153,20 @@ int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m,
* recommended way of embedding cover art within VorbisComments."
*/
if (!strcmp(tt, "METADATA_BLOCK_PICTURE") && parse_picture) {
- int ret;
- char *pict = av_malloc(vl);
+ int ret, len = AV_BASE64_DECODE_SIZE(vl);
+ char *pict = av_malloc(len);
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);
+ ret = av_base64_decode(pict, ct, len);
av_freep(&tt);
av_freep(&ct);
+ if (ret > 0)
+ ret = ff_flac_parse_picture(as, pict, ret);
av_freep(&pict);
if (ret < 0) {
av_log(as, AV_LOG_WARNING, "Failed to parse cover art block.\n");
@@ -172,16 +174,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);
@@ -219,12 +225,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;
@@ -253,6 +262,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;
@@ -267,14 +306,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])
@@ -289,6 +328,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;
@@ -296,7 +336,12 @@ static int vorbis_header(AVFormatContext *s, int idx)
if (bytestream_get_le32(&p) != 0) /* vorbis_version */
return AVERROR_INVALIDDATA;
- st->codecpar->channels = bytestream_get_byte(&p);
+ channels = bytestream_get_byte(&p);
+ if (st->codecpar->channels && channels != st->codecpar->channels) {
+ av_log(s, AV_LOG_ERROR, "Channel change is not supported\n");
+ return AVERROR_PATCHWELCOME;
+ }
+ st->codecpar->channels = channels;
srate = bytestream_get_le32(&p);
p += 4; // skip maximum bitrate
st->codecpar->bit_rate = bytestream_get_le32(&p); // nominal bitrate
@@ -322,9 +367,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);
@@ -351,7 +394,7 @@ static int vorbis_header(AVFormatContext *s, int idx)
if (!priv->vp) {
av_freep(&st->codecpar->extradata);
st->codecpar->extradata_size = 0;
- return ret;
+ return AVERROR_UNKNOWN;
}
}
@@ -363,7 +406,7 @@ 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;
if (!priv->vp)
return AVERROR_INVALIDDATA;
@@ -372,23 +415,34 @@ static int vorbis_packet(AVFormatContext *s, int idx)
* here we parse the duration of each packet in the first page and compare
* the total duration to the page granule to find the encoder delay and
* set the first timestamp */
- if (!os->lastpts) {
- int seg;
+ if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS) && (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];
}
@@ -396,20 +450,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 f38cc56..c534ab1 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
*/
@@ -82,7 +82,11 @@ static uint64_t vp8_gptopts(AVFormatContext *s, int idx,
struct ogg *ogg = s->priv_data;
struct ogg_stream *os = ogg->streams + idx;
- uint64_t pts = (granule >> 32);
+ int invcnt = !((granule >> 30) & 3);
+ // If page granule is that of an invisible vp8 frame, its pts will be
+ // that of the end of the next visible frame. We subtract 1 for those
+ // to prevent messing up pts calculations.
+ uint64_t pts = (granule >> 32) - invcnt;
uint32_t dist = (granule >> 3) & 0x07ffffff;
if (!dist)
diff --git a/libavformat/oma.c b/libavformat/oma.c
index 27b5988..f7ae3c9 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
*/
@@ -26,10 +26,12 @@
const uint16_t ff_oma_srate_tab[8] = { 320, 441, 480, 882, 960, 0 };
const AVCodecTag ff_oma_codec_tags[] = {
- { AV_CODEC_ID_ATRAC3, OMA_CODECID_ATRAC3 },
- { AV_CODEC_ID_ATRAC3P, OMA_CODECID_ATRAC3P },
- { AV_CODEC_ID_MP3, OMA_CODECID_MP3 },
- { AV_CODEC_ID_PCM_S16BE, OMA_CODECID_LPCM },
+ { AV_CODEC_ID_ATRAC3, OMA_CODECID_ATRAC3 },
+ { AV_CODEC_ID_ATRAC3P, OMA_CODECID_ATRAC3P },
+ { AV_CODEC_ID_MP3, OMA_CODECID_MP3 },
+ { AV_CODEC_ID_PCM_S16BE, OMA_CODECID_LPCM },
+ { AV_CODEC_ID_ATRAC3PAL, OMA_CODECID_ATRAC3PAL },
+ { AV_CODEC_ID_ATRAC3AL, OMA_CODECID_ATRAC3AL },
{ 0 },
};
diff --git a/libavformat/oma.h b/libavformat/oma.h
index 9a35da2..36fd012 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
*/
@@ -35,6 +35,8 @@ enum {
OMA_CODECID_MP3 = 3,
OMA_CODECID_LPCM = 4,
OMA_CODECID_WMA = 5,
+ OMA_CODECID_ATRAC3PAL = 33,
+ OMA_CODECID_ATRAC3AL = 34,
};
extern const uint16_t ff_oma_srate_tab[8];
diff --git a/libavformat/omadec.c b/libavformat/omadec.c
index e29b93e..423d52b 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
*/
@@ -75,6 +75,8 @@ typedef struct OMAContext {
uint8_t e_val[8];
uint8_t iv[8];
struct AVDES *av_des;
+
+ int (*read_packet)(AVFormatContext *s, AVPacket *pkt);
} OMAContext;
static void hex_log(AVFormatContext *s, int level,
@@ -181,13 +183,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 = av_des_alloc();
@@ -306,6 +304,87 @@ static int decrypt_init(AVFormatContext *s, ID3v2ExtraMeta *em, uint8_t *header)
return 0;
}
+static int read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ OMAContext *oc = s->priv_data;
+ AVStream *st = s->streams[0];
+ int packet_size = st->codecpar->block_align;
+ int byte_rate = st->codecpar->bit_rate >> 3;
+ int64_t pos = avio_tell(s->pb);
+ int ret = av_get_packet(s->pb, pkt, packet_size);
+
+ if (ret < packet_size)
+ pkt->flags |= AV_PKT_FLAG_CORRUPT;
+
+ if (ret < 0)
+ return ret;
+ if (!ret)
+ return AVERROR_EOF;
+
+ pkt->stream_index = 0;
+
+ if (pos >= oc->content_start && byte_rate > 0) {
+ pkt->pts =
+ pkt->dts = av_rescale(pos - oc->content_start, st->time_base.den,
+ byte_rate * (int64_t)st->time_base.num);
+ }
+
+ if (oc->encrypted) {
+ /* previous unencrypted block saved in IV for
+ * the next packet (CBC mode) */
+ if (ret == packet_size)
+ av_des_crypt(oc->av_des, pkt->data, pkt->data,
+ (packet_size >> 3), oc->iv, 1);
+ else
+ memset(oc->iv, 0, 8);
+ }
+
+ return ret;
+}
+
+static int aal_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ int64_t pos = avio_tell(s->pb);
+ int ret, pts;
+ int packet_size;
+ unsigned tag;
+
+ if (avio_feof(s->pb))
+ return AVERROR_EOF;
+
+ tag = avio_rb24(s->pb);
+ if (tag == 0)
+ return AVERROR_EOF;
+ else if (tag != MKBETAG(0,'B','L','K'))
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(s->pb, 1);
+ packet_size = avio_rb16(s->pb);
+ avio_skip(s->pb, 2);
+ pts = avio_rb32(s->pb);
+ avio_skip(s->pb, 12);
+ ret = av_get_packet(s->pb, pkt, packet_size);
+ if (ret < packet_size)
+ pkt->flags |= AV_PKT_FLAG_CORRUPT;
+
+ if (ret < 0)
+ return ret;
+ if (!ret)
+ return AVERROR_EOF;
+
+ pkt->stream_index = 0;
+ pkt->pos = pos;
+ if (s->streams[0]->codecpar->codec_id == AV_CODEC_ID_ATRAC3AL) {
+ pkt->duration = 1024;
+ pkt->pts = pts * 1024LL;
+ } else {
+ pkt->duration = 2048;
+ pkt->pts = pts * 2048LL;
+ }
+
+ return ret;
+}
+
static int oma_read_header(AVFormatContext *s)
{
int ret, framesize, jsflag, samplerate;
@@ -317,7 +396,12 @@ 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);
+ if ((ret = ff_id3v2_parse_chapters(s, &extra_meta)) < 0) {
+ ff_id3v2_free_extra_meta(&extra_meta);
+ return ret;
+ }
+
ret = avio_read(s->pb, buf, EA3_HEADER_SIZE);
if (ret < EA3_HEADER_SIZE)
return -1;
@@ -351,6 +435,8 @@ static int oma_read_header(AVFormatContext *s)
st->codecpar->codec_id = ff_codec_get_id(ff_oma_codec_tags,
st->codecpar->codec_tag);
+ oc->read_packet = read_packet;
+
switch (buf[32]) {
case OMA_CODECID_ATRAC3:
samplerate = ff_oma_srate_tab[(codec_params >> 13) & 7] * 100;
@@ -369,16 +455,14 @@ static int oma_read_header(AVFormatContext *s)
st->codecpar->channels = 2;
st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
st->codecpar->sample_rate = samplerate;
- st->codecpar->bit_rate = st->codecpar->sample_rate * framesize * 8 / 1024;
+ st->codecpar->bit_rate = st->codecpar->sample_rate * framesize / (1024 / 8);
/* fake the ATRAC3 extradata
* (wav format, makes stream copy to wav work) */
- st->codecpar->extradata_size = 14;
- edata = av_mallocz(14 + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!edata)
+ if (ff_alloc_extradata(st->codecpar, 14))
return AVERROR(ENOMEM);
- st->codecpar->extradata = edata;
+ edata = st->codecpar->extradata;
AV_WL16(&edata[0], 1); // always 1
AV_WL32(&edata[2], samplerate); // samples rate
AV_WL16(&edata[6], jsflag); // coding mode
@@ -404,11 +488,11 @@ static int oma_read_header(AVFormatContext *s)
return AVERROR_INVALIDDATA;
}
st->codecpar->sample_rate = samplerate;
- st->codecpar->bit_rate = samplerate * framesize * 8 / 2048;
+ st->codecpar->bit_rate = samplerate * framesize / (2048 / 8);
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:
@@ -423,6 +507,22 @@ static int oma_read_header(AVFormatContext *s)
av_get_bits_per_sample(st->codecpar->codec_id);
avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
break;
+ case OMA_CODECID_ATRAC3AL:
+ st->codecpar->channels = 2;
+ st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
+ st->codecpar->sample_rate = 44100;
+ avpriv_set_pts_info(st, 64, 1, 44100);
+ oc->read_packet = aal_read_packet;
+ framesize = 4096;
+ break;
+ case OMA_CODECID_ATRAC3PAL:
+ st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
+ st->codecpar->channels = 2;
+ st->codecpar->sample_rate = 44100;
+ avpriv_set_pts_info(st, 64, 1, 44100);
+ oc->read_packet = aal_read_packet;
+ framesize = 4096;
+ break;
default:
av_log(s, AV_LOG_ERROR, "Unsupported codec %d!\n", buf[32]);
return AVERROR(ENOSYS);
@@ -433,43 +533,10 @@ static int oma_read_header(AVFormatContext *s)
return 0;
}
-
static int oma_read_packet(AVFormatContext *s, AVPacket *pkt)
{
OMAContext *oc = s->priv_data;
- AVStream *st = s->streams[0];
- int packet_size = st->codecpar->block_align;
- int byte_rate = st->codecpar->bit_rate >> 3;
- int64_t pos = avio_tell(s->pb);
- int ret = av_get_packet(s->pb, pkt, packet_size);
-
- if (ret < packet_size)
- pkt->flags |= AV_PKT_FLAG_CORRUPT;
-
- if (ret < 0)
- return ret;
- if (!ret)
- return AVERROR_EOF;
-
- pkt->stream_index = 0;
-
- if (pos > 0) {
- pkt->pts =
- pkt->dts = av_rescale(pos, st->time_base.den,
- byte_rate * (int64_t)st->time_base.num);
- }
-
- if (oc->encrypted) {
- /* previous unencrypted block saved in IV for
- * the next packet (CBC mode) */
- if (ret == packet_size)
- av_des_crypt(oc->av_des, pkt->data, pkt->data,
- (packet_size >> 3), oc->iv, 1);
- else
- memset(oc->iv, 0, 8);
- }
-
- return ret;
+ return oc->read_packet(s, pkt);
}
static int oma_read_probe(AVProbeData *p)
@@ -483,7 +550,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;
@@ -497,8 +564,14 @@ static int oma_read_seek(struct AVFormatContext *s,
int stream_index, int64_t timestamp, int flags)
{
OMAContext *oc = s->priv_data;
- int err = ff_pcm_read_seek(s, stream_index, timestamp, flags);
+ AVStream *st = s->streams[0];
+ int64_t err;
+
+ if (st->codecpar->codec_id == AV_CODEC_ID_ATRAC3PAL ||
+ st->codecpar->codec_id == AV_CODEC_ID_ATRAC3AL)
+ return -1;
+ err = ff_pcm_read_seek(s, stream_index, timestamp, flags);
if (!oc->encrypted)
return err;
diff --git a/libavformat/omaenc.c b/libavformat/omaenc.c
index 793d0fd..7952808 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 (par->codec_tag) {
case OMA_CODECID_ATRAC3:
if (par->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 (par->extradata_size == 14) /* WAV format extradata */
@@ -84,8 +84,9 @@ static av_cold int oma_write_header(AVFormatContext *s)
(par->block_align/8 - 1));
break;
default:
- av_log(s, AV_LOG_ERROR, "OMA: unsupported codec tag %"PRIu32" for write\n",
- par->codec_tag);
+ av_log(s, AV_LOG_ERROR, "unsupported codec tag %s for write\n",
+ av_fourcc2str(par->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 f0d2c47..9371c72 100644
--- a/libavformat/options.c
+++ b/libavformat/options.c
@@ -1,26 +1,25 @@
/*
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "avformat.h"
#include "avio_internal.h"
#include "internal.h"
-#include "url.h"
#include "libavutil/internal.h"
#include "libavutil/opt.h"
@@ -82,6 +81,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,
@@ -89,31 +95,33 @@ 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 int io_open_default(AVFormatContext *s, AVIOContext **pb,
const char *url, int flags, AVDictionary **options)
{
- AVDictionary *opts_local = NULL;
- int ret;
+ int loglevel;
- if (!options)
- options = &opts_local;
+ if (!strcmp(url, s->filename) ||
+ s->iformat && !strcmp(s->iformat->name, "image2") ||
+ s->oformat && !strcmp(s->oformat->name, "image2")
+ ) {
+ loglevel = AV_LOG_DEBUG;
+ } else
+ loglevel = AV_LOG_INFO;
- if (s->protocol_whitelist) {
- ret = av_dict_set(options, "protocol_whitelist", s->protocol_whitelist, 0);
- if (ret < 0)
- goto finish;
- }
- if (s->protocol_blacklist) {
- ret = av_dict_set(options, "protocol_blacklist", s->protocol_blacklist, 0);
- if (ret < 0)
- goto finish;
- }
- ret = avio_open2(pb, url, flags, &s->interrupt_callback, options);
-finish:
- av_dict_free(&opts_local);
- return ret;
+ av_log(s, loglevel, "Opening \'%s\' for %s\n", url, flags & AVIO_FLAG_WRITE ? "writing" : "reading");
+
+#if FF_API_OLD_OPEN_CALLBACKS
+FF_DISABLE_DEPRECATION_WARNINGS
+ if (s->open_cb)
+ return s->open_cb(s, pb, url, flags, &s->interrupt_callback, options);
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+ return ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist, s->protocol_blacklist);
}
static void io_close_default(AVFormatContext *s, AVIOContext *pb)
@@ -146,10 +154,17 @@ AVFormatContext *avformat_alloc_context(void)
return NULL;
}
ic->internal->offset = AV_NOPTS_VALUE;
+ ic->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
+ ic->internal->shortest_end = AV_NOPTS_VALUE;
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 b566da6..118086d 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,42 @@
#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(probesize), 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"},
+{"fflags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = AVFMT_FLAG_AUTO_BSF }, 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, 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"},
+#if FF_API_LAVF_KEEPSIDE_FLAG
+{"keepside", "don't merge side data", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_KEEP_SIDE_DATA }, INT_MIN, INT_MAX, D, "fflags"},
+#endif
+{"fastseek", "fast but inaccurate seeks", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_FAST_SEEK }, 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_BOOL, {.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},
+{"shortest", "stop muxing with the shortest stream", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_SHORTEST }, 0, 0, E, "fflags" },
+{"autobsf", "add needed bsfs automatically (delays header until each stream's first packet is written)", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_AUTO_BSF }, 0, 0, E, "fflags" },
+{"analyzeduration", "specify how many microseconds are analyzed to probe the input", OFFSET(max_analyze_duration), 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,21 +78,36 @@ 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_BOOL, {.i64 = 0}, 0, 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_BOOL, {.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}, -1, 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"},
+{"very", "strictly conform to a older more strict version of the spec or reference software", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_VERY_STRICT }, 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"},
-{"protocol_blacklist", "A comma-separated list of blacklisted protocols used for opening files internally by lavf",
- OFFSET(protocol_blacklist), AV_OPT_TYPE_STRING, { .str = "concat" }, .flags = E | D },
-{"protocol_whitelist", "A comma-separated list of whitelisted protocols used for opening files internally by lavf",
- OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = E | D },
+{"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 },
+{"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D },
+{"protocol_blacklist", "List of protocols that are not allowed to be used", OFFSET(protocol_blacklist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D },
+{"max_streams", "maximum number of streams", OFFSET(max_streams), AV_OPT_TYPE_INT, { .i64 = 1000 }, 0, INT_MAX, D },
{NULL},
};
diff --git a/libavformat/os_support.c b/libavformat/os_support.c
index e9b1f88..86d0b8f 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 965e16f..91220e9 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
@@ -129,18 +146,6 @@ int ff_poll(struct pollfd *fds, nfds_t numfds, int timeout);
#include <windows.h>
#include "libavutil/wchar_filename.h"
-#ifdef WINAPI_FAMILY
-#include <winapifamily.h>
-// If a WINAPI_FAMILY is defined, check that the desktop API subset
-// is enabled
-#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
-#define USE_MOVEFILEEXA
-#endif
-#else
-// If no WINAPI_FAMILY is defined, assume the full API subset
-#define USE_MOVEFILEEXA
-#endif
-
#define DEF_FS_FUNCTION(name, wfunc, afunc) \
static inline int win32_##name(const char *filename_utf8) \
{ \
@@ -165,6 +170,29 @@ DEF_FS_FUNCTION(unlink, _wunlink, _unlink)
DEF_FS_FUNCTION(mkdir, _wmkdir, _mkdir)
DEF_FS_FUNCTION(rmdir, _wrmdir , _rmdir)
+#define DEF_FS_FUNCTION2(name, wfunc, afunc, partype) \
+static inline int win32_##name(const char *filename_utf8, partype par) \
+{ \
+ wchar_t *filename_w; \
+ int ret; \
+ \
+ if (utf8towchar(filename_utf8, &filename_w)) \
+ return -1; \
+ if (!filename_w) \
+ goto fallback; \
+ \
+ ret = wfunc(filename_w, par); \
+ av_free(filename_w); \
+ return ret; \
+ \
+fallback: \
+ /* filename may be be in CP_ACP */ \
+ return afunc(filename_utf8, par); \
+}
+
+DEF_FS_FUNCTION2(access, _waccess, _access, int)
+DEF_FS_FUNCTION2(stat, _wstati64, _stati64, struct stat*)
+
static inline int win32_rename(const char *src_utf8, const char *dest_utf8)
{
wchar_t *src_w, *dest_w;
@@ -192,7 +220,7 @@ static inline int win32_rename(const char *src_utf8, const char *dest_utf8)
fallback:
/* filename may be be in CP_ACP */
-#ifdef USE_MOVEFILEEXA
+#if !HAVE_WINRT
ret = MoveFileExA(src_utf8, dest_utf8, MOVEFILE_REPLACE_EXISTING);
if (ret)
errno = EPERM;
@@ -214,6 +242,7 @@ fallback:
#define rename win32_rename
#define rmdir win32_rmdir
#define unlink win32_unlink
+#define access win32_access
#endif
diff --git a/libavformat/paf.c b/libavformat/paf.c
index 81a45ce..fa30cdd 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 c506edd..806f91b 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]->codecpar->block_align;
+ if (size <= 0)
+ return AVERROR(EINVAL);
+
+ ret= av_get_packet(s->pb, pkt, size);
+
+ pkt->flags &= ~AV_PKT_FLAG_CORRUPT;
+ pkt->stream_index = 0;
+
+ return ret;
+}
+
int ff_pcm_read_seek(AVFormatContext *s,
int stream_index, int64_t timestamp, int flags)
{
diff --git a/libavformat/pcm.h b/libavformat/pcm.h
index 30cbc86..9af36d5 100644
--- a/libavformat/pcm.h
+++ b/libavformat/pcm.h
@@ -2,20 +2,20 @@
* PCM common functions
* Copyright (C) 2007 Aurelien Jacobs <aurel@gnuage.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -24,6 +24,7 @@
#include "avformat.h"
+int ff_pcm_read_packet(AVFormatContext *s, AVPacket *pkt);
int ff_pcm_read_seek(AVFormatContext *s,
int stream_index, int64_t timestamp, int flags);
diff --git a/libavformat/pcmdec.c b/libavformat/pcmdec.c
index 69789b6..d0ceea6 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;
@@ -37,6 +36,7 @@ static int pcm_read_header(AVFormatContext *s)
{
PCMAudioDemuxerContext *s1 = s->priv_data;
AVStream *st;
+ uint8_t *mime_type = NULL;
st = avformat_new_stream(s, NULL);
if (!st)
@@ -48,10 +48,40 @@ static int pcm_read_header(AVFormatContext *s)
st->codecpar->sample_rate = s1->sample_rate;
st->codecpar->channels = s1->channels;
+ av_opt_get(s->pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type);
+ if (mime_type && s->iformat->mime_type) {
+ int rate = 0, channels = 0;
+ size_t len = strlen(s->iformat->mime_type);
+ if (!strncmp(s->iformat->mime_type, mime_type, len)) {
+ uint8_t *options = mime_type + len;
+ len = strlen(mime_type);
+ while (options < mime_type + len) {
+ options = strstr(options, ";");
+ if (!options++)
+ break;
+ if (!rate)
+ sscanf(options, " rate=%d", &rate);
+ if (!channels)
+ sscanf(options, " channels=%d", &channels);
+ }
+ if (rate <= 0) {
+ av_log(s, AV_LOG_ERROR,
+ "Invalid sample_rate found in mime_type \"%s\"\n",
+ mime_type);
+ av_freep(&mime_type);
+ return AVERROR_INVALIDDATA;
+ }
+ st->codecpar->sample_rate = rate;
+ if (channels > 0)
+ st->codecpar->channels = channels;
+ }
+ }
+ av_freep(&mime_type);
+
st->codecpar->bits_per_coded_sample =
av_get_bits_per_sample(st->codecpar->codec_id);
- assert(st->codecpar->bits_per_coded_sample > 0);
+ av_assert0(st->codecpar->bits_per_coded_sample > 0);
st->codecpar->block_align =
st->codecpar->bits_per_coded_sample * st->codecpar->channels / 8;
@@ -60,38 +90,13 @@ 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]->codecpar->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]->codecpar->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]->codecpar->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 },
};
-#define PCMDEF(name_, long_name_, ext, codec) \
+#define PCMDEF(name_, long_name_, ext, codec, ...) \
static const AVClass name_ ## _demuxer_class = { \
.class_name = #name_ " demuxer", \
.item_name = av_default_item_name, \
@@ -103,12 +108,13 @@ 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, \
.raw_codec_id = codec, \
.priv_class = &name_ ## _demuxer_class, \
+ __VA_ARGS__ \
};
PCMDEF(f64be, "PCM 64-bit floating-point big-endian",
@@ -139,7 +145,7 @@ PCMDEF(s16be, "PCM signed 16-bit big-endian",
AV_NE("sw", NULL), AV_CODEC_ID_PCM_S16BE)
PCMDEF(s16le, "PCM signed 16-bit little-endian",
- AV_NE(NULL, "sw"), AV_CODEC_ID_PCM_S16LE)
+ AV_NE(NULL, "sw"), AV_CODEC_ID_PCM_S16LE, .mime_type = "audio/L16",)
PCMDEF(s8, "PCM signed 8-bit",
"sb", AV_CODEC_ID_PCM_S8)
@@ -170,3 +176,29 @@ PCMDEF(alaw, "PCM A-law",
PCMDEF(mulaw, "PCM mu-law",
"ul", AV_CODEC_ID_PCM_MULAW)
+
+static const AVOption sln_options[] = {
+ { "sample_rate", "", offsetof(PCMAudioDemuxerContext, sample_rate), AV_OPT_TYPE_INT, {.i64 = 8000}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
+ { "channels", "", offsetof(PCMAudioDemuxerContext, channels), AV_OPT_TYPE_INT, {.i64 = 1}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
+ { NULL },
+};
+
+static const AVClass sln_demuxer_class = {
+ .class_name = "sln demuxer",
+ .item_name = av_default_item_name,
+ .option = sln_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_sln_demuxer = {
+ .name = "sln",
+ .long_name = NULL_IF_CONFIG_SMALL("Asterisk raw pcm"),
+ .priv_data_size = sizeof(PCMAudioDemuxerContext),
+ .read_header = pcm_read_header,
+ .read_packet = ff_pcm_read_packet,
+ .read_seek = ff_pcm_read_seek,
+ .flags = AVFMT_GENERIC_INDEX,
+ .extensions = "sln",
+ .raw_codec_id = AV_CODEC_ID_PCM_S16LE,
+ .priv_class = &sln_demuxer_class,
+};
diff --git a/libavformat/pcmenc.c b/libavformat/pcmenc.c
index e45ab84..3e4f308 100644
--- a/libavformat/pcmenc.c
+++ b/libavformat/pcmenc.c
@@ -2,20 +2,20 @@
* RAW PCM muxers
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/pjsdec.c b/libavformat/pjsdec.c
new file mode 100644
index 0000000..bb587b5
--- /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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->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(s, &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 e2709bb..0e80a09 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);
@@ -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,8 +91,28 @@ 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)
@@ -101,14 +123,6 @@ static int pmp_header(AVFormatContext *s)
ast->codecpar->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/prompeg.c b/libavformat/prompeg.c
new file mode 100644
index 0000000..9770a91
--- /dev/null
+++ b/libavformat/prompeg.c
@@ -0,0 +1,481 @@
+/*
+ * Pro-MPEG Code of Practice #3 Release 2 FEC
+ * Copyright (c) 2016 Mobibase, France (http://www.mobibase.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
+ * Pro-MPEG Code of Practice #3 Release 2 FEC protocol
+ * @author Vlad Tarca <vlad.tarca@gmail.com>
+ */
+
+/*
+ * Reminder:
+
+ [RFC 2733] FEC Packet Structure
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | RTP Header |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | FEC Header |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | FEC Payload |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+ [RFC 3550] RTP header
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |V=2|P|X| CC |M| PT | sequence number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | timestamp |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | synchronization source (SSRC) identifier |
+ +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ | contributing source (CSRC) identifiers |
+ | .... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ [RFC 3550] RTP header extension (after CSRC)
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | defined by profile | length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | header extension |
+ | .... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ [Pro-MPEG COP3] FEC Header
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | SNBase low bits | length recovery |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |E| PT recovery | mask |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | TS recovery |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |X|D|type |index| offset | NA |SNBase ext bits|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ */
+
+#include "libavutil/avstring.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/random_seed.h"
+#include "avformat.h"
+#include "config.h"
+#include "url.h"
+
+#define PROMPEG_RTP_PT 0x60
+#define PROMPEG_FEC_COL 0x0
+#define PROMPEG_FEC_ROW 0x1
+
+typedef struct PrompegFec {
+ uint16_t sn;
+ uint32_t ts;
+ uint8_t *bitstring;
+} PrompegFec;
+
+typedef struct PrompegContext {
+ const AVClass *class;
+ URLContext *fec_col_hd, *fec_row_hd;
+ PrompegFec **fec_arr, **fec_col_tmp, **fec_col, *fec_row;
+ int ttl;
+ uint8_t l, d;
+ uint8_t *rtp_buf;
+ uint16_t rtp_col_sn, rtp_row_sn;
+ uint16_t length_recovery;
+ int packet_size;
+ int packet_idx, packet_idx_max;
+ int fec_arr_len;
+ int bitstring_size;
+ int rtp_buf_size;
+ int init;
+ int first;
+} PrompegContext;
+
+#define OFFSET(x) offsetof(PrompegContext, x)
+#define E AV_OPT_FLAG_ENCODING_PARAM
+
+static const AVOption options[] = {
+ { "ttl", "Time to live (in milliseconds, multicast only)", OFFSET(ttl), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = E },
+ { "l", "FEC L", OFFSET(l), AV_OPT_TYPE_INT, { .i64 = 5 }, 4, 20, .flags = E },
+ { "d", "FEC D", OFFSET(d), AV_OPT_TYPE_INT, { .i64 = 5 }, 4, 20, .flags = E },
+ { NULL }
+};
+
+static const AVClass prompeg_class = {
+ .class_name = "prompeg",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+static void xor_fast(const uint8_t *in1, const uint8_t *in2, uint8_t *out, int size) {
+ int i, n, s;
+
+#if HAVE_FAST_64BIT
+ uint64_t v1, v2;
+
+ n = size / sizeof (uint64_t);
+ s = n * sizeof (uint64_t);
+
+ for (i = 0; i < n; i++) {
+ v1 = AV_RN64A(in1);
+ v2 = AV_RN64A(in2);
+ AV_WN64A(out, v1 ^ v2);
+ in1 += 8;
+ in2 += 8;
+ out += 8;
+ }
+#else
+ uint32_t v1, v2;
+
+ n = size / sizeof (uint32_t);
+ s = n * sizeof (uint32_t);
+
+ for (i = 0; i < n; i++) {
+ v1 = AV_RN32A(in1);
+ v2 = AV_RN32A(in2);
+ AV_WN32A(out, v1 ^ v2);
+ in1 += 4;
+ in2 += 4;
+ out += 4;
+ }
+#endif
+
+ n = size - s;
+
+ for (i = 0; i < n; i++) {
+ out[i] = in1[i] ^ in2[i];
+ }
+}
+
+static int prompeg_create_bitstring(URLContext *h, const uint8_t *buf, int size,
+ uint8_t **bitstring) {
+ PrompegContext *s = h->priv_data;
+ uint8_t *b;
+
+ if (size < 12 || (buf[0] & 0xc0) != 0x80 || (buf[1] & 0x7f) != 0x21) {
+ av_log(h, AV_LOG_ERROR, "Unsupported stream format (expected MPEG-TS over RTP)\n");
+ return AVERROR(EINVAL);
+ }
+ if (size != s->packet_size) {
+ av_log(h, AV_LOG_ERROR, "The RTP packet size must be constant (set pkt_size)\n");
+ return AVERROR(EINVAL);
+ }
+
+ *bitstring = av_malloc(s->bitstring_size);
+ if (!*bitstring) {
+ av_log(h, AV_LOG_ERROR, "Failed to allocate the bitstring buffer\n");
+ return AVERROR(ENOMEM);
+ }
+ b = *bitstring;
+
+ // P, X, CC
+ b[0] = buf[0] & 0x3f;
+ // M, PT
+ b[1] = buf[1];
+ // Timestamp
+ b[2] = buf[4];
+ b[3] = buf[5];
+ b[4] = buf[6];
+ b[5] = buf[7];
+ /*
+ * length_recovery: the unsigned network-ordered sum of lengths of CSRC,
+ * padding, extension and media payload
+ */
+ AV_WB16(b + 6, s->length_recovery);
+ // Payload
+ memcpy(b + 8, buf + 12, s->length_recovery);
+
+ return 0;
+}
+
+static int prompeg_write_fec(URLContext *h, PrompegFec *fec, uint8_t type) {
+ PrompegContext *s = h->priv_data;
+ URLContext *hd;
+ uint8_t *buf = s->rtp_buf; // zero-filled
+ uint8_t *b = fec->bitstring;
+ uint16_t sn;
+ int ret;
+
+ sn = type == PROMPEG_FEC_COL ? ++s->rtp_col_sn : ++s->rtp_row_sn;
+
+ // V, P, X, CC
+ buf[0] = 0x80 | (b[0] & 0x3f);
+ // M, PT
+ buf[1] = (b[1] & 0x80) | PROMPEG_RTP_PT;
+ // SN
+ AV_WB16(buf + 2, sn);
+ // TS
+ AV_WB32(buf + 4, fec->ts);
+ // CSRC=0
+ //AV_WB32(buf + 8, 0);
+ // SNBase low bits
+ AV_WB16(buf + 12, fec->sn);
+ // Length recovery
+ buf[14] = b[6];
+ buf[15] = b[7];
+ // E=1, PT recovery
+ buf[16] = 0x80 | b[1];
+ // Mask=0
+ //buf[17] = 0x0;
+ //buf[18] = 0x0;
+ //buf[19] = 0x0;
+ // TS recovery
+ buf[20] = b[2];
+ buf[21] = b[3];
+ buf[22] = b[4];
+ buf[23] = b[5];
+ // X=0, D, type=0, index=0
+ buf[24] = type == PROMPEG_FEC_COL ? 0x0 : 0x40;
+ // offset
+ buf[25] = type == PROMPEG_FEC_COL ? s->l : 0x1;
+ // NA
+ buf[26] = type == PROMPEG_FEC_COL ? s->d : s->l;
+ // SNBase ext bits=0
+ //buf[27] = 0x0;
+ // Payload
+ memcpy(buf + 28, b + 8, s->length_recovery);
+
+ hd = type == PROMPEG_FEC_COL ? s->fec_col_hd : s->fec_row_hd;
+ ret = ffurl_write(hd, buf, s->rtp_buf_size);
+ return ret;
+}
+
+static int prompeg_open(URLContext *h, const char *uri, int flags) {
+ PrompegContext *s = h->priv_data;
+ AVDictionary *udp_opts = NULL;
+ int rtp_port;
+ char hostname[256];
+ char buf[1024];
+
+ s->fec_col_hd = NULL;
+ s->fec_row_hd = NULL;
+
+ if (s->l * s->d > 100) {
+ av_log(h, AV_LOG_ERROR, "L * D must be <= 100\n");
+ return AVERROR(EINVAL);
+ }
+
+ av_url_split(NULL, 0, NULL, 0, hostname, sizeof (hostname), &rtp_port,
+ NULL, 0, uri);
+
+ if (rtp_port < 1 || rtp_port > UINT16_MAX - 4) {
+ av_log(h, AV_LOG_ERROR, "Invalid RTP base port %d\n", rtp_port);
+ return AVERROR(EINVAL);
+ }
+
+ if (s->ttl > 0) {
+ snprintf(buf, sizeof (buf), "%d", s->ttl);
+ av_dict_set(&udp_opts, "ttl", buf, 0);
+ }
+
+ ff_url_join(buf, sizeof (buf), "udp", NULL, hostname, rtp_port + 2, NULL);
+ if (ffurl_open_whitelist(&s->fec_col_hd, buf, flags, &h->interrupt_callback,
+ &udp_opts, h->protocol_whitelist, h->protocol_blacklist, h) < 0)
+ goto fail;
+ ff_url_join(buf, sizeof (buf), "udp", NULL, hostname, rtp_port + 4, NULL);
+ if (ffurl_open_whitelist(&s->fec_row_hd, buf, flags, &h->interrupt_callback,
+ &udp_opts, h->protocol_whitelist, h->protocol_blacklist, h) < 0)
+ goto fail;
+
+ h->max_packet_size = s->fec_col_hd->max_packet_size;
+ s->init = 1;
+
+ av_dict_free(&udp_opts);
+ av_log(h, AV_LOG_INFO, "ProMPEG CoP#3-R2 FEC L=%d D=%d\n", s->l, s->d);
+ return 0;
+
+fail:
+ ffurl_closep(&s->fec_col_hd);
+ ffurl_closep(&s->fec_row_hd);
+ av_dict_free(&udp_opts);
+ return AVERROR(EIO);
+}
+
+static int prompeg_init(URLContext *h, const uint8_t *buf, int size) {
+ PrompegContext *s = h->priv_data;
+ uint32_t seed;
+ int i;
+
+ s->fec_arr = NULL;
+ s->rtp_buf = NULL;
+
+ if (size < 12 || size > UINT16_MAX + 12) {
+ av_log(h, AV_LOG_ERROR, "Invalid RTP packet size\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ s->packet_idx = 0;
+ s->packet_idx_max = s->l * s->d;
+ s->packet_size = size;
+ s->length_recovery = size - 12;
+ s->rtp_buf_size = 28 + s->length_recovery; // 12 + 16: RTP + FEC headers
+ s->bitstring_size = 8 + s->length_recovery; // 8: P, X, CC, M, PT, SN, TS
+ s->fec_arr_len = 1 + 2 * s->l; // row + column tmp + column out
+
+ if (h->flags & AVFMT_FLAG_BITEXACT) {
+ s->rtp_col_sn = 0;
+ s->rtp_row_sn = 0;
+ } else {
+ seed = av_get_random_seed();
+ s->rtp_col_sn = seed & 0x0fff;
+ s->rtp_row_sn = (seed >> 16) & 0x0fff;
+ }
+
+ s->fec_arr = av_malloc_array(s->fec_arr_len, sizeof (PrompegFec*));
+ if (!s->fec_arr) {
+ goto fail;
+ }
+ for (i = 0; i < s->fec_arr_len; i++) {
+ s->fec_arr[i] = av_malloc(sizeof (PrompegFec));
+ if (!s->fec_arr[i]) {
+ goto fail;
+ }
+ s->fec_arr[i]->bitstring = av_malloc_array(s->bitstring_size, sizeof (uint8_t));
+ if (!s->fec_arr[i]->bitstring) {
+ av_freep(&s->fec_arr[i]);
+ goto fail;
+ }
+ }
+ s->fec_row = *s->fec_arr;
+ s->fec_col = s->fec_arr + 1;
+ s->fec_col_tmp = s->fec_arr + 1 + s->l;
+
+ s->rtp_buf = av_malloc_array(s->rtp_buf_size, sizeof (uint8_t));
+ if (!s->rtp_buf) {
+ goto fail;
+ }
+ memset(s->rtp_buf, 0, s->rtp_buf_size);
+
+ s->init = 0;
+ s->first = 1;
+
+ return 0;
+
+fail:
+ av_log(h, AV_LOG_ERROR, "Failed to allocate the FEC buffer\n");
+ return AVERROR(ENOMEM);
+}
+
+static int prompeg_write(URLContext *h, const uint8_t *buf, int size) {
+ PrompegContext *s = h->priv_data;
+ PrompegFec *fec_tmp;
+ uint8_t *bitstring = NULL;
+ int col_idx, col_out_idx, row_idx;
+ int ret, written = 0;
+
+ if (s->init && ((ret = prompeg_init(h, buf, size)) < 0))
+ goto end;
+
+ if ((ret = prompeg_create_bitstring(h, buf, size, &bitstring)) < 0)
+ goto end;
+
+ col_idx = s->packet_idx % s->l;
+ row_idx = s->packet_idx / s->l % s->d;
+
+ // FEC' (row) send block-aligned, xor
+ if (col_idx == 0) {
+ if (!s->first || s->packet_idx > 0) {
+ if ((ret = prompeg_write_fec(h, s->fec_row, PROMPEG_FEC_ROW)) < 0)
+ goto end;
+ written += ret;
+ }
+ memcpy(s->fec_row->bitstring, bitstring, s->bitstring_size);
+ s->fec_row->sn = AV_RB16(buf + 2);
+ s->fec_row->ts = AV_RB32(buf + 4);
+ } else {
+ xor_fast(s->fec_row->bitstring, bitstring, s->fec_row->bitstring,
+ s->bitstring_size);
+ }
+
+ // FEC (column) xor
+ if (row_idx == 0) {
+ if (!s->first) {
+ // swap fec_col and fec_col_tmp
+ fec_tmp = s->fec_col[col_idx];
+ s->fec_col[col_idx] = s->fec_col_tmp[col_idx];
+ s->fec_col_tmp[col_idx] = fec_tmp;
+ }
+ memcpy(s->fec_col_tmp[col_idx]->bitstring, bitstring, s->bitstring_size);
+ s->fec_col_tmp[col_idx]->sn = AV_RB16(buf + 2);
+ s->fec_col_tmp[col_idx]->ts = AV_RB32(buf + 4);
+ } else {
+ xor_fast(s->fec_col_tmp[col_idx]->bitstring, bitstring,
+ s->fec_col_tmp[col_idx]->bitstring, s->bitstring_size);
+ }
+
+ // FEC (column) send block-aligned
+ if (!s->first && s->packet_idx % s->d == 0) {
+ col_out_idx = s->packet_idx / s->d;
+ if ((ret = prompeg_write_fec(h, s->fec_col[col_out_idx], PROMPEG_FEC_COL)) < 0)
+ goto end;
+ written += ret;
+ }
+
+ if (++s->packet_idx >= s->packet_idx_max) {
+ s->packet_idx = 0;
+ if (s->first)
+ s->first = 0;
+ }
+
+ ret = written;
+
+end:
+ av_free(bitstring);
+ return ret;
+}
+
+static int prompeg_close(URLContext *h) {
+ PrompegContext *s = h->priv_data;
+ int i;
+
+ ffurl_closep(&s->fec_col_hd);
+ ffurl_closep(&s->fec_row_hd);
+
+ if (s->fec_arr) {
+ for (i = 0; i < s->fec_arr_len; i++) {
+ av_free(s->fec_arr[i]->bitstring);
+ av_freep(&s->fec_arr[i]);
+ }
+ av_freep(&s->fec_arr);
+ }
+ av_freep(&s->rtp_buf);
+
+ return 0;
+}
+
+const URLProtocol ff_prompeg_protocol = {
+ .name = "prompeg",
+ .url_open = prompeg_open,
+ .url_write = prompeg_write,
+ .url_close = prompeg_close,
+ .priv_data_size = sizeof(PrompegContext),
+ .flags = URL_PROTOCOL_FLAG_NETWORK,
+ .priv_data_class = &prompeg_class,
+};
diff --git a/libavformat/protocols.c b/libavformat/protocols.c
index d254540..8d3555e 100644
--- a/libavformat/protocols.c
+++ b/libavformat/protocols.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
*/
@@ -23,11 +23,16 @@
#include "url.h"
+extern const URLProtocol ff_async_protocol;
+extern const URLProtocol ff_bluray_protocol;
+extern const URLProtocol ff_cache_protocol;
extern const URLProtocol ff_concat_protocol;
extern const URLProtocol ff_crypto_protocol;
+extern const URLProtocol ff_data_protocol;
extern const URLProtocol ff_ffrtmpcrypt_protocol;
extern const URLProtocol ff_ffrtmphttp_protocol;
extern const URLProtocol ff_file_protocol;
+extern const URLProtocol ff_ftp_protocol;
extern const URLProtocol ff_gopher_protocol;
extern const URLProtocol ff_hls_protocol;
extern const URLProtocol ff_http_protocol;
@@ -38,6 +43,7 @@ extern const URLProtocol ff_mmsh_protocol;
extern const URLProtocol ff_mmst_protocol;
extern const URLProtocol ff_md5_protocol;
extern const URLProtocol ff_pipe_protocol;
+extern const URLProtocol ff_prompeg_protocol;
extern const URLProtocol ff_rtmp_protocol;
extern const URLProtocol ff_rtmpe_protocol;
extern const URLProtocol ff_rtmps_protocol;
@@ -47,16 +53,23 @@ extern const URLProtocol ff_rtmpts_protocol;
extern const URLProtocol ff_rtp_protocol;
extern const URLProtocol ff_sctp_protocol;
extern const URLProtocol ff_srtp_protocol;
+extern const URLProtocol ff_subfile_protocol;
+extern const URLProtocol ff_tee_protocol;
extern const URLProtocol ff_tcp_protocol;
extern const URLProtocol ff_tls_gnutls_protocol;
+extern const URLProtocol ff_tls_schannel_protocol;
+extern const URLProtocol ff_tls_securetransport_protocol;
extern const URLProtocol ff_tls_openssl_protocol;
extern const URLProtocol ff_udp_protocol;
+extern const URLProtocol ff_udplite_protocol;
extern const URLProtocol ff_unix_protocol;
extern const URLProtocol ff_librtmp_protocol;
extern const URLProtocol ff_librtmpe_protocol;
extern const URLProtocol ff_librtmps_protocol;
extern const URLProtocol ff_librtmpt_protocol;
extern const URLProtocol ff_librtmpte_protocol;
+extern const URLProtocol ff_libssh_protocol;
+extern const URLProtocol ff_libsmbclient_protocol;
#include "libavformat/protocol_list.c"
@@ -65,7 +78,7 @@ const AVClass *ff_urlcontext_child_class_next(const AVClass *prev)
int i;
/* find the protocol that corresponds to prev */
- for (i = 0; url_protocols[i]; i++) {
+ for (i = 0; prev && url_protocols[i]; i++) {
if (url_protocols[i]->priv_data_class == prev) {
i++;
break;
diff --git a/libavformat/psxstr.c b/libavformat/psxstr.c
index 6897f26..f7b9495 100644
--- a/libavformat/psxstr.c
+++ b/libavformat/psxstr.c
@@ -2,20 +2,20 @@
* Sony Playstation (PSX) STR File Demuxer
* Copyright (c) 2003 The FFmpeg project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -66,11 +66,13 @@ typedef struct StrDemuxContext {
StrChannel channels[32];
} StrDemuxContext;
-static const char sync_header[12] = {0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00};
+static const uint8_t sync_header[12] = {0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00};
static int str_probe(AVProbeData *p)
{
- uint8_t *sector= p->buf;
+ const uint8_t *sector= p->buf;
+ const uint8_t *end= sector + p->buf_size;
+ int aud=0, vid=0;
if (p->buf_size < RAW_CD_SECTOR_SIZE)
return 0;
@@ -82,20 +84,46 @@ 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;
+ }
+ 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)
@@ -183,10 +211,11 @@ static int str_read_packet(AVFormatContext *s,
if(pkt->size != sector_count*VIDEO_DATA_CHUNK_SIZE){
if(pkt->data)
- av_log(s, AV_LOG_ERROR, "missmatching sector_count\n");
+ av_log(s, AV_LOG_ERROR, "mismatching sector_count\n");
av_packet_unref(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 =
@@ -252,7 +281,7 @@ static int str_read_packet(AVFormatContext *s,
break;
}
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR(EIO);
}
}
diff --git a/libavformat/pva.c b/libavformat/pva.c
index 458d219..16381db 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 0000000..c6652b9
--- /dev/null
+++ b/libavformat/pvfdec.c
@@ -0,0 +1,77 @@
+/*
+ * 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 "libavcodec/internal.h"
+#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 || channels > FF_SANE_NB_CHANNELS ||
+ bps <= 0 || bps > INT_MAX / FF_SANE_NB_CHANNELS || sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->channels = channels;
+ st->codecpar->sample_rate = sample_rate;
+ st->codecpar->codec_id = ff_get_pcm_codec_id(bps, 0, 1, 0xFFFF);
+ st->codecpar->bits_per_coded_sample = bps;
+ st->codecpar->block_align = bps * st->codecpar->channels / 8;
+
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->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 b262d60..b842e26 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
@@ -56,6 +57,11 @@ static const uint8_t guid_evrc[16] = {
0x91, 0xef, 0x73, 0x6a, 0x51, 0x00, 0xce, 0xb4
};
+static const uint8_t guid_4gv[16] = {
+ 0xca, 0x29, 0xfd, 0x3c, 0x53, 0xf6, 0xf5, 0x4e,
+ 0x90, 0xe9, 0xf4, 0x23, 0x6d, 0x59, 0x9b, 0x61
+};
+
/**
* SMV GUID as stored in the file
*/
@@ -102,13 +108,14 @@ static int qcp_read_header(AVFormatContext *s)
if (is_qcelp_13k_guid(buf)) {
st->codecpar->codec_id = AV_CODEC_ID_QCELP;
} else if (!memcmp(buf, guid_evrc, 16)) {
- avpriv_report_missing_feature(s, "EVRC codec");
- return AVERROR_PATCHWELCOME;
+ st->codecpar->codec_id = AV_CODEC_ID_EVRC;
} else if (!memcmp(buf, guid_smv, 16)) {
- avpriv_report_missing_feature(s, "SMV codec");
- return AVERROR_PATCHWELCOME;
+ st->codecpar->codec_id = AV_CODEC_ID_SMV;
+ } else if (!memcmp(buf, guid_4gv, 16)) {
+ st->codecpar->codec_id = AV_CODEC_ID_4GV;
} 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 +148,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.c b/libavformat/qtpalette.c
new file mode 100644
index 0000000..666c6b7
--- /dev/null
+++ b/libavformat/qtpalette.c
@@ -0,0 +1,116 @@
+/*
+ * QuickTime palette handling
+ * Copyright (c) 2001 Fabrice Bellard
+ * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com>
+ * Copyright (c) 2015 Mats Peterson
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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 <stdint.h>
+
+#include "avformat.h"
+#include "libavutil/intreadwrite.h"
+#include "qtpalette.h"
+
+int ff_get_qtpalette(int codec_id, AVIOContext *pb, uint32_t *palette)
+{
+ int tmp, bit_depth, color_table_id, greyscale, i;
+
+ avio_seek(pb, 82, SEEK_CUR);
+
+ /* Get the bit depth and greyscale state */
+ tmp = avio_rb16(pb);
+ bit_depth = tmp & 0x1F;
+ greyscale = tmp & 0x20;
+
+ /* Get the color table ID */
+ color_table_id = avio_rb16(pb);
+
+ /* Do not create a greyscale palette for Cinepak */
+ if (greyscale && codec_id == AV_CODEC_ID_CINEPAK)
+ return 0;
+
+ /* If the depth is 1, 2, 4, or 8 bpp, file is palettized. */
+ if ((bit_depth == 1 || bit_depth == 2 || bit_depth == 4 || bit_depth == 8)) {
+ uint32_t color_count, color_start, color_end;
+ uint32_t a, r, g, b;
+
+ /* Ignore the greyscale bit for 1-bit video and sample
+ * descriptions containing a color table. */
+ if (greyscale && bit_depth > 1 && color_table_id) {
+ int color_index, color_dec;
+ /* compute the greyscale palette */
+ color_count = 1 << bit_depth;
+ color_index = 255;
+ color_dec = 256 / (color_count - 1);
+ for (i = 0; i < color_count; i++) {
+ r = g = b = color_index;
+ palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | (b);
+ color_index -= color_dec;
+ if (color_index < 0)
+ color_index = 0;
+ }
+ } else if (color_table_id) {
+ /* The color table ID is non-zero. Interpret this as
+ * being -1, which means use the default Macintosh
+ * color table */
+ const uint8_t *color_table;
+ color_count = 1 << bit_depth;
+ if (bit_depth == 1)
+ color_table = ff_qt_default_palette_2;
+ else if (bit_depth == 2)
+ color_table = ff_qt_default_palette_4;
+ else if (bit_depth == 4)
+ color_table = ff_qt_default_palette_16;
+ else
+ color_table = ff_qt_default_palette_256;
+ for (i = 0; i < color_count; i++) {
+ r = color_table[i * 3 + 0];
+ g = color_table[i * 3 + 1];
+ b = color_table[i * 3 + 2];
+ palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | (b);
+ }
+ } else {
+ /* The color table ID is 0; the color table is in the sample
+ * description */
+ color_start = avio_rb32(pb);
+ avio_rb16(pb); /* color table flags */
+ color_end = avio_rb16(pb);
+ if ((color_start <= 255) && (color_end <= 255)) {
+ for (i = color_start; i <= color_end; i++) {
+ /* 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);
+ g = avio_r8(pb);
+ avio_r8(pb);
+ b = avio_r8(pb);
+ avio_r8(pb);
+ palette[i] = (a << 24 ) | (r << 16) | (g << 8) | (b);
+ }
+ }
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/libavformat/qtpalette.h b/libavformat/qtpalette.h
index ecc85d3..016e91f 100644
--- a/libavformat/qtpalette.h
+++ b/libavformat/qtpalette.h
@@ -3,51 +3,59 @@
* 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
*/
#ifndef AVFORMAT_QTPALETTE_H
#define AVFORMAT_QTPALETTE_H
-#include <inttypes.h>
+#include <stdint.h>
+#include "avformat.h"
+static const uint8_t ff_qt_default_palette_2[2 * 3] = {
+ 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00
+};
+
+/* From a screenshot of the "Monitors & Sound" control panel in Mac OS 7.5.5 */
static const uint8_t ff_qt_default_palette_4[4 * 3] = {
- 0x93, 0x65, 0x5E,
0xFF, 0xFF, 0xFF,
- 0xDF, 0xD0, 0xAB,
+ 0xAC, 0xAC, 0xAC,
+ 0x55, 0x55, 0x55,
0x00, 0x00, 0x00
};
+/* From a screenshot of the "Monitors & Sound" control panel in Mac OS 7.5.5 */
static const uint8_t ff_qt_default_palette_16[16 * 3] = {
- 0xFF, 0xFB, 0xFF,
- 0xEF, 0xD9, 0xBB,
- 0xE8, 0xC9, 0xB1,
- 0x93, 0x65, 0x5E,
- 0xFC, 0xDE, 0xE8,
- 0x9D, 0x88, 0x91,
- 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF,
- 0x47, 0x48, 0x37,
- 0x7A, 0x5E, 0x55,
- 0xDF, 0xD0, 0xAB,
- 0xFF, 0xFB, 0xF9,
- 0xE8, 0xCA, 0xC5,
- 0x8A, 0x7C, 0x77,
+ 0xFC, 0xF3, 0x05,
+ 0xFF, 0x64, 0x02,
+ 0xDD, 0x08, 0x06,
+ 0xF2, 0x08, 0x84,
+ 0x46, 0x00, 0xA5,
+ 0x00, 0x00, 0xD4,
+ 0x02, 0xAB, 0xEA,
+ 0x1F, 0xB7, 0x14,
+ 0x00, 0x64, 0x11,
+ 0x56, 0x2C, 0x05,
+ 0x90, 0x71, 0x3A,
+ 0xC0, 0xC0, 0xC0,
+ 0x80, 0x80, 0x80,
+ 0x40, 0x40, 0x40,
0x00, 0x00, 0x00
};
@@ -310,4 +318,15 @@ static const uint8_t ff_qt_default_palette_256[256 * 3] = {
/* 255, 0xFF */ 0x00, 0x00, 0x00
};
+/**
+ * Retrieve the palette (or "color table" in QuickTime terms), either
+ * from the video sample description, or from the default Macintosh
+ * palette.
+ *
+ * The file offset of the AVIOContext pointed to by the 'pb' variable
+ * should be the start of the video sample description (the sample
+ * description size and the data format).
+ */
+int ff_get_qtpalette(int codec_id, AVIOContext *pb, uint32_t *palette);
+
#endif /* AVFORMAT_QTPALETTE_H */
diff --git a/libavformat/r3d.c b/libavformat/r3d.c
index 9369cdd..1f53d84 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
*/
@@ -89,6 +89,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;
}
@@ -130,8 +133,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_log(s, AV_LOG_TRACE, "duration %"PRId64"\n", st->duration);
@@ -324,7 +326,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->codecpar->sample_rate);
+ if (st->codecpar->sample_rate)
+ pkt->duration = av_rescale(samples, st->time_base.den, st->codecpar->sample_rate);
av_log(s, AV_LOG_TRACE, "pkt dts %"PRId64" duration %"PRId64" samples %d sample rate %d\n",
pkt->dts, pkt->duration, samples, st->codecpar->sample_rate);
@@ -382,7 +385,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_log(s, AV_LOG_TRACE, "seek frame num %d timestamp %"PRId64"\n",
frame_num, sample_time);
diff --git a/libavformat/rawdec.c b/libavformat/rawdec.c
index 3b1bea6..e926549 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
@@ -42,16 +43,12 @@ int ff_raw_read_partial_packet(AVFormatContext *s, AVPacket *pkt)
pkt->pos= avio_tell(s->pb);
pkt->stream_index = 0;
- ret = ffio_read_partial(s->pb, pkt->data, size);
+ ret = avio_read_partial(s->pb, pkt->data, size);
if (ret < 0) {
av_packet_unref(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, AV_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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->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,133 +82,112 @@ int ff_raw_video_read_header(AVFormatContext *s)
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->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->internal->avctx->framerate = 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->codecpar->codec_type = AVMEDIA_TYPE_DATA;
+ st->codecpar->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, INT_MAX, DEC},
{ NULL },
};
-#if CONFIG_LATM_DEMUXER
-
-#define LOAS_SYNC_WORD 0x2b7
+#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
-static int latm_read_probe(AVProbeData *p)
+#if CONFIG_MJPEG_DEMUXER
+static int mjpeg_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;
+ 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++;
}
- fsize = (header & 0x1FFF) + 3;
- if (fsize < 7)
- break;
- 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 if (max_frames > 1)
- return 1;
- else
- return 0;
-}
+ if (nb_invalid*4 + 1 < nb_frames) {
+ static const char ct_jpeg[] = "\r\nContent-Type: image/jpeg\r\n";
+ int i;
-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,
- .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)
-#endif
-
-#if CONFIG_MLP_DEMUXER
-AVInputFormat ff_mlp_demuxer = {
- .name = "mlp",
- .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,
- .extensions = "mlp",
- .raw_codec_id = AV_CODEC_ID_MLP,
-};
-#endif
+ for (i=0; i<FFMIN(p->buf_size - (int)sizeof(ct_jpeg), 100); i++)
+ if (!memcmp(p->buf + i, ct_jpeg, sizeof(ct_jpeg) - 1))
+ return AVPROBE_SCORE_EXTENSION;
-#if CONFIG_TRUEHD_DEMUXER
-AVInputFormat ff_truehd_demuxer = {
- .name = "truehd",
- .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,
- .extensions = "thd",
- .raw_codec_id = AV_CODEC_ID_TRUEHD,
-};
-#endif
+ if (nb_invalid == 0 && nb_frames > 2)
+ return AVPROBE_SCORE_EXTENSION / 2;
+ return AVPROBE_SCORE_EXTENSION / 4;
+ }
-#if CONFIG_SHORTEN_DEMUXER
-AVInputFormat ff_shorten_demuxer = {
- .name = "shn",
- .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,
- .extensions = "shn",
- .raw_codec_id = AV_CODEC_ID_SHORTEN,
-};
-#endif
+ return 0;
+}
-#if CONFIG_VC1_DEMUXER
-FF_DEF_RAWVIDEO_DEMUXER(vc1, "raw VC-1", NULL, "vc1", AV_CODEC_ID_VC1)
+FF_DEF_RAWVIDEO_DEMUXER2(mjpeg, "raw MJPEG video", mjpeg_probe, "mjpg,mjpeg,mpo", AV_CODEC_ID_MJPEG, AVFMT_GENERIC_INDEX|AVFMT_NOTIMESTAMPS)
#endif
diff --git a/libavformat/rawdec.h b/libavformat/rawdec.h
index a548778..a464bbb 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 60740fb..f640121 100644
--- a/libavformat/rawenc.c
+++ b/libavformat/rawenc.c
@@ -3,25 +3,28 @@
* 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
*/
+#include "libavutil/intreadwrite.h"
+
#include "avformat.h"
#include "rawenc.h"
+#include "internal.h"
int ff_raw_write_packet(AVFormatContext *s, AVPacket *pkt)
{
@@ -29,6 +32,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,19 +52,41 @@ 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,
};
#endif
#if CONFIG_ADX_MUXER
+
+static int adx_write_trailer(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ AVCodecParameters *par = s->streams[0]->codecpar;
+
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ int64_t file_size = avio_tell(pb);
+ uint64_t sample_count = (file_size - 36) / par->channels / 18 * 32;
+ if (sample_count <= UINT32_MAX) {
+ avio_seek(pb, 12, SEEK_SET);
+ avio_wb32(pb, sample_count);
+ avio_seek(pb, file_size, SEEK_SET);
+ }
+ }
+
+ return 0;
+}
+
AVOutputFormat ff_adx_muxer = {
.name = "adx",
.long_name = NULL_IF_CONFIG_SMALL("CRI ADX"),
.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,
+ .write_trailer = adx_write_trailer,
.flags = AVFMT_NOTIMESTAMPS,
};
#endif
@@ -63,6 +98,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,
};
@@ -72,9 +118,10 @@ AVOutputFormat ff_cavsvideo_muxer = {
AVOutputFormat ff_dirac_muxer = {
.name = "dirac",
.long_name = NULL_IF_CONFIG_SMALL("raw Dirac"),
- .extensions = "drc",
+ .extensions = "drc,vc2",
.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,
};
@@ -84,9 +131,10 @@ AVOutputFormat ff_dirac_muxer = {
AVOutputFormat ff_dnxhd_muxer = {
.name = "dnxhd",
.long_name = NULL_IF_CONFIG_SMALL("raw DNxHD (SMPTE VC-3)"),
- .extensions = "dnxhd",
+ .extensions = "dnxhd,dnxhr",
.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 +148,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 +162,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 +176,7 @@ 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,
};
@@ -139,6 +190,45 @@ AVOutputFormat ff_g723_1_muxer = {
.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,
+};
+#endif
+
+#if CONFIG_G726_MUXER
+AVOutputFormat ff_g726_muxer = {
+ .name = "g726",
+ .long_name = NULL_IF_CONFIG_SMALL("raw big-endian G.726 (\"left-justified\")"),
+ .audio_codec = AV_CODEC_ID_ADPCM_G726,
+ .video_codec = AV_CODEC_ID_NONE,
+ .write_header = force_one_stream,
+ .write_packet = ff_raw_write_packet,
+ .flags = AVFMT_NOTIMESTAMPS,
+};
+#endif
+
+#if CONFIG_G726LE_MUXER
+AVOutputFormat ff_g726le_muxer = {
+ .name = "g726le",
+ .long_name = NULL_IF_CONFIG_SMALL("raw little-endian G.726 (\"right-justified\")"),
+ .audio_codec = AV_CODEC_ID_ADPCM_G726LE,
+ .video_codec = AV_CODEC_ID_NONE,
+ .write_header = force_one_stream,
+ .write_packet = ff_raw_write_packet,
+ .flags = AVFMT_NOTIMESTAMPS,
+};
+#endif
+
+#if CONFIG_GSM_MUXER
+AVOutputFormat ff_gsm_muxer = {
+ .name = "gsm",
+ .long_name = NULL_IF_CONFIG_SMALL("raw GSM"),
+ .mime_type = "audio/x-gsm",
+ .extensions = "gsm",
+ .audio_codec = AV_CODEC_ID_GSM,
+ .video_codec = AV_CODEC_ID_NONE,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -152,6 +242,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,
};
@@ -165,31 +256,54 @@ 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,
};
#endif
#if CONFIG_H264_MUXER
+static int h264_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt)
+{
+ AVStream *st = s->streams[0];
+ if (pkt->size >= 5 && AV_RB32(pkt->data) != 0x0000001 &&
+ AV_RB24(pkt->data) != 0x000001)
+ return ff_stream_add_bitstream_filter(st, "h264_mp4toannexb", NULL);
+ return 1;
+}
+
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,
+ .check_bitstream = h264_check_bitstream,
.flags = AVFMT_NOTIMESTAMPS,
};
#endif
#if CONFIG_HEVC_MUXER
+static int hevc_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt)
+{
+ AVStream *st = s->streams[0];
+ if (pkt->size >= 5 && AV_RB32(pkt->data) != 0x0000001 &&
+ AV_RB24(pkt->data) != 0x000001)
+ return ff_stream_add_bitstream_filter(st, "hevc_mp4toannexb", NULL);
+ return 1;
+}
+
AVOutputFormat ff_hevc_muxer = {
.name = "hevc",
.long_name = NULL_IF_CONFIG_SMALL("raw HEVC video"),
- .extensions = "hevc",
+ .extensions = "hevc,h265,265",
.audio_codec = AV_CODEC_ID_NONE,
.video_codec = AV_CODEC_ID_HEVC,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
+ .check_bitstream = hevc_check_bitstream,
.flags = AVFMT_NOTIMESTAMPS,
};
#endif
@@ -201,6 +315,7 @@ AVOutputFormat ff_m4v_muxer = {
.extensions = "m4v",
.audio_codec = AV_CODEC_ID_NONE,
.video_codec = AV_CODEC_ID_MPEG4,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
@@ -214,11 +329,25 @@ 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,
};
#endif
+#if CONFIG_SINGLEJPEG_MUXER
+AVOutputFormat ff_singlejpeg_muxer = {
+ .name = "singlejpeg",
+ .long_name = NULL_IF_CONFIG_SMALL("JPEG single image"),
+ .mime_type = "image/jpeg",
+ .audio_codec = AV_CODEC_ID_NONE,
+ .video_codec = AV_CODEC_ID_MJPEG,
+ .write_packet = ff_raw_write_packet,
+ .flags = AVFMT_NOTIMESTAMPS,
+ .write_header = force_one_stream,
+};
+#endif
+
#if CONFIG_MLP_MUXER
AVOutputFormat ff_mlp_muxer = {
.name = "mlp",
@@ -226,6 +355,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,
};
@@ -252,6 +382,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,
};
@@ -264,6 +395,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,
};
@@ -281,18 +413,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",
@@ -300,6 +420,20 @@ AVOutputFormat ff_truehd_muxer = {
.extensions = "thd",
.audio_codec = AV_CODEC_ID_TRUEHD,
.video_codec = AV_CODEC_ID_NONE,
+ .write_header = force_one_stream,
+ .write_packet = ff_raw_write_packet,
+ .flags = AVFMT_NOTIMESTAMPS,
+};
+#endif
+
+#if CONFIG_VC1_MUXER
+AVOutputFormat ff_vc1_muxer = {
+ .name = "vc1",
+ .long_name = NULL_IF_CONFIG_SMALL("raw VC-1 video"),
+ .extensions = "vc1",
+ .audio_codec = AV_CODEC_ID_NONE,
+ .video_codec = AV_CODEC_ID_VC1,
+ .write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.flags = AVFMT_NOTIMESTAMPS,
};
diff --git a/libavformat/rawenc.h b/libavformat/rawenc.h
index daa5489..b552309 100644
--- a/libavformat/rawenc.h
+++ b/libavformat/rawenc.h
@@ -2,20 +2,20 @@
* RAW muxers
* Copyright (C) 2007 Aurelien Jacobs <aurel@gnuage.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rawutils.c b/libavformat/rawutils.c
new file mode 100644
index 0000000..996412a
--- /dev/null
+++ b/libavformat/rawutils.c
@@ -0,0 +1,67 @@
+/*
+ * Raw video utils
+ * Copyright (c) 2016 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 "avformat.h"
+#include "internal.h"
+
+int ff_reshuffle_raw_rgb(AVFormatContext *s, AVPacket **ppkt, AVCodecParameters *par, int expected_stride)
+{
+ int ret;
+ AVPacket *pkt = *ppkt;
+ int64_t bpc = par->bits_per_coded_sample != 15 ? par->bits_per_coded_sample : 16;
+ int min_stride = (par->width * bpc + 7) >> 3;
+ int with_pal_size = min_stride * par->height + 1024;
+ int contains_pal = bpc == 8 && pkt->size == with_pal_size;
+ int size = contains_pal ? min_stride * par->height : pkt->size;
+ int stride = size / par->height;
+ int padding = expected_stride - FFMIN(expected_stride, stride);
+ int y;
+ AVPacket *new_pkt;
+
+ if (pkt->size == expected_stride * par->height)
+ return 0;
+ if (size != stride * par->height)
+ return 0;
+
+ new_pkt = av_packet_alloc();
+ if (!new_pkt)
+ return AVERROR(ENOMEM);
+
+ ret = av_new_packet(new_pkt, expected_stride * par->height);
+ if (ret < 0)
+ goto fail;
+
+ ret = av_packet_copy_props(new_pkt, pkt);
+ if (ret < 0)
+ goto fail;
+
+ for (y = 0; y<par->height; y++) {
+ memcpy(new_pkt->data + y*expected_stride, pkt->data + y*stride, FFMIN(expected_stride, stride));
+ memset(new_pkt->data + y*expected_stride + expected_stride - padding, 0, padding);
+ }
+
+ *ppkt = new_pkt;
+ return 1 + contains_pal;
+fail:
+ av_packet_free(&new_pkt);
+
+ return ret;
+}
diff --git a/libavformat/rawvideodec.c b/libavformat/rawvideodec.c
index cea1b0b..8bde22c 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
*/
@@ -28,19 +28,18 @@
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;
+ int packet_size;
st = avformat_new_stream(ctx, NULL);
if (!st)
@@ -50,29 +49,23 @@ static int rawvideo_read_header(AVFormatContext *ctx)
st->codecpar->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->codecpar->width = width;
- st->codecpar->height = height;
+ st->codecpar->width = s->width;
+ st->codecpar->height = s->height;
st->codecpar->format = pix_fmt;
+ packet_size = av_image_get_buffer_size(st->codecpar->format, s->width, s->height, 1);
+ if (packet_size < 0)
+ return packet_size;
+ ctx->packet_size = packet_size;
+ st->codecpar->bit_rate = av_rescale_q(ctx->packet_size,
+ (AVRational){8,1}, st->time_base);
return 0;
}
@@ -80,18 +73,10 @@ static int rawvideo_read_header(AVFormatContext *ctx)
static int rawvideo_read_packet(AVFormatContext *s, AVPacket *pkt)
{
- int packet_size, ret, width, height;
- AVStream *st = s->streams[0];
-
- width = st->codecpar->width;
- height = st->codecpar->height;
-
- packet_size = av_image_get_buffer_size(st->codecpar->format, width, height, 1);
- if (packet_size < 0)
- return -1;
+ int ret;
- ret = av_get_packet(s->pb, pkt, packet_size);
- pkt->pts = pkt->dts = pkt->pos / packet_size;
+ ret = av_get_packet(s->pb, pkt, s->packet_size);
+ pkt->pts = pkt->dts = pkt->pos / s->packet_size;
pkt->stream_index = 0;
if (ret < 0)
@@ -102,9 +87,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, INT_MAX, DEC },
{ NULL },
};
diff --git a/libavformat/rdt.c b/libavformat/rdt.c
index 0adfa25..b69827f 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
*/
@@ -25,8 +25,6 @@
* @author Ronald S. Bultje <rbultje@ronald.bitfreak.net>
*/
-#include "libavcodec/bitstream.h"
-
#include "avformat.h"
#include "libavutil/avstring.h"
#include "rtpdec.h"
@@ -36,6 +34,7 @@
#include "rm.h"
#include "internal.h"
#include "avio_internal.h"
+#include "libavcodec/get_bits.h"
struct RDTDemuxContext {
AVFormatContext *ic; /**< the containing (RTSP) demux context */
@@ -177,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;
@@ -192,7 +191,7 @@ ff_rdt_parse_header(const uint8_t *buf, int len,
int *pset_id, int *pseq_no, int *pstream_id,
int *pis_keyframe, uint32_t *ptimestamp)
{
- BitstreamContext bc;
+ GetBitContext gb;
int consumed = 0, set_id, seq_no, stream_id, is_keyframe,
len_included, need_reliable;
uint32_t timestamp;
@@ -262,24 +261,24 @@ ff_rdt_parse_header(const uint8_t *buf, int len,
* [2] http://www.wireshark.org/docs/dfref/r/rdt.html and
* http://anonsvn.wireshark.org/viewvc/trunk/epan/dissectors/packet-rdt.c
*/
- bitstream_init8(&bc, buf, len);
- len_included = bitstream_read_bit(&bc);
- need_reliable = bitstream_read_bit(&bc);
- set_id = bitstream_read(&bc, 5);
- bitstream_skip(&bc, 1);
- seq_no = bitstream_read(&bc, 16);
+ init_get_bits(&gb, buf, len << 3);
+ len_included = get_bits1(&gb);
+ need_reliable = get_bits1(&gb);
+ set_id = get_bits(&gb, 5);
+ skip_bits(&gb, 1);
+ seq_no = get_bits(&gb, 16);
if (len_included)
- bitstream_skip(&bc, 16);
- bitstream_skip(&bc, 2);
- stream_id = bitstream_read(&bc, 5);
- is_keyframe = !bitstream_read_bit(&bc);
- timestamp = bitstream_read(&bc, 32);
+ skip_bits(&gb, 16);
+ skip_bits(&gb, 2);
+ stream_id = get_bits(&gb, 5);
+ is_keyframe = !get_bits1(&gb);
+ timestamp = get_bits_long(&gb, 32);
if (set_id == 0x1f)
- set_id = bitstream_read(&bc, 16);
+ set_id = get_bits(&gb, 16);
if (need_reliable)
- bitstream_skip(&bc, 16);
+ skip_bits(&gb, 16);
if (stream_id == 0x1f)
- stream_id = bitstream_read(&bc, 16);
+ stream_id = get_bits(&gb, 16);
if (pset_id) *pset_id = set_id;
if (pseq_no) *pseq_no = seq_no;
@@ -287,7 +286,7 @@ ff_rdt_parse_header(const uint8_t *buf, int len,
if (pis_keyframe) *pis_keyframe = is_keyframe;
if (ptimestamp) *ptimestamp = timestamp;
- return consumed + (bitstream_tell(&bc) >> 3);
+ return consumed + (get_bits_count(&gb) >> 3);
}
/**< return 0 on packet, no more left, 1 on packet, 1 on partial packet... */
@@ -302,7 +301,7 @@ rdt_parse_packet (AVFormatContext *ctx, PayloadContext *rdt, AVStream *st,
if (rdt->audio_pkt_cnt == 0) {
int pos, rmflags;
- ffio_init_context(&pb, buf, len, 0, NULL, NULL, NULL, NULL);
+ ffio_init_context(&pb, (uint8_t *)buf, len, 0, NULL, NULL, NULL, NULL);
rmflags = (flags & RTP_FLAG_KEY) ? 2 : 0;
res = ff_rm_parse_packet (rdt->rmctx, &pb, st, rdt->rmst[st->index], len, pkt,
&seq, rmflags, *timestamp);
@@ -449,7 +448,7 @@ real_parse_asm_rule(AVStream *st, const char *p, const char *end)
{
do {
/* can be either averagebandwidth= or AverageBandwidth= */
- if (sscanf(p, " %*1[Aa]verage%*1[Bb]andwidth=%d", &st->codecpar->bit_rate) == 1)
+ if (sscanf(p, " %*1[Aa]verage%*1[Bb]andwidth=%"SCNd64, &st->codecpar->bit_rate) == 1)
break;
if (!(p = strchr(p, ',')) || p > end)
p = end;
@@ -522,8 +521,19 @@ ff_real_parse_sdp_a_line (AVFormatContext *s, int stream_index,
real_parse_asm_rulebook(s, s->streams[stream_index], p);
}
+
+
static av_cold int rdt_init(AVFormatContext *s, int st_index, PayloadContext *rdt)
{
+ int ret;
+
+ rdt->rmctx = avformat_alloc_context();
+ if (!rdt->rmctx)
+ return AVERROR(ENOMEM);
+
+ if ((ret = ff_copy_whiteblacklists(rdt->rmctx, s)) < 0)
+ return ret;
+
return avformat_open_input(&rdt->rmctx, "", &ff_rdt_demuxer, NULL);
}
diff --git a/libavformat/rdt.h b/libavformat/rdt.h
index bd16890..ce6026f 100644
--- a/libavformat/rdt.h
+++ b/libavformat/rdt.h
@@ -2,20 +2,20 @@
* Realmedia RTSP (RDT) definitions
* Copyright (c) 2007 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/realtextdec.c b/libavformat/realtextdec.c
new file mode 100644
index 0000000..618d4f7
--- /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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->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->codecpar->extradata = av_strdup(buf.str);
+ if (!st->codecpar->extradata) {
+ res = AVERROR(ENOMEM);
+ goto end;
+ }
+ st->codecpar->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(s, &rt->q);
+
+end:
+ av_bprint_finalize(&buf, NULL);
+ return res;
+}
+
+static int realtext_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ RealTextContext *rt = s->priv_data;
+ return ff_subtitles_queue_read_packet(&rt->q, pkt);
+}
+
+static int realtext_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ RealTextContext *rt = s->priv_data;
+ return ff_subtitles_queue_seek(&rt->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int realtext_read_close(AVFormatContext *s)
+{
+ RealTextContext *rt = s->priv_data;
+ ff_subtitles_queue_clean(&rt->q);
+ return 0;
+}
+
+AVInputFormat ff_realtext_demuxer = {
+ .name = "realtext",
+ .long_name = NULL_IF_CONFIG_SMALL("RealText subtitle format"),
+ .priv_data_size = sizeof(RealTextContext),
+ .read_probe = realtext_probe,
+ .read_header = realtext_read_header,
+ .read_packet = realtext_read_packet,
+ .read_seek2 = realtext_read_seek,
+ .read_close = realtext_read_close,
+ .extensions = "rt",
+};
diff --git a/libavformat/redspark.c b/libavformat/redspark.c
new file mode 100644
index 0000000..c247046
--- /dev/null
+++ b/libavformat/redspark.c
@@ -0,0 +1,162 @@
+/*
+ * 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
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+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);
+ key = data ^ 0x52656453;
+ data ^= key;
+ AV_WB32(header, data);
+ key = rol(key, 11);
+
+ key += rol(key, 3);
+ data = AV_RB32(p->buf + 4) ^ 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;
+ AVCodecParameters *par;
+ GetByteContext gbc;
+ int i, coef_off, ret = 0;
+ uint32_t key, data;
+ uint8_t header[HEADER_SIZE];
+ AVStream *st;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ par = st->codecpar;
+
+ /* Decrypt header */
+ data = avio_rb32(pb);
+ key = data ^ 0x52656453;
+ data ^= key;
+ AV_WB32(header, data);
+ key = rol(key, 11);
+
+ for (i = 4; i < HEADER_SIZE; i += 4) {
+ key += rol(key, 3);
+ data = avio_rb32(pb) ^ key;
+ AV_WB32(header + i, data);
+ }
+
+ par->codec_id = AV_CODEC_ID_ADPCM_THP;
+ par->codec_type = AVMEDIA_TYPE_AUDIO;
+
+ bytestream2_init(&gbc, header, HEADER_SIZE);
+ bytestream2_seek(&gbc, 0x3c, SEEK_SET);
+ par->sample_rate = bytestream2_get_be32u(&gbc);
+ if (par->sample_rate <= 0 || par->sample_rate > 96000) {
+ av_log(s, AV_LOG_ERROR, "Invalid sample rate: %d\n", par->sample_rate);
+ return AVERROR_INVALIDDATA;
+ }
+
+ st->duration = bytestream2_get_be32u(&gbc) * 14;
+ redspark->samples_count = 0;
+ bytestream2_skipu(&gbc, 10);
+ par->channels = bytestream2_get_byteu(&gbc);
+ if (!par->channels) {
+ return AVERROR_INVALIDDATA;
+ }
+
+ coef_off = 0x54 + par->channels * 8;
+ if (bytestream2_get_byteu(&gbc)) // Loop flag
+ coef_off += 16;
+
+ if (coef_off + par->channels * (32 + 14) > HEADER_SIZE) {
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (ff_alloc_extradata(par, 32 * par->channels)) {
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* Get the ADPCM table */
+ bytestream2_seek(&gbc, coef_off, SEEK_SET);
+ for (i = 0; i < par->channels; i++) {
+ if (bytestream2_get_bufferu(&gbc, par->extradata + i * 32, 32) != 32) {
+ return AVERROR_INVALIDDATA;
+ }
+ bytestream2_skipu(&gbc, 14);
+ }
+
+ avpriv_set_pts_info(st, 64, 1, par->sample_rate);
+
+ return ret;
+}
+
+static int redspark_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ RedSparkContext *redspark = s->priv_data;
+ uint32_t size = 8 * par->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_packet_unref(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 3188b15..707d3cd 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 ea56c17..ceacb21 100644
--- a/libavformat/replaygain.h
+++ b/libavformat/replaygain.h
@@ -1,18 +1,18 @@
/*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/riff.c b/libavformat/riff.c
index 6de4007..3f0b390 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') },
@@ -40,6 +43,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ 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('t', 's', 'h', 'd') },
+ { 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') },
@@ -51,8 +55,8 @@ 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_H263, MKTAG('V', 'S', 'M', '4') }, /* needs -vf il=l=i:c=i */
{ 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') },
@@ -91,7 +95,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') },
@@ -111,6 +114,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_MPEG4, MKTAG('G', 'L', 'V', '4') },
{ AV_CODEC_ID_MPEG4, MKTAG('G', 'M', 'P', '4') }, /* GeoVision camera */
{ AV_CODEC_ID_MPEG4, MKTAG('M', 'N', 'M', '4') }, /* March Networks DVR */
+ { AV_CODEC_ID_MPEG4, MKTAG('G', 'T', 'M', '4') }, /* Telefactor */
{ AV_CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', '4', '3') },
{ AV_CODEC_ID_MSMPEG4V3, MKTAG('D', 'I', 'V', '3') },
{ AV_CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', 'G', '3') },
@@ -147,8 +151,8 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_DVVIDEO, MKTAG('p', 'd', 'v', 'c') },
{ AV_CODEC_ID_DVVIDEO, MKTAG('S', 'L', '2', '5') },
{ AV_CODEC_ID_DVVIDEO, MKTAG('S', 'L', 'D', 'V') },
+ { AV_CODEC_ID_DVVIDEO, MKTAG('A', 'V', 'd', '1') },
{ AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'g', '1') },
- { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'g', '2') },
{ AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'p', 'g', '2') },
{ AV_CODEC_ID_MPEG2VIDEO, MKTAG('M', 'P', 'E', 'G') },
{ AV_CODEC_ID_MPEG1VIDEO, MKTAG('P', 'I', 'M', '1') },
@@ -165,6 +169,9 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_MPEG2VIDEO, MKTAG('E', 'M', '2', 'V') },
/* Matrox MPEG-2 intra-only */
{ AV_CODEC_ID_MPEG2VIDEO, MKTAG('M', '7', '0', '1') },
+ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('M', '7', '0', '2') },
+ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('M', '7', '0', '3') },
+ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('M', '7', '0', '4') },
{ AV_CODEC_ID_MPEG2VIDEO, MKTAG('M', '7', '0', '5') },
{ AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'p', 'g', 'v') },
{ AV_CODEC_ID_MPEG1VIDEO, MKTAG('B', 'W', '1', '0') },
@@ -174,6 +181,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_MJPEG, MKTAG('L', 'J', 'P', 'G') },
{ AV_CODEC_ID_MJPEG, MKTAG('d', 'm', 'b', '1') },
{ AV_CODEC_ID_MJPEG, MKTAG('m', 'j', 'p', 'a') },
+ { AV_CODEC_ID_MJPEG, MKTAG('J', 'R', '2', '4') }, /* Quadrox Mjpeg */
{ AV_CODEC_ID_LJPEG, MKTAG('L', 'J', 'P', 'G') },
/* Pegasus lossless JPEG */
{ AV_CODEC_ID_MJPEG, MKTAG('J', 'P', 'G', 'L') },
@@ -184,7 +192,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 */
@@ -241,12 +249,58 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_RAWVIDEO, MKTAG('a', 'u', 'v', '2') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('Y', 'V', 'Y', 'U') },
{ AV_CODEC_ID_RAWVIDEO, MKTAG('Y', 'U', 'Y', 'V') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', '1', '0') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', '1', '1') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', '2', '2') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', '4', '0') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', '4', '4') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('J', '4', '2', '0') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('J', '4', '2', '2') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('J', '4', '4', '0') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('J', '4', '4', '4') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', 'U', 'V', 'A') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', '0', 'A') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', '2', 'A') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', '2') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'V', '1', '5') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'V', '1', '6') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'V', '2', '4') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'V', '3', '2') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 'A') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'V', '3', '2') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('G', 'R', 'E', 'Y') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '0', '9', 'L') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '0', '9', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '2', '9', 'L') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '2', '9', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', '9', 'L') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', '9', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '0', 'A', 'L') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '0', 'A', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '2', 'A', 'L') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '2', 'A', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', 'A', 'L') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', 'A', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', 'F', 'L') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', 'F', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '0', 'C', 'L') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '0', 'C', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '2', 'C', 'L') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '2', 'C', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', 'C', 'L') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '4', 'C', 'B') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '0', 'F', 'L') },
+ { AV_CODEC_ID_RAWVIDEO, MKTAG('I', '0', 'F', 'B') },
{ AV_CODEC_ID_FRWU, MKTAG('F', 'R', 'W', 'U') },
{ AV_CODEC_ID_R10K, MKTAG('R', '1', '0', 'k') },
{ AV_CODEC_ID_R210, MKTAG('r', '2', '1', '0') },
{ AV_CODEC_ID_V210, MKTAG('v', '2', '1', '0') },
{ AV_CODEC_ID_V210, MKTAG('C', '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') },
@@ -284,6 +338,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') },
@@ -336,16 +391,24 @@ 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') },
+ { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'Y', '4') },
+ /* 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', 'L', 'H', '4') },
+ { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'Q', 'Y', '2') },
+ { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'Q', 'R', 'A') },
+ { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'Q', 'R', 'G') },
{ 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') },
@@ -354,6 +417,8 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_CLLC, MKTAG('C', 'L', 'L', 'C') },
{ AV_CODEC_ID_MSS2, MKTAG('M', 'S', 'S', '2') },
{ AV_CODEC_ID_SVQ3, MKTAG('S', 'V', 'Q', '3') },
+ { AV_CODEC_ID_012V, MKTAG('0', '1', '2', 'v') },
+ { AV_CODEC_ID_012V, MKTAG('a', '1', '2', 'v') },
{ AV_CODEC_ID_G2M, MKTAG('G', '2', 'M', '2') },
{ AV_CODEC_ID_G2M, MKTAG('G', '2', 'M', '3') },
{ AV_CODEC_ID_G2M, MKTAG('G', '2', 'M', '4') },
@@ -366,9 +431,38 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_SCREENPRESSO, MKTAG('S', 'P', 'V', '1') },
{ AV_CODEC_ID_RSCC, MKTAG('R', 'S', 'C', 'C') },
{ AV_CODEC_ID_RSCC, MKTAG('I', 'S', 'C', 'C') },
+ { AV_CODEC_ID_CFHD, MKTAG('C', 'F', 'H', 'D') },
+ { AV_CODEC_ID_M101, MKTAG('M', '1', '0', '1') },
+ { AV_CODEC_ID_M101, MKTAG('M', '1', '0', '2') },
{ AV_CODEC_ID_MAGICYUV, MKTAG('M', 'A', 'G', 'Y') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '8', 'R', 'G') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '8', 'R', 'A') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '8', 'G', '0') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '8', 'Y', '0') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '8', 'Y', '2') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '8', 'Y', '4') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '8', 'Y', 'A') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '0', 'R', 'A') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '0', 'R', 'G') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '0', 'G', '0') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '0', 'Y', '2') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '2', 'R', 'A') },
+ { AV_CODEC_ID_MAGICYUV, MKTAG('M', '2', 'R', 'G') },
+ { AV_CODEC_ID_YLC, MKTAG('Y', 'L', 'C', '0') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '0') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '1') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '2') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '3') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '4') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '5') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '7') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '9') },
+ { AV_CODEC_ID_FMVC, MKTAG('F', 'M', 'V', 'C') },
+ { AV_CODEC_ID_SCPR, MKTAG('S', 'C', 'P', 'R') },
+ { AV_CODEC_ID_CLEARVIDEO, MKTAG('U', 'C', 'O', 'D') },
{ AV_CODEC_ID_AV1, MKTAG('A', 'V', '0', '1') },
- { AV_CODEC_ID_CFHD, MKTAG('C', 'F', 'H', 'D') },
+ { AV_CODEC_ID_MSCC, MKTAG('M', 'S', 'C', 'C') },
+ { AV_CODEC_ID_SRGC, MKTAG('S', 'R', 'G', 'C') },
{ AV_CODEC_ID_NONE, 0 }
};
@@ -378,6 +472,7 @@ const AVCodecTag ff_codec_wav_tags[] = {
{ AV_CODEC_ID_PCM_U8, 0x0001 },
{ AV_CODEC_ID_PCM_S24LE, 0x0001 },
{ AV_CODEC_ID_PCM_S32LE, 0x0001 },
+ { AV_CODEC_ID_PCM_S64LE, 0x0001 },
{ AV_CODEC_ID_ADPCM_MS, 0x0002 },
{ AV_CODEC_ID_PCM_F32LE, 0x0003 },
/* must come after f32le in this list */
@@ -385,16 +480,20 @@ 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 },
+ { AV_CODEC_ID_ADPCM_G726, 0x0014 }, /* g723 Antex */
+ { AV_CODEC_ID_ADPCM_G726, 0x0040 }, /* g721 Antex */
{ AV_CODEC_ID_MP2, 0x0050 },
{ AV_CODEC_ID_MP3, 0x0055 },
{ AV_CODEC_ID_AMR_NB, 0x0057 },
@@ -406,13 +505,19 @@ const AVCodecTag ff_codec_wav_tags[] = {
{ AV_CODEC_ID_ADPCM_G726, 0x0064 },
{ AV_CODEC_ID_ADPCM_IMA_WAV, 0x0069 },
{ AV_CODEC_ID_METASOUND, 0x0075 },
+ { AV_CODEC_ID_G729, 0x0083 },
{ 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 },
{ AV_CODEC_ID_WMAPRO, 0x0162 },
{ AV_CODEC_ID_WMALOSSLESS, 0x0163 },
+ { AV_CODEC_ID_XMA1, 0x0165 },
+ { AV_CODEC_ID_XMA2, 0x0166 },
{ AV_CODEC_ID_ADPCM_CT, 0x0200 },
+ { AV_CODEC_ID_DVAUDIO, 0x0215 },
+ { AV_CODEC_ID_DVAUDIO, 0x0216 },
{ AV_CODEC_ID_ATRAC3, 0x0270 },
{ AV_CODEC_ID_ADPCM_G722, 0x028F },
{ AV_CODEC_ID_IMC, 0x0401 },
@@ -425,11 +530,16 @@ 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 },
{ AV_CODEC_ID_XAN_DPCM, 0x594a },
+ { AV_CODEC_ID_G729, 0x729A },
{ AV_CODEC_ID_G723_1, 0xA100 }, /* Comverse Infosys Ltd. G723 1 */
{ AV_CODEC_ID_AAC, 0xA106 },
{ AV_CODEC_ID_SPEEX, 0xA109 },
@@ -466,3 +576,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 e5f4645..323aa38 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
*/
@@ -46,12 +46,31 @@ void ff_end_tag(AVIOContext *pb, int64_t start);
*/
int ff_get_bmp_header(AVIOContext *pb, AVStream *st, uint32_t *size);
-void ff_put_bmp_header(AVIOContext *pb, AVCodecParameters *par, const AVCodecTag *tags, int for_asf);
-int ff_put_wav_header(AVFormatContext *s, AVIOContext *pb, AVCodecParameters *par);
+void ff_put_bmp_header(AVIOContext *pb, AVCodecParameters *par, 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
+
+/**
+ * Tell ff_put_wav_header() to write an empty channel mask.
+ */
+#define FF_PUT_WAV_HEADER_SKIP_CHANNELMASK 0x00000002
+
+/**
+ * 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(AVFormatContext *s, AVIOContext *pb, AVCodecParameters *par, int flags);
+
enum AVCodecID ff_wav_codec_get_id(unsigned int tag, int bps);
-int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb, AVCodecParameters *par, int size);
+int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb, AVCodecParameters *par, 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);
@@ -78,24 +97,30 @@ 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 \
0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71
+#define FF_AMBISONIC_BASE_GUID \
+ 0x21, 0x07, 0xD3, 0x11, 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00
+#define FF_BROKEN_BASE_GUID \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA
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 *ff_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 db83b32..b448f91 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,17 @@
#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)
+{
+ int ret;
+ av_assert0(sizeof(*g) == 16); //compiler will optimize this out
+ ret = avio_read(s, *g, sizeof(*g));
+ if (ret < (int)sizeof(*g)) {
+ memset(*g, 0, sizeof(*g));
+ return ret < 0 ? ret : AVERROR_INVALIDDATA;
+ }
+ return 0;
+}
enum AVCodecID ff_codec_guid_get_id(const AVCodecGuid *guids, ff_asf_guid guid)
{
@@ -66,6 +70,10 @@ static void parse_waveformatex(AVIOContext *pb, AVCodecParameters *par)
ff_get_guid(pb, &subformat);
if (!memcmp(subformat + 4,
+ (const uint8_t[]){ FF_AMBISONIC_BASE_GUID }, 12) ||
+ !memcmp(subformat + 4,
+ (const uint8_t[]){ FF_BROKEN_BASE_GUID }, 12) ||
+ !memcmp(subformat + 4,
(const uint8_t[]){ FF_MEDIASUBTYPE_BASE_GUID }, 12)) {
par->codec_tag = AV_RL32(subformat);
par->codec_id = ff_wav_codec_get_id(par->codec_tag,
@@ -79,33 +87,56 @@ static void parse_waveformatex(AVIOContext *pb, AVCodecParameters *par)
}
}
+/* "big_endian" values are needed for RIFX file format */
int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb,
- AVCodecParameters *par, int size)
+ AVCodecParameters *par, int size, int big_endian)
{
int id;
- uint64_t bitrate;
+ uint64_t bitrate = 0;
- if (size < 14)
+ if (size < 14) {
+ avpriv_request_sample(s, "wav header size < 14");
return AVERROR_INVALIDDATA;
+ }
- id = avio_rl16(pb);
- par->codec_type = AVMEDIA_TYPE_AUDIO;
- par->channels = avio_rl16(pb);
- par->sample_rate = avio_rl32(pb);
- bitrate = avio_rl32(pb) * 8;
- par->block_align = avio_rl16(pb);
+ par->codec_type = AVMEDIA_TYPE_AUDIO;
+ if (!big_endian) {
+ id = avio_rl16(pb);
+ if (id != 0x0165) {
+ par->channels = avio_rl16(pb);
+ par->sample_rate = avio_rl32(pb);
+ bitrate = avio_rl32(pb) * 8LL;
+ par->block_align = avio_rl16(pb);
+ }
+ } else {
+ id = avio_rb16(pb);
+ par->channels = avio_rb16(pb);
+ par->sample_rate = avio_rb32(pb);
+ bitrate = avio_rb32(pb) * 8LL;
+ par->block_align = avio_rb16(pb);
+ }
if (size == 14) { /* We're dealing with plain vanilla WAVEFORMAT */
par->bits_per_coded_sample = 8;
- } else
- par->bits_per_coded_sample = avio_rl16(pb);
+ } else {
+ if (!big_endian) {
+ par->bits_per_coded_sample = avio_rl16(pb);
+ } else {
+ par->bits_per_coded_sample = avio_rb16(pb);
+ }
+ }
if (id == 0xFFFE) {
par->codec_tag = 0;
} else {
par->codec_tag = id;
- par->codec_id = ff_wav_codec_get_id(id, par->bits_per_coded_sample);
+ par->codec_id = ff_wav_codec_get_id(id,
+ par->bits_per_coded_sample);
}
- if (size >= 18) { /* We're obviously dealing with WAVEFORMATEX */
+ if (size >= 18 && id != 0x0165) { /* We're obviously dealing with WAVEFORMATEX */
int cbSize = avio_rl16(pb); /* cbSize */
+ if (big_endian) {
+ avpriv_report_missing_feature(s, "WAVEFORMATEX support for RIFX files");
+ return AVERROR_PATCHWELCOME;
+ }
size -= 18;
cbSize = FFMIN(size, cbSize);
if (cbSize >= 22 && id == 0xfffe) { /* WAVEFORMATEXTENSIBLE */
@@ -113,38 +144,35 @@ int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb,
cbSize -= 22;
size -= 22;
}
- par->extradata_size = cbSize;
if (cbSize > 0) {
- av_free(par->extradata);
- par->extradata = av_mallocz(par->extradata_size +
- AV_INPUT_BUFFER_PADDING_SIZE);
- if (!par->extradata)
+ av_freep(&par->extradata);
+ if (ff_get_extradata(s, par, pb, cbSize) < 0)
return AVERROR(ENOMEM);
- avio_read(pb, par->extradata, par->extradata_size);
size -= cbSize;
}
/* It is possible for the chunk to contain garbage at the end */
if (size > 0)
avio_skip(pb, size);
- }
+ } else if (id == 0x0165 && size >= 32) {
+ int nb_streams, i;
- if (bitrate > INT_MAX) {
- if (s->error_recognition & AV_EF_EXPLODE) {
- av_log(s, AV_LOG_ERROR,
- "The bitrate %"PRIu64" is too large.\n",
- bitrate);
+ size -= 4;
+ av_freep(&par->extradata);
+ if (ff_get_extradata(s, par, pb, size) < 0)
+ return AVERROR(ENOMEM);
+ nb_streams = AV_RL16(par->extradata + 4);
+ par->sample_rate = AV_RL32(par->extradata + 12);
+ par->channels = 0;
+ bitrate = 0;
+ if (size < 8 + nb_streams * 20)
return AVERROR_INVALIDDATA;
- } else {
- av_log(s, AV_LOG_WARNING,
- "The bitrate %"PRIu64" is too large, resetting to 0.",
- bitrate);
- par->bit_rate = 0;
- }
- } else {
- par->bit_rate = bitrate;
+ for (i = 0; i < nb_streams; i++)
+ par->channels += par->extradata[8 + i * 20 + 17];
}
+ par->bit_rate = bitrate;
+
if (par->sample_rate <= 0) {
av_log(s, AV_LOG_ERROR,
"Invalid sample rate: %d\n", par->sample_rate);
@@ -157,7 +185,7 @@ int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb,
par->sample_rate = 0;
}
/* override bits_per_coded_sample for G.726 */
- if (par->codec_id == AV_CODEC_ID_ADPCM_G726)
+ if (par->codec_id == AV_CODEC_ID_ADPCM_G726 && par->sample_rate)
par->bits_per_coded_sample = par->bit_rate / par->sample_rate;
return 0;
@@ -216,12 +244,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);
@@ -236,7 +275,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");
@@ -244,16 +283,15 @@ int ff_read_riff_info(AVFormatContext *s, int64_t size)
}
AV_WL32(key, chunk_code);
+ // Work around VC++ 2015 Update 1 code-gen bug:
+ // https://connect.microsoft.com/VisualStudio/feedback/details/2291638
+ key[4] = 0;
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 e6b8411..c04d55c 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,19 +39,24 @@ 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(AVFormatContext *s, AVIOContext *pb,
- AVCodecParameters *par)
+ AVCodecParameters *par, 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,7 +71,10 @@ int ff_put_wav_header(AVFormatContext *s, AVIOContext *pb,
frame_size = av_get_audio_frame_duration2(par, par->block_align);
waveformatextensible = (par->channels > 2 && par->channel_layout) ||
+ par->channels == 1 && par->channel_layout && par->channel_layout != AV_CH_LAYOUT_MONO ||
+ par->channels == 2 && par->channel_layout && par->channel_layout != AV_CH_LAYOUT_STEREO ||
par->sample_rate > 48000 ||
+ par->codec_id == AV_CODEC_ID_EAC3 ||
av_get_bits_per_sample(par->codec_id) > 16;
if (waveformatextensible)
@@ -76,8 +84,10 @@ int ff_put_wav_header(AVFormatContext *s, AVIOContext *pb,
avio_wl16(pb, par->channels);
avio_wl32(pb, par->sample_rate);
- if (par->codec_id == AV_CODEC_ID_MP2 ||
- par->codec_id == AV_CODEC_ID_MP3 ||
+ if (par->codec_id == AV_CODEC_ID_ATRAC3 ||
+ par->codec_id == AV_CODEC_ID_G723_1 ||
+ par->codec_id == AV_CODEC_ID_MP2 ||
+ par->codec_id == AV_CODEC_ID_MP3 ||
par->codec_id == AV_CODEC_ID_GSM_MS) {
bps = 0;
} else {
@@ -96,11 +106,15 @@ int ff_put_wav_header(AVFormatContext *s, AVIOContext *pb,
}
if (par->codec_id == AV_CODEC_ID_MP2) {
- blkalign = frame_size;
+ blkalign = (144 * par->bit_rate - 1)/par->sample_rate + 1;
} else if (par->codec_id == AV_CODEC_ID_MP3) {
- blkalign = 576 * (par->sample_rate <= 24000 ? 1 : 2);
+ blkalign = 576 * (par->sample_rate <= (24000 + 32000)/2 ? 1 : 2);
} else if (par->codec_id == AV_CODEC_ID_AC3) {
blkalign = 3840; /* maximum bytes per frame */
+ } else if (par->codec_id == AV_CODEC_ID_AAC) {
+ blkalign = 768 * par->channels; /* maximum bytes per frame */
+ } else if (par->codec_id == AV_CODEC_ID_G723_1) {
+ blkalign = 24;
} else if (par->block_align != 0) { /* specified by the codec */
blkalign = par->block_align;
} else
@@ -112,6 +126,8 @@ int ff_put_wav_header(AVFormatContext *s, AVIOContext *pb,
par->codec_id == AV_CODEC_ID_PCM_F64LE ||
par->codec_id == AV_CODEC_ID_PCM_S16LE) {
bytespersec = par->sample_rate * blkalign;
+ } else if (par->codec_id == AV_CODEC_ID_G723_1) {
+ bytespersec = 800;
} else {
bytespersec = par->bit_rate / 8;
}
@@ -119,14 +135,12 @@ int ff_put_wav_header(AVFormatContext *s, AVIOContext *pb,
avio_wl16(pb, blkalign); /* block align */
avio_wl16(pb, bps); /* bits per sample */
if (par->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 (par->codec_id == AV_CODEC_ID_MP2) {
- hdrsize += 22;
/* fwHeadLayer */
bytestream_put_le16(&riff_extradata, 2);
/* dwHeadBitrate */
@@ -143,34 +157,46 @@ int ff_put_wav_header(AVFormatContext *s, AVIOContext *pb,
bytestream_put_le32(&riff_extradata, 0);
/* dwPTSHigh */
bytestream_put_le32(&riff_extradata, 0);
+ } else if (par->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 (par->codec_id == AV_CODEC_ID_GSM_MS ||
par->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV) {
- hdrsize += 2;
/* wSamplesPerBlock */
bytestream_put_le16(&riff_extradata, frame_size);
} else if (par->extradata_size) {
riff_extradata_start = par->extradata;
riff_extradata = par->extradata + par->extradata_size;
- hdrsize += par->extradata_size;
}
/* write WAVEFORMATEXTENSIBLE extensions */
if (waveformatextensible) {
- hdrsize += 22;
+ int write_channel_mask = !(flags & FF_PUT_WAV_HEADER_SKIP_CHANNELMASK) &&
+ (s->strict_std_compliance < FF_COMPLIANCE_NORMAL ||
+ par->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, par->channel_layout);
+ avio_wl32(pb, write_channel_mask ? par->channel_layout : 0);
/* GUID + next 3 */
+ if (par->codec_id == AV_CODEC_ID_EAC3) {
+ ff_put_guid(pb, ff_get_codec_guid(par->codec_id, ff_codec_wav_guids));
+ } else {
avio_wl32(pb, par->codec_tag);
avio_wl32(pb, 0x00100000);
avio_wl32(pb, 0xAA000080);
avio_wl32(pb, 0x719B3800);
- } else {
+ }
+ } else if ((flags & FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX) ||
+ par->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);
@@ -181,29 +207,59 @@ int ff_put_wav_header(AVFormatContext *s, AVIOContext *pb,
/* BITMAPINFOHEADER header */
void ff_put_bmp_header(AVIOContext *pb, AVCodecParameters *par,
- const AVCodecTag *tags, int for_asf)
+ int for_asf, int ignore_extradata)
{
- /* size */
- avio_wl32(pb, 40 + par->extradata_size);
+ int keep_height = par->extradata_size >= 9 &&
+ !memcmp(par->extradata + par->extradata_size - 9, "BottomUp", 9);
+ int extradata_size = par->extradata_size - 9*keep_height;
+ enum AVPixelFormat pix_fmt = par->format;
+ int pal_avi;
+
+ if (pix_fmt == AV_PIX_FMT_NONE && par->bits_per_coded_sample == 1)
+ pix_fmt = AV_PIX_FMT_MONOWHITE;
+ pal_avi = !for_asf &&
+ (pix_fmt == AV_PIX_FMT_PAL8 ||
+ pix_fmt == AV_PIX_FMT_MONOWHITE ||
+ pix_fmt == AV_PIX_FMT_MONOBLACK);
+
+ /* Size (not including the size of the color table or color masks) */
+ avio_wl32(pb, 40 + (ignore_extradata || pal_avi ? 0 : extradata_size));
avio_wl32(pb, par->width);
//We always store RGB TopDown
- avio_wl32(pb, par->codec_tag ? par->height : -par->height);
+ avio_wl32(pb, par->codec_tag || keep_height ? par->height : -par->height);
/* planes */
avio_wl16(pb, 1);
/* depth */
avio_wl16(pb, par->bits_per_coded_sample ? par->bits_per_coded_sample : 24);
/* compression type */
avio_wl32(pb, par->codec_tag);
- avio_wl32(pb, par->width * par->height * 3);
- avio_wl32(pb, 0);
+ avio_wl32(pb, (par->width * par->height * (par->bits_per_coded_sample ? par->bits_per_coded_sample : 24)+7) / 8);
avio_wl32(pb, 0);
avio_wl32(pb, 0);
+ /* Number of color indices in the color table that are used.
+ * A value of 0 means 2^biBitCount indices, but this doesn't work
+ * with Windows Media Player and files containing xxpc chunks. */
+ avio_wl32(pb, pal_avi ? 1 << par->bits_per_coded_sample : 0);
avio_wl32(pb, 0);
- avio_write(pb, par->extradata, par->extradata_size);
-
- if (!for_asf && par->extradata_size & 1)
- avio_w8(pb, 0);
+ if (!ignore_extradata) {
+ if (par->extradata_size) {
+ avio_write(pb, par->extradata, extradata_size);
+ if (!for_asf && extradata_size & 1)
+ avio_w8(pb, 0);
+ } else if (pal_avi) {
+ int i;
+ for (i = 0; i < 1 << par->bits_per_coded_sample; i++) {
+ /* Initialize 1 bpp palette to black & white */
+ if (i == 0 && pix_fmt == AV_PIX_FMT_MONOWHITE)
+ avio_wl32(pb, 0xffffff);
+ else if (i == 1 && pix_fmt == AV_PIX_FMT_MONOBLACK)
+ avio_wl32(pb, 0xffffff);
+ else
+ avio_wl32(pb, 0);
+ }
+ }
+ }
}
void ff_parse_specific_params(AVStream *st, int *au_rate,
@@ -214,6 +270,8 @@ void ff_parse_specific_params(AVStream *st, int *au_rate,
int audio_frame_size;
audio_frame_size = av_get_audio_frame_duration2(par, 0);
+ if (!audio_frame_size)
+ audio_frame_size = par->frame_size;
*au_ssize = par->block_align;
if (audio_frame_size && par->sample_rate) {
@@ -236,8 +294,8 @@ void ff_parse_specific_params(AVStream *st, int *au_rate,
void ff_riff_write_info_tag(AVIOContext *pb, const char *tag, const char *str)
{
- int len = strlen(str);
- if (len > 0) {
+ size_t len = strlen(str);
+ if (len > 0 && len < UINT32_MAX) {
len++;
ffio_wfourcc(pb, tag);
avio_wl32(pb, len);
@@ -248,7 +306,8 @@ void ff_riff_write_info_tag(AVIOContext *pb, const char *tag, const char *str)
}
static const char riff_tags[][5] = {
- "IARL", "IART", "ICMS", "ICMT", "ICOP", "ICRD", "ICRP", "IDIM", "IDPI",
+ "IARL", "IART", "IAS1", "IAS2", "IAS3", "IAS4", "IAS5", "IAS6", "IAS7",
+ "IAS8", "IAS9", "ICMS", "ICMT", "ICOP", "ICRD", "ICRP", "IDIM", "IDPI",
"IENG", "IGNR", "IKEY", "ILGT", "ILNG", "IMED", "INAM", "IPLT", "IPRD",
"IPRT", "ITRK", "ISBJ", "ISFT", "ISHP", "ISMP", "ISRC", "ISRF", "ITCH",
{ 0 }
@@ -286,3 +345,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 *ff_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 0e8d346..eb1682d 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->codecpar->extradata_size += back_size;
- st->codecpar->extradata = av_mallocz(st->codecpar->extradata_size +
- AV_INPUT_BUFFER_PADDING_SIZE);
- if(!st->codecpar->extradata)
+ if(ff_get_extradata(s, st->codecpar, pb, st->codecpar->extradata_size) < 0)
return AVERROR(ENOMEM);
- if(avio_read(pb,st->codecpar->extradata,st->codecpar->extradata_size) !=
- st->codecpar->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;
@@ -175,12 +170,21 @@ static av_cold int rl2_read_header(AVFormatContext *s)
}
/** read offset and size tables */
- for(i=0; i < frame_count;i++)
+ for(i=0; i < frame_count;i++) {
+ if (avio_feof(pb))
+ return AVERROR_INVALIDDATA;
chunk_size[i] = avio_rl32(pb);
- for(i=0; i < frame_count;i++)
+ }
+ for(i=0; i < frame_count;i++) {
+ if (avio_feof(pb))
+ return AVERROR_INVALIDDATA;
chunk_offset[i] = avio_rl32(pb);
- for(i=0; i < frame_count;i++)
+ }
+ for(i=0; i < frame_count;i++) {
+ if (avio_feof(pb))
+ return AVERROR_INVALIDDATA;
audio_size[i] = avio_rl32(pb) & 0xFFFF;
+ }
/** build the sample index */
for(i=0;i<frame_count;i++){
@@ -235,7 +239,7 @@ static int rl2_read_packet(AVFormatContext *s,
}
if(stream_id == -1)
- return AVERROR(EIO);
+ return AVERROR_EOF;
++rl2->index_pos[stream_id];
diff --git a/libavformat/rm.c b/libavformat/rm.c
index 761be3f..52c7ccc 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
*/
@@ -43,5 +43,6 @@ const AVCodecTag ff_rm_codec_tags[] = {
{ AV_CODEC_ID_AAC, MKTAG('r','a','a','c') },
{ AV_CODEC_ID_AAC, MKTAG('r','a','c','p') },
{ AV_CODEC_ID_RALF, MKTAG('L','S','D',':') },
+ { AV_CODEC_ID_CLEARVIDEO, MKTAG('C','L','V','1') },
{ AV_CODEC_ID_NONE },
};
diff --git a/libavformat/rm.h b/libavformat/rm.h
index 3aa1773..7b080e2 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 2e93f1d..d6d7d9c 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"
@@ -61,8 +63,11 @@ typedef struct RMDemuxContext {
int remaining_len;
int audio_stream_num; ///< Stream number for audio packets
int audio_pkt_cnt; ///< Output packet counter
+ int data_end;
} RMDemuxContext;
+static int rm_read_close(AVFormatContext *s);
+
static inline void get_strl(AVIOContext *pb, char *buf, int buf_size, int len)
{
int i;
@@ -82,16 +87,14 @@ static void get_str8(AVIOContext *pb, char *buf, int buf_size)
get_strl(pb, buf, buf_size, avio_r8(pb));
}
-static int rm_read_extradata(AVIOContext *pb, AVCodecParameters *par, unsigned size)
+static int rm_read_extradata(AVFormatContext *s, AVIOContext *pb, AVCodecParameters *par, unsigned size)
{
- if (size >= 1<<24)
+ if (size >= 1<<24) {
+ av_log(s, AV_LOG_ERROR, "extradata size %u too large\n", size);
return -1;
- par->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!par->extradata)
+ }
+ if (ff_get_extradata(s, par, pb, size) < 0)
return AVERROR(ENOMEM);
- par->extradata_size = avio_read(pb, par->extradata, size);
- if (par->extradata_size != size)
- return AVERROR(EIO);
return 0;
}
@@ -99,6 +102,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);
@@ -130,9 +134,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")
@@ -142,6 +149,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->codecpar->bit_rate = 8LL * bytes_per_minute / 60;
st->codecpar->sample_rate = 8000;
st->codecpar->channels = 1;
st->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
@@ -151,6 +160,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 */
@@ -160,7 +170,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->codecpar->bit_rate = 8LL * bytes_per_minute / 60;
+ }
avio_rb32(pb); /* ??? */
ast->sub_packet_h = sub_packet_h = avio_rb16(pb); /* 1 */
st->codecpar->block_align= avio_rb16(pb); /* frame size */
@@ -177,6 +191,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 */
@@ -192,6 +207,7 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb,
break;
case AV_CODEC_ID_RA_288:
st->codecpar->extradata_size= 0;
+ av_freep(&st->codecpar->extradata);
ast->audio_framesize = st->codecpar->block_align;
st->codecpar->block_align = coded_framesize;
break;
@@ -199,13 +215,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 + AV_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 + AV_INPUT_BUFFER_PADDING_SIZE <= (unsigned)codecdata_length){
+ av_log(s, AV_LOG_ERROR, "codecdata_length too large\n");
+ return -1;
+ }
}
ast->audio_framesize = st->codecpar->block_align;
@@ -216,6 +236,7 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb,
return -1;
}
st->codecpar->block_align = ff_sipr_subpk_size[flavor];
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
} else {
if(sub_packet_size <= 0){
av_log(s, AV_LOG_ERROR, "sub_packet_size is invalid\n");
@@ -223,8 +244,9 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb,
}
st->codecpar->block_align = ast->sub_packet_size;
}
- if ((ret = rm_read_extradata(pb, st->codecpar, codecdata_length)) < 0)
+ if ((ret = rm_read_extradata(s, pb, st->codecpar, codecdata_length)) < 0)
return ret;
+
break;
case AV_CODEC_ID_AAC:
avio_rb16(pb); avio_r8(pb);
@@ -237,32 +259,28 @@ static int rm_read_audio_stream_info(AVFormatContext *s, AVIOContext *pb,
}
if (codecdata_length >= 1) {
avio_r8(pb);
- if ((ret = rm_read_extradata(pb, st->codecpar, codecdata_length - 1)) < 0)
+ if ((ret = rm_read_extradata(s, pb, st->codecpar, codecdata_length - 1)) < 0)
return ret;
}
break;
}
- if (ast->deint_id == DEINT_ID_INT4 ||
- ast->deint_id == DEINT_ID_GENR ||
- ast->deint_id == DEINT_ID_SIPR) {
- if (st->codecpar->block_align <= 0 ||
- ast->audio_framesize * sub_packet_h > (unsigned)INT_MAX ||
- ast->audio_framesize * sub_packet_h < st->codecpar->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:
@@ -270,9 +288,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->codecpar->block_align <= 0 ||
+ ast->audio_framesize * sub_packet_h > (unsigned)INT_MAX ||
+ ast->audio_framesize * sub_packet_h < st->codecpar->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);
@@ -286,29 +314,62 @@ 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;
+ if (codec_data_size == 0)
+ return 0;
+
avpriv_set_pts_info(st, 64, 1, 1000);
codec_pos = avio_tell(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))
return -1;
} else if (v == MKBETAG('L', 'S', 'D', ':')) {
avio_seek(pb, -4, SEEK_CUR);
- if ((ret = rm_read_extradata(pb, st->codecpar, codec_data_size)) < 0)
+ if ((ret = rm_read_extradata(s, pb, st->codecpar, codec_data_size)) < 0)
return ret;
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_tag = AV_RL32(st->codecpar->extradata);
st->codecpar->codec_id = ff_codec_get_id(ff_rm_codec_tags,
- st->codecpar->codec_tag);
+ st->codecpar->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')) {
@@ -319,7 +380,8 @@ int ff_rm_read_mdpr_codecdata(AVFormatContext *s, AVIOContext *pb,
st->codecpar->codec_tag = avio_rl32(pb);
st->codecpar->codec_id = ff_codec_get_id(ff_rm_codec_tags,
st->codecpar->codec_tag);
- av_log(s, AV_LOG_TRACE, "%"PRIX32" %X\n", st->codecpar->codec_tag, MKTAG('R', 'V', '2', '0'));
+ av_log(s, AV_LOG_TRACE, "%"PRIX32" %X\n",
+ st->codecpar->codec_tag, MKTAG('R', 'V', '2', '0'));
if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
goto fail1;
st->codecpar->width = avio_rb16(pb);
@@ -330,12 +392,15 @@ int ff_rm_read_mdpr_codecdata(AVFormatContext *s, AVIOContext *pb,
st->need_parsing = AVSTREAM_PARSE_TIMESTAMPS;
fps = avio_rb32(pb);
- if ((ret = rm_read_extradata(pb, st->codecpar, codec_data_size - (avio_tell(pb) - codec_pos))) < 0)
+ if ((ret = rm_read_extradata(s, pb, st->codecpar, codec_data_size - (avio_tell(pb) - codec_pos))) < 0)
return ret;
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;
@@ -345,7 +410,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;
}
@@ -423,6 +492,47 @@ static int rm_read_header_old(AVFormatContext *s)
return rm_read_audio_stream_info(s, s->pb, st, st->priv_data, 1);
}
+static int rm_read_multi(AVFormatContext *s, AVIOContext *pb,
+ AVStream *st, char *mime)
+{
+ int number_of_streams = avio_rb16(pb);
+ int number_of_mdpr;
+ int i, ret;
+ unsigned size2;
+ 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 (%d) MDPR", number_of_mdpr);
+ }
+ for (i = 0; i < number_of_mdpr; i++) {
+ AVStream *st2;
+ if (i > 0) {
+ st2 = avformat_new_stream(s, NULL);
+ if (!st2) {
+ ret = AVERROR(ENOMEM);
+ return ret;
+ }
+ st2->id = st->id + (i<<16);
+ st2->codecpar->bit_rate = st->codecpar->bit_rate;
+ st2->start_time = st->start_time;
+ st2->duration = st->duration;
+ st2->codecpar->codec_type = AVMEDIA_TYPE_DATA;
+ st2->priv_data = ff_rm_alloc_rmstream();
+ if (!st2->priv_data)
+ return AVERROR(ENOMEM);
+ } else
+ st2 = st;
+
+ size2 = avio_rb32(pb);
+ ret = ff_rm_read_mdpr_codecdata(s, s->pb, st2, st2->priv_data,
+ size2, mime);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
static int rm_read_header(AVFormatContext *s)
{
RMDemuxContext *rm = s->priv_data;
@@ -432,8 +542,11 @@ 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;
+ unsigned size, v;
+ int64_t codec_pos;
tag = avio_rl32(pb);
if (tag == MKTAG('.', 'r', 'a', 0xfd)) {
@@ -447,20 +560,15 @@ 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);
- av_log(s, AV_LOG_TRACE, "tag=%c%c%c%c (%08x) size=%d\n",
- (tag ) & 0xff,
- (tag >> 8) & 0xff,
- (tag >> 16) & 0xff,
- (tag >> 24) & 0xff,
- tag,
- tag_size);
+ av_log(s, AV_LOG_TRACE, "tag=%s size=%d\n",
+ av_fourcc2str(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 */
@@ -469,7 +577,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 */
@@ -481,8 +590,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->codecpar->bit_rate = avio_rb32(pb); /* bit rate */
@@ -493,15 +604,32 @@ 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->codecpar->codec_type = AVMEDIA_TYPE_DATA;
st->priv_data = ff_rm_alloc_rmstream();
if (!st->priv_data)
return AVERROR(ENOMEM);
- if (ff_rm_read_mdpr_codecdata(s, s->pb, st, st->priv_data,
- avio_rb32(pb)) < 0)
- return -1;
+
+ size = avio_rb32(pb);
+ codec_pos = avio_tell(pb);
+
+ ffio_ensure_seekback(pb, 4);
+ v = avio_rb32(pb);
+ if (v == MKBETAG('M', 'L', 'T', 'I')) {
+ ret = rm_read_multi(s, s->pb, st, mime);
+ if (ret < 0)
+ goto fail;
+ avio_seek(pb, codec_pos + size, SEEK_SET);
+ } else {
+ avio_skip(pb, -4);
+ if (ff_rm_read_mdpr_codecdata(s, s->pb, st, st->priv_data,
+ size, mime) < 0)
+ goto fail;
+ }
+
break;
case MKTAG('D', 'A', 'T', 'A'):
goto header_end;
@@ -527,6 +655,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)
@@ -548,17 +680,19 @@ 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;
+ int mlti_id;
*pos= avio_tell(pb) - 3;
if(rm->remaining_len > 0){
num= rm->current_stream;
+ mlti_id = 0;
len= rm->remaining_len;
*timestamp = AV_NOPTS_VALUE;
*flags= 0;
@@ -594,12 +728,13 @@ static int sync(AVFormatContext *s, int64_t *timestamp, int *flags, int *stream_
num = avio_rb16(pb);
*timestamp = avio_rb32(pb);
- avio_r8(pb); /* reserved */
+ mlti_id = (avio_r8(pb)>>1)-1<<16;
+ mlti_id = FFMAX(mlti_id, 0);
*flags = avio_r8(pb); /* flags */
}
for(i=0;i<s->nb_streams;i++) {
st = s->streams[i];
- if (num == st->id)
+ if (mlti_id + num == st->id)
break;
}
if (i == s->nb_streams) {
@@ -621,8 +756,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 silence compiler warning
int type;
+ int ret;
hdr = avio_r8(pb); len--;
type = hdr >> 6;
@@ -635,34 +772,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_packet_unref(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_packet_unref(&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;
@@ -671,12 +821,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;
@@ -716,17 +872,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->codecpar->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->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
if ((ast->deint_id == DEINT_ID_GENR) ||
(ast->deint_id == DEINT_ID_INT4) ||
@@ -746,14 +914,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;
}
@@ -764,6 +932,10 @@ ff_rm_parse_packet (AVFormatContext *s, AVIOContext *pb,
ast->sub_packet_cnt = 0;
rm->audio_stream_num = st->index;
+ if (st->codecpar->block_align <= 0) {
+ av_log(s, AV_LOG_ERROR, "Invalid block alignment %d\n", st->codecpar->block_align);
+ return AVERROR_INVALIDDATA;
+ }
rm->audio_pkt_cnt = h * w / st->codecpar->block_align;
} else if ((ast->deint_id == DEINT_ID_VBRF) ||
(ast->deint_id == DEINT_ID_VBRS)) {
@@ -778,11 +950,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;
@@ -799,12 +974,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->codecpar->block_align);
if (ret < 0)
return ret;
@@ -826,7 +1003,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;
@@ -835,7 +1012,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) {
@@ -849,16 +1028,20 @@ 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 (avio_feof(s->pb))
+ return AVERROR_EOF;
+ if (len <= 0)
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)
@@ -910,13 +1093,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;
@@ -942,6 +1127,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"),
@@ -951,6 +1148,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 = {
@@ -960,3 +1158,242 @@ AVInputFormat ff_rdt_demuxer = {
.read_close = rm_read_close,
.flags = AVFMT_NOFILE,
};
+
+static int ivr_probe(AVProbeData *p)
+{
+ if (memcmp(p->buf, ".R1M\x0\x1\x1", 7) &&
+ memcmp(p->buf, ".REC", 4))
+ return 0;
+
+ return AVPROBE_SCORE_MAX;
+}
+
+static int ivr_read_header(AVFormatContext *s)
+{
+ unsigned tag, type, len, tlen, value;
+ int i, j, n, count, nb_streams = 0, ret;
+ uint8_t key[256], val[256];
+ AVIOContext *pb = s->pb;
+ AVStream *st;
+ int64_t pos, offset, temp;
+
+ pos = avio_tell(pb);
+ tag = avio_rl32(pb);
+ if (tag == MKTAG('.','R','1','M')) {
+ if (avio_rb16(pb) != 1)
+ return AVERROR_INVALIDDATA;
+ if (avio_r8(pb) != 1)
+ return AVERROR_INVALIDDATA;
+ len = avio_rb32(pb);
+ avio_skip(pb, len);
+ avio_skip(pb, 5);
+ temp = avio_rb64(pb);
+ while (!avio_feof(pb) && temp) {
+ offset = temp;
+ temp = avio_rb64(pb);
+ }
+ avio_skip(pb, offset - avio_tell(pb));
+ if (avio_r8(pb) != 1)
+ return AVERROR_INVALIDDATA;
+ len = avio_rb32(pb);
+ avio_skip(pb, len);
+ if (avio_r8(pb) != 2)
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, 16);
+ pos = avio_tell(pb);
+ tag = avio_rl32(pb);
+ }
+
+ if (tag != MKTAG('.','R','E','C'))
+ return AVERROR_INVALIDDATA;
+
+ if (avio_r8(pb) != 0)
+ return AVERROR_INVALIDDATA;
+ count = avio_rb32(pb);
+ for (i = 0; i < count; i++) {
+ if (avio_feof(pb))
+ return AVERROR_INVALIDDATA;
+
+ type = avio_r8(pb);
+ tlen = avio_rb32(pb);
+ avio_get_str(pb, tlen, key, sizeof(key));
+ len = avio_rb32(pb);
+ if (type == 5) {
+ avio_get_str(pb, len, val, sizeof(val));
+ av_log(s, AV_LOG_DEBUG, "%s = '%s'\n", key, val);
+ } else if (type == 4) {
+ av_log(s, AV_LOG_DEBUG, "%s = '0x", key);
+ for (j = 0; j < len; j++) {
+ if (avio_feof(pb))
+ return AVERROR_INVALIDDATA;
+ av_log(s, AV_LOG_DEBUG, "%X", avio_r8(pb));
+ }
+ av_log(s, AV_LOG_DEBUG, "'\n");
+ } else if (len == 4 && type == 3 && !strncmp(key, "StreamCount", tlen)) {
+ nb_streams = value = avio_rb32(pb);
+ } else if (len == 4 && type == 3) {
+ value = avio_rb32(pb);
+ av_log(s, AV_LOG_DEBUG, "%s = %d\n", key, value);
+ } else {
+ av_log(s, AV_LOG_DEBUG, "Skipping unsupported key: %s\n", key);
+ avio_skip(pb, len);
+ }
+ }
+
+ for (n = 0; n < nb_streams; n++) {
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+ st->priv_data = ff_rm_alloc_rmstream();
+ if (!st->priv_data)
+ return AVERROR(ENOMEM);
+
+ if (avio_r8(pb) != 1)
+ return AVERROR_INVALIDDATA;
+
+ count = avio_rb32(pb);
+ for (i = 0; i < count; i++) {
+ if (avio_feof(pb))
+ return AVERROR_INVALIDDATA;
+
+ type = avio_r8(pb);
+ tlen = avio_rb32(pb);
+ avio_get_str(pb, tlen, key, sizeof(key));
+ len = avio_rb32(pb);
+ if (type == 5) {
+ avio_get_str(pb, len, val, sizeof(val));
+ av_log(s, AV_LOG_DEBUG, "%s = '%s'\n", key, val);
+ } else if (type == 4 && !strncmp(key, "OpaqueData", tlen)) {
+ ret = ffio_ensure_seekback(pb, 4);
+ if (ret < 0)
+ return ret;
+ if (avio_rb32(pb) == MKBETAG('M', 'L', 'T', 'I')) {
+ ret = rm_read_multi(s, pb, st, NULL);
+ } else {
+ avio_seek(pb, -4, SEEK_CUR);
+ ret = ff_rm_read_mdpr_codecdata(s, pb, st, st->priv_data, len, NULL);
+ }
+
+ if (ret < 0)
+ return ret;
+ } else if (type == 4) {
+ int j;
+
+ av_log(s, AV_LOG_DEBUG, "%s = '0x", key);
+ for (j = 0; j < len; j++)
+ av_log(s, AV_LOG_DEBUG, "%X", avio_r8(pb));
+ av_log(s, AV_LOG_DEBUG, "'\n");
+ } else if (len == 4 && type == 3 && !strncmp(key, "Duration", tlen)) {
+ st->duration = avio_rb32(pb);
+ } else if (len == 4 && type == 3) {
+ value = avio_rb32(pb);
+ av_log(s, AV_LOG_DEBUG, "%s = %d\n", key, value);
+ } else {
+ av_log(s, AV_LOG_DEBUG, "Skipping unsupported key: %s\n", key);
+ avio_skip(pb, len);
+ }
+ }
+ }
+
+ if (avio_r8(pb) != 6)
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, 12);
+ avio_skip(pb, avio_rb64(pb) + pos - avio_tell(s->pb));
+ if (avio_r8(pb) != 8)
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, 8);
+
+ return 0;
+}
+
+static int ivr_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ RMDemuxContext *rm = s->priv_data;
+ int ret = AVERROR_EOF, opcode;
+ AVIOContext *pb = s->pb;
+ unsigned size, index;
+ int64_t pos, pts;
+
+ if (avio_feof(pb) || rm->data_end)
+ return AVERROR_EOF;
+
+ pos = avio_tell(pb);
+
+ for (;;) {
+ if (rm->audio_pkt_cnt) {
+ // If there are queued audio packet return them first
+ AVStream *st;
+
+ st = s->streams[rm->audio_stream_num];
+ ret = ff_rm_retrieve_cache(s, pb, st, st->priv_data, pkt);
+ if (ret < 0) {
+ return ret;
+ }
+ } else {
+ if (rm->remaining_len) {
+ avio_skip(pb, rm->remaining_len);
+ rm->remaining_len = 0;
+ }
+
+ if (avio_feof(pb))
+ return AVERROR_EOF;
+
+ opcode = avio_r8(pb);
+ if (opcode == 2) {
+ AVStream *st;
+ int seq = 1;
+
+ pts = avio_rb32(pb);
+ index = avio_rb16(pb);
+ if (index >= s->nb_streams)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(pb, 4);
+ size = avio_rb32(pb);
+ avio_skip(pb, 4);
+
+ if (size < 1 || size > INT_MAX/4) {
+ av_log(s, AV_LOG_ERROR, "size %u is invalid\n", size);
+ return AVERROR_INVALIDDATA;
+ }
+
+ st = s->streams[index];
+ ret = ff_rm_parse_packet(s, pb, st, st->priv_data, size, pkt,
+ &seq, 0, pts);
+ if (ret < -1) {
+ return ret;
+ } else if (ret) {
+ continue;
+ }
+
+ pkt->pos = pos;
+ pkt->pts = pts;
+ pkt->stream_index = index;
+ } else if (opcode == 7) {
+ pos = avio_rb64(pb);
+ if (!pos) {
+ rm->data_end = 1;
+ return AVERROR_EOF;
+ }
+ } else {
+ av_log(s, AV_LOG_ERROR, "Unsupported opcode=%d at %"PRIX64"\n", opcode, avio_tell(pb) - 1);
+ return AVERROR(EIO);
+ }
+ }
+
+ break;
+ }
+
+ return ret;
+}
+
+AVInputFormat ff_ivr_demuxer = {
+ .name = "ivr",
+ .long_name = NULL_IF_CONFIG_SMALL("IVR (Internet Video Recording)"),
+ .priv_data_size = sizeof(RMDemuxContext),
+ .read_probe = ivr_probe,
+ .read_header = ivr_read_header,
+ .read_packet = ivr_read_packet,
+ .read_close = rm_read_close,
+ .extensions = "ivr",
+};
diff --git a/libavformat/rmenc.c b/libavformat/rmenc.c
index 6cbe700..3bff4da 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"
@@ -29,7 +29,7 @@ typedef struct StreamInfo {
int packet_max_size;
/* codec related output */
int bit_rate;
- float frame_rate;
+ AVRational frame_rate;
int nb_frames; /* current frame number */
int total_frames; /* total number of frames */
int num;
@@ -72,14 +72,12 @@ static int rv10_write_header(AVFormatContext *ctx,
RMMuxContext *rm = ctx->priv_data;
AVIOContext *s = ctx->pb;
StreamInfo *stream;
- unsigned char *data_offset_ptr, *start_ptr;
const char *desc, *mimetype;
int nb_packets, packet_total_size, packet_max_size, size, packet_avg_size, i;
- int bit_rate, v, duration, flags, data_pos;
+ int bit_rate, v, duration, flags;
+ int data_offset;
AVDictionaryEntry *tag;
- start_ptr = s->buf_ptr;
-
ffio_wfourcc(s, ".RMF");
avio_wb32(s,18); /* header size */
avio_wb16(s,0);
@@ -102,7 +100,7 @@ static int rv10_write_header(AVFormatContext *ctx,
nb_packets += stream->nb_packets;
packet_total_size += stream->packet_total_size;
/* select maximum duration */
- v = (int) (1000.0 * (float)stream->total_frames / stream->frame_rate);
+ v = av_rescale_q_rnd(stream->total_frames, (AVRational){1000, 1}, stream->frame_rate, AV_ROUND_ZERO);
if (v > duration)
duration = v;
}
@@ -119,7 +117,7 @@ static int rv10_write_header(AVFormatContext *ctx,
avio_wb32(s, BUFFER_DURATION); /* preroll */
avio_wb32(s, index_pos); /* index offset */
/* computation of data the data offset */
- data_offset_ptr = s->buf_ptr;
+ data_offset = avio_tell(s);
avio_wb32(s, 0); /* data offset : will be patched after */
avio_wb16(s, ctx->nb_streams); /* num streams */
flags = 1 | 2; /* save allowed & perfect play */
@@ -178,7 +176,7 @@ static int rv10_write_header(AVFormatContext *ctx,
if (!(s->seekable & AVIO_SEEKABLE_NORMAL) || !stream->total_frames)
avio_wb32(s, (int)(3600 * 1000));
else
- avio_wb32(s, (int)(stream->total_frames * 1000 / stream->frame_rate));
+ avio_wb32(s, av_rescale_q_rnd(stream->total_frames, (AVRational){1000, 1}, stream->frame_rate, AV_ROUND_ZERO));
put_str8(s, desc);
put_str8(s, mimetype);
avio_wb32(s, codec_data_size);
@@ -222,8 +220,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->par->bit_rate / 8 * 60); /* bytes per minute */
+ avio_wb32(s, stream->par->bit_rate / 8 * 60); /* bytes per minute */
avio_wb16(s, 0x01);
/* frame length : seems to be very important */
avio_wb16(s, coded_frame_size);
@@ -253,9 +251,15 @@ static int rv10_write_header(AVFormatContext *ctx,
ffio_wfourcc(s,"RV20");
avio_wb16(s, stream->par->width);
avio_wb16(s, stream->par->height);
- avio_wb16(s, (int) stream->frame_rate); /* frames per seconds ? */
+
+ if (stream->frame_rate.num / stream->frame_rate.den > 65535) {
+ av_log(s, AV_LOG_ERROR, "Frame rate %d is too high\n", stream->frame_rate.num / stream->frame_rate.den);
+ return AVERROR(EINVAL);
+ }
+
+ avio_wb16(s, stream->frame_rate.num / stream->frame_rate.den); /* frames per seconds ? */
avio_wb32(s,0); /* unknown meaning */
- avio_wb16(s, (int) stream->frame_rate); /* unknown meaning */
+ avio_wb16(s, stream->frame_rate.num / stream->frame_rate.den); /* unknown meaning */
avio_wb32(s,0); /* unknown meaning */
avio_wb16(s, 8); /* unknown meaning */
/* Seems to be the codec version: only use basic H.263. The next
@@ -270,12 +274,11 @@ static int rv10_write_header(AVFormatContext *ctx,
}
/* patch data offset field */
- data_pos = s->buf_ptr - start_ptr;
- rm->data_pos = data_pos;
- data_offset_ptr[0] = data_pos >> 24;
- data_offset_ptr[1] = data_pos >> 16;
- data_offset_ptr[2] = data_pos >> 8;
- data_offset_ptr[3] = data_pos;
+ rm->data_pos = avio_tell(s);
+ if (avio_seek(s, data_offset, SEEK_SET) >= 0) {
+ avio_wb32(s, rm->data_pos);
+ avio_seek(s, rm->data_pos, SEEK_SET);
+ }
/* data stream */
ffio_wfourcc(s, "DATA");
@@ -301,7 +304,7 @@ static void write_packet_header(AVFormatContext *ctx, StreamInfo *stream,
avio_wb16(s,0); /* version */
avio_wb16(s,length + 12);
avio_wb16(s, stream->num); /* stream number */
- timestamp = (1000 * (float)stream->nb_frames) / stream->frame_rate;
+ timestamp = av_rescale_q_rnd(stream->nb_frames, (AVRational){1000, 1}, stream->frame_rate, AV_ROUND_ZERO);
avio_wb32(s, timestamp); /* timestamp */
avio_w8(s, 0); /* reserved */
avio_w8(s, key_frame ? 2 : 0); /* flags */
@@ -314,6 +317,11 @@ static int rm_write_header(AVFormatContext *s)
int n;
AVCodecParameters *par;
+ 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];
int frame_size;
@@ -330,7 +338,7 @@ static int rm_write_header(AVFormatContext *s)
case AVMEDIA_TYPE_AUDIO:
rm->audio_stream = stream;
frame_size = av_get_audio_frame_duration2(par, 0);
- stream->frame_rate = (float)par->sample_rate / (float)frame_size;
+ stream->frame_rate = (AVRational){par->sample_rate, frame_size};
/* XXX: dummy values */
stream->packet_max_size = 1024;
stream->nb_packets = 0;
@@ -339,7 +347,7 @@ static int rm_write_header(AVFormatContext *s)
case AVMEDIA_TYPE_VIDEO:
rm->video_stream = stream;
// TODO: should be avg_frame_rate
- stream->frame_rate = (float)st->time_base.den / (float)st->time_base.num;
+ stream->frame_rate = av_inv_q(st->time_base);
/* XXX: dummy values */
stream->packet_max_size = 4096;
stream->nb_packets = 0;
@@ -390,8 +398,8 @@ static int rm_write_video(AVFormatContext *s, const uint8_t *buf, int size, int
/* Well, I spent some time finding the meaning of these bits. I am
not sure I understood everything, but it works !! */
if (size > MAX_PACKET_SIZE) {
- avpriv_report_missing_feature(s, "Muxing packets larger than 64 kB");
- return AVERROR(ENOSYS);
+ av_log(s, AV_LOG_ERROR, "Muxing packets larger than 64 kB (%d) is not supported\n", size);
+ return AVERROR_PATCHWELCOME;
}
write_packet_header(s, stream, size + 7 + (size >= 0x4000)*4, key_frame);
/* bit 7: '1' if final packet of a frame converted in several packets */
diff --git a/libavformat/rmsipr.c b/libavformat/rmsipr.c
index 89fbd0e..14ec8ea 100644
--- a/libavformat/rmsipr.c
+++ b/libavformat/rmsipr.c
@@ -1,20 +1,20 @@
/*
* tables and functions for demuxing SIPR audio muxed RealMedia style
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rmsipr.h b/libavformat/rmsipr.h
index c8d3d0a..2837336 100644
--- a/libavformat/rmsipr.h
+++ b/libavformat/rmsipr.h
@@ -1,20 +1,20 @@
/*
* tables and functions for demuxing SIPR audio muxed RealMedia style
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rpl.c b/libavformat/rpl.c
index 6f6119c..d373600 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->codecpar->codec_id = AV_CODEC_ID_ESCAPE130;
break;
default:
- av_log(s, AV_LOG_WARNING,
- "RPL video format %"PRIu32" not supported yet!\n",
- vst->codecpar->codec_tag);
+ avpriv_report_missing_feature(s, "Video format %s",
+ av_fourcc2str(vst->codecpar->codec_tag));
vst->codecpar->codec_id = AV_CODEC_ID_NONE;
}
@@ -222,11 +221,9 @@ static int rpl_read_header(AVFormatContext *s)
}
break;
}
- if (ast->codecpar->codec_id == AV_CODEC_ID_NONE) {
- av_log(s, AV_LOG_WARNING,
- "RPL audio format %"PRId32" not supported yet!\n",
- audio_format);
- }
+ if (ast->codecpar->codec_id == AV_CODEC_ID_NONE)
+ avpriv_request_sample(s, "Audio format %"PRId32,
+ audio_format);
avpriv_set_pts_info(ast, 32, 1, ast->codecpar->bit_rate);
} else {
for (i = 0; i < 3; i++)
@@ -236,8 +233,8 @@ static int rpl_read_header(AVFormatContext *s)
rpl->frames_per_chunk = read_line_and_int(pb, &error); // video frames per chunk
if (rpl->frames_per_chunk > 1 && vst->codecpar->codec_tag != 124)
av_log(s, AV_LOG_WARNING,
- "Don't know how to split frames for video format %"PRIu32". "
- "Video stream will be broken!\n", vst->codecpar->codec_tag);
+ "Don't know how to split frames for video format %s. "
+ "Video stream will be broken!\n", av_fourcc2str(vst->codecpar->codec_tag));
number_of_chunks = read_line_and_int(pb, &error); // number of chunks in the file
// The number in the header is actually the index of the last chunk.
@@ -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_packet_unref(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_packet_unref(pkt);
return AVERROR(EIO);
diff --git a/libavformat/rsd.c b/libavformat/rsd.c
new file mode 100644
index 0000000..1c99f8c
--- /dev/null
+++ b/libavformat/rsd.c
@@ -0,0 +1,221 @@
+/*
+ * 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_PSX, MKTAG('V','A','G',' ') },
+ { AV_CODEC_ID_ADPCM_THP_LE, MKTAG('G','A','D','P') },
+ { AV_CODEC_ID_ADPCM_THP, MKTAG('W','A','D','P') },
+ { AV_CODEC_ID_ADPCM_IMA_RAD, MKTAG('R','A','D','P') },
+ { AV_CODEC_ID_ADPCM_IMA_WAV, MKTAG('X','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_XMA2, MKTAG('X','M','A',' ') },
+ { AV_CODEC_ID_NONE, 0 },
+};
+
+static const uint32_t rsd_unsupported_tags[] = {
+ MKTAG('O','G','G',' '),
+};
+
+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, ret, version, start = 0x800;
+ AVCodecParameters *par;
+ AVStream *st = avformat_new_stream(s, NULL);
+
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avio_skip(pb, 3); // "RSD"
+ version = avio_r8(pb) - '0';
+
+ par = st->codecpar;
+ par->codec_type = AVMEDIA_TYPE_AUDIO;
+ par->codec_tag = avio_rl32(pb);
+ par->codec_id = ff_codec_get_id(rsd_tags, par->codec_tag);
+ if (!par->codec_id) {
+ const char *tag_buf = av_fourcc2str(par->codec_tag);
+ for (i=0; i < FF_ARRAY_ELEMS(rsd_unsupported_tags); i++) {
+ if (par->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;
+ }
+
+ par->channels = avio_rl32(pb);
+ if (par->channels <= 0 || par->channels > INT_MAX / 36) {
+ av_log(s, AV_LOG_ERROR, "Invalid number of channels: %d\n", par->channels);
+ return AVERROR_INVALIDDATA;
+ }
+
+ avio_skip(pb, 4); // Bit depth
+ par->sample_rate = avio_rl32(pb);
+ if (!par->sample_rate)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(pb, 4); // Unknown
+
+ switch (par->codec_id) {
+ case AV_CODEC_ID_XMA2:
+ par->block_align = 2048;
+ ff_alloc_extradata(par, 34);
+ if (!par->extradata)
+ return AVERROR(ENOMEM);
+ memset(par->extradata, 0, 34);
+ break;
+ case AV_CODEC_ID_ADPCM_PSX:
+ par->block_align = 16 * par->channels;
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL)
+ st->duration = av_get_audio_frame_duration2(par, avio_size(pb) - start);
+ break;
+ case AV_CODEC_ID_ADPCM_IMA_RAD:
+ par->block_align = 20 * par->channels;
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL)
+ st->duration = av_get_audio_frame_duration2(par, avio_size(pb) - start);
+ break;
+ case AV_CODEC_ID_ADPCM_IMA_WAV:
+ if (version == 2)
+ start = avio_rl32(pb);
+
+ par->bits_per_coded_sample = 4;
+ par->block_align = 36 * par->channels;
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL)
+ st->duration = av_get_audio_frame_duration2(par, avio_size(pb) - start);
+ break;
+ case AV_CODEC_ID_ADPCM_THP_LE:
+ /* RSD3GADP is mono, so only alloc enough memory
+ to store the coeff table for a single channel. */
+
+ start = avio_rl32(pb);
+
+ if ((ret = ff_get_extradata(s, par, s->pb, 32)) < 0)
+ return ret;
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL)
+ st->duration = av_get_audio_frame_duration2(par, avio_size(pb) - start);
+ break;
+ case AV_CODEC_ID_ADPCM_THP:
+ par->block_align = 8 * par->channels;
+ avio_skip(s->pb, 0x1A4 - avio_tell(s->pb));
+
+ if ((ret = ff_alloc_extradata(st->codecpar, 32 * par->channels)) < 0)
+ return ret;
+
+ for (i = 0; i < par->channels; i++) {
+ avio_read(s->pb, st->codecpar->extradata + 32 * i, 32);
+ avio_skip(s->pb, 8);
+ }
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL)
+ st->duration = (avio_size(pb) - start) / (8 * par->channels) * 14;
+ break;
+ case AV_CODEC_ID_PCM_S16LE:
+ case AV_CODEC_ID_PCM_S16BE:
+ if (version != 4)
+ start = avio_rl32(pb);
+
+ if (pb->seekable & AVIO_SEEKABLE_NORMAL)
+ st->duration = (avio_size(pb) - start) / 2 / par->channels;
+ break;
+ }
+
+ avio_skip(pb, start - avio_tell(pb));
+ if (par->codec_id == AV_CODEC_ID_XMA2) {
+ avio_skip(pb, avio_rb32(pb) + avio_rb32(pb));
+ st->duration = avio_rb32(pb);
+ }
+
+ avpriv_set_pts_info(st, 64, 1, par->sample_rate);
+
+ return 0;
+}
+
+static int rsd_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ int ret, size = 1024;
+ int64_t pos;
+
+ if (avio_feof(s->pb))
+ return AVERROR_EOF;
+
+ pos = avio_tell(s->pb);
+ if (par->codec_id == AV_CODEC_ID_ADPCM_IMA_RAD ||
+ par->codec_id == AV_CODEC_ID_ADPCM_PSX ||
+ par->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV ||
+ par->codec_id == AV_CODEC_ID_XMA2) {
+ ret = av_get_packet(s->pb, pkt, par->block_align);
+ } else if (par->codec_tag == MKTAG('W','A','D','P') &&
+ par->channels > 1) {
+ int i, ch;
+
+ ret = av_new_packet(pkt, par->block_align);
+ if (ret < 0)
+ return ret;
+ for (i = 0; i < 4; i++) {
+ for (ch = 0; ch < par->channels; ch++) {
+ pkt->data[ch * 8 + i * 2 + 0] = avio_r8(s->pb);
+ pkt->data[ch * 8 + i * 2 + 1] = avio_r8(s->pb);
+ }
+ }
+ ret = 0;
+ } else {
+ ret = av_get_packet(s->pb, pkt, size);
+ }
+
+ if (par->codec_id == AV_CODEC_ID_XMA2 && pkt->size >= 1)
+ pkt->duration = (pkt->data[0] >> 2) * 512;
+
+ pkt->pos = pos;
+ 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},
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/rso.c b/libavformat/rso.c
index 4ce61e7..7d88f0f 100644
--- a/libavformat/rso.c
+++ b/libavformat/rso.c
@@ -2,20 +2,20 @@
* RSO format common data
* Copyright (c) 2010 Rafael Carre
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rso.h b/libavformat/rso.h
index e3e88ea..1f65dd9 100644
--- a/libavformat/rso.h
+++ b/libavformat/rso.h
@@ -2,20 +2,20 @@
* RSO format common data
* Copyright (c) 2010 Rafael Carre
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rsodec.c b/libavformat/rsodec.c
index 6f352af..b2d9a7c 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->codecpar->channels = 1;
st->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
st->codecpar->sample_rate = rate;
+ st->codecpar->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]->codecpar->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 f9dd419..e34e2c6 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 f3390a1..6600da7 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 cf121c6..c41ae43 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
*/
@@ -264,8 +264,9 @@ static int rtmpe_open(URLContext *h, const char *uri, int flags)
}
/* open the tcp or ffrtmphttp connection */
- if ((ret = ffurl_open(&rt->stream, url, AVIO_FLAG_READ_WRITE,
- &h->interrupt_callback, NULL, h->protocols, h)) < 0) {
+ if ((ret = ffurl_open_whitelist(&rt->stream, url, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, NULL,
+ h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {
rtmpe_close(h);
return ret;
}
diff --git a/libavformat/rtmpcrypt.h b/libavformat/rtmpcrypt.h
index 2799433..590a8c8 100644
--- a/libavformat/rtmpcrypt.h
+++ b/libavformat/rtmpcrypt.h
@@ -2,20 +2,20 @@
* RTMPE encryption utilities
* Copyright (c) 2012 Samuel Pitoiset
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtmpdh.c b/libavformat/rtmpdh.c
index 0593eac..8eb0882 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
*/
@@ -107,6 +107,33 @@ static int bn_modexp(FFBigNum bn, FFBigNum y, FFBigNum q, FFBigNum p)
mpz_powm(bn, y, q, p);
return 0;
}
+#elif CONFIG_GCRYPT
+#define bn_new(bn) \
+ do { \
+ if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) { \
+ if (!gcry_check_version("1.5.4")) \
+ return AVERROR(EINVAL); \
+ gcry_control(GCRYCTL_DISABLE_SECMEM, 0); \
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); \
+ } \
+ bn = gcry_mpi_new(1); \
+ } while (0)
+#define bn_free(bn) gcry_mpi_release(bn)
+#define bn_set_word(bn, w) gcry_mpi_set_ui(bn, w)
+#define bn_cmp(a, b) gcry_mpi_cmp(a, b)
+#define bn_copy(to, from) gcry_mpi_set(to, from)
+#define bn_sub_word(bn, w) gcry_mpi_sub_ui(bn, bn, w)
+#define bn_cmp_1(bn) gcry_mpi_cmp_ui(bn, 1)
+#define bn_num_bytes(bn) (gcry_mpi_get_nbits(bn) + 7) / 8
+#define bn_bn2bin(bn, buf, len) gcry_mpi_print(GCRYMPI_FMT_USG, buf, len, NULL, bn)
+#define bn_bin2bn(bn, buf, len) gcry_mpi_scan(&bn, GCRYMPI_FMT_USG, buf, len, NULL)
+#define bn_hex2bn(bn, buf, ret) ret = (gcry_mpi_scan(&bn, GCRYMPI_FMT_HEX, buf, 0, 0) == 0)
+#define bn_random(bn, num_bits) gcry_mpi_randomize(bn, num_bits, GCRY_WEAK_RANDOM)
+static int bn_modexp(FFBigNum bn, FFBigNum y, FFBigNum q, FFBigNum p)
+{
+ gcry_mpi_powm(bn, y, q, p);
+ return 0;
+}
#elif CONFIG_OPENSSL
#define bn_new(bn) bn = BN_new()
#define bn_free(bn) BN_free(bn)
diff --git a/libavformat/rtmpdh.h b/libavformat/rtmpdh.h
index b4d6121..188aad7 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
*/
@@ -30,6 +30,11 @@
#include <gmp.h>
typedef mpz_ptr FFBigNum;
+#elif CONFIG_GCRYPT
+#include <gcrypt.h>
+
+typedef gcry_mpi_t FFBigNum;
+
#elif CONFIG_OPENSSL
#include <openssl/bn.h>
#include <openssl/dh.h>
diff --git a/libavformat/rtmphttp.c b/libavformat/rtmphttp.c
index 5b27ade..ef6146c 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
*/
@@ -208,7 +208,7 @@ static int rtmp_http_open(URLContext *h, const char *uri, int flags)
}
/* alloc the http context */
- if ((ret = ffurl_alloc(&rt->stream, url, AVIO_FLAG_READ_WRITE, NULL, h->protocols)) < 0)
+ if ((ret = ffurl_alloc(&rt->stream, url, AVIO_FLAG_READ_WRITE, &h->interrupt_callback)) < 0)
goto fail;
/* set options */
@@ -220,6 +220,14 @@ static int rtmp_http_open(URLContext *h, const char *uri, int flags)
av_opt_set(rt->stream->priv_data, "multiple_requests", "1", 0);
av_opt_set_bin(rt->stream->priv_data, "post_data", "", 1, 0);
+ if (!rt->stream->protocol_whitelist && h->protocol_whitelist) {
+ rt->stream->protocol_whitelist = av_strdup(h->protocol_whitelist);
+ if (!rt->stream->protocol_whitelist) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ }
+
/* open the http context */
if ((ret = ffurl_connect(rt->stream, NULL)) < 0)
goto fail;
@@ -254,7 +262,7 @@ fail:
#define DEC AV_OPT_FLAG_DECODING_PARAM
static const AVOption ffrtmphttp_options[] = {
- {"ffrtmphttp_tls", "Use a HTTPS tunneling connection (RTMPTS).", OFFSET(tls), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC},
+ {"ffrtmphttp_tls", "Use a HTTPS tunneling connection (RTMPTS).", OFFSET(tls), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DEC},
{ NULL },
};
@@ -274,4 +282,5 @@ const URLProtocol ff_ffrtmphttp_protocol = {
.priv_data_size = sizeof(RTMP_HTTPContext),
.flags = URL_PROTOCOL_FLAG_NETWORK,
.priv_data_class= &ffrtmphttp_class,
+ .default_whitelist = "https,http,tcp,tls",
};
diff --git a/libavformat/rtmppkt.c b/libavformat/rtmppkt.c
index 0bb0614..1eeae17 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
*/
@@ -287,6 +287,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);
}
@@ -432,96 +433,142 @@ void ff_rtmp_packet_destroy(RTMPPacket *pkt)
pkt->size = 0;
}
-int ff_amf_tag_size(const uint8_t *data, const uint8_t *data_end)
+static int amf_tag_skip(GetByteContext *gb)
{
- const uint8_t *base = data;
AMFDataType type;
unsigned nb = -1;
int parse_key = 1;
- if (data >= data_end)
+ if (bytestream2_get_bytes_left(gb) < 1)
return -1;
- switch ((type = *data++)) {
- case AMF_DATA_TYPE_NUMBER: return 9;
- case AMF_DATA_TYPE_BOOL: return 2;
- case AMF_DATA_TYPE_STRING: return 3 + AV_RB16(data);
- case AMF_DATA_TYPE_LONG_STRING: return 5 + AV_RB32(data);
- case AMF_DATA_TYPE_NULL: return 1;
+
+ type = bytestream2_get_byte(gb);
+ switch (type) {
+ case AMF_DATA_TYPE_NUMBER:
+ bytestream2_get_be64(gb);
+ return 0;
+ case AMF_DATA_TYPE_BOOL:
+ bytestream2_get_byte(gb);
+ return 0;
+ case AMF_DATA_TYPE_STRING:
+ bytestream2_skip(gb, bytestream2_get_be16(gb));
+ return 0;
+ case AMF_DATA_TYPE_LONG_STRING:
+ bytestream2_skip(gb, bytestream2_get_be32(gb));
+ return 0;
+ case AMF_DATA_TYPE_NULL:
+ return 0;
+ case AMF_DATA_TYPE_DATE:
+ bytestream2_skip(gb, 10);
+ return 0;
case AMF_DATA_TYPE_ARRAY:
parse_key = 0;
case AMF_DATA_TYPE_MIXEDARRAY:
- nb = bytestream_get_be32(&data);
+ nb = bytestream2_get_be32(gb);
case AMF_DATA_TYPE_OBJECT:
while (nb-- > 0 || type != AMF_DATA_TYPE_ARRAY) {
int t;
if (parse_key) {
- int size = bytestream_get_be16(&data);
+ int size = bytestream2_get_be16(gb);
if (!size) {
- data++;
+ bytestream2_get_byte(gb);
break;
}
- if (size < 0 || size >= data_end - data)
+ if (size < 0 || size >= bytestream2_get_bytes_left(gb))
return -1;
- data += size;
+ bytestream2_skip(gb, size);
}
- t = ff_amf_tag_size(data, data_end);
- if (t < 0 || t >= data_end - data)
+ t = amf_tag_skip(gb);
+ if (t < 0 || bytestream2_get_bytes_left(gb) <= 0)
return -1;
- data += t;
}
- return data - base;
- case AMF_DATA_TYPE_OBJECT_END: return 1;
+ return 0;
+ case AMF_DATA_TYPE_OBJECT_END: return 0;
default: return -1;
}
}
-int ff_amf_get_field_value(const uint8_t *data, const uint8_t *data_end,
+int ff_amf_tag_size(const uint8_t *data, const uint8_t *data_end)
+{
+ GetByteContext gb;
+ int ret;
+
+ if (data >= data_end)
+ return -1;
+
+ bytestream2_init(&gb, data, data_end - data);
+
+ ret = amf_tag_skip(&gb);
+ if (ret < 0 || bytestream2_get_bytes_left(&gb) <= 0)
+ return -1;
+ av_assert0(bytestream2_tell(&gb) >= 0 && bytestream2_tell(&gb) <= data_end - data);
+ return bytestream2_tell(&gb);
+}
+
+static int amf_get_field_value2(GetByteContext *gb,
const uint8_t *name, uint8_t *dst, int dst_size)
{
int namelen = strlen(name);
int len;
- while (*data != AMF_DATA_TYPE_OBJECT && data < data_end) {
- len = ff_amf_tag_size(data, data_end);
- if (len < 0)
- len = data_end - data;
- data += len;
+ while (bytestream2_peek_byte(gb) != AMF_DATA_TYPE_OBJECT && bytestream2_get_bytes_left(gb) > 0) {
+ int ret = amf_tag_skip(gb);
+ if (ret < 0)
+ return -1;
}
- if (data_end - data < 3)
+ if (bytestream2_get_bytes_left(gb) < 3)
return -1;
- data++;
+ bytestream2_get_byte(gb);
+
for (;;) {
- int size = bytestream_get_be16(&data);
+ int size = bytestream2_get_be16(gb);
if (!size)
break;
- if (size < 0 || size >= data_end - data)
+ if (size < 0 || size >= bytestream2_get_bytes_left(gb))
return -1;
- data += size;
- if (size == namelen && !memcmp(data-size, name, namelen)) {
- switch (*data++) {
+ bytestream2_skip(gb, size);
+ if (size == namelen && !memcmp(gb->buffer-size, name, namelen)) {
+ switch (bytestream2_get_byte(gb)) {
case AMF_DATA_TYPE_NUMBER:
- snprintf(dst, dst_size, "%g", av_int2double(AV_RB64(data)));
+ snprintf(dst, dst_size, "%g", av_int2double(bytestream2_get_be64(gb)));
break;
case AMF_DATA_TYPE_BOOL:
- snprintf(dst, dst_size, "%s", *data ? "true" : "false");
+ snprintf(dst, dst_size, "%s", bytestream2_get_byte(gb) ? "true" : "false");
break;
case AMF_DATA_TYPE_STRING:
- len = bytestream_get_be16(&data);
- av_strlcpy(dst, data, FFMIN(len+1, dst_size));
+ len = bytestream2_get_be16(gb);
+ if (dst_size < 1)
+ return -1;
+ if (dst_size < len + 1)
+ len = dst_size - 1;
+ bytestream2_get_buffer(gb, dst, len);
+ dst[len] = 0;
break;
default:
return -1;
}
return 0;
}
- len = ff_amf_tag_size(data, data_end);
- if (len < 0 || len >= data_end - data)
+ len = amf_tag_skip(gb);
+ if (len < 0 || bytestream2_get_bytes_left(gb) <= 0)
return -1;
- data += len;
}
return -1;
}
+int ff_amf_get_field_value(const uint8_t *data, const uint8_t *data_end,
+ const uint8_t *name, uint8_t *dst, int dst_size)
+{
+ GetByteContext gb;
+
+ if (data >= data_end)
+ return -1;
+
+ bytestream2_init(&gb, data, data_end - data);
+
+ return amf_get_field_value2(&gb, name, dst, dst_size);
+}
+
static const char* rtmp_packet_type(int type)
{
switch (type) {
diff --git a/libavformat/rtmppkt.h b/libavformat/rtmppkt.h
index eccd299..eb68f1d 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 8e03696..7320b4f 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,9 +48,9 @@
#include <zlib.h>
#endif
-#define APP_MAX_LENGTH 128
-#define PLAYPATH_MAX_LENGTH 256
-#define TCURL_MAX_LENGTH 512
+#define APP_MAX_LENGTH 1024
+#define PLAYPATH_MAX_LENGTH 512
+#define TCURL_MAX_LENGTH 1024
#define FLASHVER_MAX_LENGTH 64
#define RTMP_PKTDATA_DEFAULT_SIZE 4096
#define RTMP_HEADER 11
@@ -94,8 +94,8 @@ typedef struct RTMPContext {
int flv_nb_packets; ///< number of flv packets published
RTMPPacket out_pkt; ///< rtmp packet, created from flv a/v or metadata (for output)
uint32_t receive_report_size; ///< number of bytes after which we should report the number of received bytes to the peer
- uint32_t bytes_read; ///< number of bytes read from server
- uint32_t last_bytes_read; ///< number of bytes read last reported to server
+ uint64_t bytes_read; ///< number of bytes read from server
+ uint64_t last_bytes_read; ///< number of bytes read last reported to server
uint32_t last_timestamp; ///< last timestamp received in a packet
int skip_bytes; ///< number of bytes to skip from the input FLV stream in the next write call
int has_audio; ///< presence of audio data
@@ -156,6 +156,8 @@ static const uint8_t rtmp_server_key[] = {
};
static int handle_chunk_size(URLContext *s, RTMPPacket *pkt);
+static int handle_window_ack_size(URLContext *s, RTMPPacket *pkt);
+static int handle_set_peer_bw(URLContext *s, RTMPPacket *pkt);
static int add_tracked_method(RTMPContext *rt, const char *name, int id)
{
@@ -217,9 +219,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 +323,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;
@@ -340,9 +341,12 @@ static int gen_connect(URLContext *s, RTMPContext *rt)
ff_amf_write_field_name(&p, "flashVer");
ff_amf_write_string(&p, rt->flashver);
- if (rt->swfurl) {
+ if (rt->swfurl || rt->swfverify) {
ff_amf_write_field_name(&p, "swfUrl");
- ff_amf_write_string(&p, rt->swfurl);
+ if (rt->swfurl)
+ ff_amf_write_string(&p, rt->swfurl);
+ else
+ ff_amf_write_string(&p, rt->swfverify);
}
ff_amf_write_field_name(&p, "tcUrl");
@@ -400,6 +404,9 @@ static int gen_connect(URLContext *s, RTMPContext *rt)
return rtmp_send_packet(rt, &pkt, 1);
}
+
+#define RTMP_CTRL_ABORT_MESSAGE (2)
+
static int read_connect(URLContext *s, RTMPContext *rt)
{
RTMPPacket pkt = { 0 };
@@ -412,17 +419,42 @@ static int read_connect(URLContext *s, RTMPContext *rt)
uint8_t tmpstr[256];
GetByteContext gbc;
- if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->in_chunk_size,
- &rt->prev_pkt[0], &rt->nb_prev_pkt[0])) < 0)
- return ret;
-
- if (pkt.type == RTMP_PT_CHUNK_SIZE) {
- if ((ret = handle_chunk_size(s, &pkt)) < 0)
- return ret;
- ff_rtmp_packet_destroy(&pkt);
+ // handle RTMP Protocol Control Messages
+ for (;;) {
if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->in_chunk_size,
&rt->prev_pkt[0], &rt->nb_prev_pkt[0])) < 0)
return ret;
+#ifdef DEBUG
+ ff_rtmp_packet_dump(s, &pkt);
+#endif
+ if (pkt.type == RTMP_PT_CHUNK_SIZE) {
+ if ((ret = handle_chunk_size(s, &pkt)) < 0) {
+ ff_rtmp_packet_destroy(&pkt);
+ return ret;
+ }
+ } else if (pkt.type == RTMP_CTRL_ABORT_MESSAGE) {
+ av_log(s, AV_LOG_ERROR, "received abort message\n");
+ ff_rtmp_packet_destroy(&pkt);
+ return AVERROR_UNKNOWN;
+ } else if (pkt.type == RTMP_PT_BYTES_READ) {
+ av_log(s, AV_LOG_TRACE, "received acknowledgement\n");
+ } else if (pkt.type == RTMP_PT_WINDOW_ACK_SIZE) {
+ if ((ret = handle_window_ack_size(s, &pkt)) < 0) {
+ ff_rtmp_packet_destroy(&pkt);
+ return ret;
+ }
+ } else if (pkt.type == RTMP_PT_SET_PEER_BW) {
+ if ((ret = handle_set_peer_bw(s, &pkt)) < 0) {
+ ff_rtmp_packet_destroy(&pkt);
+ return ret;
+ }
+ } else if (pkt.type == RTMP_PT_INVOKE) {
+ // received RTMP Command Message
+ break;
+ } else {
+ av_log(s, AV_LOG_ERROR, "Unknown control message type (%d)\n", pkt.type);
+ }
+ ff_rtmp_packet_destroy(&pkt);
}
cp = pkt.data;
@@ -1122,8 +1154,9 @@ static int rtmp_calc_swfhash(URLContext *s)
int ret = 0;
/* Get the SWF player file. */
- if ((ret = ffurl_open(&stream, rt->swfverify, AVIO_FLAG_READ,
- &s->interrupt_callback, NULL, s->protocols, s)) < 0) {
+ if ((ret = ffurl_open_whitelist(&stream, rt->swfverify, AVIO_FLAG_READ,
+ &s->interrupt_callback, NULL,
+ s->protocol_whitelist, s->protocol_blacklist, s)) < 0) {
av_log(s, AV_LOG_ERROR, "Cannot open connection %s.\n", rt->swfverify);
goto fail;
}
@@ -1760,18 +1793,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;
}
@@ -2480,7 +2518,7 @@ static int get_packet(URLContext *s, int for_header)
} else if (rpkt.type == RTMP_PT_METADATA) {
ret = handle_metadata(rt, &rpkt);
ff_rtmp_packet_destroy(&rpkt);
- return 0;
+ return ret;
}
ff_rtmp_packet_destroy(&rpkt);
}
@@ -2544,7 +2582,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);
@@ -2585,14 +2623,13 @@ static int inject_fake_duration_metadata(RTMPContext *rt)
* and 'playpath' is a file name (the rest of the path,
* may be prefixed with "mp4:")
*/
-static int rtmp_open(URLContext *s, const char *uri, int flags)
+static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **opts)
{
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;
int ret;
if (rt->listen_timeout > 0)
@@ -2604,11 +2641,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]) {
@@ -2627,7 +2666,7 @@ static int rtmp_open(URLContext *s, const char *uri, int flags)
}
if (!strcmp(proto, "rtmpt") || !strcmp(proto, "rtmpts")) {
if (!strcmp(proto, "rtmpts"))
- av_dict_set(&opts, "ffrtmphttp_tls", "1", 1);
+ av_dict_set(opts, "ffrtmphttp_tls", "1", 1);
/* open the http tunneling connection */
ff_url_join(buf, sizeof(buf), "ffrtmphttp", NULL, hostname, port, NULL);
@@ -2638,7 +2677,7 @@ static int rtmp_open(URLContext *s, const char *uri, int flags)
ff_url_join(buf, sizeof(buf), "tls", NULL, hostname, port, NULL);
} else if (!strcmp(proto, "rtmpe") || (!strcmp(proto, "rtmpte"))) {
if (!strcmp(proto, "rtmpte"))
- av_dict_set(&opts, "ffrtmpcrypt_tunneling", "1", 1);
+ av_dict_set(opts, "ffrtmpcrypt_tunneling", "1", 1);
/* open the encrypted connection */
ff_url_join(buf, sizeof(buf), "ffrtmpcrypt", NULL, hostname, port, NULL);
@@ -2656,8 +2695,9 @@ static int rtmp_open(URLContext *s, const char *uri, int flags)
}
reconnect:
- if ((ret = ffurl_open(&rt->stream, buf, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, &opts, s->protocols, s)) < 0) {
+ if ((ret = ffurl_open_whitelist(&rt->stream, buf, AVIO_FLAG_READ_WRITE,
+ &s->interrupt_callback, opts,
+ s->protocol_whitelist, s->protocol_blacklist, s)) < 0) {
av_log(s , AV_LOG_ERROR, "Cannot open connection %s\n", buf);
goto fail;
}
@@ -2707,8 +2747,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, ':');
@@ -2725,29 +2771,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) {
@@ -2861,7 +2914,7 @@ reconnect:
return 0;
fail:
- av_dict_free(&opts);
+ av_dict_free(opts);
rtmp_close(s);
return ret;
}
@@ -2949,6 +3002,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;
@@ -3105,7 +3159,7 @@ static const AVClass flavor##_class = { \
\
const URLProtocol ff_##flavor##_protocol = { \
.name = #flavor, \
- .url_open = rtmp_open, \
+ .url_open2 = rtmp_open, \
.url_read = rtmp_read, \
.url_read_seek = rtmp_seek, \
.url_read_pause = rtmp_pause, \
diff --git a/libavformat/rtp.c b/libavformat/rtp.c
index 5eb92e4..4745e54 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 01b83df..54512c6 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 4eae626..4acb1ca 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
*/
@@ -33,6 +33,18 @@
#define MIN_FEEDBACK_INTERVAL 200000 /* 200 ms in us */
+static RTPDynamicProtocolHandler l24_dynamic_handler = {
+ .enc_name = "L24",
+ .codec_type = AVMEDIA_TYPE_AUDIO,
+ .codec_id = AV_CODEC_ID_PCM_S24BE,
+};
+
+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,
@@ -53,7 +65,7 @@ static RTPDynamicProtocolHandler opus_dynamic_handler = {
static RTPDynamicProtocolHandler t140_dynamic_handler = { /* RFC 4103 */
.enc_name = "t140",
- .codec_type = AVMEDIA_TYPE_DATA,
+ .codec_type = AVMEDIA_TYPE_SUBTITLE,
.codec_id = AV_CODEC_ID_TEXT,
};
@@ -75,6 +87,10 @@ void ff_register_rtp_dynamic_payload_handlers(void)
ff_register_dynamic_payload_handler(&ff_g726_24_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_g726_32_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_g726_40_dynamic_handler);
+ ff_register_dynamic_payload_handler(&ff_g726le_16_dynamic_handler);
+ ff_register_dynamic_payload_handler(&ff_g726le_24_dynamic_handler);
+ ff_register_dynamic_payload_handler(&ff_g726le_32_dynamic_handler);
+ ff_register_dynamic_payload_handler(&ff_g726le_40_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_h261_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_h263_1998_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_h263_2000_dynamic_handler);
@@ -98,11 +114,15 @@ void ff_register_rtp_dynamic_payload_handlers(void)
ff_register_dynamic_payload_handler(&ff_qt_rtp_vid_handler);
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_rfc4175_rtp_handler);
ff_register_dynamic_payload_handler(&ff_svq3_dynamic_handler);
ff_register_dynamic_payload_handler(&ff_theora_dynamic_handler);
+ ff_register_dynamic_payload_handler(&ff_vc2hq_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(&ff_vp9_dynamic_handler);
+ ff_register_dynamic_payload_handler(&gsm_dynamic_handler);
+ ff_register_dynamic_payload_handler(&l24_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);
@@ -675,8 +695,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;
@@ -736,8 +756,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;
@@ -863,7 +883,7 @@ int ff_parse_fmtp(AVFormatContext *s,
int value_size = strlen(p) + 1;
if (!(value = av_malloc(value_size))) {
- av_log(s, AV_LOG_ERROR, "Failed to allocate data for FMTP.");
+ av_log(s, AV_LOG_ERROR, "Failed to allocate data for FMTP.\n");
return AVERROR(ENOMEM);
}
diff --git a/libavformat/rtpdec.h b/libavformat/rtpdec.h
index a0c5f77..77596b6 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
*/
@@ -175,9 +175,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
index e519b28..48b2d9c 100644
--- a/libavformat/rtpdec_ac3.c
+++ b/libavformat/rtpdec_ac3.c
@@ -2,20 +2,20 @@
* RTP parser for AC3 payload format (RFC 4184)
* Copyright (c) 2015 Gilles Chanteperdrix <gch@xenomai.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/rtpdec_amr.c b/libavformat/rtpdec_amr.c
index baf4de9..8687e65 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 d4972bd..2c09fda 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"
@@ -53,6 +54,7 @@ static int rtp_asf_fix_header(uint8_t *buf, int len)
p += sizeof(ff_asf_guid) + 14;
do {
uint64_t chunksize = AV_RL64(p + sizeof(ff_asf_guid));
+ int skip = 6 * 8 + 3 * 4 + sizeof(ff_asf_guid) * 2;
if (memcmp(p, ff_asf_file_header, sizeof(ff_asf_guid))) {
if (chunksize > end - p)
return -1;
@@ -60,9 +62,11 @@ static int rtp_asf_fix_header(uint8_t *buf, int len)
continue;
}
+ if (end - p < 8 + skip)
+ break;
/* skip most of the file header, to min_pktsize */
- p += 6 * 8 + 3 * 4 + sizeof(ff_asf_guid) * 2;
- if (p + 8 <= end && AV_RL32(p) == AV_RL32(p + 4)) {
+ p += skip;
+ if (AV_RL32(p) == AV_RL32(p + 4)) {
/* and set that to zero */
AV_WL32(p, 0);
return 0;
@@ -97,11 +101,12 @@ int ff_wms_parse_sdp_a_line(AVFormatContext *s, const char *p)
{
int ret = 0;
if (av_strstart(p, "pgmpu:data:application/vnd.ms.wms-hdr.asfv1;base64,", &p)) {
- AVIOContext pb;
+ AVIOContext pb = { 0 };
RTSPState *rt = s->priv_data;
AVDictionary *opts = NULL;
int len = strlen(p) * 6 / 8;
char *buf = av_mallocz(len);
+ AVInputFormat *iformat;
if (!buf)
return AVERROR(ENOMEM);
@@ -114,6 +119,10 @@ 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;
+
rt->asf_ctx = avformat_alloc_context();
if (!rt->asf_ctx) {
av_free(buf);
@@ -121,7 +130,13 @@ int ff_wms_parse_sdp_a_line(AVFormatContext *s, const char *p)
}
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_whiteblacklists(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) {
av_free(buf);
@@ -194,7 +209,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_dv.c b/libavformat/rtpdec_dv.c
index 09d497a..de99d27 100644
--- a/libavformat/rtpdec_dv.c
+++ b/libavformat/rtpdec_dv.c
@@ -2,20 +2,20 @@
* RTP parser for DV payload format (RFC 6469)
* Copyright (c) 2015 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_formats.h b/libavformat/rtpdec_formats.h
index 5424c17..a436c9d 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
*/
@@ -38,7 +38,7 @@ int ff_h263_handle_packet(AVFormatContext *ctx, PayloadContext *data,
int ff_h264_parse_sprop_parameter_sets(AVFormatContext *s,
uint8_t **data_ptr, int *size_ptr,
const char *value);
-int ff_h264_handle_aggregated_packet(AVFormatContext *ctx, AVPacket *pkt,
+int ff_h264_handle_aggregated_packet(AVFormatContext *ctx, PayloadContext *data, AVPacket *pkt,
const uint8_t *buf, int len,
int start_skip, int *nal_counters,
int nal_mask);
@@ -55,6 +55,10 @@ extern RTPDynamicProtocolHandler ff_g726_16_dynamic_handler;
extern RTPDynamicProtocolHandler ff_g726_24_dynamic_handler;
extern RTPDynamicProtocolHandler ff_g726_32_dynamic_handler;
extern RTPDynamicProtocolHandler ff_g726_40_dynamic_handler;
+extern RTPDynamicProtocolHandler ff_g726le_16_dynamic_handler;
+extern RTPDynamicProtocolHandler ff_g726le_24_dynamic_handler;
+extern RTPDynamicProtocolHandler ff_g726le_32_dynamic_handler;
+extern RTPDynamicProtocolHandler ff_g726le_40_dynamic_handler;
extern RTPDynamicProtocolHandler ff_h261_dynamic_handler;
extern RTPDynamicProtocolHandler ff_h263_1998_dynamic_handler;
extern RTPDynamicProtocolHandler ff_h263_2000_dynamic_handler;
@@ -78,8 +82,10 @@ extern RTPDynamicProtocolHandler ff_qt_rtp_aud_handler;
extern RTPDynamicProtocolHandler ff_qt_rtp_vid_handler;
extern RTPDynamicProtocolHandler ff_quicktime_rtp_aud_handler;
extern RTPDynamicProtocolHandler ff_quicktime_rtp_vid_handler;
+extern RTPDynamicProtocolHandler ff_rfc4175_rtp_handler;
extern RTPDynamicProtocolHandler ff_svq3_dynamic_handler;
extern RTPDynamicProtocolHandler ff_theora_dynamic_handler;
+extern RTPDynamicProtocolHandler ff_vc2hq_dynamic_handler;
extern RTPDynamicProtocolHandler ff_vorbis_dynamic_handler;
extern RTPDynamicProtocolHandler ff_vp8_dynamic_handler;
extern RTPDynamicProtocolHandler ff_vp9_dynamic_handler;
diff --git a/libavformat/rtpdec_g726.c b/libavformat/rtpdec_g726.c
index 53889bf..2de09ac 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
*/
@@ -36,10 +36,16 @@ static av_cold int g726_ ## bitrate ##_init(AVFormatContext *s, int st_index, \
} \
\
RTPDynamicProtocolHandler ff_g726_ ## bitrate ## _dynamic_handler = { \
- .enc_name = "G726-" #bitrate, \
+ .enc_name = "AAL2-G726-" #bitrate, \
.codec_type = AVMEDIA_TYPE_AUDIO, \
.codec_id = AV_CODEC_ID_ADPCM_G726, \
.init = g726_ ## bitrate ## _init, \
+}; \
+RTPDynamicProtocolHandler ff_g726le_ ## bitrate ## _dynamic_handler = { \
+ .enc_name = "G726-" #bitrate, \
+ .codec_type = AVMEDIA_TYPE_AUDIO, \
+ .codec_id = AV_CODEC_ID_ADPCM_G726LE, \
+ .init = g726_ ## bitrate ## _init, \
}
RTP_G726_HANDLER(16);
diff --git a/libavformat/rtpdec_h261.c b/libavformat/rtpdec_h261.c
index b1bd1e0..9729f21 100644
--- a/libavformat/rtpdec_h261.c
+++ b/libavformat/rtpdec_h261.c
@@ -2,25 +2,24 @@
* 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
*/
-#include "libavcodec/bitstream.h"
-
+#include "libavcodec/get_bits.h"
#include "avformat.h"
#include "avio_internal.h"
#include "rtpdec_formats.h"
@@ -119,18 +118,18 @@ static int h261_handle_packet(AVFormatContext *ctx, PayloadContext *rtp_h261_ctx
avio_w8(rtp_h261_ctx->buf, rtp_h261_ctx->endbyte);
} else {
/* ebit/sbit values inconsistent, assuming packet loss */
- BitstreamContext bc;
- bitstream_init(&bc, buf, len * 8 - ebit);
- bitstream_skip(&bc, sbit);
+ GetBitContext gb;
+ init_get_bits(&gb, buf, len*8 - ebit);
+ skip_bits(&gb, sbit);
if (rtp_h261_ctx->endbyte_bits) {
- rtp_h261_ctx->endbyte |= bitstream_read(&bc, 8 - rtp_h261_ctx->endbyte_bits);
+ rtp_h261_ctx->endbyte |= get_bits(&gb, 8 - rtp_h261_ctx->endbyte_bits);
avio_w8(rtp_h261_ctx->buf, rtp_h261_ctx->endbyte);
}
- while (bitstream_bits_left(&bc) >= 8)
- avio_w8(rtp_h261_ctx->buf, bitstream_read(&bc, 8));
- rtp_h261_ctx->endbyte_bits = bitstream_bits_left(&bc);
+ while (get_bits_left(&gb) >= 8)
+ avio_w8(rtp_h261_ctx->buf, get_bits(&gb, 8));
+ rtp_h261_ctx->endbyte_bits = get_bits_left(&gb);
if (rtp_h261_ctx->endbyte_bits)
- rtp_h261_ctx->endbyte = bitstream_read(&bc, rtp_h261_ctx->endbyte_bits) <<
+ rtp_h261_ctx->endbyte = get_bits(&gb, rtp_h261_ctx->endbyte_bits) <<
(8 - rtp_h261_ctx->endbyte_bits);
ebit = 0;
len = 0;
diff --git a/libavformat/rtpdec_h263.c b/libavformat/rtpdec_h263.c
index 710cfd2..97aa4ad 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 5744d71..6ba2814 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
*/
@@ -30,8 +30,7 @@
#include "rtpdec_formats.h"
#include "libavutil/attributes.h"
#include "libavutil/intreadwrite.h"
-
-#include "libavcodec/bitstream.h"
+#include "libavcodec/get_bits.h"
struct PayloadContext {
AVIOContext *buf;
@@ -142,18 +141,18 @@ static int h263_handle_packet(AVFormatContext *ctx, PayloadContext *data,
avio_w8(data->buf, data->endbyte);
} else {
/* Start/end skip bits not matching - missed packets? */
- BitstreamContext bc;
- bitstream_init(&bc, buf, len * 8 - ebit);
- bitstream_skip(&bc, sbit);
+ GetBitContext gb;
+ init_get_bits(&gb, buf, len*8 - ebit);
+ skip_bits(&gb, sbit);
if (data->endbyte_bits) {
- data->endbyte |= bitstream_read(&bc, 8 - data->endbyte_bits);
+ data->endbyte |= get_bits(&gb, 8 - data->endbyte_bits);
avio_w8(data->buf, data->endbyte);
}
- while (bitstream_bits_left(&bc) >= 8)
- avio_w8(data->buf, bitstream_read(&bc, 8));
- data->endbyte_bits = bitstream_bits_left(&bc);
+ while (get_bits_left(&gb) >= 8)
+ avio_w8(data->buf, get_bits(&gb, 8));
+ data->endbyte_bits = get_bits_left(&gb);
if (data->endbyte_bits)
- data->endbyte = bitstream_read(&bc, data->endbyte_bits) <<
+ data->endbyte = get_bits(&gb, data->endbyte_bits) <<
(8 - data->endbyte_bits);
ebit = 0;
len = 0;
diff --git a/libavformat/rtpdec_h264.c b/libavformat/rtpdec_h264.c
index 401d6f6..6f8148a 100644
--- a/libavformat/rtpdec_h264.c
+++ b/libavformat/rtpdec_h264.c
@@ -2,20 +2,20 @@
* RTP H.264 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
*/
@@ -166,6 +166,10 @@ static int sdp_parse_fmtp_config_h264(AVFormatContext *s,
parse_profile_level_id(s, h264_data, value);
} else if (!strcmp(attr, "sprop-parameter-sets")) {
int ret;
+ if (*value == 0 || value[strlen(value) - 1] == ',') {
+ av_log(s, AV_LOG_WARNING, "Missing PPS in sprop-parameter-sets, ignoring\n");
+ return 0;
+ }
par->extradata_size = 0;
av_freep(&par->extradata);
ret = ff_h264_parse_sprop_parameter_sets(s, &par->extradata,
@@ -199,7 +203,7 @@ void ff_h264_parse_framesize(AVCodecParameters *par, const char *p)
par->height = atoi(p + 1); // skip the -
}
-int ff_h264_handle_aggregated_packet(AVFormatContext *ctx, AVPacket *pkt,
+int ff_h264_handle_aggregated_packet(AVFormatContext *ctx, PayloadContext *data, AVPacket *pkt,
const uint8_t *buf, int len,
int skip_between, int *nal_counters,
int nal_mask)
@@ -278,7 +282,7 @@ int ff_h264_handle_frag_packet(AVPacket *pkt, const uint8_t *buf, int len,
return 0;
}
-static int h264_handle_packet_fu_a(AVFormatContext *ctx, AVPacket *pkt,
+static int h264_handle_packet_fu_a(AVFormatContext *ctx, PayloadContext *data, AVPacket *pkt,
const uint8_t *buf, int len,
int *nal_counters, int nal_mask)
{
@@ -339,7 +343,7 @@ static int h264_handle_packet(AVFormatContext *ctx, PayloadContext *data,
// consume the STAP-A NAL
buf++;
len--;
- result = ff_h264_handle_aggregated_packet(ctx, pkt, buf, len, 0,
+ result = ff_h264_handle_aggregated_packet(ctx, data, pkt, buf, len, 0,
NAL_COUNTERS, NAL_MASK);
break;
@@ -352,7 +356,7 @@ static int h264_handle_packet(AVFormatContext *ctx, PayloadContext *data,
break;
case 28: // FU-A (fragmented nal)
- result = h264_handle_packet_fu_a(ctx, pkt, buf, len,
+ result = h264_handle_packet_fu_a(ctx, data, pkt, buf, len,
NAL_COUNTERS, NAL_MASK);
break;
diff --git a/libavformat/rtpdec_hevc.c b/libavformat/rtpdec_hevc.c
index 97f87ae..a0e3a7c 100644
--- a/libavformat/rtpdec_hevc.c
+++ b/libavformat/rtpdec_hevc.c
@@ -2,35 +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"
#include "rtpdec_formats.h"
-#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 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 {
@@ -82,7 +85,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);
ff_h264_parse_sprop_parameter_sets(s, data_ptr,
size_ptr, value);
@@ -265,7 +269,7 @@ static int hevc_handle_packet(AVFormatContext *ctx, PayloadContext *rtp_hevc_ctx
len -= RTP_HEVC_DONL_FIELD_SIZE;
}
- res = ff_h264_handle_aggregated_packet(ctx, pkt, buf, len,
+ res = ff_h264_handle_aggregated_packet(ctx, rtp_hevc_ctx, pkt, buf, len,
rtp_hevc_ctx->using_donl_field ?
RTP_HEVC_DOND_FIELD_SIZE : 0,
NULL, 0);
@@ -306,12 +310,16 @@ static int hevc_handle_packet(AVFormatContext *ctx, PayloadContext *rtp_hevc_ctx
av_log(ctx, AV_LOG_TRACE, " 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) {
- /* 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);
- return 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);
+ return AVERROR_INVALIDDATA;
+ } else {
+ return AVERROR(EAGAIN);
+ }
}
if (first_fragment && last_fragment) {
diff --git a/libavformat/rtpdec_ilbc.c b/libavformat/rtpdec_ilbc.c
index 798ae36..cb48f76 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 c3e5c8d..465d9bc 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
*/
@@ -92,7 +92,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;
@@ -118,6 +119,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));
@@ -213,7 +220,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");
@@ -229,6 +236,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;
+ }
if (type > 1) {
avpriv_report_missing_feature(ctx, "RTP/JPEG type %"PRIu8, type);
return AVERROR_PATCHWELCOME;
@@ -313,7 +330,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 9893aeb..a25c07f 100644
--- a/libavformat/rtpdec_latm.c
+++ b/libavformat/rtpdec_latm.c
@@ -2,30 +2,28 @@
* 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
*/
-#include "libavutil/avstring.h"
-
-#include "libavcodec/bitstream.h"
-
#include "avio_internal.h"
#include "rtpdec_formats.h"
#include "internal.h"
+#include "libavutil/avstring.h"
+#include "libavcodec/get_bits.h"
struct PayloadContext {
AVIOContext *dyn_buf;
@@ -37,7 +35,7 @@ struct PayloadContext {
static void latm_close_context(PayloadContext *data)
{
ffio_free_dyn_buf(&data->dyn_buf);
- av_free(data->buf);
+ av_freep(&data->buf);
}
static int latm_parse_packet(AVFormatContext *ctx, PayloadContext *data,
@@ -60,7 +58,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;
@@ -94,7 +92,7 @@ static int latm_parse_packet(AVFormatContext *ctx, PayloadContext *data,
static int parse_fmtp_config(AVStream *st, const char *value)
{
int len = ff_hex_to_data(NULL, value), i, ret = 0;
- BitstreamContext bc;
+ GetBitContext gb;
uint8_t *config;
int audio_mux_version, same_time_framing, num_programs, num_layers;
@@ -103,12 +101,12 @@ static int parse_fmtp_config(AVStream *st, const char *value)
if (!config)
return AVERROR(ENOMEM);
ff_hex_to_data(config, value);
- bitstream_init8(&bc, config, len);
- audio_mux_version = bitstream_read(&bc, 1);
- same_time_framing = bitstream_read(&bc, 1);
- bitstream_skip(&bc, 6); /* num_sub_frames */
- num_programs = bitstream_read(&bc, 4);
- num_layers = bitstream_read(&bc, 3);
+ init_get_bits(&gb, config, len*8);
+ audio_mux_version = get_bits(&gb, 1);
+ same_time_framing = get_bits(&gb, 1);
+ skip_bits(&gb, 6); /* num_sub_frames */
+ num_programs = get_bits(&gb, 4);
+ num_layers = get_bits(&gb, 3);
if (audio_mux_version != 0 || same_time_framing != 1 || num_programs != 0 ||
num_layers != 0) {
avpriv_report_missing_feature(NULL, "LATM config (%d,%d,%d,%d)",
@@ -118,15 +116,12 @@ static int parse_fmtp_config(AVStream *st, const char *value)
goto end;
}
av_freep(&st->codecpar->extradata);
- st->codecpar->extradata_size = (bitstream_bits_left(&bc) + 7) / 8;
- st->codecpar->extradata = av_mallocz(st->codecpar->extradata_size +
- AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata) {
+ if (ff_alloc_extradata(st->codecpar, (get_bits_left(&gb) + 7)/8)) {
ret = AVERROR(ENOMEM);
goto end;
}
for (i = 0; i < st->codecpar->extradata_size; i++)
- st->codecpar->extradata[i] = bitstream_read(&bc, 8);
+ st->codecpar->extradata[i] = get_bits(&gb, 8);
end:
av_free(config);
diff --git a/libavformat/rtpdec_mpa_robust.c b/libavformat/rtpdec_mpa_robust.c
index a65e139..86c8958 100644
--- a/libavformat/rtpdec_mpa_robust.c
+++ b/libavformat/rtpdec_mpa_robust.c
@@ -2,20 +2,20 @@
* RTP parser for loss tolerant payload format for MP3 audio (RFC 5219)
* Copyright (c) 2015 Gilles Chanteperdrix <gch@xenomai.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
*/
@@ -166,7 +166,7 @@ static int mpa_robust_parse_packet(AVFormatContext *ctx, PayloadContext *data,
"Received packet without a start fragment; dropping.\n");
return AVERROR(EAGAIN);
}
- if (adu_size = data->adu_size ||
+ if (adu_size != data->adu_size ||
data->timestamp != *timestamp) {
ffio_free_dyn_buf(&data->fragment);
av_log(ctx, AV_LOG_ERROR, "Invalid packet received\n");
diff --git a/libavformat/rtpdec_mpeg12.c b/libavformat/rtpdec_mpeg12.c
index 89ecd30..b93de3d 100644
--- a/libavformat/rtpdec_mpeg12.c
+++ b/libavformat/rtpdec_mpeg12.c
@@ -2,20 +2,20 @@
* Common code for the RTP depacketization of MPEG-1/2 formats.
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpdec_mpeg4.c b/libavformat/rtpdec_mpeg4.c
index 2a7e54a..994ab49 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
*/
@@ -27,13 +27,11 @@
* @author Romain Degez
*/
-#include "libavutil/attributes.h"
-#include "libavutil/avstring.h"
-
-#include "libavcodec/bitstream.h"
-
#include "rtpdec_formats.h"
#include "internal.h"
+#include "libavutil/attributes.h"
+#include "libavutil/avstring.h"
+#include "libavcodec/get_bits.h"
#define MAX_AAC_HBR_FRAME_SIZE 8191
@@ -95,19 +93,17 @@ static const AttrNameMap attr_names[] = {
static void close_context(PayloadContext *data)
{
- av_free(data->au_headers);
- av_free(data->mode);
+ av_freep(&data->au_headers);
+ av_freep(&data->mode);
}
static int parse_fmtp_config(AVCodecParameters *par, const char *value)
{
/* decode the hexa encoded parameter */
int len = ff_hex_to_data(NULL, value);
- av_free(par->extradata);
- par->extradata = av_mallocz(len + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!par->extradata)
+ av_freep(&par->extradata);
+ if (ff_alloc_extradata(par, len))
return AVERROR(ENOMEM);
- par->extradata_size = len;
ff_hex_to_data(par->extradata, value);
return 0;
}
@@ -115,7 +111,7 @@ static int parse_fmtp_config(AVCodecParameters *par, const char *value)
static int rtp_parse_mp4_au(PayloadContext *data, const uint8_t *buf, int len)
{
int au_headers_length, au_header_size, i;
- BitstreamContext bctx;
+ GetBitContext getbitcontext;
if (len < 2)
return AVERROR_INVALIDDATA;
@@ -136,7 +132,7 @@ static int rtp_parse_mp4_au(PayloadContext *data, const uint8_t *buf, int len)
if (len < data->au_headers_length_bytes)
return AVERROR_INVALIDDATA;
- bitstream_init8(&bctx, buf, data->au_headers_length_bytes);
+ init_get_bits(&getbitcontext, buf, data->au_headers_length_bytes * 8);
/* XXX: Wrong if optional additional sections are present (cts, dts etc...) */
au_header_size = data->sizelength + data->indexlength;
@@ -153,8 +149,8 @@ static int rtp_parse_mp4_au(PayloadContext *data, const uint8_t *buf, int len)
}
for (i = 0; i < data->nb_au_headers; ++i) {
- data->au_headers[i].size = bitstream_read(&bctx, data->sizelength);
- data->au_headers[i].index = bitstream_read(&bctx, data->indexlength);
+ data->au_headers[i].size = get_bits_long(&getbitcontext, data->sizelength);
+ data->au_headers[i].index = get_bits_long(&getbitcontext, data->indexlength);
}
return 0;
@@ -169,6 +165,7 @@ static int aac_parse_packet(AVFormatContext *ctx, PayloadContext *data,
{
int ret;
+
if (!buf) {
if (data->cur_au_index > data->nb_au_headers) {
av_log(ctx, AV_LOG_ERROR, "Invalid parser state\n");
@@ -295,7 +292,7 @@ static int parse_fmtp(AVFormatContext *s,
int val = atoi(value);
if (val > 32) {
av_log(s, AV_LOG_ERROR,
- "The %s field size is invalid (%d).",
+ "The %s field size is invalid (%d)\n",
attr, val);
return AVERROR_INVALIDDATA;
}
diff --git a/libavformat/rtpdec_mpegts.c b/libavformat/rtpdec_mpegts.c
index 0fb0a0d..5bf0f18 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
*/
@@ -35,13 +35,13 @@ static void mpegts_close_context(PayloadContext *data)
if (!data)
return;
if (data->ts)
- ff_mpegts_parse_close(data->ts);
+ avpriv_mpegts_parse_close(data->ts);
}
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;
@@ -63,8 +63,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;
@@ -74,8 +74,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 d5322d6..41cc826 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 9a77a70..1f4fd5a 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->codecpar->extradata);
- st->codecpar->extradata_size = 26 + item_len;
- if (!(st->codecpar->extradata = av_mallocz(st->codecpar->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE))) {
- st->codecpar->extradata_size = 0;
+ if (ff_alloc_extradata(st->codecpar, 26 + item_len)) {
return AVERROR(ENOMEM);
}
AV_WB32(st->codecpar->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 e6c586f..77a3ce4 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
*/
@@ -25,14 +25,13 @@
* @author Ronald S. Bultje <rbultje@ronald.bitfreak.net>
*/
-#include "libavcodec/bitstream.h"
-
#include "avformat.h"
#include "internal.h"
#include "avio_internal.h"
#include "rtp.h"
#include "rtpdec.h"
#include "isom.h"
+#include "libavcodec/get_bits.h"
struct PayloadContext {
AVPacket pkt;
@@ -46,7 +45,7 @@ static int qt_rtp_parse_packet(AVFormatContext *s, PayloadContext *qt,
int len, uint16_t seq, int flags)
{
AVIOContext pb;
- BitstreamContext bc;
+ GetBitContext gb;
int packing_scheme, has_payload_desc, has_packet_info, alen,
has_marker_bit = flags & RTP_FLAG_MARKER,
keyframe;
@@ -72,38 +71,38 @@ static int qt_rtp_parse_packet(AVFormatContext *s, PayloadContext *qt,
* The RTP payload is described in:
* http://developer.apple.com/quicktime/icefloe/dispatch026.html
*/
- bitstream_init8(&bc, buf, len);
- ffio_init_context(&pb, buf, len, 0, NULL, NULL, NULL, NULL);
+ init_get_bits(&gb, buf, len << 3);
+ ffio_init_context(&pb, (uint8_t*)buf, len, 0, NULL, NULL, NULL, NULL);
if (len < 4)
return AVERROR_INVALIDDATA;
- bitstream_skip(&bc, 4); // version
- if ((packing_scheme = bitstream_read(&bc, 2)) == 0)
+ skip_bits(&gb, 4); // version
+ if ((packing_scheme = get_bits(&gb, 2)) == 0)
return AVERROR_INVALIDDATA;
- keyframe = bitstream_read_bit(&bc);
- has_payload_desc = bitstream_read_bit(&bc);
- has_packet_info = bitstream_read_bit(&bc);
- bitstream_skip(&bc, 23); // reserved:7, cache payload info:1, payload ID:15
+ keyframe = get_bits1(&gb);
+ has_payload_desc = get_bits1(&gb);
+ has_packet_info = get_bits1(&gb);
+ skip_bits(&gb, 23); // reserved:7, cache payload info:1, payload ID:15
if (has_payload_desc) {
int data_len, pos, is_start, is_finish;
uint32_t tag;
- pos = bitstream_tell(&bc) >> 3;
+ pos = get_bits_count(&gb) >> 3;
if (pos + 12 > len)
return AVERROR_INVALIDDATA;
- bitstream_skip(&bc, 2); // has non-I-frames:1, is sparse:1
- is_start = bitstream_read_bit(&bc);
- is_finish = bitstream_read_bit(&bc);
+ skip_bits(&gb, 2); // has non-I-frames:1, is sparse:1
+ is_start = get_bits1(&gb);
+ is_finish = get_bits1(&gb);
if (!is_start || !is_finish) {
avpriv_request_sample(s, "RTP-X-QT with payload description "
"split over several packets");
return AVERROR_PATCHWELCOME;
}
- bitstream_skip(&bc, 12); // reserved
- data_len = bitstream_read(&bc, 16);
+ skip_bits(&gb, 12); // reserved
+ data_len = get_bits(&gb, 16);
avio_seek(&pb, pos + 4, SEEK_SET);
tag = avio_rl32(&pb);
diff --git a/libavformat/rtpdec_rfc4175.c b/libavformat/rtpdec_rfc4175.c
new file mode 100644
index 0000000..498381d
--- /dev/null
+++ b/libavformat/rtpdec_rfc4175.c
@@ -0,0 +1,236 @@
+/*
+ * RTP Depacketization of RAW video (TR-03)
+ * Copyright (c) 2016 Savoir-faire Linux, Inc
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Development sponsored by CBC/Radio-Canada */
+
+#include "avio_internal.h"
+#include "rtpdec_formats.h"
+#include "libavutil/avstring.h"
+#include "libavutil/pixdesc.h"
+
+struct PayloadContext {
+ char *sampling;
+ int depth;
+ int width;
+ int height;
+
+ uint8_t *frame;
+ unsigned int frame_size;
+ unsigned int pgroup; /* size of the pixel group in bytes */
+ unsigned int xinc;
+
+ uint32_t timestamp;
+};
+
+static int rfc4175_parse_format(AVStream *stream, PayloadContext *data)
+{
+ enum AVPixelFormat pixfmt = AV_PIX_FMT_NONE;
+ int bits_per_sample = 0;
+ int tag = 0;
+
+ if (!strncmp(data->sampling, "YCbCr-4:2:2", 11)) {
+ tag = MKTAG('U', 'Y', 'V', 'Y');
+ data->xinc = 2;
+
+ if (data->depth == 8) {
+ data->pgroup = 4;
+ bits_per_sample = 16;
+ pixfmt = AV_PIX_FMT_UYVY422;
+ } else if (data->depth == 10) {
+ data->pgroup = 5;
+ bits_per_sample = 20;
+ pixfmt = AV_PIX_FMT_YUV422P10;
+ } else {
+ return AVERROR_INVALIDDATA;
+ }
+ } else {
+ return AVERROR_INVALIDDATA;
+ }
+
+ stream->codecpar->format = pixfmt;
+ stream->codecpar->codec_tag = tag;
+ stream->codecpar->bits_per_coded_sample = bits_per_sample;
+ data->frame_size = data->width * data->height * data->pgroup / data->xinc;
+
+ return 0;
+}
+
+static int rfc4175_parse_fmtp(AVFormatContext *s, AVStream *stream,
+ PayloadContext *data, const char *attr,
+ const char *value)
+{
+ if (!strncmp(attr, "width", 5))
+ data->width = atoi(value);
+ else if (!strncmp(attr, "height", 6))
+ data->height = atoi(value);
+ else if (!strncmp(attr, "sampling", 8))
+ data->sampling = av_strdup(value);
+ else if (!strncmp(attr, "depth", 5))
+ data->depth = atoi(value);
+
+ return 0;
+}
+
+static int rfc4175_parse_sdp_line(AVFormatContext *s, int st_index,
+ PayloadContext *data, const char *line)
+{
+ const char *p;
+
+ if (st_index < 0)
+ return 0;
+
+ if (av_strstart(line, "fmtp:", &p)) {
+ AVStream *stream = s->streams[st_index];
+ int ret = ff_parse_fmtp(s, stream, data, p, rfc4175_parse_fmtp);
+
+ if (ret < 0)
+ return ret;
+
+
+ if (!data->sampling || !data->depth || !data->width || !data->height)
+ return -1;
+
+ stream->codecpar->width = data->width;
+ stream->codecpar->height = data->height;
+
+ ret = rfc4175_parse_format(stream, data);
+ av_freep(&data->sampling);
+
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rfc4175_finalize_packet(PayloadContext *data, AVPacket *pkt,
+ int stream_index)
+{
+ int ret;
+
+ pkt->stream_index = stream_index;
+ ret = av_packet_from_data(pkt, data->frame, data->frame_size);
+ if (ret < 0) {
+ av_freep(&data->frame);
+ }
+
+ data->frame = NULL;
+
+ return ret;
+}
+
+static int rfc4175_handle_packet(AVFormatContext *ctx, PayloadContext *data,
+ AVStream *st, AVPacket *pkt, uint32_t *timestamp,
+ const uint8_t * buf, int len,
+ uint16_t seq, int flags)
+{
+ int length, line, offset, cont;
+ const uint8_t *headers = buf + 2; /* skip extended seqnum */
+ const uint8_t *payload = buf + 2;
+ int payload_len = len - 2;
+ int missed_last_packet = 0;
+
+ uint8_t *dest;
+
+ if (*timestamp != data->timestamp) {
+ if (data->frame) {
+ /*
+ * if we're here, it means that two RTP packets didn't have the
+ * same timestamp, which is a sign that they were packets from two
+ * different frames, but we didn't get the flag RTP_FLAG_MARKER on
+ * the first one of these frames (last packet of a frame).
+ * Finalize the previous frame anyway by filling the AVPacket.
+ */
+ av_log(ctx, AV_LOG_ERROR, "Missed previous RTP Marker\n");
+ missed_last_packet = 1;
+ rfc4175_finalize_packet(data, pkt, st->index);
+ }
+
+ data->frame = av_malloc(data->frame_size);
+
+ data->timestamp = *timestamp;
+
+ if (!data->frame) {
+ av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
+ return AVERROR(ENOMEM);
+ }
+ }
+
+ /*
+ * looks for the 'Continuation bit' in scan lines' headers
+ * to find where data start
+ */
+ do {
+ if (payload_len < 6)
+ return AVERROR_INVALIDDATA;
+
+ cont = payload[4] & 0x80;
+ payload += 6;
+ payload_len -= 6;
+ } while (cont);
+
+ /* and now iterate over every scan lines */
+ do {
+ int copy_offset;
+
+ if (payload_len < data->pgroup)
+ return AVERROR_INVALIDDATA;
+
+ length = (headers[0] << 8) | headers[1];
+ line = ((headers[2] & 0x7f) << 8) | headers[3];
+ offset = ((headers[4] & 0x7f) << 8) | headers[5];
+ cont = headers[4] & 0x80;
+ headers += 6;
+
+ if (length % data->pgroup)
+ return AVERROR_INVALIDDATA;
+
+ if (length > payload_len)
+ length = payload_len;
+
+ /* prevent ill-formed packets to write after buffer's end */
+ copy_offset = (line * data->width + offset) * data->pgroup / data->xinc;
+ if (copy_offset + length > data->frame_size)
+ return AVERROR_INVALIDDATA;
+
+ dest = data->frame + copy_offset;
+ memcpy(dest, payload, length);
+
+ payload += length;
+ payload_len -= length;
+ } while (cont);
+
+ if ((flags & RTP_FLAG_MARKER)) {
+ return rfc4175_finalize_packet(data, pkt, st->index);
+ } else if (missed_last_packet) {
+ return 0;
+ }
+
+ return AVERROR(EAGAIN);
+}
+
+RTPDynamicProtocolHandler ff_rfc4175_rtp_handler = {
+ .enc_name = "raw",
+ .codec_type = AVMEDIA_TYPE_VIDEO,
+ .codec_id = AV_CODEC_ID_BITPACKED,
+ .priv_data_size = sizeof(PayloadContext),
+ .parse_sdp_a_line = rfc4175_parse_sdp_line,
+ .parse_packet = rfc4175_handle_packet,
+};
diff --git a/libavformat/rtpdec_svq3.c b/libavformat/rtpdec_svq3.c
index 267d9db..18d79d2 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
*/
@@ -29,6 +29,7 @@
#include <string.h>
#include "libavutil/intreadwrite.h"
#include "avio_internal.h"
+#include "internal.h"
#include "rtp.h"
#include "rtpdec.h"
#include "rtpdec_formats.h"
@@ -61,11 +62,9 @@ static int svq3_parse_packet (AVFormatContext *s, PayloadContext *sv,
av_freep(&st->codecpar->extradata);
st->codecpar->extradata_size = 0;
- if (len < 2 || !(st->codecpar->extradata =
- av_malloc(len + 8 + AV_INPUT_BUFFER_PADDING_SIZE)))
+ if (len < 2 || ff_alloc_extradata(st->codecpar, len + 8))
return AVERROR_INVALIDDATA;
- st->codecpar->extradata_size = len + 8;
memcpy(st->codecpar->extradata, "SEQH", 4);
AV_WB32(st->codecpar->extradata + 4, len);
memcpy(st->codecpar->extradata + 8, buf, len);
diff --git a/libavformat/rtpdec_vc2hq.c b/libavformat/rtpdec_vc2hq.c
new file mode 100644
index 0000000..8a3996a
--- /dev/null
+++ b/libavformat/rtpdec_vc2hq.c
@@ -0,0 +1,225 @@
+/*
+ * RTP parser for VC-2 HQ payload format (draft version 1) - experimental
+ * Copyright (c) 2016 Thomas Volkert <thomas@netzeal.de>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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/dirac.h"
+
+#include "avio_internal.h"
+#include "rtpdec_formats.h"
+
+#define RTP_VC2HQ_PL_HEADER_SIZE 4
+
+#define DIRAC_DATA_UNIT_HEADER_SIZE 13
+#define DIRAC_PIC_NR_SIZE 4
+#define DIRAC_RTP_PCODE_HQ_PIC_FRAGMENT 0xEC
+
+struct PayloadContext {
+ AVIOContext *buf;
+ uint32_t frame_size;
+ uint32_t frame_nr;
+ uint32_t timestamp;
+ uint32_t last_unit_size;
+ int seen_sequence_header;
+};
+
+static const uint8_t start_sequence[] = { 'B', 'B', 'C', 'D' };
+
+static void fill_parse_info_header(PayloadContext *pl_ctx, uint8_t *buf,
+ uint8_t parse_code, uint32_t data_unit_size)
+{
+ memcpy(buf, start_sequence, sizeof(start_sequence));
+ buf[4] = parse_code;
+ AV_WB32(&buf[5], data_unit_size);
+ AV_WB32(&buf[9], pl_ctx->last_unit_size);
+
+ pl_ctx->last_unit_size = data_unit_size;
+}
+
+static int vc2hq_handle_sequence_header(PayloadContext *pl_ctx, AVStream *st, AVPacket *pkt,
+ const uint8_t *buf, int len)
+{
+ int res;
+ uint32_t size = DIRAC_DATA_UNIT_HEADER_SIZE + len;
+
+ if ((res = av_new_packet(pkt, DIRAC_DATA_UNIT_HEADER_SIZE + len)) < 0)
+ return res;
+
+ fill_parse_info_header(pl_ctx, pkt->data, 0x00, size);
+ /* payload of seq. header */
+ memcpy(pkt->data + DIRAC_DATA_UNIT_HEADER_SIZE, buf, len);
+ pkt->stream_index = st->index;
+
+ pl_ctx->seen_sequence_header = 1;
+
+ return 0;
+}
+
+static int vc2hq_mark_end_of_sequence(PayloadContext *pl_ctx, AVStream *st, AVPacket *pkt)
+{
+ int res;
+ uint32_t size = 0;
+
+ /* create A/V packet */
+ if ((res = av_new_packet(pkt, DIRAC_DATA_UNIT_HEADER_SIZE)) < 0)
+ return res;
+
+ fill_parse_info_header(pl_ctx, pkt->data, 0x10, size);
+ pkt->stream_index = st->index;
+
+ pl_ctx->seen_sequence_header = 0;
+
+ return 0;
+}
+
+static int vc2hq_handle_frame_fragment(AVFormatContext *ctx, PayloadContext *pl_ctx, AVStream *st,
+ AVPacket *pkt, uint32_t *timestamp, const uint8_t *buf, int len,
+ int flags)
+{
+ int res;
+ uint32_t pic_nr;
+ uint16_t frag_len;
+ uint16_t no_slices;
+
+ /* sanity check for size of input packet: 16 bytes header in any case as minimum */
+ if (len < 16) {
+ av_log(ctx, AV_LOG_ERROR, "Too short RTP/VC2hq packet, got %d bytes\n", len);
+ return AVERROR_INVALIDDATA;
+ }
+
+ pic_nr = AV_RB32(&buf[4]);
+ frag_len = AV_RB16(&buf[12]);
+ no_slices = AV_RB16(&buf[14]);
+
+ if (pl_ctx->buf && pl_ctx->frame_nr != pic_nr) {
+ av_log(ctx, AV_LOG_WARNING, "Dropping buffered RTP/VC2hq packet fragments - non-continuous picture numbers\n");
+ ffio_free_dyn_buf(&pl_ctx->buf);
+ }
+
+ /* transform parameters? */
+ if (no_slices == 0) {
+ if (len < frag_len + 16) {
+ av_log(ctx, AV_LOG_ERROR, "Too short RTP/VC2hq packet, got %d bytes\n", len);
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* start frame buffering with new dynamic buffer */
+ if (!pl_ctx->buf) {
+
+ res = avio_open_dyn_buf(&pl_ctx->buf);
+ if (res < 0)
+ return res;
+
+ /* reserve memory for frame header */
+ res = avio_seek(pl_ctx->buf, DIRAC_DATA_UNIT_HEADER_SIZE + DIRAC_PIC_NR_SIZE, SEEK_SET);
+ if (res < 0)
+ return res;
+
+ pl_ctx->frame_nr = pic_nr;
+ pl_ctx->timestamp = *timestamp;
+ pl_ctx->frame_size = DIRAC_DATA_UNIT_HEADER_SIZE + DIRAC_PIC_NR_SIZE;
+ }
+
+ avio_write(pl_ctx->buf, buf + 16 /* skip pl header */, frag_len);
+ pl_ctx->frame_size += frag_len;
+
+ return AVERROR(EAGAIN);
+ } else {
+ if (len < frag_len + 20) {
+ av_log(ctx, AV_LOG_ERROR, "Too short RTP/VC2hq packet, got %d bytes\n", len);
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* transform parameters were missed, no buffer available */
+ if (!pl_ctx->buf)
+ return AVERROR_INVALIDDATA;
+
+ avio_write(pl_ctx->buf, buf + 20 /* skip pl header */, frag_len);
+ pl_ctx->frame_size += frag_len;
+
+ /* RTP marker bit means: last fragment of current frame was received;
+ otherwise, an additional fragment is needed for the current frame */
+ if (!(flags & RTP_FLAG_MARKER))
+ return AVERROR(EAGAIN);
+ }
+
+ /* close frame buffering and create A/V packet */
+ res = ff_rtp_finalize_packet(pkt, &pl_ctx->buf, st->index);
+ if (res < 0)
+ return res;
+
+ fill_parse_info_header(pl_ctx, pkt->data, DIRAC_PCODE_PICTURE_HQ, pl_ctx->frame_size);
+ AV_WB32(&pkt->data[13], pl_ctx->frame_nr);
+
+ pl_ctx->frame_size = 0;
+
+ return 0;
+}
+
+static int vc2hq_handle_packet(AVFormatContext *ctx, PayloadContext *pl_ctx,
+ AVStream *st, AVPacket *pkt, uint32_t *timestamp,
+ const uint8_t *buf, int len, uint16_t seq,
+ int flags)
+{
+ uint8_t parse_code = 0;
+ int res = 0;
+
+ if (pl_ctx->buf && pl_ctx->timestamp != *timestamp) {
+ av_log(ctx, AV_LOG_WARNING, "Dropping buffered RTP/VC2hq packet fragments - non-continuous timestamps\n");
+ ffio_free_dyn_buf(&pl_ctx->buf);
+ pl_ctx->frame_size = 0;
+ }
+
+ /* sanity check for size of input packet: needed header data as minimum */
+ if (len < RTP_VC2HQ_PL_HEADER_SIZE) {
+ av_log(ctx, AV_LOG_ERROR, "Too short RTP/VC2hq packet, got %d bytes\n", len);
+ return AVERROR_INVALIDDATA;
+ }
+
+ parse_code = buf[3];
+
+ /* wait for next sequence header? */
+ if (pl_ctx->seen_sequence_header || parse_code == DIRAC_PCODE_SEQ_HEADER) {
+ switch(parse_code) {
+ /* sequence header */
+ case DIRAC_PCODE_SEQ_HEADER:
+ res = vc2hq_handle_sequence_header(pl_ctx, st, pkt, buf + RTP_VC2HQ_PL_HEADER_SIZE, len - RTP_VC2HQ_PL_HEADER_SIZE);
+ break;
+ /* end of sequence */
+ case DIRAC_PCODE_END_SEQ:
+ res = vc2hq_mark_end_of_sequence(pl_ctx, st, pkt);
+ break;
+ /* HQ picture fragment */
+ case DIRAC_RTP_PCODE_HQ_PIC_FRAGMENT:
+ res = vc2hq_handle_frame_fragment(ctx, pl_ctx, st, pkt, timestamp, buf, len, flags);
+ break;
+ }
+ }
+
+ return res;
+}
+
+RTPDynamicProtocolHandler ff_vc2hq_dynamic_handler = {
+ .enc_name = "VC2",
+ .codec_type = AVMEDIA_TYPE_VIDEO,
+ .codec_id = AV_CODEC_ID_DIRAC,
+ .priv_data_size = sizeof(PayloadContext),
+ .parse_packet = vc2hq_handle_packet
+};
diff --git a/libavformat/rtpdec_vp8.c b/libavformat/rtpdec_vp8.c
index b7f9939..f0e457b 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_vp9.c b/libavformat/rtpdec_vp9.c
index c0e6217..4a7f934 100644
--- a/libavformat/rtpdec_vp9.c
+++ b/libavformat/rtpdec_vp9.c
@@ -2,20 +2,20 @@
* RTP parser for VP9 payload format (draft version 02) - experimental
* Copyright (c) 2015 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_xiph.c b/libavformat/rtpdec_xiph.c
index 920a8a7..43de6ce 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,13 +28,13 @@
*/
#include "libavutil/attributes.h"
+#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/base64.h"
#include "libavcodec/bytestream.h"
-#include <assert.h>
-
#include "avio_internal.h"
+#include "internal.h"
#include "rtpdec.h"
#include "rtpdec_formats.h"
@@ -53,7 +53,7 @@ struct PayloadContext {
static void xiph_close_context(PayloadContext * data)
{
ffio_free_dyn_buf(&data->fragment);
- av_free(data->split_buf);
+ av_freep(&data->split_buf);
}
@@ -73,7 +73,7 @@ static int xiph_handle_packet(AVFormatContext *ctx, PayloadContext *data,
}
pkt_len = AV_RB16(data->split_buf + data->split_pos);
data->split_pos += 2;
- if (data->split_pos + pkt_len > data->split_buf_len) {
+ if (pkt_len > data->split_buf_len - data->split_pos) {
av_log(ctx, AV_LOG_ERROR, "Not enough data to return\n");
return AVERROR_INVALIDDATA;
}
@@ -88,7 +88,7 @@ static int xiph_handle_packet(AVFormatContext *ctx, PayloadContext *data,
return data->split_pkts > 0;
}
- if (len < 6) {
+ if (len < 6 || len > INT_MAX/2) {
av_log(ctx, AV_LOG_ERROR, "Invalid %d byte packet\n", len);
return AVERROR_INVALIDDATA;
}
@@ -167,7 +167,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
@@ -232,7 +232,7 @@ parse_packed_headers(AVFormatContext *s,
if (packed_headers_end - packed_headers < 9) {
av_log(s, AV_LOG_ERROR,
- "Invalid %td byte packed header.",
+ "Invalid %"PTRDIFF_SPECIFIER" byte packed header.",
packed_headers_end - packed_headers);
return AVERROR_INVALIDDATA;
}
@@ -253,7 +253,7 @@ parse_packed_headers(AVFormatContext *s,
if (packed_headers_end - packed_headers != length ||
length1 > length || length2 > length - length1) {
av_log(s, AV_LOG_ERROR,
- "Bad packed header lengths (%u,%u,%td,%u)\n", length1,
+ "Bad packed header lengths (%d,%d,%"PTRDIFF_SPECIFIER",%u)\n", length1,
length2, packed_headers_end - packed_headers, length);
return AVERROR_INVALIDDATA;
}
@@ -264,11 +264,11 @@ parse_packed_headers(AVFormatContext *s,
* -- AV_INPUT_BUFFER_PADDING_SIZE required */
extradata_alloc = length + length/255 + 3 + AV_INPUT_BUFFER_PADDING_SIZE;
- ptr = par->extradata = av_malloc(extradata_alloc);
- if (!ptr) {
+ if (ff_alloc_extradata(par, extradata_alloc)) {
av_log(s, AV_LOG_ERROR, "Out of memory\n");
return AVERROR(ENOMEM);
}
+ ptr = par->extradata;
*ptr++ = 2;
ptr += av_xiphlacing(ptr, length1);
ptr += av_xiphlacing(ptr, length2);
diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c
index 28c309e..573593f 100644
--- a/libavformat/rtpenc.c
+++ b/libavformat/rtpenc.c
@@ -2,20 +2,20 @@
* RTP output format
* Copyright (c) 2002 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -49,6 +49,7 @@ static const AVClass rtp_muxer_class = {
static int is_supported(enum AVCodecID id)
{
switch(id) {
+ case AV_CODEC_ID_DIRAC:
case AV_CODEC_ID_H261:
case AV_CODEC_ID_H263:
case AV_CODEC_ID_H263P:
@@ -74,8 +75,10 @@ static int is_supported(enum AVCodecID id)
case AV_CODEC_ID_VORBIS:
case AV_CODEC_ID_THEORA:
case AV_CODEC_ID_VP8:
+ case AV_CODEC_ID_VP9:
case AV_CODEC_ID_ADPCM_G722:
case AV_CODEC_ID_ADPCM_G726:
+ case AV_CODEC_ID_ADPCM_G726LE:
case AV_CODEC_ID_ILBC:
case AV_CODEC_ID_MJPEG:
case AV_CODEC_ID_SPEEX:
@@ -98,7 +101,7 @@ static int rtp_write_header(AVFormatContext *s1)
}
st = s1->streams[0];
if (!is_supported(st->codecpar->codec_id)) {
- av_log(s1, AV_LOG_ERROR, "Unsupported codec %x\n", st->codecpar->codec_id);
+ av_log(s1, AV_LOG_ERROR, "Unsupported codec %s\n", avcodec_get_name(st->codecpar->codec_id));
return -1;
}
@@ -121,16 +124,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) {
@@ -170,6 +176,17 @@ static int rtp_write_header(AVFormatContext *s1)
n = 1;
s->max_payload_size = n * TS_PACKET_SIZE;
break;
+ case AV_CODEC_ID_DIRAC:
+ if (s1->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
+ av_log(s, AV_LOG_ERROR,
+ "Packetizing VC-2 is experimental and does not use all values "
+ "of the specification "
+ "(even though most receivers may handle it just fine). "
+ "Please set -strict experimental in order to enable it.\n");
+ ret = AVERROR_EXPERIMENTAL;
+ goto fail;
+ }
+ break;
case AV_CODEC_ID_H261:
if (s1->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
av_log(s, AV_LOG_ERROR,
@@ -196,6 +213,16 @@ static int rtp_write_header(AVFormatContext *s1)
s->nal_length_size = (st->codecpar->extradata[21] & 0x03) + 1;
}
break;
+ case AV_CODEC_ID_VP9:
+ if (s1->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
+ av_log(s, AV_LOG_ERROR,
+ "Packetizing VP9 is experimental and its specification is "
+ "still in draft state. "
+ "Please set -strict experimental in order to enable it.\n");
+ ret = AVERROR_EXPERIMENTAL;
+ goto fail;
+ }
+ break;
case AV_CODEC_ID_VORBIS:
case AV_CODEC_ID_THEORA:
s->max_frames_per_packet = 15;
@@ -259,8 +286,7 @@ static void rtcp_send_sr(AVFormatContext *s1, int64_t ntp_time, int bye)
RTPMuxContext *s = s1->priv_data;
uint32_t rtp_ts;
- av_log(s1, AV_LOG_TRACE, "RTCP: %02x %"PRIx64" %"PRIx32"\n",
- s->payload_type, ntp_time, s->timestamp);
+ av_log(s1, AV_LOG_TRACE, "RTCP: %02x %"PRIx64" %"PRIx32"\n", s->payload_type, ntp_time, s->timestamp);
s->last_rtcp_ntp_time = ntp_time;
rtp_ts = av_rescale_q(ntp_time - s->first_rtcp_ntp_time, (AVRational){1, 1000000},
@@ -269,7 +295,8 @@ static void rtcp_send_sr(AVFormatContext *s1, int64_t ntp_time, int bye)
avio_w8(s1->pb, RTCP_SR);
avio_wb16(s1->pb, 6); /* length in words - 1 */
avio_wb32(s1->pb, s->ssrc);
- avio_wb64(s1->pb, NTP_TO_RTP_FORMAT(ntp_time));
+ avio_wb32(s1->pb, ntp_time / 1000000);
+ avio_wb32(s1->pb, ((ntp_time % 1000000) << 32) / 1000000);
avio_wb32(s1->pb, rtp_ts);
avio_wb32(s1->pb, s->packet_count);
avio_wb32(s1->pb, s->octet_count);
@@ -524,6 +551,7 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt)
* clock. */
return rtp_send_samples(s1, pkt->data, size, 8 * st->codecpar->channels);
case AV_CODEC_ID_ADPCM_G726:
+ case AV_CODEC_ID_ADPCM_G726LE:
return rtp_send_samples(s1, pkt->data, size,
st->codecpar->bits_per_coded_sample * st->codecpar->channels);
case AV_CODEC_ID_MP2:
@@ -547,6 +575,9 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt)
case AV_CODEC_ID_MPEG2TS:
rtp_send_mpegts_raw(s1, pkt->data, size);
break;
+ case AV_CODEC_ID_DIRAC:
+ ff_rtp_send_vc2hq(s1, pkt->data, size, st->codecpar->field_order != AV_FIELD_PROGRESSIVE ? 1 : 0);
+ break;
case AV_CODEC_ID_H264:
ff_rtp_send_h264_hevc(s1, pkt->data, size);
break;
@@ -576,6 +607,9 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt)
case AV_CODEC_ID_VP8:
ff_rtp_send_vp8(s1, pkt->data, size);
break;
+ case AV_CODEC_ID_VP9:
+ ff_rtp_send_vp9(s1, pkt->data, size);
+ break;
case AV_CODEC_ID_ILBC:
rtp_send_ilbc(s1, pkt->data, size);
break;
diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h
index c4b27f0..62dc9ab 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
@@ -91,10 +91,12 @@ void ff_rtp_send_latm(AVFormatContext *s1, const uint8_t *buff, int size);
void ff_rtp_send_amr(AVFormatContext *s1, const uint8_t *buff, int size);
void ff_rtp_send_mpegvideo(AVFormatContext *s1, const uint8_t *buf1, int size);
void ff_rtp_send_xiph(AVFormatContext *s1, const uint8_t *buff, int size);
+void ff_rtp_send_vc2hq(AVFormatContext *s1, const uint8_t *buf, int size, int interlaced);
void ff_rtp_send_vp8(AVFormatContext *s1, const uint8_t *buff, int size);
+void ff_rtp_send_vp9(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 ee01b04..fad8ea2 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 0adbf49..8df5a3c 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 7b06130..f768fb0 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 | AVFMT_FLAG_BITEXACT);
+
/* 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) {
ffio_free_dyn_buf(&rtpctx->pb);
}
diff --git a/libavformat/rtpenc_chain.h b/libavformat/rtpenc_chain.h
index 4117239..21c27c1 100644
--- a/libavformat/rtpenc_chain.h
+++ b/libavformat/rtpenc_chain.h
@@ -2,20 +2,20 @@
* RTP muxer chaining code
* Copyright (c) 2010 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpenc_h261.c b/libavformat/rtpenc_h261.c
index 550f04f..22461ca 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 87f0bd7..9cea013 100644
--- a/libavformat/rtpenc_h263.c
+++ b/libavformat/rtpenc_h263.c
@@ -3,28 +3,28 @@
* Copyright (c) 2009 Luca Abeni
* Copyright (c) 2009 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "avformat.h"
#include "rtpenc.h"
-const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *restrict start,
- const uint8_t *restrict end)
+const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *av_restrict start,
+ const uint8_t *av_restrict end)
{
const uint8_t *p = end - 1;
start += 1; /* Make sure we never return the original start. */
diff --git a/libavformat/rtpenc_h263_rfc2190.c b/libavformat/rtpenc_h263_rfc2190.c
index 3b3479a..3567202 100644
--- a/libavformat/rtpenc_h263_rfc2190.c
+++ b/libavformat/rtpenc_h263_rfc2190.c
@@ -2,28 +2,27 @@
* 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
*/
-#include "libavcodec/bitstream.h"
-#include "libavcodec/put_bits.h"
-
#include "avformat.h"
#include "rtpenc.h"
+#include "libavcodec/put_bits.h"
+#include "libavcodec/get_bits.h"
struct H263Info {
int src;
@@ -104,7 +103,7 @@ void ff_rtp_send_h263_rfc2190(AVFormatContext *s1, const uint8_t *buf, int size,
{
RTPMuxContext *s = s1->priv_data;
int len, sbits = 0, ebits = 0;
- BitstreamContext bc;
+ GetBitContext gb;
struct H263Info info = { 0 };
struct H263State state = { 0 };
int mb_info_pos = 0, mb_info_count = mb_info_size / 12;
@@ -112,17 +111,17 @@ void ff_rtp_send_h263_rfc2190(AVFormatContext *s1, const uint8_t *buf, int size,
s->timestamp = s->cur_timestamp;
- bitstream_init8(&bc, buf, size);
- if (bitstream_read(&bc, 22) == 0x20) { /* Picture Start Code */
- info.tr = bitstream_read(&bc, 8);
- bitstream_skip(&bc, 2); /* PTYPE start, H.261 disambiguation */
- bitstream_skip(&bc, 3); /* Split screen, document camera, freeze picture release */
- info.src = bitstream_read(&bc, 3);
- info.i = bitstream_read(&bc, 1);
- info.u = bitstream_read(&bc, 1);
- info.s = bitstream_read(&bc, 1);
- info.a = bitstream_read(&bc, 1);
- info.pb = bitstream_read(&bc, 1);
+ init_get_bits(&gb, buf, size*8);
+ if (get_bits(&gb, 22) == 0x20) { /* Picture Start Code */
+ info.tr = get_bits(&gb, 8);
+ skip_bits(&gb, 2); /* PTYPE start, H.261 disambiguation */
+ skip_bits(&gb, 3); /* Split screen, document camera, freeze picture release */
+ info.src = get_bits(&gb, 3);
+ info.i = get_bits(&gb, 1);
+ info.u = get_bits(&gb, 1);
+ info.s = get_bits(&gb, 1);
+ info.a = get_bits(&gb, 1);
+ info.pb = get_bits(&gb, 1);
}
while (size > 0) {
@@ -151,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]);
@@ -161,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 H.263 packet, use -mb_info %d "
- "or lower.\n", s->max_payload_size - 8);
}
} else {
av_log(s1, AV_LOG_ERROR, "Unable to split H.263 packet, "
diff --git a/libavformat/rtpenc_h264_hevc.c b/libavformat/rtpenc_h264_hevc.c
index 9f329a6..0c88fc2 100644
--- a/libavformat/rtpenc_h264_hevc.c
+++ b/libavformat/rtpenc_h264_hevc.c
@@ -4,20 +4,20 @@
* Copyright (c) 2008 Luca Abeni
* 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 9f22a09..38eb2e6 100644
--- a/libavformat/rtpenc_jpeg.c
+++ b/libavformat/rtpenc_jpeg.c
@@ -2,32 +2,33 @@
* 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
*/
#include "libavcodec/bytestream.h"
#include "libavcodec/mjpeg.h"
+#include "libavcodec/jpegtables.h"
#include "libavutil/intreadwrite.h"
#include "rtpenc.h"
void ff_rtp_send_jpeg(AVFormatContext *s1, const uint8_t *buf, int size)
{
RTPMuxContext *s = s1->priv_data;
- const uint8_t *qtables = NULL;
+ const uint8_t *qtables[4] = { NULL };
int nb_qtables = 0;
uint8_t type;
uint8_t w, h;
@@ -35,13 +36,14 @@ void ff_rtp_send_jpeg(AVFormatContext *s1, const uint8_t *buf, int size)
int off = 0; /* fragment offset of the current JPEG frame */
int len;
int i;
+ int default_huffman_tables = 0;
s->buf_ptr = s->buf;
s->timestamp = s->cur_timestamp;
/* convert video pixel dimensions from pixels to blocks */
- w = (s1->streams[0]->codecpar->width + 7) >> 3;
- h = (s1->streams[0]->codecpar->height + 7) >> 3;
+ w = AV_CEIL_RSHIFT(s1->streams[0]->codecpar->width, 3);
+ h = AV_CEIL_RSHIFT(s1->streams[0]->codecpar->height, 3);
/* get the pixel format type or fail */
if (s1->streams[0]->codecpar->format == AV_PIX_FMT_YUVJ422P ||
@@ -63,30 +65,113 @@ void ff_rtp_send_jpeg(AVFormatContext *s1, const uint8_t *buf, int size)
continue;
if (buf[i + 1] == DQT) {
- if (buf[i + 4])
+ int tables, j;
+ if (buf[i + 4] & 0xF0)
av_log(s1, AV_LOG_WARNING,
"Only 8-bit precision is supported.\n");
/* a quantization table is 64 bytes long */
- nb_qtables = AV_RB16(&buf[i + 2]) / 65;
- if (i + 4 + nb_qtables * 65 > size) {
+ tables = AV_RB16(&buf[i + 2]) / 65;
+ if (i + 5 + tables * 65 > size) {
av_log(s1, AV_LOG_ERROR, "Too short JPEG header. Aborted!\n");
return;
}
+ if (nb_qtables + tables > 4) {
+ av_log(s1, AV_LOG_ERROR, "Invalid number of quantisation tables\n");
+ return;
+ }
- qtables = &buf[i + 4];
+ for (j = 0; j < tables; j++)
+ qtables[nb_qtables + j] = buf + i + 5 + j * 65;
+ nb_qtables += tables;
} else if (buf[i + 1] == SOF0) {
if (buf[i + 14] != 17 || buf[i + 17] != 17) {
av_log(s1, AV_LOG_ERROR,
"Only 1x1 chroma blocks are supported. Aborted!\n");
return;
}
+ } else if (buf[i + 1] == DHT) {
+ int dht_size = AV_RB16(&buf[i + 2]);
+ default_huffman_tables |= 1 << 4;
+ i += 3;
+ dht_size -= 2;
+ if (i + dht_size >= size)
+ continue;
+ while (dht_size > 0)
+ switch (buf[i + 1]) {
+ case 0x00:
+ if ( dht_size >= 29
+ && !memcmp(buf + i + 2, avpriv_mjpeg_bits_dc_luminance + 1, 16)
+ && !memcmp(buf + i + 18, avpriv_mjpeg_val_dc, 12)) {
+ default_huffman_tables |= 1;
+ i += 29;
+ dht_size -= 29;
+ } else {
+ i += dht_size;
+ dht_size = 0;
+ }
+ break;
+ case 0x01:
+ if ( dht_size >= 29
+ && !memcmp(buf + i + 2, avpriv_mjpeg_bits_dc_chrominance + 1, 16)
+ && !memcmp(buf + i + 18, avpriv_mjpeg_val_dc, 12)) {
+ default_huffman_tables |= 1 << 1;
+ i += 29;
+ dht_size -= 29;
+ } else {
+ i += dht_size;
+ dht_size = 0;
+ }
+ break;
+ case 0x10:
+ if ( dht_size >= 179
+ && !memcmp(buf + i + 2, avpriv_mjpeg_bits_ac_luminance + 1, 16)
+ && !memcmp(buf + i + 18, avpriv_mjpeg_val_ac_luminance, 162)) {
+ default_huffman_tables |= 1 << 2;
+ i += 179;
+ dht_size -= 179;
+ } else {
+ i += dht_size;
+ dht_size = 0;
+ }
+ break;
+ case 0x11:
+ if ( dht_size >= 179
+ && !memcmp(buf + i + 2, avpriv_mjpeg_bits_ac_chrominance + 1, 16)
+ && !memcmp(buf + i + 18, avpriv_mjpeg_val_ac_chrominance, 162)) {
+ default_huffman_tables |= 1 << 3;
+ i += 179;
+ dht_size -= 179;
+ } else {
+ i += dht_size;
+ dht_size = 0;
+ }
+ break;
+ default:
+ i += dht_size;
+ dht_size = 0;
+ continue;
+ }
} else if (buf[i + 1] == SOS) {
/* SOS is last marker in the header */
i += AV_RB16(&buf[i + 2]) + 2;
+ if (i > size) {
+ av_log(s1, AV_LOG_ERROR,
+ "Insufficient data. Aborted!\n");
+ return;
+ }
break;
}
}
+ if (default_huffman_tables && default_huffman_tables != 31) {
+ av_log(s1, AV_LOG_ERROR,
+ "RFC 2435 requires standard Huffman tables for jpeg\n");
+ return;
+ }
+ if (nb_qtables && nb_qtables != 2)
+ av_log(s1, AV_LOG_WARNING,
+ "RFC 2435 suggests two quantization tables, %d provided\n",
+ nb_qtables);
/* skip JPEG header */
buf += i;
@@ -125,7 +210,7 @@ void ff_rtp_send_jpeg(AVFormatContext *s1, const uint8_t *buf, int size)
bytestream_put_be16(&p, 64 * nb_qtables);
for (i = 0; i < nb_qtables; i++)
- bytestream_put_buffer(&p, &qtables[65 * i + 1], 64);
+ bytestream_put_buffer(&p, qtables[i], 64);
}
/* copy payload data */
diff --git a/libavformat/rtpenc_latm.c b/libavformat/rtpenc_latm.c
index d07dd52..7bda66a 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 d93a26a..7af02e0 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 70248c4..4b45f51 100644
--- a/libavformat/rtpenc_mpv.c
+++ b/libavformat/rtpenc_mpv.c
@@ -3,20 +3,20 @@
* Copyright (c) 2002 Fabrice Bellard
* Copyright (c) 2007 Luca Abeni
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpenc_vc2hq.c b/libavformat/rtpenc_vc2hq.c
new file mode 100644
index 0000000..085204f
--- /dev/null
+++ b/libavformat/rtpenc_vc2hq.c
@@ -0,0 +1,134 @@
+/*
+ * RTP packetizer for VC-2 HQ payload format (draft version 1) - experimental
+ * Copyright (c) 2016 Thomas Volkert <thomas@netzeal.de>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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/dirac.h"
+#include "libavcodec/get_bits.h"
+#include "libavcodec/golomb.h"
+
+#include "avformat.h"
+#include "rtpenc.h"
+
+#define RTP_VC2HQ_PL_HEADER_SIZE 4
+
+#define DIRAC_DATA_UNIT_HEADER_SIZE 13
+#define DIRAC_PIC_NR_SIZE 4
+#define DIRAC_RTP_PCODE_HQ_PIC_FRAGMENT 0xEC
+
+static void send_packet(AVFormatContext *ctx, uint8_t parse_code, int info_hdr_size, const uint8_t *buf, int size, int i, int f, int rtp_m)
+{
+ RTPMuxContext *rtp_ctx = ctx->priv_data;
+
+ AV_WB16(&rtp_ctx->buf[0], 0); /* extended sequence number */
+ AV_WB8 (&rtp_ctx->buf[2], i ? (f ? (0x03) : (0x02)) : 0x00); /* flags: interlaced, second field */
+ AV_WB8 (&rtp_ctx->buf[3], parse_code);
+ if (size > 0)
+ memcpy(&rtp_ctx->buf[4 + info_hdr_size], buf, size);
+ ff_rtp_send_data(ctx, rtp_ctx->buf, RTP_VC2HQ_PL_HEADER_SIZE + info_hdr_size + size, rtp_m);
+}
+
+static void send_picture(AVFormatContext *ctx, const uint8_t *buf, int size, int interlaced)
+{
+ RTPMuxContext *rtp_ctx = ctx->priv_data;
+ GetBitContext gc;
+ int lvl, second_field;
+ uint32_t pic_nr, wavelet_depth, prefix_bytes, size_scaler;
+ uint16_t frag_len;
+ char *info_hdr = &rtp_ctx->buf[4];
+
+ pic_nr = AV_RB32(&buf[0]);
+ buf += DIRAC_PIC_NR_SIZE;
+ size -= DIRAC_PIC_NR_SIZE;
+ second_field = interlaced && (pic_nr & 0x01);
+
+ init_get_bits(&gc, buf, 8 * size);
+ get_interleaved_ue_golomb(&gc); /* wavelet_idx */
+ wavelet_depth = get_interleaved_ue_golomb(&gc);
+ get_interleaved_ue_golomb(&gc); /* num_x */
+ get_interleaved_ue_golomb(&gc); /* num_y */
+ prefix_bytes = get_interleaved_ue_golomb(&gc);
+ size_scaler = get_interleaved_ue_golomb(&gc);
+ /* pass the quantization matrices */
+ get_interleaved_ue_golomb(&gc);
+ for(lvl = 0; lvl < wavelet_depth; lvl++)
+ {
+ get_interleaved_ue_golomb(&gc);
+ get_interleaved_ue_golomb(&gc);
+ get_interleaved_ue_golomb(&gc);
+ }
+
+ frag_len = (get_bits_count(&gc) + 7) / 8; /* length of transform parameters */
+
+ AV_WB32(&info_hdr[ 0], pic_nr);
+ AV_WB16(&info_hdr[ 4], prefix_bytes);
+ AV_WB16(&info_hdr[ 6], size_scaler);
+ AV_WB16(&info_hdr[ 8], frag_len);
+ AV_WB16(&info_hdr[10], 0 /* nr. of slices */);
+ send_packet(ctx, DIRAC_RTP_PCODE_HQ_PIC_FRAGMENT, 12, buf, frag_len, interlaced, second_field, 0);
+ buf += frag_len;
+ size -= frag_len;
+
+ while (size > 0) {
+ frag_len = FFMIN(rtp_ctx->max_payload_size - 20 /* pl header */, size);
+ AV_WB16(&info_hdr[ 8], frag_len);
+ AV_WB16(&info_hdr[10], 1 /* nr. of slices */);
+ AV_WB16(&info_hdr[12], 0 /* slice x */);
+ AV_WB16(&info_hdr[14], 0 /* slice y */);
+
+ size -= frag_len;
+ send_packet(ctx, DIRAC_RTP_PCODE_HQ_PIC_FRAGMENT, 16, buf, frag_len, interlaced, second_field, size > 0 ? 0 : 1);
+ buf += frag_len;
+ }
+}
+
+void ff_rtp_send_vc2hq(AVFormatContext *ctx, const uint8_t *frame_buf, int frame_size, int interlaced)
+{
+ const uint8_t *end = frame_buf + frame_size;
+ const uint8_t *unit = frame_buf;
+ uint8_t parse_code;
+ uint32_t unit_size;
+
+ while (unit < end) {
+ parse_code = unit[4];
+ unit_size = AV_RB32(&unit[5]);
+
+ switch (parse_code) {
+ /* sequence header */
+ /* end of sequence */
+ case DIRAC_PCODE_SEQ_HEADER:
+ case DIRAC_PCODE_END_SEQ:
+ send_packet(ctx, parse_code, 0, unit + DIRAC_DATA_UNIT_HEADER_SIZE, unit_size - DIRAC_DATA_UNIT_HEADER_SIZE, 0, 0, 0);
+ break;
+ /* HQ picture */
+ case DIRAC_PCODE_PICTURE_HQ:
+ send_picture(ctx, unit + DIRAC_DATA_UNIT_HEADER_SIZE, unit_size - DIRAC_DATA_UNIT_HEADER_SIZE, interlaced);
+ break;
+ /* parse codes without specification */
+ case DIRAC_PCODE_AUX:
+ case DIRAC_PCODE_PAD:
+ break;
+ default:
+ avpriv_report_missing_feature(ctx, "VC-2 parse code %d", parse_code);
+ break;
+ }
+ unit += unit_size;
+ }
+}
diff --git a/libavformat/rtpenc_vp8.c b/libavformat/rtpenc_vp8.c
index d24c8bc..671d245 100644
--- a/libavformat/rtpenc_vp8.c
+++ b/libavformat/rtpenc_vp8.c
@@ -2,20 +2,20 @@
* RTP VP8 Packetizer
* Copyright (c) 2010 Josh Allmann
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtpenc_vp9.c b/libavformat/rtpenc_vp9.c
new file mode 100644
index 0000000..172ab21
--- /dev/null
+++ b/libavformat/rtpenc_vp9.c
@@ -0,0 +1,54 @@
+/*
+ * RTP packetizer for VP9 payload format (draft version 02) - experimental
+ * Copyright (c) 2016 Thomas Volkert <thomas@netzeal.de>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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 "rtpenc.h"
+
+#define RTP_VP9_DESC_REQUIRED_SIZE 1
+
+void ff_rtp_send_vp9(AVFormatContext *ctx, const uint8_t *buf, int size)
+{
+ RTPMuxContext *rtp_ctx = ctx->priv_data;
+ int len;
+
+ rtp_ctx->timestamp = rtp_ctx->cur_timestamp;
+ rtp_ctx->buf_ptr = rtp_ctx->buf;
+
+ /* mark the first fragment */
+ *rtp_ctx->buf_ptr++ = 0x08;
+
+ while (size > 0) {
+ len = FFMIN(size, rtp_ctx->max_payload_size - RTP_VP9_DESC_REQUIRED_SIZE);
+
+ if (len == size) {
+ /* mark the last fragment */
+ rtp_ctx->buf[0] |= 0x04;
+ }
+
+ memcpy(rtp_ctx->buf_ptr, buf, len);
+ ff_rtp_send_data(ctx, rtp_ctx->buf, len + RTP_VP9_DESC_REQUIRED_SIZE, size == len);
+
+ size -= len;
+ buf += len;
+
+ /* clear the end bit */
+ rtp_ctx->buf[0] &= ~0x08;
+ }
+}
diff --git a/libavformat/rtpenc_xiph.c b/libavformat/rtpenc_xiph.c
index f6ab77f..00c29a7 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 "libavutil/intreadwrite.h"
#include "avformat.h"
@@ -75,7 +76,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 ||
diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c
index 21a96d2..0706cae 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
*/
@@ -44,7 +44,7 @@
typedef struct RTPContext {
const AVClass *class;
- URLContext *rtp_hd, *rtcp_hd;
+ URLContext *rtp_hd, *rtcp_hd, *fec_hd;
int rtp_fd, rtcp_fd, nb_ssm_include_addrs, nb_ssm_exclude_addrs;
struct sockaddr_storage **ssm_include_addrs, **ssm_exclude_addrs;
int write_to_source;
@@ -55,8 +55,10 @@ typedef struct RTPContext {
int rtcp_port, local_rtpport, local_rtcpport;
int connect;
int pkt_size;
+ int dscp;
char *sources;
char *block;
+ char *fec_options_str;
} RTPContext;
#define OFFSET(x) offsetof(RTPContext, x)
@@ -68,11 +70,13 @@ static const AVOption options[] = {
{ "rtcp_port", "Custom rtcp port", OFFSET(rtcp_port), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
{ "local_rtpport", "Local rtp port", OFFSET(local_rtpport), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
{ "local_rtcpport", "Local rtcp port", OFFSET(local_rtcpport), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
- { "connect", "Connect socket", OFFSET(connect), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E },
- { "write_to_source", "Send packets to the source address of the latest received packet", OFFSET(write_to_source), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E },
+ { "connect", "Connect socket", OFFSET(connect), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = D|E },
+ { "write_to_source", "Send packets to the source address of the latest received packet", OFFSET(write_to_source), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = D|E },
{ "pkt_size", "Maximum packet size", OFFSET(pkt_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
+ { "dscp", "DSCP class", OFFSET(dscp), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
{ "sources", "Source list", OFFSET(sources), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E },
{ "block", "Block list", OFFSET(block), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E },
+ { "fec", "FEC", OFFSET(fec_options_str), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = E },
{ NULL }
};
@@ -239,6 +243,9 @@ static void build_udp_url(RTPContext *s,
url_add_option(buf, buf_size, "pkt_size=%d", s->pkt_size);
if (s->connect)
url_add_option(buf, buf_size, "connect=1");
+ if (s->dscp >= 0)
+ url_add_option(buf, buf_size, "dscp=%d", s->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])
@@ -298,6 +305,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
*
@@ -310,12 +318,16 @@ static void rtp_parse_addr_list(URLContext *h, char *buf,
static int rtp_open(URLContext *h, const char *uri, int flags)
{
RTPContext *s = h->priv_data;
+ AVDictionary *fec_opts = NULL;
int rtp_port;
char hostname[256], include_sources[1024] = "", exclude_sources[1024] = "";
char *sources = include_sources, *block = exclude_sources;
+ char *fec_protocol = NULL;
char buf[1024];
char path[1024];
const char *p;
+ int i, max_retry_count = 3;
+ int rtcpflags;
av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &rtp_port,
path, sizeof(path), uri);
@@ -349,6 +361,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)) {
+ s->dscp = strtol(buf, NULL, 10);
+ }
if (av_find_info_tag(buf, sizeof(buf), "sources", p)) {
av_strlcpy(include_sources, buf, sizeof(include_sources));
@@ -366,19 +381,73 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
}
}
- build_udp_url(s, buf, sizeof(buf),
- hostname, rtp_port, s->local_rtpport, sources, block);
- if (ffurl_open(&s->rtp_hd, buf, flags, &h->interrupt_callback, NULL,
- h->protocols, h) < 0)
- goto fail;
- if (s->local_rtpport >= 0 && s->local_rtcpport < 0)
- s->local_rtcpport = ff_udp_get_local_port(s->rtp_hd) + 1;
+ if (s->fec_options_str) {
+ p = s->fec_options_str;
+
+ if (!(fec_protocol = av_get_token(&p, "="))) {
+ av_log(h, AV_LOG_ERROR, "Failed to parse the FEC protocol value\n");
+ goto fail;
+ }
+ if (strcmp(fec_protocol, "prompeg")) {
+ av_log(h, AV_LOG_ERROR, "Unsupported FEC protocol %s\n", fec_protocol);
+ goto fail;
+ }
+
+ p = s->fec_options_str + strlen(fec_protocol);
+ while (*p && *p == '=') p++;
- build_udp_url(s, buf, sizeof(buf),
- hostname, s->rtcp_port, s->local_rtcpport, sources, block);
- if (ffurl_open(&s->rtcp_hd, buf, flags, &h->interrupt_callback, NULL,
- h->protocols, h) < 0)
- goto fail;
+ if (av_dict_parse_string(&fec_opts, p, "=", ":", 0) < 0) {
+ av_log(h, AV_LOG_ERROR, "Failed to parse the FEC options\n");
+ goto fail;
+ }
+ if (s->ttl > 0) {
+ snprintf(buf, sizeof (buf), "%d", s->ttl);
+ av_dict_set(&fec_opts, "ttl", buf, 0);
+ }
+ }
+
+ for (i = 0; i < max_retry_count; i++) {
+ build_udp_url(s, buf, sizeof(buf),
+ hostname, rtp_port, s->local_rtpport,
+ sources, block);
+ if (ffurl_open_whitelist(&s->rtp_hd, buf, flags, &h->interrupt_callback,
+ NULL, h->protocol_whitelist, h->protocol_blacklist, h) < 0)
+ goto fail;
+ s->local_rtpport = ff_udp_get_local_port(s->rtp_hd);
+ if(s->local_rtpport == 65535) {
+ s->local_rtpport = -1;
+ continue;
+ }
+ rtcpflags = flags | AVIO_FLAG_WRITE;
+ if (s->local_rtcpport < 0) {
+ s->local_rtcpport = s->local_rtpport + 1;
+ build_udp_url(s, buf, sizeof(buf),
+ hostname, s->rtcp_port, s->local_rtcpport,
+ sources, block);
+ if (ffurl_open_whitelist(&s->rtcp_hd, buf, rtcpflags,
+ &h->interrupt_callback, NULL,
+ h->protocol_whitelist, h->protocol_blacklist, h) < 0) {
+ s->local_rtpport = s->local_rtcpport = -1;
+ continue;
+ }
+ break;
+ }
+ build_udp_url(s, buf, sizeof(buf),
+ hostname, s->rtcp_port, s->local_rtcpport,
+ sources, block);
+ if (ffurl_open_whitelist(&s->rtcp_hd, buf, rtcpflags, &h->interrupt_callback,
+ NULL, h->protocol_whitelist, h->protocol_blacklist, h) < 0)
+ goto fail;
+ break;
+ }
+
+ s->fec_hd = NULL;
+ if (fec_protocol) {
+ ff_url_join(buf, sizeof(buf), fec_protocol, NULL, hostname, rtp_port, NULL);
+ if (ffurl_open_whitelist(&s->fec_hd, buf, flags, &h->interrupt_callback,
+ &fec_opts, h->protocol_whitelist, h->protocol_blacklist, h) < 0)
+ goto fail;
+ }
/* just to ease handle access. XXX: need to suppress direct handle
access */
@@ -387,6 +456,10 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
h->max_packet_size = s->rtp_hd->max_packet_size;
h->is_streamed = 1;
+
+ av_free(fec_protocol);
+ av_dict_free(&fec_opts);
+
return 0;
fail:
@@ -394,6 +467,9 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
ffurl_close(s->rtp_hd);
if (s->rtcp_hd)
ffurl_close(s->rtcp_hd);
+ ffurl_closep(&s->fec_hd);
+ av_free(fec_protocol);
+ av_dict_free(&fec_opts);
return AVERROR(EIO);
}
@@ -441,7 +517,7 @@ static int rtp_read(URLContext *h, uint8_t *buf, int size)
static int rtp_write(URLContext *h, const uint8_t *buf, int size)
{
RTPContext *s = h->priv_data;
- int ret;
+ int ret, ret_fec;
URLContext *hd;
if (size < 2)
@@ -510,7 +586,17 @@ static int rtp_write(URLContext *h, const uint8_t *buf, int size)
hd = s->rtp_hd;
}
- ret = ffurl_write(hd, buf, size);
+ if ((ret = ffurl_write(hd, buf, size)) < 0) {
+ return ret;
+ }
+
+ if (s->fec_hd && !RTP_PT_IS_RTCP(buf[1])) {
+ if ((ret_fec = ffurl_write(s->fec_hd, buf, size)) < 0) {
+ av_log(h, AV_LOG_ERROR, "Failed to send FEC\n");
+ return ret_fec;
+ }
+ }
+
return ret;
}
@@ -520,14 +606,15 @@ 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);
ffurl_close(s->rtcp_hd);
+ ffurl_closep(&s->fec_hd);
return 0;
}
diff --git a/libavformat/rtpproto.h b/libavformat/rtpproto.h
index 7acc80e..5b243fb 100644
--- a/libavformat/rtpproto.h
+++ b/libavformat/rtpproto.h
@@ -1,20 +1,20 @@
/*
* RTP network protocol
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c
index 141477b..b6da61b 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,48 +65,52 @@
#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 COMMON_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 }, \
{ "buffer_size", "Underlying protocol send/receive buffer size", OFFSET(buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, DEC|ENC } \
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_BOOL, {.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 },
COMMON_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"),
COMMON_OPTS(),
{ NULL },
};
static const AVOption rtp_options[] = {
- RTSP_FLAG_OPTS("rtp_flags", "RTP flags"),
+ RTSP_FLAG_OPTS("rtp_flags", "set RTP flags"),
COMMON_OPTS(),
{ NULL },
};
@@ -174,7 +179,8 @@ static void rtsp_parse_range_npt(const char *p, int64_t *start, int64_t *end)
if (*p == '-') {
p++;
get_word_sep(buf, sizeof(buf), "-", &p);
- av_parse_time(end, buf, 1);
+ if (av_parse_time(end, buf, 1) < 0)
+ av_log(NULL, AV_LOG_DEBUG, "Failed to parse interval end specification '%s'\n", buf);
}
}
@@ -435,8 +441,10 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
codec_type = AVMEDIA_TYPE_AUDIO;
} else if (!strcmp(st_type, "video")) {
codec_type = AVMEDIA_TYPE_VIDEO;
- } else if (!strcmp(st_type, "application") || !strcmp(st_type, "text")) {
+ } 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;
@@ -477,7 +485,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(
@@ -702,10 +710,10 @@ 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);
return 0;
@@ -730,7 +738,7 @@ void ff_rtsp_undo_setup(AVFormatContext *s, int send_packets)
ff_rtsp_tcp_write_packet(s, rtsp_st);
ffio_free_dyn_buf(&rtpctx->pb);
} else {
- avio_close(rtpctx->pb);
+ avio_closep(&rtpctx->pb);
}
avformat_free_context(rtpctx);
} else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RDT)
@@ -763,24 +771,23 @@ void ff_rtsp_close_streams(AVFormatContext *s)
av_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_freep(&rt->protocols);
- 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)
@@ -801,7 +808,7 @@ int ff_rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st)
if (!st)
s->ctx_flags |= AVFMTCTX_NOHEADER;
- if (CONFIG_RTSP_MUXER && s->oformat) {
+ if (CONFIG_RTSP_MUXER && s->oformat && st) {
int ret = ff_rtp_chain_mux_open((AVFormatContext **)&rtsp_st->transport_priv,
s, st, rtsp_st->rtp_handle,
RTSP_TCP_MAX_PACKET_SIZE,
@@ -813,7 +820,7 @@ int ff_rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st)
st->time_base = ((AVFormatContext*)rtsp_st->transport_priv)->streams[0]->time_base;
} else if (rt->transport == RTSP_TRANSPORT_RAW) {
return 0; // Don't need to open any parser here
- } else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RDT)
+ } else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RDT && st)
rtsp_st->transport_priv = ff_rdt_parse_open(s, st->index,
rtsp_st->dynamic_protocol_context,
rtsp_st->dynamic_handler);
@@ -1214,7 +1221,7 @@ start:
if (content_ptr)
*content_ptr = content;
else
- av_free(content);
+ av_freep(&content);
if (request) {
char buf[1024];
@@ -1303,7 +1310,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);
@@ -1413,10 +1420,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. */
@@ -1468,15 +1471,14 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port,
"?localport=%d", j);
/* we will use two ports per rtp stream (rtp and rtcp) */
j += 2;
- err = ffurl_open(&rtsp_st->rtp_handle, buf, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, &opts, rt->protocols, NULL);
+ err = ffurl_open_whitelist(&rtsp_st->rtp_handle, buf, AVIO_FLAG_READ_WRITE,
+ &s->interrupt_callback, &opts, s->protocol_whitelist, s->protocol_blacklist, NULL);
av_dict_free(&opts);
if (!err)
goto rtp_opened;
}
-
av_log(s, AV_LOG_ERROR, "Unable to open an input RTP port\n");
err = AVERROR(EIO);
goto fail;
@@ -1544,7 +1546,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;
}
@@ -1612,8 +1614,8 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port,
namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST);
ff_url_join(url, sizeof(url), "rtp", NULL, namebuf,
port, "%s", optbuf);
- if (ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, NULL, rt->protocols, NULL) < 0) {
+ if (ffurl_open_whitelist(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
+ &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL) < 0) {
err = AVERROR_INVALIDDATA;
goto fail;
}
@@ -1670,13 +1672,6 @@ int ff_rtsp_connect(AVFormatContext *s)
if (!ff_network_init())
return AVERROR(EIO);
- if (!rt->protocols) {
- rt->protocols = ffurl_get_protocols(s->protocol_whitelist,
- s->protocol_blacklist);
- if (!rt->protocols)
- return AVERROR(ENOMEM);
- }
-
if (s->max_delay < 0) /* Not set by the caller */
s->max_delay = s->iformat ? DEFAULT_REORDERING_DELAY : 0;
@@ -1740,7 +1735,7 @@ redirect:
/* GET requests */
if (ffurl_alloc(&rt->rtsp_hd, httpname, AVIO_FLAG_READ,
- &s->interrupt_callback, rt->protocols) < 0) {
+ &s->interrupt_callback) < 0) {
err = AVERROR(EIO);
goto fail;
}
@@ -1754,6 +1749,14 @@ redirect:
sessioncookie);
av_opt_set(rt->rtsp_hd->priv_data, "headers", headers, 0);
+ if (!rt->rtsp_hd->protocol_whitelist && s->protocol_whitelist) {
+ rt->rtsp_hd->protocol_whitelist = av_strdup(s->protocol_whitelist);
+ if (!rt->rtsp_hd->protocol_whitelist) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+ }
+
/* complete the connection */
if (ffurl_connect(rt->rtsp_hd, NULL)) {
err = AVERROR(EIO);
@@ -1762,7 +1765,7 @@ redirect:
/* POST requests */
if (ffurl_alloc(&rt->rtsp_hd_out, httpname, AVIO_FLAG_WRITE,
- &s->interrupt_callback, rt->protocols) < 0 ) {
+ &s->interrupt_callback) < 0 ) {
err = AVERROR(EIO);
goto fail;
}
@@ -1803,12 +1806,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, rt->protocols, NULL) < 0) {
- err = AVERROR(EIO);
+ host, port,
+ "?timeout=%d", rt->stimeout);
+ if ((ret = ffurl_open_whitelist(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
+ &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL)) < 0) {
+ err = ret;
goto fail;
}
rt->rtsp_hd_out = rt->rtsp_hd;
@@ -1847,7 +1852,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;
}
@@ -1866,6 +1871,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;
@@ -1873,6 +1880,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);
@@ -1907,12 +1918,39 @@ redirect:
#endif /* CONFIG_RTSP_DEMUXER || CONFIG_RTSP_MUXER */
#if CONFIG_RTPDEC
+static int parse_rtsp_message(AVFormatContext *s)
+{
+ RTSPState *rt = s->priv_data;
+ int ret;
+
+ if (rt->rtsp_flags & RTSP_FLAG_LISTEN) {
+ if (rt->state == RTSP_STATE_STREAMING) {
+ if (!ff_rtsp_parse_streaming_commands(s))
+ return AVERROR_EOF;
+ else
+ av_log(s, AV_LOG_WARNING,
+ "Unable to answer to TEARDOWN\n");
+ } else
+ return 0;
+ } else {
+ RTSPMessageHeader reply;
+ ret = ff_rtsp_read_reply(s, &reply, NULL, 0, NULL);
+ if (ret < 0)
+ return ret;
+ /* XXX: parse message */
+ if (rt->state != RTSP_STATE_STREAMING)
+ return 0;
+ }
+
+ return 0;
+}
+
static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
uint8_t *buf, int buf_size, int64_t wait_end)
{
RTSPState *rt = s->priv_data;
RTSPStream *rtsp_st;
- int n, i, ret, tcp_fd, timeout_cnt = 0;
+ int n, i, ret, timeout_cnt = 0;
struct pollfd *p = rt->p;
int *fds = NULL, fdsnum, fdsidx;
@@ -1922,11 +1960,8 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
return AVERROR(ENOMEM);
if (rt->rtsp_hd) {
- tcp_fd = ffurl_get_file_handle(rt->rtsp_hd);
- p[rt->max_p].fd = tcp_fd;
+ p[rt->max_p].fd = ffurl_get_file_handle(rt->rtsp_hd);
p[rt->max_p++].events = POLLIN;
- } else {
- tcp_fd = -1;
}
for (i = 0; i < rt->nb_rtsp_streams; i++) {
rtsp_st = rt->rtsp_streams[i];
@@ -1945,7 +1980,7 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
p[rt->max_p].fd = fds[fdsidx];
p[rt->max_p++].events = POLLIN;
}
- av_free(fds);
+ av_freep(&fds);
}
}
}
@@ -1957,7 +1992,7 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
return AVERROR(EAGAIN);
n = poll(p, rt->max_p, POLL_TIMEOUT_MS);
if (n > 0) {
- int j = 1 - (tcp_fd == -1);
+ int j = rt->rtsp_hd ? 1 : 0;
timeout_cnt = 0;
for (i = 0; i < rt->nb_rtsp_streams; i++) {
rtsp_st = rt->rtsp_streams[i];
@@ -1973,25 +2008,8 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
}
}
#if CONFIG_RTSP_DEMUXER
- if (tcp_fd != -1 && p[0].revents & POLLIN) {
- if (rt->rtsp_flags & RTSP_FLAG_LISTEN) {
- if (rt->state == RTSP_STATE_STREAMING) {
- if (!ff_rtsp_parse_streaming_commands(s))
- return AVERROR_EOF;
- else
- av_log(s, AV_LOG_WARNING,
- "Unable to answer to TEARDOWN\n");
- } else
- return 0;
- } else {
- RTSPMessageHeader reply;
- ret = ff_rtsp_read_reply(s, &reply, NULL, 0, NULL);
- if (ret < 0)
- return ret;
- /* XXX: parse message */
- if (rt->state != RTSP_STATE_STREAMING)
- return 0;
- }
+ if (rt->rtsp_hd && p[0].revents & POLLIN) {
+ return parse_rtsp_message(s);
}
#endif
} else if (n == 0 && ++timeout_cnt >= MAX_TIMEOUTS) {
@@ -2070,7 +2088,7 @@ static int read_packet(AVFormatContext *s,
wait_end && wait_end < av_gettime_relative())
len = AVERROR(EAGAIN);
else
- len = ffio_read_partial(s->pb, rt->recvbuf, RECVBUF_SIZE);
+ len = avio_read_partial(s->pb, rt->recvbuf, RECVBUF_SIZE);
len = pick_stream(s, rtsp_st, rt->recvbuf, len);
if (len > 0 && (*rtsp_st)->transport_priv && rt->transport == RTSP_TRANSPORT_RTP)
ff_rtp_check_and_send_back_rr((*rtsp_st)->transport_priv, NULL, s->pb, len);
@@ -2100,7 +2118,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;
@@ -2195,6 +2213,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++;
@@ -2207,7 +2235,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;
@@ -2239,7 +2267,7 @@ static int sdp_probe(AVProbeData *p1)
/* we look for a line beginning "c=IN IP" */
while (p < p_end && *p != '\0') {
- if (p + sizeof("c=IN IP") - 1 < p_end &&
+ if (sizeof("c=IN IP") - 1 < p_end - p &&
av_strstart(p, "c=IN IP", NULL))
return AVPROBE_SCORE_EXTENSION;
@@ -2274,13 +2302,6 @@ static int sdp_read_header(AVFormatContext *s)
if (!ff_network_init())
return AVERROR(EIO);
- if (!rt->protocols) {
- rt->protocols = ffurl_get_protocols(s->protocol_whitelist,
- s->protocol_blacklist);
- if (!rt->protocols)
- return AVERROR(ENOMEM);
- }
-
if (s->max_delay < 0) /* Not set by the caller */
s->max_delay = DEFAULT_REORDERING_DELAY;
if (rt->rtsp_flags & RTSP_FLAG_CUSTOM_IO)
@@ -2299,7 +2320,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 */
@@ -2332,8 +2353,8 @@ static int sdp_read_header(AVFormatContext *s)
append_source_addrs(url, sizeof(url), "block",
rtsp_st->nb_exclude_source_addrs,
rtsp_st->exclude_source_addrs);
- err = ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, &opts, rt->protocols, NULL);
+ err = ffurl_open_whitelist(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ,
+ &s->interrupt_callback, &opts, s->protocol_whitelist, s->protocol_blacklist, NULL);
av_dict_free(&opts);
@@ -2402,15 +2423,8 @@ static int rtp_read_header(AVFormatContext *s)
if (!ff_network_init())
return AVERROR(EIO);
- if (!rt->protocols) {
- rt->protocols = ffurl_get_protocols(s->protocol_whitelist,
- s->protocol_blacklist);
- if (!rt->protocols)
- return AVERROR(ENOMEM);
- }
-
- ret = ffurl_open(&in, s->filename, AVIO_FLAG_READ,
- &s->interrupt_callback, NULL, rt->protocols, NULL);
+ ret = ffurl_open_whitelist(&in, s->filename, AVIO_FLAG_READ,
+ &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret)
goto fail;
@@ -2477,7 +2491,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 9dfbc53..36fdae4 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
@@ -393,14 +393,22 @@ 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];
int buffer_size;
-
- const URLProtocol **protocols;
} RTSPState;
#define RTSP_FLAG_FILTER_SRC 0x1 /**< Filter incoming UDP packets -
@@ -410,6 +418,7 @@ typedef struct RTSPState {
#define RTSP_FLAG_CUSTOM_IO 0x4 /**< Do all IO via the AVIOContext. */
#define RTSP_FLAG_RTCP_TO_SOURCE 0x8 /**< Send RTCP packets to the source
address of received packets. */
+#define RTSP_FLAG_PREFER_TCP 0x10 /**< Try RTP via TCP first if possible. */
typedef struct RTSPSource {
char addr[128]; /**< Source-specific multicast include source IP address (from SDP content) */
diff --git a/libavformat/rtspcodes.h b/libavformat/rtspcodes.h
index 31ab336..0ae490a 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 8e8b340..fdf75a0 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
*/
@@ -294,8 +294,9 @@ static int rtsp_read_setup(AVFormatContext *s, char* host, char *controlurl)
av_dict_set(&opts, "buffer_size", buf, 0);
ff_url_join(url, sizeof(url), "rtp", NULL, host, localport, NULL);
av_log(s, AV_LOG_TRACE, "Opening: %s", url);
- ret = ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, &opts, rt->protocols, NULL);
+ ret = ffurl_open_whitelist(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
+ &s->interrupt_callback, &opts,
+ s->protocol_whitelist, s->protocol_blacklist, NULL);
av_dict_free(&opts);
if (ret)
localport += 2;
@@ -367,8 +368,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");
@@ -495,7 +498,6 @@ int ff_rtsp_parse_streaming_commands(AVFormatContext *s)
} else if (methodcode == TEARDOWN) {
rt->state = RTSP_STATE_IDLE;
ret = rtsp_send_reply(s, RTSP_STATUS_OK, NULL , request.seq);
- return 0;
}
return ret;
}
@@ -548,7 +550,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) {
@@ -558,6 +560,7 @@ static int rtsp_read_play(AVFormatContext *s)
AVStream *st = NULL;
if (!rtpctx || rtsp_st->stream_index < 0)
continue;
+
st = s->streams[rtsp_st->stream_index];
rtpctx->range_start_offset =
av_rescale_q(reply->range_start, AV_TIME_BASE_Q,
@@ -580,7 +583,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;
@@ -607,12 +610,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 */
@@ -639,13 +642,6 @@ static int rtsp_listen(AVFormatContext *s)
int ret;
enum RTSPMethod methodcode;
- if (!rt->protocols) {
- rt->protocols = ffurl_get_protocols(s->protocol_whitelist,
- s->protocol_blacklist);
- if (!rt->protocols)
- return AVERROR(ENOMEM);
- }
-
/* extract hostname and port */
av_url_split(proto, sizeof(proto), auth, sizeof(auth), host, sizeof(host),
&port, path, sizeof(path), s->filename);
@@ -666,8 +662,9 @@ static int rtsp_listen(AVFormatContext *s)
ff_url_join(tcpname, sizeof(tcpname), lower_proto, NULL, host, port,
"?listen&listen_timeout=%d", rt->initial_timeout * 1000);
- if (ret = ffurl_open(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, NULL, rt->protocols, NULL)) {
+ if (ret = ffurl_open_whitelist(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
+ &s->interrupt_callback, NULL,
+ s->protocol_whitelist, s->protocol_blacklist, NULL)) {
av_log(s, AV_LOG_ERROR, "Unable to open RTSP for listening\n");
return ret;
}
@@ -731,7 +728,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;
@@ -739,10 +736,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;
}
}
}
@@ -777,7 +774,7 @@ redo:
id = buf[0];
len = AV_RB16(buf + 1);
av_log(s, AV_LOG_TRACE, "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);
@@ -836,7 +833,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;
}
}
@@ -871,7 +868,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)
@@ -932,6 +929,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,
@@ -941,11 +939,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 3db53ac..e7707bb 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/s337m.c b/libavformat/s337m.c
new file mode 100644
index 0000000..2e85d48
--- /dev/null
+++ b/libavformat/s337m.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2017 foo86
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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 "spdif.h"
+
+#define MARKER_16LE 0x72F81F4E
+#define MARKER_20LE 0x20876FF0E154
+#define MARKER_24LE 0x72F8961F4EA5
+
+#define IS_16LE_MARKER(state) ((state & 0xFFFFFFFF) == MARKER_16LE)
+#define IS_20LE_MARKER(state) ((state & 0xF0FFFFF0FFFF) == MARKER_20LE)
+#define IS_24LE_MARKER(state) ((state & 0xFFFFFFFFFFFF) == MARKER_24LE)
+#define IS_LE_MARKER(state) (IS_16LE_MARKER(state) || IS_20LE_MARKER(state) || IS_24LE_MARKER(state))
+
+static int s337m_get_offset_and_codec(AVFormatContext *s,
+ uint64_t state,
+ int data_type, int data_size,
+ int *offset, enum AVCodecID *codec)
+{
+ int word_bits;
+
+ if (IS_16LE_MARKER(state)) {
+ word_bits = 16;
+ } else if (IS_20LE_MARKER(state)) {
+ data_type >>= 8;
+ data_size >>= 4;
+ word_bits = 20;
+ } else {
+ data_type >>= 8;
+ word_bits = 24;
+ }
+
+ if ((data_type & 0x1F) != 0x1C) {
+ if (s)
+ avpriv_report_missing_feature(s, "Data type %#x in SMPTE 337M", data_type & 0x1F);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ if (codec)
+ *codec = AV_CODEC_ID_DOLBY_E;
+
+ switch (data_size / word_bits) {
+ case 3648:
+ *offset = 1920;
+ break;
+ case 3644:
+ *offset = 2002;
+ break;
+ case 3640:
+ *offset = 2000;
+ break;
+ case 3040:
+ *offset = 1601;
+ break;
+ default:
+ if (s)
+ avpriv_report_missing_feature(s, "Dolby E data size %d in SMPTE 337M", data_size);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ *offset -= 4;
+ *offset *= (word_bits + 7 >> 3) * 2;
+ return 0;
+}
+
+static int s337m_probe(AVProbeData *p)
+{
+ uint64_t state = 0;
+ int markers[3] = { 0 };
+ int i, pos, sum, max, data_type, data_size, offset;
+ uint8_t *buf;
+
+ for (pos = 0; pos < p->buf_size; pos++) {
+ state = (state << 8) | p->buf[pos];
+ if (!IS_LE_MARKER(state))
+ continue;
+
+ buf = p->buf + pos + 1;
+ if (IS_16LE_MARKER(state)) {
+ data_type = AV_RL16(buf );
+ data_size = AV_RL16(buf + 2);
+ } else {
+ data_type = AV_RL24(buf );
+ data_size = AV_RL24(buf + 3);
+ }
+
+ if (s337m_get_offset_and_codec(NULL, state, data_type, data_size, &offset, NULL))
+ continue;
+
+ i = IS_16LE_MARKER(state) ? 0 : IS_20LE_MARKER(state) ? 1 : 2;
+ markers[i]++;
+
+ pos += IS_16LE_MARKER(state) ? 4 : 6;
+ pos += offset;
+ state = 0;
+ }
+
+ 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 && markers[max] * 4 > sum * 3)
+ return AVPROBE_SCORE_EXTENSION + 1;
+
+ return 0;
+}
+
+static int s337m_read_header(AVFormatContext *s)
+{
+ s->ctx_flags |= AVFMTCTX_NOHEADER;
+ return 0;
+}
+
+static void bswap_buf24(uint8_t *data, int size)
+{
+ int i;
+
+ for (i = 0; i < size / 3; i++, data += 3)
+ FFSWAP(uint8_t, data[0], data[2]);
+}
+
+static int s337m_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVIOContext *pb = s->pb;
+ uint64_t state = 0;
+ int ret, data_type, data_size, offset;
+ enum AVCodecID codec;
+ int64_t pos;
+
+ while (!IS_LE_MARKER(state)) {
+ state = (state << 8) | avio_r8(pb);
+ if (avio_feof(pb))
+ return AVERROR_EOF;
+ }
+
+ if (IS_16LE_MARKER(state)) {
+ data_type = avio_rl16(pb);
+ data_size = avio_rl16(pb);
+ } else {
+ data_type = avio_rl24(pb);
+ data_size = avio_rl24(pb);
+ }
+
+ pos = avio_tell(pb);
+
+ if ((ret = s337m_get_offset_and_codec(s, state, data_type, data_size, &offset, &codec)) < 0)
+ return ret;
+
+ if ((ret = av_new_packet(pkt, offset)) < 0)
+ return ret;
+
+ pkt->pos = pos;
+
+ if (avio_read(pb, pkt->data, pkt->size) < pkt->size) {
+ av_packet_unref(pkt);
+ return AVERROR_EOF;
+ }
+
+ if (IS_16LE_MARKER(state))
+ ff_spdif_bswap_buf16((uint16_t *)pkt->data, (uint16_t *)pkt->data, pkt->size >> 1);
+ else
+ bswap_buf24(pkt->data, pkt->size);
+
+ if (!s->nb_streams) {
+ AVStream *st = avformat_new_stream(s, NULL);
+ if (!st) {
+ av_packet_unref(pkt);
+ return AVERROR(ENOMEM);
+ }
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = codec;
+ }
+
+ return 0;
+}
+
+AVInputFormat ff_s337m_demuxer = {
+ .name = "s337m",
+ .long_name = NULL_IF_CONFIG_SMALL("SMPTE 337M"),
+ .read_probe = s337m_probe,
+ .read_header = s337m_read_header,
+ .read_packet = s337m_read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/samidec.c b/libavformat/samidec.c
new file mode 100644
index 0000000..7ea1bdf
--- /dev/null
+++ b/libavformat/samidec.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
+ * 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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->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, is_body, n = ff_smil_extract_next_text_chunk(&tr, &buf, &c);
+
+ if (n == 0)
+ break;
+
+ is_body = !av_strncasecmp(buf.str, "</BODY", 6);
+ if (is_body) {
+ av_bprint_clear(&buf);
+ 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 = ff_bprint_to_codecpar_extradata(st->codecpar, &hdr_buf);
+ if (res < 0)
+ goto end;
+
+ ff_subtitles_queue_finalize(s, &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 ce0ceaa..522b38d 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"
@@ -39,8 +40,6 @@ struct SAPState {
uint16_t hash;
char *sdp;
int eof;
-
- const URLProtocol **protocols;
};
static int sap_probe(AVProbeData *p)
@@ -57,7 +56,6 @@ static int sap_read_close(AVFormatContext *s)
avformat_close_input(&sap->sdp_ctx);
if (sap->ann_fd)
ffurl_close(sap->ann_fd);
- av_freep(&sap->protocols);
av_freep(&sap->sdp);
ff_network_close();
return 0;
@@ -85,17 +83,11 @@ static int sap_read_header(AVFormatContext *s)
av_strlcpy(host, "224.2.127.254", sizeof(host));
}
- sap->protocols = ffurl_get_protocols(s->protocol_whitelist,
- s->protocol_blacklist);
- if (!sap->protocols) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
-
ff_url_join(url, sizeof(url), "udp", NULL, host, port, "?localport=%d",
port);
- ret = ffurl_open(&sap->ann_fd, url, AVIO_FLAG_READ,
- &s->interrupt_callback, NULL, sap->protocols, NULL);
+ ret = ffurl_open_whitelist(&sap->ann_fd, url, AVIO_FLAG_READ,
+ &s->interrupt_callback, NULL,
+ s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret)
goto fail;
@@ -168,6 +160,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_whiteblacklists(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 ed3a024..3098e34 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
*/
@@ -37,8 +37,6 @@ struct SAPState {
int ann_size;
URLContext *ann_fd;
int64_t last_time;
-
- const URLProtocol **protocols;
};
static int sap_write_close(AVFormatContext *s)
@@ -51,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;
}
@@ -61,8 +59,6 @@ static int sap_write_close(AVFormatContext *s)
ffurl_write(sap->ann_fd, sap->ann, sap->ann_size);
}
- av_freep(&sap->protocols);
-
av_freep(&sap->ann);
if (sap->ann_fd)
ffurl_close(sap->ann_fd);
@@ -138,20 +134,14 @@ static int sap_write_header(AVFormatContext *s)
freeaddrinfo(ai);
}
- sap->protocols = ffurl_get_protocols(s->protocol_whitelist,
- s->protocol_blacklist);
- if (!sap->protocols) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
-
- 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;
@@ -159,8 +149,9 @@ static int sap_write_header(AVFormatContext *s)
"?ttl=%d", ttl);
if (!same_port)
base_port += 2;
- ret = ffurl_open(&fd, url, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL,
- sap->protocols, NULL);
+ ret = ffurl_open_whitelist(&fd, url, AVIO_FLAG_WRITE,
+ &s->interrupt_callback, NULL,
+ s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret) {
ret = AVERROR(EIO);
goto fail;
@@ -178,8 +169,9 @@ static int sap_write_header(AVFormatContext *s)
ff_url_join(url, sizeof(url), "udp", NULL, announce_addr, port,
"?ttl=%d&connect=1", ttl);
- ret = ffurl_open(&sap->ann_fd, url, AVIO_FLAG_WRITE,
- &s->interrupt_callback, NULL, sap->protocols, NULL);
+ ret = ffurl_open_whitelist(&sap->ann_fd, url, AVIO_FLAG_WRITE,
+ &s->interrupt_callback, NULL,
+ s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret) {
ret = AVERROR(EIO);
goto fail;
@@ -267,7 +259,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 335c989..5ac9ca9 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]->codecpar->height = t2<<4;
} else if (datatype == 5) {
- if (filetype > 1) {
+ if (filetype) {
avctx->streams[0]->codecpar->width = (filetype == 1 ? t1 : filetype) << 4;
*got_width = 1;
}
diff --git a/libavformat/sauce.h b/libavformat/sauce.h
index 62d8e68..0ba9ae5 100644
--- a/libavformat/sauce.h
+++ b/libavformat/sauce.h
@@ -2,20 +2,20 @@
* SAUCE header parser
* Copyright (c) 2010 Peter Ross <pross@xvid.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/sbgdec.c b/libavformat/sbgdec.c
new file mode 100644
index 0000000..cbedd12
--- /dev/null
+++ b/libavformat/sbgdec.c
@@ -0,0 +1,1514 @@
+/*
+ * 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) ? 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);
+ if (!ev)
+ return AVERROR(ENOMEM);
+ 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, AVCodecParameters *par,
+ 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(par, edata_size))
+ return AVERROR(ENOMEM);
+ edata = par->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 != par->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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_FFWAVESYNTH;
+ st->codecpar->channels = 2;
+ st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
+ st->codecpar->sample_rate = sbg->sample_rate;
+ st->codecpar->frame_size = sbg->frame_size;
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->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->codecpar, &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]->codecpar->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/sccdec.c b/libavformat/sccdec.c
new file mode 100644
index 0000000..89d21b9
--- /dev/null
+++ b/libavformat/sccdec.c
@@ -0,0 +1,180 @@
+/*
+ * SCC subtitle demuxer
+ * Copyright (c) 2017 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 "subtitles.h"
+#include "libavutil/bprint.h"
+#include "libavutil/intreadwrite.h"
+
+typedef struct SCCContext {
+ FFDemuxSubtitlesQueue q;
+} SCCContext;
+
+static int scc_probe(AVProbeData *p)
+{
+ char buf[18];
+ 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);
+
+ ff_text_read(&tr, buf, sizeof(buf));
+
+ if (!memcmp(buf, "Scenarist_SCC V1.0", 18))
+ return AVPROBE_SCORE_MAX;
+
+ return 0;
+}
+
+static int convert(uint8_t x)
+{
+ if (x >= 'a')
+ x -= 87;
+ else if (x >= 'A')
+ x -= 55;
+ else
+ x -= '0';
+ return x;
+}
+
+static int scc_read_header(AVFormatContext *s)
+{
+ SCCContext *scc = s->priv_data;
+ AVStream *st = avformat_new_stream(s, NULL);
+ char line[4096], line2[4096];
+ int count = 0, ret = 0;
+ ptrdiff_t len2, len;
+ uint8_t out[4096];
+ FFTextReader tr;
+
+ ff_text_init_avio(s, &tr, s->pb);
+
+ if (!st)
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 64, 1, 1000);
+ st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->codec_id = AV_CODEC_ID_EIA_608;
+
+ while (!ff_text_eof(&tr)) {
+ const int64_t pos = ff_text_pos(&tr);
+ char *saveptr = NULL, *lline;
+ int hh1, mm1, ss1, fs1, i;
+ int hh2, mm2, ss2, fs2;
+ int64_t ts_start, ts_end;
+ AVPacket *sub;
+
+ if (count == 0) {
+ while (!ff_text_eof(&tr)) {
+ len = ff_subtitles_read_line(&tr, line, sizeof(line));
+ if (len > 13)
+ break;
+ }
+ }
+
+ if (!strncmp(line, "Scenarist_SCC V1.0", 18))
+ continue;
+ if (sscanf(line, "%d:%d:%d%*[:;]%d", &hh1, &mm1, &ss1, &fs1) != 4)
+ continue;
+
+ ts_start = (hh1 * 3600LL + mm1 * 60LL + ss1) * 1000LL + fs1 * 33;
+
+ while (!ff_text_eof(&tr)) {
+ len2 = ff_subtitles_read_line(&tr, line2, sizeof(line2));
+ if (len2 > 13)
+ break;
+ }
+ if (sscanf(line2, "%d:%d:%d%*[:;]%d", &hh2, &mm2, &ss2, &fs2) != 4)
+ continue;
+
+ ts_end = (hh2 * 3600LL + mm2 * 60LL + ss2) * 1000LL + fs2 * 33;
+ count++;
+
+ lline = (char *)&line;
+ lline += 12;
+
+ for (i = 0; i < 4095; i += 3) {
+ char *ptr = av_strtok(lline, " ", &saveptr);
+ char c1, c2, c3, c4;
+
+ if (!ptr)
+ break;
+
+ if (sscanf(ptr, "%c%c%c%c", &c1, &c2, &c3, &c4) != 4)
+ break;
+
+ lline = NULL;
+ out[i+0] = 0xfc;
+ out[i+1] = convert(c2) | (convert(c1) << 4);
+ out[i+2] = convert(c4) | (convert(c3) << 4);
+ }
+ out[i] = 0;
+
+ sub = ff_subtitles_queue_insert(&scc->q, out, i, 0);
+ if (!sub)
+ return AVERROR(ENOMEM);
+
+ sub->pos = pos;
+ sub->pts = ts_start;
+ sub->duration = FFMAX(1200, ts_end - ts_start);
+ memmove(line, line2, sizeof(line));
+ FFSWAP(ptrdiff_t, len, len2);
+ }
+
+ ff_subtitles_queue_finalize(s, &scc->q);
+
+ return ret;
+}
+
+static int scc_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ SCCContext *scc = s->priv_data;
+ return ff_subtitles_queue_read_packet(&scc->q, pkt);
+}
+
+static int scc_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ SCCContext *scc = s->priv_data;
+ return ff_subtitles_queue_seek(&scc->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int scc_read_close(AVFormatContext *s)
+{
+ SCCContext *scc = s->priv_data;
+ ff_subtitles_queue_clean(&scc->q);
+ return 0;
+}
+
+AVInputFormat ff_scc_demuxer = {
+ .name = "scc",
+ .long_name = NULL_IF_CONFIG_SMALL("Scenarist Closed Captions"),
+ .priv_data_size = sizeof(SCCContext),
+ .read_probe = scc_probe,
+ .read_header = scc_read_header,
+ .read_packet = scc_read_packet,
+ .read_seek2 = scc_read_seek,
+ .read_close = scc_read_close,
+ .extensions = "scc",
+};
diff --git a/libavformat/sccenc.c b/libavformat/sccenc.c
new file mode 100644
index 0000000..f3cf3d5
--- /dev/null
+++ b/libavformat/sccenc.c
@@ -0,0 +1,123 @@
+/*
+ * SCC muxer
+ * Copyright (c) 2017 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 "libavutil/log.h"
+#include "libavutil/intreadwrite.h"
+
+typedef struct SCCContext {
+ int prev_h, prev_m, prev_s, prev_f;
+ int inside;
+ int n;
+} SCCContext;
+
+static int scc_write_header(AVFormatContext *avf)
+{
+ SCCContext *scc = avf->priv_data;
+
+ if (avf->nb_streams != 1 ||
+ avf->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
+ av_log(avf, AV_LOG_ERROR,
+ "SCC supports only a single subtitles stream.\n");
+ return AVERROR(EINVAL);
+ }
+ if (avf->streams[0]->codecpar->codec_id != AV_CODEC_ID_EIA_608) {
+ av_log(avf, AV_LOG_ERROR,
+ "Unsupported subtitles codec: %s\n",
+ avcodec_get_name(avf->streams[0]->codecpar->codec_id));
+ return AVERROR(EINVAL);
+ }
+ avpriv_set_pts_info(avf->streams[0], 64, 1, 1000);
+ avio_printf(avf->pb, "Scenarist_SCC V1.0\n");
+
+ scc->prev_h = scc->prev_m = scc->prev_s = scc->prev_f = -1;
+ scc->inside = 0;
+
+ return 0;
+}
+
+static int scc_write_packet(AVFormatContext *avf, AVPacket *pkt)
+{
+ SCCContext *scc = avf->priv_data;
+ int64_t pts = pkt->pts;
+ int i, h, m, s, f;
+
+ if (pts == AV_NOPTS_VALUE) {
+ av_log(avf, AV_LOG_WARNING,
+ "Insufficient timestamps.\n");
+ return 0;
+ }
+
+ h = (int)(pts / (3600000));
+ m = (int)(pts / (60000)) % 60;
+ s = (int)(pts / 1000) % 60;
+ f = (int)(pts % 1000) / 33;
+
+ for (i = 0; i < pkt->size; i+=3) {
+ if (pkt->data[i] == 0xfc && ((pkt->data[i + 1] != 0x80 || pkt->data[i + 2] != 0x80)))
+ break;
+ }
+ if (i >= pkt->size)
+ return 0;
+
+ if (!scc->inside && (scc->prev_h != h || scc->prev_m != m || scc->prev_s != s || scc->prev_f != f)) {
+ avio_printf(avf->pb, "\n%02d:%02d:%02d:%02d\t", h, m, s, f);
+ scc->inside = 1;
+ }
+ for (i = 0; i < pkt->size; i+=3) {
+ if (i + 3 > pkt->size)
+ break;
+
+ if (pkt->data[i] != 0xfc || (pkt->data[i + 1] == 0x80 && pkt->data[i + 2] == 0x80))
+ continue;
+ if (!scc->inside) {
+ avio_printf(avf->pb, "\n%02d:%02d:%02d:%02d\t", h, m, s, f);
+ scc->inside = 1;
+ }
+ if (scc->n > 0)
+ avio_printf(avf->pb, " ");
+ avio_printf(avf->pb, "%02x%02x", pkt->data[i + 1], pkt->data[i + 2]);
+ scc->n++;
+ }
+ if (scc->inside && (scc->prev_h != h || scc->prev_m != m || scc->prev_s != s || scc->prev_f != f)) {
+ avio_printf(avf->pb, "\n");
+ scc->n = 0;
+ scc->inside = 0;
+ }
+
+ scc->prev_h = h;
+ scc->prev_m = m;
+ scc->prev_s = s;
+ scc->prev_f = f;
+ return 0;
+}
+
+AVOutputFormat ff_scc_muxer = {
+ .name = "scc",
+ .long_name = NULL_IF_CONFIG_SMALL("Scenarist Closed Captions"),
+ .extensions = "scc",
+ .priv_data_size = sizeof(SCCContext),
+ .write_header = scc_write_header,
+ .write_packet = scc_write_packet,
+ .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT,
+ .subtitle_codec = AV_CODEC_ID_EIA_608,
+};
diff --git a/libavformat/sctp.c b/libavformat/sctp.c
index b321139..9a80e9b 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
*/
@@ -99,7 +99,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))
@@ -122,7 +122,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;
@@ -161,7 +161,7 @@ typedef struct SCTPContext {
#define D AV_OPT_FLAG_DECODING_PARAM
#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 },
+ { "listen", "Listen for incoming connections", OFFSET(listen), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, .flags = D|E },
{ "timeout", "Connection timeout (in milliseconds)", OFFSET(timeout), AV_OPT_TYPE_INT, { .i64 = 10000 }, INT_MIN, INT_MAX, .flags = D|E },
{ "listen_timeout", "Bind timeout (in milliseconds)", OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, INT_MIN, INT_MAX, .flags = D|E },
{ "max_streams", "Max stream to allocate", OFFSET(max_streams), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT16_MAX, .flags = D|E },
@@ -335,8 +335,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 be6c95d..0242ca3 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(AVFormatContext *s, AVCodecParameters *par)
sps_end = r1;
}
if (!av_base64_encode(p, MAX_PSET_SIZE - (p - psets), r, r1 - r)) {
- av_log(s, AV_LOG_ERROR, "Cannot Base64-encode %td %td!\n", MAX_PSET_SIZE - (p - psets), r1 - r);
+ av_log(s, 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(AVFormatContext *s, AVCodecParameters *par)
static char *xiph_extradata2config(AVFormatContext *s, AVCodecParameters *par)
{
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;
@@ -479,11 +479,15 @@ static char *latm_context2config(AVFormatContext *s, AVCodecParameters *par)
return config;
}
-static char *sdp_write_media_attributes(char *buff, int size, AVCodecParameters *p, int payload_type, AVFormatContext *fmt)
+static char *sdp_write_media_attributes(char *buff, int size, AVStream *st, int payload_type, AVFormatContext *fmt)
{
char *config = NULL;
+ AVCodecParameters *p = st->codecpar;
switch (p->codec_id) {
+ case AV_CODEC_ID_DIRAC:
+ av_strlcatf(buff, size, "a=rtpmap:%d VC2/90000\r\n", payload_type);
+ break;
case AV_CODEC_ID_H264: {
int mode = 1;
if (fmt && fmt->oformat && fmt->oformat->priv_class &&
@@ -544,7 +548,7 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecParameters
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(fmt, p);
if (!config)
@@ -637,7 +641,7 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecParameters
if (p->extradata_size)
config = xiph_extradata2config(fmt, p);
else
- av_log(fmt, AV_LOG_ERROR, "Theora configuation info missing\n");
+ av_log(fmt, AV_LOG_ERROR, "Theora configuration info missing\n");
if (!config)
return NULL;
@@ -653,6 +657,10 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecParameters
av_strlcatf(buff, size, "a=rtpmap:%d VP8/90000\r\n",
payload_type);
break;
+ case AV_CODEC_ID_VP9:
+ av_strlcatf(buff, size, "a=rtpmap:%d VP9/90000\r\n",
+ payload_type);
+ break;
case AV_CODEC_ID_MJPEG:
if (payload_type >= RTP_PT_PRIVATE)
av_strlcatf(buff, size, "a=rtpmap:%d JPEG/90000\r\n",
@@ -666,6 +674,14 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecParameters
break;
case AV_CODEC_ID_ADPCM_G726: {
if (payload_type >= RTP_PT_PRIVATE)
+ av_strlcatf(buff, size, "a=rtpmap:%d AAL2-G726-%d/%d\r\n",
+ payload_type,
+ p->bits_per_coded_sample*8,
+ p->sample_rate);
+ break;
+ }
+ case AV_CODEC_ID_ADPCM_G726LE: {
+ if (payload_type >= RTP_PT_PRIVATE)
av_strlcatf(buff, size, "a=rtpmap:%d G726-%d/%d\r\n",
payload_type,
p->bits_per_coded_sample*8,
@@ -681,6 +697,20 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecParameters
case AV_CODEC_ID_SPEEX:
av_strlcatf(buff, size, "a=rtpmap:%d speex/%d\r\n",
payload_type, p->sample_rate);
+ if (st->codec) {
+ const char *mode;
+ uint64_t vad_option;
+
+ if (st->codec->flags & AV_CODEC_FLAG_QSCALE)
+ mode = "on";
+ else if (!av_opt_get_int(st->codec, "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
@@ -726,10 +756,10 @@ void ff_sdp_write_media(char *buff, int size, AVStream *st, int idx,
av_strlcatf(buff, size, "m=%s %d RTP/AVP %d\r\n", type, port, payload_type);
sdp_write_address(buff, size, dest_addr, dest_type, ttl);
if (p->bit_rate) {
- av_strlcatf(buff, size, "b=AS:%d\r\n", p->bit_rate / 1000);
+ av_strlcatf(buff, size, "b=AS:%"PRId64"\r\n", p->bit_rate / 1000);
}
- sdp_write_media_attributes(buff, size, p, payload_type, fmt);
+ sdp_write_media_attributes(buff, size, st, payload_type, fmt);
}
int av_sdp_create(AVFormatContext *ac[], int n_files, char *buf, int size)
diff --git a/libavformat/sdr2.c b/libavformat/sdr2.c
new file mode 100644
index 0000000..c995333
--- /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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->width = avio_rl32(s->pb);
+ st->codecpar->height = avio_rl32(s->pb);
+ st->codecpar->codec_id = AV_CODEC_ID_H264;
+ st->need_parsing = AVSTREAM_PARSE_FULL;
+
+ ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ ast->codecpar->channels = 1;
+ ast->codecpar->sample_rate = 8000;
+ ast->codecpar->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_packet_unref(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/sdsdec.c b/libavformat/sdsdec.c
new file mode 100644
index 0000000..081bb4c
--- /dev/null
+++ b/libavformat/sdsdec.c
@@ -0,0 +1,165 @@
+/*
+ * MIDI Sample Dump Standard format demuxer
+ * Copyright (c) 2017 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"
+
+typedef struct SDSContext {
+ uint8_t data[120];
+ int bit_depth;
+ int size;
+ void (*read_block)(const uint8_t *src, uint32_t *dst);
+} SDSContext;
+
+static int sds_probe(AVProbeData *p)
+{
+ if (AV_RB32(p->buf) == 0xF07E0001 && p->buf[20] == 0xF7 &&
+ p->buf[6] >= 8 && p->buf[6] <= 28)
+ return AVPROBE_SCORE_EXTENSION;
+ return 0;
+}
+
+static void byte2_read(const uint8_t *src, uint32_t *dst)
+{
+ int i;
+
+ for (i = 0; i < 120; i += 2) {
+ unsigned sample = (src[i + 0] << 25) + (src[i + 1] << 18);
+
+ dst[i / 2] = sample;
+ }
+}
+
+static void byte3_read(const uint8_t *src, uint32_t *dst)
+{
+ int i;
+
+ for (i = 0; i < 120; i += 3) {
+ unsigned sample;
+
+ sample = (src[i + 0] << 25) | (src[i + 1] << 18) | (src[i + 2] << 11);
+ dst[i / 3] = sample;
+ }
+}
+
+static void byte4_read(const uint8_t *src, uint32_t *dst)
+{
+ int i;
+
+ for (i = 0; i < 120; i += 4) {
+ unsigned sample;
+
+ sample = (src[i + 0] << 25) | (src[i + 1] << 18) | (src[i + 2] << 11) | (src[i + 3] << 4);
+ dst[i / 4] = sample;
+ }
+}
+
+#define SDS_3BYTE_TO_INT_DECODE(x) (((x) & 0x7F) | (((x) & 0x7F00) >> 1) | (((x) & 0x7F0000) >> 2))
+
+static int sds_read_header(AVFormatContext *ctx)
+{
+ SDSContext *s = ctx->priv_data;
+ unsigned sample_period;
+ AVIOContext *pb = ctx->pb;
+ AVStream *st;
+
+ st = avformat_new_stream(ctx, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avio_skip(pb, 4);
+ avio_skip(pb, 2);
+
+ s->bit_depth = avio_r8(pb);
+ if (s->bit_depth < 8 || s->bit_depth > 28)
+ return AVERROR_INVALIDDATA;
+
+ if (s->bit_depth < 14) {
+ s->read_block = byte2_read;
+ s->size = 60 * 4;
+ } else if (s->bit_depth < 21) {
+ s->read_block = byte3_read;
+ s->size = 40 * 4;
+ } else {
+ s->read_block = byte4_read;
+ s->size = 30 * 4;
+ }
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_U32LE;
+
+ sample_period = avio_rl24(pb);
+ sample_period = SDS_3BYTE_TO_INT_DECODE(sample_period);
+ avio_skip(pb, 11);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->channels = 1;
+ st->codecpar->sample_rate = sample_period ? 1000000000 / sample_period : 16000;
+ st->duration = (avio_size(pb) - 21) / (127) * s->size / 4;
+
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ return 0;
+}
+
+static int sds_read_packet(AVFormatContext *ctx, AVPacket *pkt)
+{
+ SDSContext *s = ctx->priv_data;
+ AVIOContext *pb = ctx->pb;
+ int64_t pos;
+ int ret;
+
+ if (avio_feof(pb))
+ return AVERROR_EOF;
+
+ pos = avio_tell(pb);
+ if (avio_rb16(pb) != 0xF07E)
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, 3);
+
+ ret = av_new_packet(pkt, s->size);
+ if (ret < 0)
+ return ret;
+
+ ret = avio_read(pb, s->data, 120);
+
+ s->read_block(s->data, (uint32_t *)pkt->data);
+
+ avio_skip(pb, 1); // checksum
+ if (avio_r8(pb) != 0xF7)
+ return AVERROR_INVALIDDATA;
+
+ pkt->flags &= ~AV_PKT_FLAG_CORRUPT;
+ pkt->stream_index = 0;
+ pkt->pos = pos;
+
+ return ret;
+}
+
+AVInputFormat ff_sds_demuxer = {
+ .name = "sds",
+ .long_name = NULL_IF_CONFIG_SMALL("MIDI Sample Dump Standard"),
+ .priv_data_size = sizeof(SDSContext),
+ .read_probe = sds_probe,
+ .read_header = sds_read_header,
+ .read_packet = sds_read_packet,
+ .extensions = "sds",
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/sdxdec.c b/libavformat/sdxdec.c
new file mode 100644
index 0000000..e8e7a4f
--- /dev/null
+++ b/libavformat/sdxdec.c
@@ -0,0 +1,90 @@
+/*
+ * SDX demuxer
+ * Copyright (c) 2017 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 sdx_probe(AVProbeData *p)
+{
+ if (AV_RB32(p->buf) == AV_RB32("SDX:"))
+ return AVPROBE_SCORE_EXTENSION;
+ return 0;
+}
+
+static int sdx_read_header(AVFormatContext *s)
+{
+ AVStream *st;
+ int depth, length;
+
+ avio_skip(s->pb, 4);
+ while (!avio_feof(s->pb)) {
+ if (avio_r8(s->pb) == 0x1a)
+ break;
+ }
+ if (avio_r8(s->pb) != 1)
+ return AVERROR_INVALIDDATA;
+ length = avio_r8(s->pb);
+ avio_skip(s->pb, length);
+ avio_skip(s->pb, 4);
+ depth = avio_r8(s->pb);
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->channels = 1;
+ st->codecpar->sample_rate = avio_rl32(s->pb);
+ switch (depth) {
+ case 8:
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_U8;
+ break;
+ case 16:
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_U16LE;
+ break;
+ case 24:
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_U24LE;
+ break;
+ case 32:
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_U32LE;
+ break;
+ default:
+ return AVERROR_INVALIDDATA;
+ }
+ avio_skip(s->pb, 16);
+ st->codecpar->block_align = depth / 8;
+
+ return 0;
+}
+
+AVInputFormat ff_sdx_demuxer = {
+ .name = "sdx",
+ .long_name = NULL_IF_CONFIG_SMALL("Sample Dump eXchange"),
+ .read_probe = sdx_probe,
+ .read_header = sdx_read_header,
+ .read_packet = ff_pcm_read_packet,
+ .read_seek = ff_pcm_read_seek,
+ .extensions = "sdx",
+ .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/segafilm.c b/libavformat/segafilm.c
index 16500b4..1fdef50 100644
--- a/libavformat/segafilm.c
+++ b/libavformat/segafilm.c
@@ -2,20 +2,20 @@
* Sega FILM Format (CPK) Demuxer
* Copyright (c) 2003 The FFmpeg project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -30,6 +30,7 @@
#include "libavutil/intreadwrite.h"
#include "avformat.h"
#include "internal.h"
+#include "avio_internal.h"
#define FILM_TAG MKBETAG('F', 'I', 'L', 'M')
#define FDSC_TAG MKBETAG('F', 'D', 'S', 'C')
@@ -68,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;
}
@@ -115,13 +119,8 @@ 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)
@@ -201,7 +200,7 @@ static int film_read_header(AVFormatContext *s)
film->sample_count = AV_RB32(&scratch[12]);
if(film->sample_count >= UINT_MAX / sizeof(film_sample))
return -1;
- film->sample_table = av_malloc(film->sample_count * sizeof(film_sample));
+ film->sample_table = av_malloc_array(film->sample_count, sizeof(film_sample));
if (!film->sample_table)
return AVERROR(ENOMEM);
@@ -242,11 +241,12 @@ static int film_read_header(AVFormatContext *s)
film->sample_table[i].pts = AV_RB32(&scratch[8]) & 0x7FFFFFFF;
film->sample_table[i].keyframe = (scratch[8] & 0x80) ? 0 : 1;
video_frame_counter++;
- av_add_index_entry(s->streams[film->video_stream_index],
- film->sample_table[i].sample_offset,
- film->sample_table[i].pts,
- film->sample_table[i].sample_size, 0,
- film->sample_table[i].keyframe);
+ if (film->video_type)
+ av_add_index_entry(s->streams[film->video_stream_index],
+ film->sample_table[i].sample_offset,
+ film->sample_table[i].pts,
+ film->sample_table[i].sample_size, 0,
+ film->sample_table[i].keyframe);
}
}
@@ -273,7 +273,7 @@ static int film_read_packet(AVFormatContext *s,
int ret = 0;
if (film->current_sample >= film->sample_count)
- return AVERROR(EIO);
+ return AVERROR_EOF;
sample = &film->sample_table[film->current_sample];
@@ -281,8 +281,8 @@ static int film_read_packet(AVFormatContext *s,
avio_seek(pb, sample->sample_offset, SEEK_SET);
ret = av_get_packet(pb, pkt, sample->sample_size);
- if (ret < 0)
- return ret;
+ 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 7d23afc..81d3f1d 100644
--- a/libavformat/segment.c
+++ b/libavformat/segment.c
@@ -1,175 +1,557 @@
/*
- * 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}
+ */
+
#include <float.h>
+#include <time.h>
#include "avformat.h"
+#include "avio_internal.h"
#include "internal.h"
+#include "libavutil/avassert.h"
+#include "libavutil/internal.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/timecode.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 clocktime_offset; //< clock offset for cutting the segments at regular clock time
+ int64_t clocktime_wrap_duration; //< wrapping duration considered for starting a new segment
+ int64_t last_val; ///< remember last time for wrap around detection
+ int cut_pending;
+ int header_written; ///< whether we've already called avformat_write_header
+
+ char *entry_prefix; ///< prefix to add to list entry filenames
+ int 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
+ int increment_tc; ///< flag to increment timecode if found
+
+ 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;
+ char *header_filename; ///< filename to write the output header to
+
+ int reset_timestamps; ///< reset timestamps at the beginning of each segment
+ int64_t initial_offset; ///< initial timestamps offset, expressed in microseconds
+ char *reference_stream_specifier; ///< reference stream specifier
+ int reference_stream_index;
+ int break_non_keyframes;
+ int write_empty;
+
+ int use_rename;
+ char temp_list_filename[1024];
+
+ 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);
oc->opaque = s->opaque;
oc->io_close = s->io_close;
oc->io_open = s->io_open;
+ oc->flags = s->flags;
for (i = 0; i < s->nb_streams; i++) {
AVStream *st;
+ AVCodecParameters *ipar, *opar;
+
if (!(st = avformat_new_stream(oc, NULL)))
return AVERROR(ENOMEM);
- avcodec_parameters_copy(st->codecpar, s->streams[i]->codecpar);
+ ipar = s->streams[i]->codecpar;
+ opar = st->codecpar;
+ avcodec_parameters_copy(opar, ipar);
+ if (!oc->oformat->codec_tag ||
+ av_codec_get_id (oc->oformat->codec_tag, ipar->codec_tag) == opar->codec_id ||
+ av_codec_get_tag(oc->oformat->codec_tag, ipar->codec_id) <= 0) {
+ opar->codec_tag = ipar->codec_tag;
+ } else {
+ opar->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 = s->io_open(s, &seg->pb, seg->list, AVIO_FLAG_WRITE, 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;
+ int ret;
+
+ 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:
- ff_format_io_close(s, &seg->pb);
+ /* copy modified name in list entry */
+ size = strlen(av_basename(oc->filename)) + 1;
+ if (seg->entry_prefix)
+ size += strlen(seg->entry_prefix);
- return ret;
+ if ((ret = av_reallocp(&seg->cur_entry.filename, size)) < 0)
+ return ret;
+ 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 = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0)
+ if ((err = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0) {
+ av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", oc->filename);
return err;
+ }
+ if (!seg->individual_header_trailer)
+ oc->pb->seekable = 0;
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)
+ AVDictionary *options = NULL;
+ av_dict_copy(&options, seg->format_options, 0);
+ av_dict_set(&options, "fflags", "-autobsf", 0);
+ err = avformat_write_header(oc, &options);
+ av_dict_free(&options);
+ if (err < 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;
+
+ snprintf(seg->temp_list_filename, sizeof(seg->temp_list_filename), seg->use_rename ? "%s.tmp" : "%s", seg->list);
+ ret = s->io_open(s, &seg->list_pb, seg->temp_list_filename, AVIO_FLAG_WRITE, 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;
+ AVTimecode tc;
+ AVRational rate;
+ AVDictionaryEntry *tcr;
+ char buf[AV_TIMECODE_STR_SIZE];
+ int i;
+ int err;
+
+ if (!oc || !oc->pb)
+ return AVERROR(EINVAL);
av_write_frame(oc, NULL); /* Flush any buffered data (fragmented mp4) */
if (write_trailer)
- av_write_trailer(oc);
+ ret = av_write_trailer(oc);
+
+ if (ret < 0)
+ av_log(s, AV_LOG_ERROR, "Failure occurred when ending segment '%s'\n",
+ oc->filename);
+
+ if (seg->list) {
+ if (seg->list_size || seg->list_type == LIST_TYPE_M3U8) {
+ SegmentListEntry *entry = av_mallocz(sizeof(*entry));
+ if (!entry) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+
+ /* append new element */
+ memcpy(entry, &seg->cur_entry, sizeof(*entry));
+ entry->filename = av_strdup(entry->filename);
+ 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);
+ }
+
+ 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");
+ ff_format_io_close(s, &seg->list_pb);
+ if (seg->use_rename)
+ ff_rename(seg->temp_list_filename, seg->list, s);
+ } 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++;
+
+ if (seg->increment_tc) {
+ tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
+ if (tcr) {
+ /* search the first video stream */
+ for (i = 0; i < s->nb_streams; i++) {
+ if (s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ rate = s->streams[i]->avg_frame_rate;/* Get fps from the video stream */
+ err = av_timecode_init_from_string(&tc, rate, tcr->value, s);
+ if (err < 0) {
+ av_log(s, AV_LOG_WARNING, "Could not increment timecode, error occurred during timecode creation.");
+ break;
+ }
+ tc.start += (int)((seg->cur_entry.end_time - seg->cur_entry.start_time) * av_q2d(rate));/* increment timecode */
+ av_dict_set(&s->metadata, "timecode",
+ av_timecode_make_string(&tc, buf, 0), 0);
+ break;
+ }
+ }
+ } else {
+ av_log(s, AV_LOG_WARNING, "Could not increment timecode, no timecode metadata found");
+ }
+ }
+
+end:
ff_format_io_close(oc, &oc->pb);
return ret;
}
+static int parse_times(void *log_ctx, int64_t **times, int *nb_times,
+ const char *times_str)
+{
+ char *p;
+ int i, ret = 0;
+ char *times_str1 = av_strdup(times_str);
+ char *saveptr = NULL;
+
+ if (!times_str1)
+ return AVERROR(ENOMEM);
+
+#define FAIL(err) ret = err; goto end
+
+ *nb_times = 1;
+ for (p = times_str1; *p; p++)
+ if (*p == ',')
+ (*nb_times)++;
+
+ *times = av_malloc_array(*nb_times, sizeof(**times));
+ if (!*times) {
+ av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced times array\n");
+ FAIL(AVERROR(ENOMEM));
+ }
+
+ p = times_str1;
+ for (i = 0; i < *nb_times; i++) {
+ int64_t t;
+ char *tstr = av_strtok(p, ",", &saveptr);
+ p = NULL;
+
+ if (!tstr || !tstr[0]) {
+ av_log(log_ctx, AV_LOG_ERROR, "Empty time specification in times list %s\n",
+ times_str);
+ FAIL(AVERROR(EINVAL));
+ }
+
+ ret = av_parse_time(&t, tstr, 1);
+ if (ret < 0) {
+ av_log(log_ctx, AV_LOG_ERROR,
+ "Invalid time duration specification '%s' in times list %s\n", tstr, times_str);
+ FAIL(AVERROR(EINVAL));
+ }
+ (*times)[i] = t;
+
+ /* check on monotonicity */
+ if (i && (*times)[i-1] > (*times)[i]) {
+ av_log(log_ctx, AV_LOG_ERROR,
+ "Specified time %f is greater than the following time %f\n",
+ (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000);
+ FAIL(AVERROR(EINVAL));
+ }
+ }
+
+end:
+ av_free(times_str1);
+ return ret;
+}
+
+static int parse_frames(void *log_ctx, int **frames, int *nb_frames,
+ const char *frames_str)
+{
+ char *p;
+ int i, ret = 0;
+ char *frames_str1 = av_strdup(frames_str);
+ char *saveptr = NULL;
+
+ if (!frames_str1)
+ return AVERROR(ENOMEM);
+
+#define FAIL(err) ret = err; goto end
+
+ *nb_frames = 1;
+ for (p = frames_str1; *p; p++)
+ if (*p == ',')
+ (*nb_frames)++;
+
+ *frames = av_malloc_array(*nb_frames, sizeof(**frames));
+ if (!*frames) {
+ av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced frames array\n");
+ FAIL(AVERROR(ENOMEM));
+ }
+
+ p = frames_str1;
+ for (i = 0; i < *nb_frames; i++) {
+ long int f;
+ char *tailptr;
+ char *fstr = av_strtok(p, ",", &saveptr);
+
+ p = NULL;
+ if (!fstr) {
+ av_log(log_ctx, AV_LOG_ERROR, "Empty frame specification in frame list %s\n",
+ frames_str);
+ FAIL(AVERROR(EINVAL));
+ }
+ f = strtol(fstr, &tailptr, 10);
+ if (*tailptr || f <= 0 || f >= INT_MAX) {
+ av_log(log_ctx, AV_LOG_ERROR,
+ "Invalid argument '%s', must be a positive integer <= INT64_MAX\n",
+ fstr);
+ FAIL(AVERROR(EINVAL));
+ }
+ (*frames)[i] = f;
+
+ /* check on monotonicity */
+ if (i && (*frames)[i-1] > (*frames)[i]) {
+ av_log(log_ctx, AV_LOG_ERROR,
+ "Specified frame %d is greater than the following frame %d\n",
+ (*frames)[i], (*frames)[i-1]);
+ FAIL(AVERROR(EINVAL));
+ }
+ }
+
+end:
+ av_free(frames_str1);
+ return ret;
+}
+
static int open_null_ctx(AVIOContext **ctx)
{
int buf_size = 32768;
@@ -184,156 +566,386 @@ 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_freep(&(*pb)->buffer);
avio_context_free(pb);
}
-static void seg_free_context(SegmentContext *seg)
+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]->codecpar->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(AVFormatContext *s)
{
- ff_format_io_close(seg->avf, &seg->pb);
+ SegmentContext *seg = s->priv_data;
+ ff_format_io_close(seg->avf, &seg->list_pb);
avformat_free_context(seg->avf);
seg->avf = NULL;
}
-static int seg_write_header(AVFormatContext *s)
+static int seg_init(AVFormatContext *s)
{
SegmentContext *seg = s->priv_data;
- AVFormatContext *oc = NULL;
- int ret, i;
+ AVFormatContext *oc = seg->avf;
+ 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 = s->io_open(s, &seg->pb, seg->list, AVIO_FLAG_WRITE, NULL)) < 0)
- goto fail;
+ if (seg->header_filename) {
+ seg->write_header_trailer = 1;
+ seg->individual_header_trailer = 0;
+ }
- for (i = 0; i < s->nb_streams; i++)
- seg->has_video +=
- (s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO);
+ if (seg->initial_offset > 0) {
+ av_log(s, AV_LOG_WARNING, "NOTE: the option initial_offset is deprecated,"
+ "you can use output_ts_offset instead of it\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 (!!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);
+ }
- seg->oformat = av_guess_format(seg->format, s->filename, NULL);
+ 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->use_clocktime) {
+ if (seg->time <= 0) {
+ av_log(s, AV_LOG_ERROR, "Invalid negative segment_time with segment_atclocktime option set\n");
+ return AVERROR(EINVAL);
+ }
+ seg->clocktime_offset = seg->time - (seg->clocktime_offset % seg->time);
+ }
+ }
- if (!seg->oformat) {
- ret = AVERROR_MUXER_NOT_FOUND;
- goto fail;
+ 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);
+ return ret;
+ }
}
+
+ if (seg->list) {
+ if (seg->list_type == LIST_TYPE_UNDEFINED) {
+ if (av_match_ext(seg->list, "csv" )) seg->list_type = LIST_TYPE_CSV;
+ else if (av_match_ext(seg->list, "ext" )) seg->list_type = LIST_TYPE_EXT;
+ else if (av_match_ext(seg->list, "m3u8")) seg->list_type = LIST_TYPE_M3U8;
+ else if (av_match_ext(seg->list, "ffcat,ffconcat")) seg->list_type = LIST_TYPE_FFCONCAT;
+ else seg->list_type = LIST_TYPE_FLAT;
+ }
+ if (!seg->list_size && seg->list_type != LIST_TYPE_M3U8) {
+ if ((ret = segment_list_open(s)) < 0)
+ return ret;
+ } else {
+ const char *proto = avio_find_protocol_name(seg->list);
+ seg->use_rename = proto && !strcmp(proto, "file");
+ }
+ }
+
+ if (seg->list_type == LIST_TYPE_EXT)
+ av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n");
+
+ if ((ret = select_reference_stream(s)) < 0)
+ return ret;
+ 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]->codecpar->codec_type));
+
+ seg->oformat = av_guess_format(seg->format, s->filename, NULL);
+
+ if (!seg->oformat)
+ return AVERROR_MUXER_NOT_FOUND;
if (seg->oformat->flags & AVFMT_NOFILE) {
av_log(s, AV_LOG_ERROR, "format %s not supported.\n",
seg->oformat->name);
- ret = AVERROR(EINVAL);
- goto fail;
+ return AVERROR(EINVAL);
}
if ((ret = segment_mux_init(s)) < 0)
- goto fail;
- oc = seg->avf;
+ return ret;
- if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
- s->filename, seg->number++) < 0) {
- ret = AVERROR(EINVAL);
- goto fail;
- }
+ if ((ret = set_segment_filename(s)) < 0)
+ return ret;
+ oc = seg->avf;
if (seg->write_header_trailer) {
- if ((ret = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0)
- goto fail;
+ if ((ret = s->io_open(s, &oc->pb,
+ seg->header_filename ? seg->header_filename : oc->filename,
+ AVIO_FLAG_WRITE, NULL)) < 0) {
+ av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", oc->filename);
+ return ret;
+ }
+ if (!seg->individual_header_trailer)
+ oc->pb->seekable = 0;
} else {
if ((ret = open_null_ctx(&oc->pb)) < 0)
- goto fail;
+ return ret;
+ }
+
+ av_dict_copy(&options, seg->format_options, 0);
+ av_dict_set(&options, "fflags", "-autobsf", 0);
+ ret = avformat_init_output(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);
+ av_dict_free(&options);
+ return AVERROR(EINVAL);
}
+ av_dict_free(&options);
- if ((ret = avformat_write_header(oc, NULL)) < 0) {
+ if (ret < 0) {
ff_format_io_close(oc, &oc->pb);
- goto fail;
+ return ret;
+ }
+ seg->segment_frame_count = 0;
+
+ av_assert0(s->nb_streams == oc->nb_streams);
+ if (ret == AVSTREAM_INIT_IN_WRITE_HEADER) {
+ ret = avformat_write_header(oc, NULL);
+ if (ret < 0)
+ return ret;
+ seg->header_written = 1;
}
- if (!seg->write_header_trailer) {
- close_null_ctx(&oc->pb);
- if ((ret = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0)
- goto fail;
+ 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 (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 (oc->avoid_negative_ts > 0 && s->avoid_negative_ts < 0)
+ s->avoid_negative_ts = 1;
+
+ return ret;
+}
+
+static int seg_write_header(AVFormatContext *s)
+{
+ SegmentContext *seg = s->priv_data;
+ AVFormatContext *oc = seg->avf;
+ int ret, i;
+
+ if (!seg->header_written) {
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = oc->streams[i];
+ AVCodecParameters *ipar, *opar;
+
+ ipar = s->streams[i]->codecpar;
+ opar = oc->streams[i]->codecpar;
+ avcodec_parameters_copy(opar, ipar);
+ if (!oc->oformat->codec_tag ||
+ av_codec_get_id (oc->oformat->codec_tag, ipar->codec_tag) == opar->codec_id ||
+ av_codec_get_tag(oc->oformat->codec_tag, ipar->codec_id) <= 0) {
+ opar->codec_tag = ipar->codec_tag;
+ } else {
+ opar->codec_tag = 0;
+ }
+ st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
+ st->time_base = s->streams[i]->time_base;
}
+ ret = avformat_write_header(oc, NULL);
+ if (ret < 0)
+ return ret;
}
-fail:
- if (ret < 0)
- seg_free_context(seg);
+ if (!seg->write_header_trailer || seg->header_filename) {
+ if (seg->header_filename) {
+ av_write_frame(oc, NULL);
+ ff_format_io_close(oc, &oc->pb);
+ } else {
+ close_null_ctxp(&oc->pb);
+ }
+ if ((ret = oc->io_open(oc, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0)
+ return ret;
+ if (!seg->individual_header_trailer)
+ oc->pb->seekable = 0;
+ }
- return ret;
+ return 0;
}
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 || !seg->avf->pb)
return AVERROR(EINVAL);
- if (seg->has_video) {
- can_split = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
- pkt->flags & AV_PKT_FLAG_KEY;
+calc_times:
+ 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->clocktime_offset) % seg->time;
+ if (wrapped_val < seg->last_val && wrapped_val < seg->clocktime_wrap_duration)
+ seg->cut_pending = 1;
+ 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);
+ ff_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->break_non_keyframes) &&
+ (seg->segment_frame_count > 0 || seg->write_empty) &&
+ (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;
- ret = segment_end(oc, seg->individual_header_trailer);
+ if ((ret = segment_start(s, seg->individual_header_trailer)) < 0)
+ goto fail;
- if (!ret)
- ret = segment_start(s, seg->individual_header_trailer);
+ 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;
- if (ret)
- goto fail;
+ if (seg->times || (!seg->frames && !seg->use_clocktime) && seg->write_empty)
+ goto calc_times;
+ }
- oc = seg->avf;
+ if (pkt->stream_index == seg->reference_stream_index) {
+ if (pkt->pts != AV_NOPTS_VALUE)
+ seg->cur_entry.end_time =
+ FFMAX(seg->cur_entry.end_time, (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base));
+ 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)) {
- ff_format_io_close(s, &seg->pb);
- if ((ret = s->io_open(s, &seg->pb, seg->list,
- AVIO_FLAG_WRITE, NULL)) < 0)
- goto fail;
- }
- }
- }
+ if (seg->segment_frame_count == 0) {
+ av_log(s, AV_LOG_VERBOSE, "segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
+ seg->avf->filename, pkt->stream_index,
+ av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), seg->frame_count);
}
- ret = ff_write_chained(oc, pkt->stream_index, pkt, s);
+ av_log(s, AV_LOG_DEBUG, "stream:%d start_pts_time:%s pts:%s pts_time:%s dts:%s dts_time:%s",
+ pkt->stream_index,
+ av_ts2timestr(seg->cur_entry.start_pts, &AV_TIME_BASE_Q),
+ av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
+ av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
+
+ /* compute new timestamps */
+ offset = av_rescale_q(seg->initial_offset - (seg->reset_timestamps ? seg->cur_entry.start_pts : 0),
+ AV_TIME_BASE_Q, st->time_base);
+ if (pkt->pts != AV_NOPTS_VALUE)
+ pkt->pts += offset;
+ if (pkt->dts != AV_NOPTS_VALUE)
+ pkt->dts += offset;
+
+ av_log(s, AV_LOG_DEBUG, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n",
+ av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
+ av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
+
+ ret = ff_write_chained(seg->avf, pkt->stream_index, pkt, s, seg->initial_offset || seg->reset_timestamps);
fail:
- if (ret < 0)
- seg_free_context(seg);
+ if (pkt->stream_index == seg->reference_stream_index) {
+ seg->frame_count++;
+ seg->segment_frame_count++;
+ }
return ret;
}
@@ -342,50 +954,107 @@ 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;
if ((ret = open_null_ctx(&oc->pb)) < 0)
goto fail;
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)
+ ff_format_io_close(s, &seg->list_pb);
+
+ av_dict_free(&seg->format_options);
+ av_opt_free(seg);
+ av_freep(&seg->times);
+ av_freep(&seg->frames);
+ av_freep(&seg->cur_entry.filename);
+
+ cur = seg->segment_list_entries;
+ while (cur) {
+ next = cur->next;
+ av_freep(&cur->filename);
+ av_free(cur);
+ cur = next;
}
-fail:
- ff_format_io_close(s, &seg->pb);
avformat_free_context(oc);
+ seg->avf = NULL;
return ret;
}
+static int seg_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt)
+{
+ SegmentContext *seg = s->priv_data;
+ AVFormatContext *oc = seg->avf;
+ if (oc->oformat->check_bitstream) {
+ int ret = oc->oformat->check_bitstream(oc, pkt);
+ if (ret == 1) {
+ AVStream *st = s->streams[pkt->stream_index];
+ AVStream *ost = oc->streams[pkt->stream_index];
+ st->internal->bsfcs = ost->internal->bsfcs;
+ st->internal->nb_bsfcs = ost->internal->nb_bsfcs;
+ ost->internal->bsfcs = NULL;
+ ost->internal->nb_bsfcs = 0;
+ }
+ return ret;
+ }
+ return 1;
+}
+
#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 },
- { "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 },
+ { "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_header_filename", "write a single file containing the header", OFFSET(header_filename), 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_BOOL, {.i64 = 0}, 0, 1, E},
+ { "segment_clocktime_offset", "set segment clocktime offset", OFFSET(clocktime_offset), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, 86400000000LL, E},
+ { "segment_clocktime_wrap_duration", "set segment clocktime wrapping duration", OFFSET(clocktime_wrap_duration), AV_OPT_TYPE_DURATION, {.i64 = INT64_MAX}, 0, INT64_MAX, 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_BOOL, {.i64 = 0 }, 0, 1, E },
+ { "increment_tc", "increment timecode between each segment", OFFSET(increment_tc), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
+ { "break_non_keyframes", "allow breaking segments on non-keyframes", OFFSET(break_non_keyframes), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E },
+
+ { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_BOOL, {.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_BOOL, {.i64 = 1}, 0, 1, E },
+ { "reset_timestamps", "reset timestamps at the beginning of each segment", OFFSET(reset_timestamps), AV_OPT_TYPE_BOOL, {.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 },
+ { "write_empty_segments", "allow writing empty 'filler' segments", OFFSET(write_empty), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E },
{ NULL },
};
@@ -396,14 +1065,37 @@ 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,
+ .init = seg_init,
.write_header = seg_write_header,
.write_packet = seg_write_packet,
.write_trailer = seg_write_trailer,
+ .deinit = seg_free,
+ .check_bitstream = seg_check_bitstream,
.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,
+ .init = seg_init,
+ .write_header = seg_write_header,
+ .write_packet = seg_write_packet,
+ .write_trailer = seg_write_trailer,
+ .deinit = seg_free,
+ .check_bitstream = seg_check_bitstream,
+ .priv_class = &sseg_class,
+};
diff --git a/libavformat/shortendec.c b/libavformat/shortendec.c
new file mode 100644
index 0000000..42fcdf7
--- /dev/null
+++ b/libavformat/shortendec.c
@@ -0,0 +1,71 @@
+/*
+ * Shorten demuxer
+ * Copyright (c) 2001 Fabrice Bellard
+ * Copyright (c) 2005 Alex Beregszaszi
+ * Copyright (c) 2015 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 "rawdec.h"
+#include "libavcodec/golomb.h"
+
+static int shn_probe(AVProbeData *p)
+{
+ GetBitContext gb;
+ int version, internal_ftype, channels, blocksize;
+
+ if (AV_RB32(p->buf) != 0x616a6b67)
+ return 0;
+ version = p->buf[4];
+ if (init_get_bits8(&gb, p->buf + 5, p->buf_size - 5 - AV_INPUT_BUFFER_PADDING_SIZE) < 0)
+ return 0;
+ if (!version) {
+ internal_ftype = get_ur_golomb_shorten(&gb, 4);
+ channels = get_ur_golomb_shorten(&gb, 0);
+ blocksize = 256;
+ } else {
+ int k;
+ k = get_ur_golomb_shorten(&gb, 2);
+ internal_ftype = get_ur_golomb_shorten(&gb, k);
+ k = get_ur_golomb_shorten(&gb, 2);
+ channels = get_ur_golomb_shorten(&gb, k);
+ k = get_ur_golomb_shorten(&gb, 2);
+ blocksize = get_ur_golomb_shorten(&gb, k);
+ }
+
+ if (internal_ftype != 2 && internal_ftype != 3 && internal_ftype != 5)
+ return 0;
+ if (channels < 1 || channels > 8)
+ return 0;
+ if (blocksize < 1 || blocksize > 65535)
+ return 0;
+
+ return AVPROBE_SCORE_EXTENSION + 1;
+}
+
+AVInputFormat ff_shorten_demuxer = {
+ .name = "shn",
+ .long_name = NULL_IF_CONFIG_SMALL("raw Shorten"),
+ .read_probe = shn_probe,
+ .read_header = ff_raw_audio_read_header,
+ .read_packet = ff_raw_read_partial_packet,
+ .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS,
+ .extensions = "shn",
+ .raw_codec_id = AV_CODEC_ID_SHORTEN,
+};
diff --git a/libavformat/sierravmd.c b/libavformat/sierravmd.c
index 604ab6b..6960c28 100644
--- a/libavformat/sierravmd.c
+++ b/libavformat/sierravmd.c
@@ -2,20 +2,20 @@
* Sierra VMD Format Demuxer
* Copyright (c) 2004 The FFmpeg project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -31,6 +31,7 @@
#include "libavutil/intreadwrite.h"
#include "avformat.h"
#include "internal.h"
+#include "avio_internal.h"
#define VMD_HEADER_SIZE 0x0330
#define BYTES_PER_FRAME_RECORD 16
@@ -63,8 +64,8 @@ typedef struct VmdDemuxContext {
static int vmd_probe(AVProbeData *p)
{
- int w, h;
- if (p->buf_size < 16)
+ int w, h, sample_rate;
+ if (p->buf_size < 806)
return 0;
/* check if the first 2 bytes of the file contain the appropriate size
* of a VMD header chunk */
@@ -72,7 +73,9 @@ static int vmd_probe(AVProbeData *p)
return 0;
w = AV_RL16(&p->buf[12]);
h = AV_RL16(&p->buf[14]);
- if (!w || w > 2048 || !h || h > 2048)
+ sample_rate = AV_RL16(&p->buf[804]);
+ if ((!w || w > 2048 || !h || h > 2048) &&
+ sample_rate != 22050)
return 0;
/* only return half certainty since this check is a bit sketchy */
@@ -83,12 +86,13 @@ static int vmd_read_header(AVFormatContext *s)
{
VmdDemuxContext *vmd = s->priv_data;
AVIOContext *pb = s->pb;
- AVStream *st = NULL, *vst;
+ AVStream *st = NULL, *vst = NULL;
unsigned int toc_offset;
unsigned char *raw_frame_table;
int raw_frame_table_size;
int64_t current_offset;
int i, j, ret;
+ int width, height;
unsigned int total_frames;
int64_t current_audio_pts = 0;
unsigned char chunk[BYTES_PER_FRAME_RECORD];
@@ -100,28 +104,33 @@ static int vmd_read_header(AVFormatContext *s)
if (avio_read(pb, vmd->vmd_header, VMD_HEADER_SIZE) != VMD_HEADER_SIZE)
return AVERROR(EIO);
- if(vmd->vmd_header[24] == 'i' && vmd->vmd_header[25] == 'v' && vmd->vmd_header[26] == '3')
- vmd->is_indeo3 = 1;
- else
- vmd->is_indeo3 = 0;
- /* start up the decoders */
- vst = avformat_new_stream(s, NULL);
- if (!vst)
- return AVERROR(ENOMEM);
- avpriv_set_pts_info(vst, 33, 1, 10);
- vmd->video_stream_index = vst->index;
- vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
- vst->codecpar->codec_id = vmd->is_indeo3 ? AV_CODEC_ID_INDEO3 : AV_CODEC_ID_VMDVIDEO;
- vst->codecpar->codec_tag = 0; /* no fourcc */
- vst->codecpar->width = AV_RL16(&vmd->vmd_header[12]);
- vst->codecpar->height = AV_RL16(&vmd->vmd_header[14]);
- if(vmd->is_indeo3 && vst->codecpar->width > 320){
- vst->codecpar->width >>= 1;
- vst->codecpar->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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ vst->codecpar->codec_id = vmd->is_indeo3 ? AV_CODEC_ID_INDEO3 : AV_CODEC_ID_VMDVIDEO;
+ vst->codecpar->codec_tag = 0; /* no fourcc */
+ vst->codecpar->width = width;
+ vst->codecpar->height = height;
+ if(vmd->is_indeo3 && vst->codecpar->width > 320){
+ vst->codecpar->width >>= 1;
+ vst->codecpar->height >>= 1;
+ }
+ if (ff_alloc_extradata(vst->codecpar, VMD_HEADER_SIZE))
+ return AVERROR(ENOMEM);
+ memcpy(vst->codecpar->extradata, vmd->vmd_header, VMD_HEADER_SIZE);
}
- vst->codecpar->extradata_size = VMD_HEADER_SIZE;
- vst->codecpar->extradata = av_mallocz(VMD_HEADER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
- memcpy(vst->codecpar->extradata, vmd->vmd_header, VMD_HEADER_SIZE);
/* if sample rate is 0, assume no audio */
vmd->sample_rate = AV_RL16(&vmd->vmd_header[804]);
@@ -133,13 +142,6 @@ static int vmd_read_header(AVFormatContext *s)
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = AV_CODEC_ID_VMDAUDIO;
st->codecpar->codec_tag = 0; /* no fourcc */
- if (vmd->vmd_header[811] & 0x80) {
- st->codecpar->channels = 2;
- st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
- } else {
- st->codecpar->channels = 1;
- st->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
- }
st->codecpar->sample_rate = vmd->sample_rate;
st->codecpar->block_align = AV_RL16(&vmd->vmd_header[806]);
if (st->codecpar->block_align & 0x8000) {
@@ -148,14 +150,28 @@ static int vmd_read_header(AVFormatContext *s)
} else {
st->codecpar->bits_per_coded_sample = 8;
}
+ if (vmd->vmd_header[811] & 0x80) {
+ st->codecpar->channels = 2;
+ st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
+ } else if (vmd->vmd_header[811] & 0x2) {
+ /* Shivers 2 stereo audio */
+ /* Frame length is for 1 channel */
+ st->codecpar->channels = 2;
+ st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
+ st->codecpar->block_align = st->codecpar->block_align << 1;
+ } else {
+ st->codecpar->channels = 1;
+ st->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
+ }
st->codecpar->bit_rate = st->codecpar->sample_rate *
st->codecpar->bits_per_coded_sample * st->codecpar->channels;
/* calculate pts */
num = st->codecpar->block_align;
den = st->codecpar->sample_rate * st->codecpar->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);
}
@@ -173,7 +189,7 @@ static int vmd_read_header(AVFormatContext *s)
return -1;
}
raw_frame_table = av_malloc(raw_frame_table_size);
- vmd->frame_table = av_malloc((vmd->frame_count * vmd->frames_per_block + sound_buffers) * sizeof(vmd_frame));
+ vmd->frame_table = av_malloc_array(vmd->frame_count * vmd->frames_per_block + sound_buffers, sizeof(vmd_frame));
if (!raw_frame_table || !vmd->frame_table) {
ret = AVERROR(ENOMEM);
goto error;
@@ -194,7 +210,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 +261,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 +275,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 +313,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 e11d5bd..ddd1715 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'),
@@ -55,7 +56,7 @@ typedef struct SIFFContext {
int curstrm;
unsigned int pktsize;
int gmcsize;
- int sndsize;
+ unsigned int sndsize;
unsigned int flags;
uint8_t gmc[4];
@@ -128,6 +129,8 @@ static int siff_parse_vbv1(AVFormatContext *s, SIFFContext *c, AVIOContext *pb)
st->codecpar->width = width;
st->codecpar->height = height;
st->codecpar->format = 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) {
unsigned int size;
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,20 @@ static int siff_read_packet(AVFormatContext *s, AVPacket *pkt)
}
if (!c->curstrm) {
- size = c->pktsize - c->sndsize;
- if (av_new_packet(pkt, size) < 0)
+ if (c->pktsize < 2LL + c->sndsize + c->gmcsize)
+ return AVERROR_INVALIDDATA;
+
+ size = c->pktsize - c->sndsize - c->gmcsize - 2;
+ size = ffio_limit(s->pb, size);
+ 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_packet_unref(pkt);
+ return AVERROR_INVALIDDATA;
+ }
pkt->stream_index = 0;
c->curstrm = -1;
} else {
@@ -228,6 +238,8 @@ static int siff_read_packet(AVFormatContext *s, AVPacket *pkt)
c->cur_frame++;
} else {
int pktsize = av_get_packet(s->pb, pkt, c->block_align);
+ if (!pktsize)
+ return AVERROR_EOF;
if (pktsize <= 0)
return AVERROR(EIO);
pkt->duration = pktsize;
diff --git a/libavformat/smacker.c b/libavformat/smacker.c
index eb4b63f..8a21cc0 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,11 +115,16 @@ 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);
smk->pts_inc = (int32_t)avio_rl32(pb);
+ if (smk->pts_inc > INT_MAX / 100) {
+ av_log(s, AV_LOG_ERROR, "pts_inc %d is too large\n", smk->pts_inc);
+ return AVERROR_INVALIDDATA;
+ }
+
smk->flags = avio_rl32(pb);
if(smk->flags & SMACKER_FLAG_RING_FRAME)
smk->frames++;
@@ -126,7 +134,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 +150,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 +173,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->codecpar->width = smk->width;
st->codecpar->height = smk->height;
@@ -182,6 +195,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]->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
if (smk->aflags[i] & SMK_AUD_BINKAUD) {
@@ -212,21 +227,18 @@ static int smacker_read_header(AVFormatContext *s)
/* load trees to extradata, they will be unpacked by decoder */
- st->codecpar->extradata = av_mallocz(smk->treesize + 16 +
- AV_INPUT_BUFFER_PADDING_SIZE);
- st->codecpar->extradata_size = smk->treesize + 16;
- if (!st->codecpar->extradata) {
+ if(ff_alloc_extradata(st->codecpar, 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->codecpar->extradata + 16, st->codecpar->extradata_size - 16);
if(ret != st->codecpar->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->codecpar->extradata)[0] = av_le2ne32(smk->mmap_size);
@@ -250,7 +262,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 +279,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 +326,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 +362,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 +383,16 @@ static int smacker_read_close(AVFormatContext *s)
int i;
for(i = 0; i < 7; i++)
- av_free(smk->bufs[i]);
- av_free(smk->frm_size);
- av_free(smk->frm_flags);
+ av_freep(&smk->bufs[i]);
+ av_freep(&smk->frm_size);
+ av_freep(&smk->frm_flags);
return 0;
}
AVInputFormat ff_smacker_demuxer = {
.name = "smk",
- .long_name = NULL_IF_CONFIG_SMALL("Smacker video"),
+ .long_name = NULL_IF_CONFIG_SMALL("Smacker"),
.priv_data_size = sizeof(SmackerContext),
.read_probe = smacker_probe,
.read_header = smacker_read_header,
diff --git a/libavformat/smjpeg.c b/libavformat/smjpeg.c
index 52e45e9..4edf5e8 100644
--- a/libavformat/smjpeg.c
+++ b/libavformat/smjpeg.c
@@ -2,20 +2,20 @@
* SMJPEG common code
* Copyright (c) 2011-2012 Paul B Mahol
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/smjpeg.h b/libavformat/smjpeg.h
index c56fe46..995ddf2 100644
--- a/libavformat/smjpeg.h
+++ b/libavformat/smjpeg.h
@@ -2,20 +2,20 @@
* SMJPEG common code
* Copyright (c) 2011-2012 Paul B Mahol
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/smjpegdec.c b/libavformat/smjpegdec.c
index bc69c89..c184c0d 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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
vst->codecpar->width = avio_rb16(pb);
vst->codecpar->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 91b0939..68a1286 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
*/
@@ -46,6 +46,7 @@ static int smjpeg_write_header(AVFormatContext *s)
avio_wb32(pb, 0);
avio_wb32(pb, 0);
+ ff_standardize_creation_time(s);
while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) {
avio_wl32(pb, SMJPEG_TXT);
avio_wb32(pb, strlen(t->key) + strlen(t->value) + 3);
@@ -66,7 +67,7 @@ static int smjpeg_write_header(AVFormatContext *s)
avio_wl32(pb, SMJPEG_SND);
avio_wb32(pb, 8);
avio_wb16(pb, par->sample_rate);
- avio_w8(pb, av_get_bits_per_sample(par->codec_id));
+ avio_w8(pb, par->bits_per_coded_sample);
avio_w8(pb, par->channels);
avio_wl32(pb, tag);
avpriv_set_pts_info(st, 32, 1, 1000);
diff --git a/libavformat/smoothstreamingenc.c b/libavformat/smoothstreamingenc.c
index 997b9e6..54a1c49 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
*/
@@ -26,6 +26,7 @@
#endif
#include "avformat.h"
+#include "avio_internal.h"
#include "internal.h"
#include "os_support.h"
#include "avc.h"
@@ -64,8 +65,6 @@ typedef struct OutputStream {
char *private_str;
int packet_size;
int audio_tag;
-
- const URLProtocol **protocols;
} OutputStream;
typedef struct SmoothStreamingContext {
@@ -78,8 +77,6 @@ typedef struct SmoothStreamingContext {
OutputStream *streams;
int has_video, has_audio;
int nb_fragments;
-
- const URLProtocol **protocols;
} SmoothStreamingContext;
static int ism_write(void *opaque, uint8_t *buf, int buf_size)
@@ -125,8 +122,8 @@ static int64_t ism_seek(void *opaque, int64_t offset, int whence)
AVDictionary *opts = NULL;
os->tail_out = os->out;
av_dict_set(&opts, "truncate", "0", 0);
- ret = ffurl_open(&os->out, frag->file, AVIO_FLAG_WRITE, &os->ctx->interrupt_callback, &opts,
- os->protocols, NULL);
+ ret = ffurl_open_whitelist(&os->out, frag->file, AVIO_FLAG_WRITE,
+ &os->ctx->interrupt_callback, &opts, os->ctx->protocol_whitelist, os->ctx->protocol_blacklist, NULL);
av_dict_free(&opts);
if (ret < 0) {
os->out = os->tail_out;
@@ -134,8 +131,8 @@ static int64_t ism_seek(void *opaque, int64_t offset, int whence)
return ret;
}
av_dict_set(&opts, "truncate", "0", 0);
- ffurl_open(&os->out2, frag->infofile, AVIO_FLAG_WRITE, &os->ctx->interrupt_callback, &opts,
- os->protocols, NULL);
+ ffurl_open_whitelist(&os->out2, frag->infofile, AVIO_FLAG_WRITE,
+ &os->ctx->interrupt_callback, &opts, os->ctx->protocol_whitelist, os->ctx->protocol_blacklist, NULL);
av_dict_free(&opts);
ffurl_seek(os->out, offset - frag->start_pos, SEEK_SET);
if (os->out2)
@@ -174,9 +171,6 @@ static void ism_free(AVFormatContext *s)
{
SmoothStreamingContext *c = s->priv_data;
int i, j;
-
- av_freep(&c->protocols);
-
if (!c->streams)
return;
for (i = 0; i < s->nb_streams; i++) {
@@ -187,14 +181,14 @@ static void ism_free(AVFormatContext *s)
os->out = os->out2 = os->tail_out = NULL;
if (os->ctx && os->ctx_inited)
av_write_trailer(os->ctx);
- if (os->ctx)
+ if (os->ctx && os->ctx->pb)
avio_context_free(&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);
}
@@ -269,7 +263,7 @@ static int write_manifest(AVFormatContext *s, int final)
if (s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
continue;
last = i;
- avio_printf(out, "<QualityLevel Index=\"%d\" Bitrate=\"%d\" FourCC=\"%s\" MaxWidth=\"%d\" MaxHeight=\"%d\" CodecPrivateData=\"%s\" />\n", index, s->streams[i]->codecpar->bit_rate, os->fourcc, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height, os->private_str);
+ avio_printf(out, "<QualityLevel Index=\"%d\" Bitrate=\"%"PRId64"\" FourCC=\"%s\" MaxWidth=\"%d\" MaxHeight=\"%d\" CodecPrivateData=\"%s\" />\n", index, s->streams[i]->codecpar->bit_rate, os->fourcc, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height, os->private_str);
index++;
}
output_chunk_list(&c->streams[last], out, final, c->lookahead_count, c->window_size);
@@ -283,7 +277,7 @@ static int write_manifest(AVFormatContext *s, int final)
if (s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
continue;
last = i;
- avio_printf(out, "<QualityLevel Index=\"%d\" Bitrate=\"%d\" FourCC=\"%s\" SamplingRate=\"%d\" Channels=\"%d\" BitsPerSample=\"16\" PacketSize=\"%d\" AudioTag=\"%d\" CodecPrivateData=\"%s\" />\n", index, s->streams[i]->codecpar->bit_rate, os->fourcc, s->streams[i]->codecpar->sample_rate, s->streams[i]->codecpar->channels, os->packet_size, os->audio_tag, os->private_str);
+ avio_printf(out, "<QualityLevel Index=\"%d\" Bitrate=\"%"PRId64"\" FourCC=\"%s\" SamplingRate=\"%d\" Channels=\"%d\" BitsPerSample=\"16\" PacketSize=\"%d\" AudioTag=\"%d\" CodecPrivateData=\"%s\" />\n", index, s->streams[i]->codecpar->bit_rate, os->fourcc, s->streams[i]->codecpar->sample_rate, s->streams[i]->codecpar->channels, os->packet_size, os->audio_tag, os->private_str);
index++;
}
output_chunk_list(&c->streams[last], out, final, c->lookahead_count, c->window_size);
@@ -292,7 +286,7 @@ static int write_manifest(AVFormatContext *s, int final)
avio_printf(out, "</SmoothStreamingMedia>\n");
avio_flush(out);
ff_format_io_close(s, &out);
- return ff_rename(temp_filename, filename);
+ return ff_rename(temp_filename, filename, s);
}
static int ism_write_header(AVFormatContext *s)
@@ -303,6 +297,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;
}
@@ -312,13 +307,7 @@ static int ism_write_header(AVFormatContext *s)
goto fail;
}
- c->protocols = ffurl_get_protocols(s->protocol_whitelist, s->protocol_blacklist);
- if (!c->protocols) {
- ret = AVERROR(ENOMEM);
- 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;
@@ -329,23 +318,21 @@ static int ism_write_header(AVFormatContext *s)
AVFormatContext *ctx;
AVStream *st;
AVDictionary *opts = NULL;
- char buf[10];
if (!s->streams[i]->codecpar->bit_rate) {
av_log(s, AV_LOG_ERROR, "No bit rate set for stream %d\n", i);
ret = AVERROR(EINVAL);
goto fail;
}
- snprintf(os->dirname, sizeof(os->dirname), "%s/QualityLevels(%d)", s->filename, s->streams[i]->codecpar->bit_rate);
+ snprintf(os->dirname, sizeof(os->dirname), "%s/QualityLevels(%"PRId64")", s->filename, s->streams[i]->codecpar->bit_rate);
if (mkdir(os->dirname, 0777) == -1 && errno != EEXIST) {
ret = AVERROR(errno);
+ av_log(s, AV_LOG_ERROR, "mkdir failed\n");
goto fail;
}
- os->protocols = c->protocols;
-
ctx = avformat_alloc_context();
- if (!ctx) {
+ if (!ctx || ff_copy_whiteblacklists(ctx, s) < 0) {
ret = AVERROR(ENOMEM);
goto fail;
}
@@ -367,8 +354,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;
@@ -540,8 +526,7 @@ static int ism_flush(AVFormatContext *s, int final)
continue;
snprintf(filename, sizeof(filename), "%s/temp", os->dirname);
- ret = ffurl_open(&os->out, filename, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL,
- c->protocols, NULL);
+ ret = ffurl_open_whitelist(&os->out, filename, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret < 0)
break;
os->cur_start_pos = os->tail_pos;
@@ -559,7 +544,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,
@@ -577,7 +562,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));
@@ -614,7 +599,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)
@@ -640,7 +625,7 @@ static const AVOption options[] = {
{ "extra_window_size", "number of fragments kept outside of the manifest before removing from disk", OFFSET(extra_window_size), AV_OPT_TYPE_INT, { .i64 = 5 }, 0, INT_MAX, E },
{ "lookahead_count", "number of lookahead fragments", OFFSET(lookahead_count), AV_OPT_TYPE_INT, { .i64 = 2 }, 0, INT_MAX, E },
{ "min_frag_duration", "minimum fragment duration (in microseconds)", OFFSET(min_frag_duration), AV_OPT_TYPE_INT64, { .i64 = 5000000 }, 0, INT_MAX, E },
- { "remove_at_exit", "remove all fragments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, E },
+ { "remove_at_exit", "remove all fragments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
{ NULL },
};
@@ -662,6 +647,5 @@ AVOutputFormat ff_smoothstreaming_muxer = {
.write_header = ism_write_header,
.write_packet = ism_write_packet,
.write_trailer = ism_write_trailer,
- .codec_tag = (const AVCodecTag* const []){ ff_mp4_obj_type, 0 },
.priv_class = &ism_class,
};
diff --git a/libavformat/smush.c b/libavformat/smush.c
index 817e736..fe544d8 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);
@@ -157,11 +157,7 @@ static int smush_read_header(AVFormatContext *ctx)
vst->codecpar->height = height;
if (!smush->version) {
- av_free(vst->codecpar->extradata);
- vst->codecpar->extradata_size = 1024 + 2;
- vst->codecpar->extradata = av_malloc(vst->codecpar->extradata_size +
- AV_INPUT_BUFFER_PADDING_SIZE);
- if (!vst->codecpar->extradata)
+ if (ff_alloc_extradata(vst->codecpar, 1024 + 2))
return AVERROR(ENOMEM);
AV_WL16(vst->codecpar->extradata, subversion);
@@ -199,7 +195,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 5c7f43d..5796f8d2 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 e59531b..f4a12e9 100644
--- a/libavformat/sox.h
+++ b/libavformat/sox.h
@@ -2,20 +2,20 @@
* SoX native format common data
* Copyright (c) 2009 Daniel Verkamp <daniel@drv.nu>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/soxdec.c b/libavformat/soxdec.c
index ee3d1dc..12a94c8 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->codecpar->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);
@@ -111,7 +113,7 @@ static int sox_read_header(AVFormatContext *s)
st->codecpar->sample_rate = sample_rate;
st->codecpar->bits_per_coded_sample = 32;
- st->codecpar->bit_rate = st->codecpar->sample_rate *
+ st->codecpar->bit_rate = (int64_t)st->codecpar->sample_rate *
st->codecpar->bits_per_coded_sample *
st->codecpar->channels;
st->codecpar->block_align = st->codecpar->bits_per_coded_sample *
@@ -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]->codecpar->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 d0f7d41..7b37bd4 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 777ac47..604141a 100644
--- a/libavformat/spdif.c
+++ b/libavformat/spdif.c
@@ -2,20 +2,20 @@
* IEC 61937 common code
* Copyright (c) 2009 Bartlomiej Wolowiec
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/spdif.h b/libavformat/spdif.h
index 5ebfd91..0039fcf 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 7c21235..f728837 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 a9b3b52..b47ec12 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
*/
@@ -95,7 +95,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,
@@ -463,6 +463,7 @@ static int spdif_write_header(AVFormatContext *s)
ctx->header_info = spdif_header_aac;
break;
case AV_CODEC_ID_TRUEHD:
+ case AV_CODEC_ID_MLP:
ctx->header_info = spdif_header_truehd;
ctx->hd_buf = av_malloc(MAT_FRAME_SIZE);
if (!ctx->hd_buf)
@@ -524,13 +525,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 + AV_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 + AV_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 */
@@ -556,5 +557,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 6f8206c..56bd0c4 100644
--- a/libavformat/srtdec.c
+++ b/libavformat/srtdec.c
@@ -1,102 +1,244 @@
/*
* SubRip subtitle demuxer
* Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org>
+ * Copyright (c) 2015 Clément Bœsch <u pkh me>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free 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.
+ * Also, that number can be followed by random garbage, so we can not
+ * unfortunately check that we only have a number. */
+ if (ff_subtitles_read_line(&tr, buf, sizeof(buf)) < 0 ||
+ strtol(buf, &pbuf, 10) < 0 || pbuf == buf)
+ return 0;
+
+ /* Check if the next line matches a SRT timestamp */
+ if (ff_subtitles_read_line(&tr, buf, sizeof(buf)) < 0)
+ return 0;
+ pbuf = buf;
+ if (buf[0] == '-')
+ pbuf++;
+ if (pbuf[0] >= '0' && pbuf[0] <= '9' && strstr(buf, " --> ")
+ && sscanf(buf, "%*d:%*d:%*d%*1[,.]%*d --> %*d:%*d:%*d%*1[,.]%d", &v) == 1)
+ return AVPROBE_SCORE_MAX;
+
+ return 0;
+}
+
+struct event_info {
+ int32_t x1, x2, y1, y2;
+ int duration;
+ int64_t pts;
+ int64_t pos;
+};
+
+static int get_event_info(const char *line, struct event_info *ei)
+{
+ int hh1, mm1, ss1, ms1;
+ int hh2, mm2, ss2, ms2;
+
+ ei->x1 = ei->x2 = ei->y1 = ei->y2 = ei->duration = -1;
+ ei->pts = AV_NOPTS_VALUE;
+ ei->pos = -1;
+ if (sscanf(line, "%d:%d:%d%*1[,.]%d --> %d:%d:%d%*1[,.]%d"
+ "%*[ ]X1:%"PRId32" X2:%"PRId32" Y1:%"PRId32" Y2:%"PRId32,
+ &hh1, &mm1, &ss1, &ms1,
+ &hh2, &mm2, &ss2, &ms2,
+ &ei->x1, &ei->x2, &ei->y1, &ei->y2) >= 8) {
+ const int64_t start = (hh1*3600LL + mm1*60LL + ss1) * 1000LL + ms1;
+ const int64_t end = (hh2*3600LL + mm2*60LL + ss2) * 1000LL + ms2;
+ ei->duration = end - start;
+ ei->pts = start;
+ return 0;
+ }
+ return -1;
+}
+
+static int add_event(FFDemuxSubtitlesQueue *q, AVBPrint *buf, char *line_cache,
+ const struct event_info *ei, int append_cache)
+{
+ if (append_cache && line_cache[0])
+ av_bprintf(buf, "%s\n", line_cache);
+ line_cache[0] = 0;
- if (AV_RB24(ptr) == 0xEFBBBF)
- ptr += 3; /* skip UTF-8 BOM */
+ while (buf->len > 0 && buf->str[buf->len - 1] == '\n')
+ buf->str[--buf->len] = 0;
- 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;
+ if (buf->len) {
+ AVPacket *sub = ff_subtitles_queue_insert(q, buf->str, buf->len, 0);
+ if (!sub)
+ return AVERROR(ENOMEM);
+ av_bprint_clear(buf);
+ sub->pos = ei->pos;
+ sub->pts = ei->pts;
+ sub->duration = ei->duration;
+ if (ei->x1 != -1) {
+ uint8_t *p = av_packet_new_side_data(sub, AV_PKT_DATA_SUBTITLE_POSITION, 16);
+ if (p) {
+ AV_WL32(p, ei->x1);
+ AV_WL32(p + 4, ei->y1);
+ AV_WL32(p + 8, ei->x2);
+ AV_WL32(p + 12, ei->y2);
+ }
+ }
}
+
return 0;
}
static int srt_read_header(AVFormatContext *s)
{
+ SRTContext *srt = s->priv_data;
+ AVBPrint buf;
AVStream *st = avformat_new_stream(s, NULL);
+ int res = 0;
+ char line[4096], line_cache[4096];
+ int has_event_info = 0;
+ struct event_info ei;
+ 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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
- st->codecpar->codec_id = AV_CODEC_ID_SRT;
- return 0;
-}
+ st->codecpar->codec_id = AV_CODEC_ID_SUBRIP;
-static int64_t get_pts(const char *buf)
-{
- int i, v, hour, min, sec, hsec;
-
- 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;
+ av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+ line_cache[0] = 0;
+
+ while (!ff_text_eof(&tr)) {
+ struct event_info tmp_ei;
+ const int64_t pos = ff_text_pos(&tr);
+ ptrdiff_t len = ff_subtitles_read_line(&tr, line, sizeof(line));
+
+ if (len < 0)
+ break;
+
+ if (!len || !line[0])
+ continue;
+
+ if (get_event_info(line, &tmp_ei) < 0) {
+ char *pline;
+
+ if (!has_event_info)
+ continue;
+
+ if (line_cache[0]) {
+ /* We got some cache and a new line so we assume the cached
+ * line was actually part of the payload */
+ av_bprintf(&buf, "%s\n", line_cache);
+ line_cache[0] = 0;
+ }
+
+ /* If the line doesn't start with a number, we assume it's part of
+ * the payload, otherwise is likely an event number preceding the
+ * timing information... but we can't be sure of this yet, so we
+ * cache it */
+ if (strtol(line, &pline, 10) < 0 || line == pline)
+ av_bprintf(&buf, "%s\n", line);
+ else
+ strcpy(line_cache, line);
+ } else {
+ if (has_event_info) {
+ /* We have the information of previous event, append it to the
+ * queue. We insert the cached line if and only if the payload
+ * is empty and the cached line is not a standalone number. */
+ char *pline = NULL;
+ const int standalone_number = strtol(line_cache, &pline, 10) >= 0 && pline && !*pline;
+ res = add_event(&srt->q, &buf, line_cache, &ei, !buf.len && !standalone_number);
+ if (res < 0)
+ goto end;
+ } else {
+ has_event_info = 1;
+ }
+ tmp_ei.pos = pos;
+ ei = tmp_ei;
}
- buf += strcspn(buf, "\n") + 1;
}
- return AV_NOPTS_VALUE;
+
+ /* Append the last event. Here we force the cache to be flushed, because a
+ * trailing number is more likely to be geniune (for example a copyright
+ * date) and not the event index of an inexistant event */
+ if (has_event_info) {
+ res = add_event(&srt->q, &buf, line_cache, &ei, 1);
+ if (res < 0)
+ goto end;
+ }
+
+ ff_subtitles_queue_finalize(s, &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 0000000..d811a4d
--- /dev/null
+++ b/libavformat/srtenc.c
@@ -0,0 +1,115 @@
+/*
+ * SubRip subtitle muxer
+ * Copyright (c) 2012 Nicolas George <nicolas.george@normalesup.org>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "libavutil/log.h"
+#include "libavutil/intreadwrite.h"
+
+/* TODO: add options for:
+ - character encoding;
+ - LF / CRLF;
+ - byte order mark.
+ */
+
+typedef struct SRTContext{
+ unsigned index;
+} SRTContext;
+
+static int srt_write_header(AVFormatContext *avf)
+{
+ SRTContext *srt = avf->priv_data;
+
+ if (avf->nb_streams != 1 ||
+ avf->streams[0]->codecpar->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]->codecpar->codec_id != AV_CODEC_ID_TEXT &&
+ avf->streams[0]->codecpar->codec_id != AV_CODEC_ID_SUBRIP) {
+ av_log(avf, AV_LOG_ERROR,
+ "Unsupported subtitles codec: %s\n",
+ avcodec_get_name(avf->streams[0]->codecpar->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;
+
+ 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 FF_API_CONVERGENCE_DURATION
+FF_DISABLE_DEPRECATION_WARNINGS
+ if (d <= 0)
+ /* For backward compatibility, fallback to convergence_duration. */
+ d = pkt->convergence_duration;
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+ 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 e054fcc..f8b686c 100644
--- a/libavformat/srtp.c
+++ b/libavformat/srtp.c
@@ -2,20 +2,20 @@
* SRTP encryption/decryption
* Copyright (c) 2012 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/srtp.h b/libavformat/srtp.h
index 18ed177..3189f8f 100644
--- a/libavformat/srtp.h
+++ b/libavformat/srtp.h
@@ -2,20 +2,20 @@
* SRTP encryption/decryption
* Copyright (c) 2012 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/srtpproto.c b/libavformat/srtpproto.c
index e6e035a..5e6e516 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
*/
@@ -80,8 +80,8 @@ static int srtp_open(URLContext *h, const char *uri, int flags)
av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &rtp_port,
path, sizeof(path), uri);
ff_url_join(buf, sizeof(buf), "rtp", NULL, hostname, rtp_port, "%s", path);
- if ((ret = ffurl_open(&s->rtp_hd, buf, flags, &h->interrupt_callback, NULL,
- h->protocols, h)) < 0)
+ if ((ret = ffurl_open_whitelist(&s->rtp_hd, buf, flags, &h->interrupt_callback,
+ NULL, h->protocol_whitelist, h->protocol_blacklist, h)) < 0)
goto fail;
h->max_packet_size = FFMIN(s->rtp_hd->max_packet_size,
diff --git a/libavformat/stldec.c b/libavformat/stldec.c
new file mode 100644
index 0000000..35de493
--- /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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->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(s, &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 0000000..497cf85
--- /dev/null
+++ b/libavformat/subfile.c
@@ -0,0 +1,149 @@
+/*
+ * 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_whitelist(&c->h, filename, flags, &h->interrupt_callback,
+ options, h->protocol_whitelist, h->protocol_blacklist, h);
+ 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 AVERROR_EOF;
+ 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;
+}
+
+const 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,
+ .default_whitelist = "file",
+};
diff --git a/libavformat/subtitles.c b/libavformat/subtitles.c
new file mode 100644
index 0000000..93c9ef0
--- /dev/null
+++ b/libavformat/subtitles.c
@@ -0,0 +1,425 @@
+/*
+ * 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, size_t 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)
+ return FFDIFFSIGN(s1->pos, s2->pos);
+ return FFDIFFSIGN(s1->pts , s2->pts);
+}
+
+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;
+}
+
+static void drop_dups(void *log_ctx, FFDemuxSubtitlesQueue *q)
+{
+ int i, drop = 0;
+
+ for (i = 1; i < q->nb_subs; i++) {
+ const int last_id = i - 1 - drop;
+ const AVPacket *last = &q->subs[last_id];
+
+ if (q->subs[i].pts == last->pts &&
+ q->subs[i].duration == last->duration &&
+ q->subs[i].stream_index == last->stream_index &&
+ !strcmp(q->subs[i].data, last->data)) {
+
+ av_packet_unref(&q->subs[i]);
+ drop++;
+ } else if (drop) {
+ q->subs[last_id + 1] = q->subs[i];
+ memset(&q->subs[i], 0, sizeof(q->subs[i])); // for safety
+ }
+ }
+
+ if (drop) {
+ q->nb_subs -= drop;
+ av_log(log_ctx, AV_LOG_WARNING, "Dropping %d duplicated subtitle events\n", drop);
+ }
+}
+
+void ff_subtitles_queue_finalize(void *log_ctx, 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 < 0 && i < q->nb_subs - 1)
+ q->subs[i].duration = q->subs[i + 1].pts - q->subs[i].pts;
+
+ if (!q->keep_duplicates)
+ drop_dups(log_ctx, q);
+}
+
+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_packet_ref(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_packet_unref(&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 size_t 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 0000000..ca78db2
--- /dev/null
+++ b/libavformat/subtitles.h
@@ -0,0 +1,212 @@
+/*
+ * 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
+ int keep_duplicates; ///< set to 1 to keep duplicated subtitle events
+} 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, size_t len, int merge);
+
+/**
+ * Set missing durations, sort subtitles by PTS (and then byte position), and
+ * drop duplicated events.
+ */
+void ff_subtitles_queue_finalize(void *log_ctx, 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 0000000..e579d1c
--- /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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->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(s, &subviewer1->q);
+ return 0;
+}
+
+static int subviewer1_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ SubViewer1Context *subviewer1 = s->priv_data;
+ return ff_subtitles_queue_read_packet(&subviewer1->q, pkt);
+}
+
+static int subviewer1_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ SubViewer1Context *subviewer1 = s->priv_data;
+ return ff_subtitles_queue_seek(&subviewer1->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int subviewer1_read_close(AVFormatContext *s)
+{
+ SubViewer1Context *subviewer1 = s->priv_data;
+ ff_subtitles_queue_clean(&subviewer1->q);
+ return 0;
+}
+
+AVInputFormat ff_subviewer1_demuxer = {
+ .name = "subviewer1",
+ .long_name = NULL_IF_CONFIG_SMALL("SubViewer v1 subtitle format"),
+ .priv_data_size = sizeof(SubViewer1Context),
+ .read_probe = subviewer1_probe,
+ .read_header = subviewer1_read_header,
+ .read_packet = subviewer1_read_packet,
+ .read_seek2 = subviewer1_read_seek,
+ .read_close = subviewer1_read_close,
+ .extensions = "sub",
+};
diff --git a/libavformat/subviewerdec.c b/libavformat/subviewerdec.c
new file mode 100644
index 0000000..af084f4
--- /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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->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->codecpar->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 = ff_bprint_to_codecpar_extradata(st->codecpar, &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(s, &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 0000000..0930dbc
--- /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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->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/supenc.c b/libavformat/supenc.c
new file mode 100644
index 0000000..f5f6b58
--- /dev/null
+++ b/libavformat/supenc.c
@@ -0,0 +1,96 @@
+/*
+ * SUP muxer
+ * Copyright (c) 2014 Petri Hintukainen <phintuka@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 "avformat.h"
+#include "internal.h"
+#include "libavutil/intreadwrite.h"
+
+#define SUP_PGS_MAGIC 0x5047 /* "PG", big endian */
+
+static int sup_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ uint8_t *data = pkt->data;
+ size_t size = pkt->size;
+ uint32_t pts = 0, dts = 0;
+
+ if (pkt->pts != AV_NOPTS_VALUE) {
+ pts = (uint32_t)pkt->pts;
+ }
+ if (pkt->dts != AV_NOPTS_VALUE) {
+ dts = (uint32_t)pkt->dts;
+ }
+
+ /*
+ Split frame to segments.
+ mkvmerge stores multiple segments in one frame.
+ */
+ while (size > 2) {
+ size_t len = AV_RB16(data + 1) + 3;
+
+ if (len > size) {
+ av_log(s, AV_LOG_ERROR, "Not enough data, skipping %d bytes\n",
+ (int)size);
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* header */
+ avio_wb16(s->pb, SUP_PGS_MAGIC);
+ avio_wb32(s->pb, pts);
+ avio_wb32(s->pb, dts);
+
+ avio_write(s->pb, data, len);
+
+ data += len;
+ size -= len;
+ }
+
+ if (size > 0) {
+ av_log(s, AV_LOG_ERROR, "Skipping %d bytes after last segment in frame\n",
+ (int)size);
+ return AVERROR_INVALIDDATA;
+ }
+
+ return 0;
+}
+
+static int sup_write_header(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);
+ }
+
+ avpriv_set_pts_info(s->streams[0], 32, 1, 90000);
+
+ return 0;
+}
+
+AVOutputFormat ff_sup_muxer = {
+ .name = "sup",
+ .long_name = NULL_IF_CONFIG_SMALL("raw HDMV Presentation Graphic Stream subtitles"),
+ .extensions = "sup",
+ .mime_type = "application/x-pgs",
+ .subtitle_codec = AV_CODEC_ID_HDMV_PGS_SUBTITLE,
+ .write_header = sup_write_header,
+ .write_packet = sup_write_packet,
+ .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT,
+};
diff --git a/libavformat/svag.c b/libavformat/svag.c
new file mode 100644
index 0000000..828b853
--- /dev/null
+++ b/libavformat/svag.c
@@ -0,0 +1,78 @@
+/*
+ * SVAG demuxer
+ * Copyright (c) 2015 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"
+
+static int svag_probe(AVProbeData *p)
+{
+ if (memcmp(p->buf, "Svag", 4))
+ return 0;
+
+ return AVPROBE_SCORE_MAX;
+}
+
+static int svag_read_header(AVFormatContext *s)
+{
+ unsigned size, align;
+ AVStream *st;
+
+ avio_skip(s->pb, 4);
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ size = avio_rl32(s->pb);
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_PSX;
+ st->codecpar->sample_rate = avio_rl32(s->pb);
+ if (st->codecpar->sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->channels = avio_rl32(s->pb);
+ if (st->codecpar->channels <= 0 || st->codecpar->channels > 8)
+ return AVERROR_INVALIDDATA;
+ st->duration = size / (16 * st->codecpar->channels) * 28;
+ align = avio_rl32(s->pb);
+ if (align <= 0 || align > INT_MAX / st->codecpar->channels)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->block_align = align * st->codecpar->channels;
+ avio_skip(s->pb, 0x800 - avio_tell(s->pb));
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ return 0;
+}
+
+static int svag_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+
+ return av_get_packet(s->pb, pkt, par->block_align);
+}
+
+AVInputFormat ff_svag_demuxer = {
+ .name = "svag",
+ .long_name = NULL_IF_CONFIG_SMALL("Konami PS2 SVAG"),
+ .read_probe = svag_probe,
+ .read_header = svag_read_header,
+ .read_packet = svag_read_packet,
+ .extensions = "svag",
+};
diff --git a/libavformat/swf.c b/libavformat/swf.c
index e6adf69..1aa434a 100644
--- a/libavformat/swf.c
+++ b/libavformat/swf.c
@@ -3,20 +3,20 @@
* Copyright (c) 2000 Fabrice Bellard
* Copyright (c) 2003 Tinic Uro
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/swf.h b/libavformat/swf.h
index 9b90251..d0f0194 100644
--- a/libavformat/swf.h
+++ b/libavformat/swf.h
@@ -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
*/
@@ -38,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
diff --git a/libavformat/swfdec.c b/libavformat/swfdec.c
index 984fedf..57b619f 100644
--- a/libavformat/swfdec.c
+++ b/libavformat/swfdec.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
*/
@@ -26,8 +26,12 @@
#include <zlib.h>
#endif
+#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/internal.h"
#include "libavutil/intreadwrite.h"
+#include "libavcodec/get_bits.h"
#include "swf.h"
static const AVCodecTag swf_audio_codec_tags[] = {
@@ -43,8 +47,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;
@@ -59,12 +63,39 @@ 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;
+
+ if ( AV_RB24(p->buf) == AV_RB24("CWS")
+ && p->buf[3] <= 20)
+ return AVPROBE_SCORE_MAX / 4 + 1;
+
+ if (init_get_bits8(&gb, p->buf + 3, p->buf_size - 3) < 0)
return 0;
+
+ 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
@@ -88,10 +119,10 @@ retry:
z->avail_out = buf_size;
ret = inflate(z, Z_NO_FLUSH);
- if (ret != Z_OK && ret != Z_STREAM_END) {
- av_log(s, AV_LOG_ERROR, "Inflate error: %d\n", ret);
- return AVERROR_UNKNOWN;
- }
+ if (ret == Z_STREAM_END)
+ return AVERROR_EOF;
+ if (ret != Z_OK)
+ return AVERROR(EINVAL);
if (buf_size - z->avail_out == 0)
goto retry;
@@ -110,26 +141,22 @@ static int swf_read_header(AVFormatContext *s)
avio_rl32(pb);
if (tag == MKBETAG('C', 'W', 'S', 0)) {
- av_log(s, AV_LOG_INFO, "Compressed SWF file detected\n");
+ av_log(s, AV_LOG_INFO, "SWF compressed file detected\n");
#if CONFIG_ZLIB
- if (inflateInit(&swf->zstream) != Z_OK) {
- av_log(s, AV_LOG_ERROR, "Unable to init zlib context\n");
- return AVERROR(EINVAL);
- }
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) {
- av_freep(&swf->zbuf_in);
- av_freep(&swf->zbuf_out);
- av_freep(&swf->zpb);
+ 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, "missing zlib support, unable to open\n");
+ av_log(s, AV_LOG_ERROR, "zlib support is required to read SWF compressed files\n");
return AVERROR(EIO);
#endif
} else if (tag != MKBETAG('F', 'W', 'S', 0))
@@ -146,6 +173,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->codecpar->channels = 2;
+ ast->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
+ } else {
+ ast->codecpar->channels = 1;
+ ast->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
+ }
+ ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ ast->codecpar->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->codecpar->codec_id == AV_CODEC_ID_PCM_S16LE)
+ ast->codecpar->codec_id = AV_CODEC_ID_PCM_U8;
+ ast->codecpar->sample_rate = 44100 >> (3 - sample_rate_code);
+ avpriv_set_pts_info(ast, 64, 1, ast->codecpar->sample_rate);
+ return ast;
+}
+
static int swf_read_packet(AVFormatContext *s, AVPacket *pkt)
{
SWFContext *swf = s->priv_data;
@@ -162,7 +215,7 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt)
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;
@@ -184,7 +237,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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
vst->codecpar->codec_id = ff_codec_get_id(ff_swf_codec_tags, avio_r8(pb));
@@ -192,7 +245,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];
@@ -203,24 +255,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->codecpar->channels = 2;
- ast->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
- } else {
- ast->codecpar->channels = 1;
- ast->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
- }
- ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
- ast->codecpar->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->codecpar->sample_rate = 44100 >> (3 - sample_rate_code);
- avpriv_set_pts_info(ast, 64, 1, ast->codecpar->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->codecpar->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;
@@ -239,6 +305,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;
+
+ ff_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->codecpar->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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ vst->codecpar->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->codecpar->width && !st->codecpar->height) {
+ st->codecpar->width = width;
+ st->codecpar->height = height;
+ } else {
+ ff_add_param_change(pkt, 0, 0, 0, width, height);
+ }
+ pkt->pos = pos;
+ pkt->stream_index = st->index;
+
+ if (linesize * height > pkt->size) {
+ res = AVERROR_INVALIDDATA;
+ av_packet_unref(pkt);
+ goto bitmap_end;
+ }
+
+ 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->codecpar->format != AV_PIX_FMT_NONE && st->codecpar->format != pix_fmt) {
+ av_log(s, AV_LOG_ERROR, "pixel format change unsupported\n");
+ } else
+ st->codecpar->format = pix_fmt;
+
+ 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];
@@ -270,7 +473,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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
vst->codecpar->codec_id = AV_CODEC_ID_MJPEG;
@@ -283,21 +486,39 @@ 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_packet_unref(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_packet_unref(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, "Clipping len %d\n", len);
len = FFMAX(0, len);
avio_skip(pb, len);
}
@@ -310,9 +531,7 @@ static av_cold int swf_read_close(AVFormatContext *avctx)
inflateEnd(&s->zstream);
av_freep(&s->zbuf_in);
av_freep(&s->zbuf_out);
-
avio_context_free(&s->zpb);
-
return 0;
}
#endif
diff --git a/libavformat/swfenc.c b/libavformat/swfenc.c
index 345688c..cada45e 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);
@@ -231,7 +232,7 @@ static int swf_write_header(AVFormatContext *s)
}
if (!swf->audio_par)
- swf->samples_per_frame = (44100.0 * rate_base) / rate;
+ swf->samples_per_frame = (44100LL * rate_base) / rate;
else
swf->samples_per_frame = (swf->audio_par->sample_rate * rate_base) / rate;
@@ -251,6 +252,10 @@ static int swf_write_header(AVFormatContext *s)
(will be patched if not streamed) */
put_swf_rect(pb, 0, width * 20, 0, height * 20);
+ if ((rate * 256LL) / rate_base >= (1<<16)) {
+ av_log(s, AV_LOG_ERROR, "Invalid (too large) frame rate %d/%d\n", rate, rate_base);
+ return AVERROR(EINVAL);
+ }
avio_wl16(pb, (rate * 256) / rate_base); /* frame rate */
swf->duration_pos = avio_tell(pb);
avio_wl16(pb, (uint16_t)(DUMMY_DURATION * (int64_t)rate / rate_base)); /* frame count */
@@ -274,8 +279,8 @@ static int swf_write_header(AVFormatContext *s)
avio_w8(pb, 0x41); /* clipped bitmap fill */
avio_wl16(pb, BITMAP_ID); /* bitmap ID */
/* position of the bitmap */
- put_swf_matrix(pb, (int)(1.0 * (1 << FRAC_BITS)), 0,
- 0, (int)(1.0 * (1 << FRAC_BITS)), 0, 0);
+ put_swf_matrix(pb, 1 << FRAC_BITS, 0,
+ 0, 1 << FRAC_BITS, 0, 0);
avio_w8(pb, 0); /* no line style */
/* shape drawing */
@@ -427,8 +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,
- (void (*)(void *, void *, int)) &avio_write);
+ av_fifo_generic_read(swf->audio_fifo, pb, frame_size, (void*)avio_write);
put_swf_end_tag(s);
/* update FIFO */
@@ -487,8 +491,9 @@ static int swf_write_trailer(AVFormatContext *s)
par = s->streams[i]->codecpar;
if (par->codec_type == AVMEDIA_TYPE_VIDEO)
video_par = par;
- else
- av_fifo_free(swf->audio_fifo);
+ else {
+ av_fifo_freep(&swf->audio_fifo);
+ }
}
put_swf_tag(s, TAG_END);
@@ -501,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 4b3037d..1535bec 100644
--- a/libavformat/takdec.c
+++ b/libavformat/takdec.c
@@ -2,29 +2,31 @@
* 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"
+
#define BITSTREAM_READER_LE
-#include "libavcodec/bitstream.h"
#include "libavcodec/tak.h"
#include "apetag.h"
#include "avformat.h"
+#include "avio_internal.h"
#include "internal.h"
#include "rawdec.h"
@@ -40,11 +42,17 @@ 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;
AVIOContext *pb = s->pb;
- BitstreamContext bc;
+ GetBitContext gb;
AVStream *st;
uint8_t *buffer = NULL;
int ret;
@@ -55,7 +63,7 @@ static int tak_read_header(AVFormatContext *s)
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->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')) {
@@ -63,7 +71,7 @@ static int tak_read_header(AVFormatContext *s)
return 0;
}
- while (!pb->eof_reached) {
+ while (!avio_feof(pb)) {
enum TAKMetaDataType type;
int size;
@@ -74,16 +82,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 + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (size <= 3)
+ return AVERROR_INVALIDDATA;
+
+ buffer = av_malloc(size - 3 + AV_INPUT_BUFFER_PADDING_SIZE);
if (!buffer)
return AVERROR(ENOMEM);
+ memset(buffer + size - 3, 0, AV_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;
+ }
+ }
- bitstream_init8(&bc, buffer, size);
+ init_get_bits8(&gb, buffer, size - 3);
break;
case TAK_METADATA_MD5: {
uint8_t md5[16];
@@ -91,8 +111,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]);
@@ -119,7 +145,7 @@ static int tak_read_header(AVFormatContext *s)
if (type == TAK_METADATA_STREAMINFO) {
TAKStreamInfo ti;
- avpriv_tak_parse_streaminfo(&bc, &ti);
+ avpriv_tak_parse_streaminfo(&gb, &ti);
if (ti.samples > 0)
st->duration = ti.samples;
st->codecpar->bits_per_coded_sample = ti.bps;
@@ -130,18 +156,18 @@ static int tak_read_header(AVFormatContext *s)
st->start_time = 0;
avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
st->codecpar->extradata = buffer;
- st->codecpar->extradata_size = size;
+ st->codecpar->extradata_size = size - 3;
buffer = NULL;
} else if (type == TAK_METADATA_LAST_FRAME) {
if (size != 11)
return AVERROR_INVALIDDATA;
tc->mlast_frame = 1;
- tc->data_end = bitstream_read_63(&bc, TAK_LAST_FRAME_POS_BITS) +
- bitstream_read(&bc, TAK_LAST_FRAME_SIZE_BITS);
+ tc->data_end = get_bits64(&gb, TAK_LAST_FRAME_POS_BITS) +
+ get_bits(&gb, TAK_LAST_FRAME_SIZE_BITS);
av_freep(&buffer);
} else if (type == TAK_METADATA_ENCODER) {
av_log(s, AV_LOG_VERBOSE, "encoder version: %0X\n",
- bitstream_read(&bc, TAK_ENCODER_VERSION_BITS));
+ get_bits_long(&gb, TAK_ENCODER_VERSION_BITS));
av_freep(&buffer);
}
}
@@ -158,7 +184,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 1498c26..07b4ed9 100644
--- a/libavformat/tcp.c
+++ b/libavformat/tcp.c
@@ -2,25 +2,27 @@
* 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/avassert.h"
#include "libavutil/parseutils.h"
#include "libavutil/opt.h"
+#include "libavutil/time.h"
#include "internal.h"
#include "network.h"
@@ -34,17 +36,22 @@ typedef struct TCPContext {
const AVClass *class;
int fd;
int listen;
- int timeout;
+ int open_timeout;
+ int rw_timeout;
int listen_timeout;
+ int recv_buffer_size;
+ int send_buffer_size;
} TCPContext;
#define OFFSET(x) offsetof(TCPContext, x)
#define D AV_OPT_FLAG_DECODING_PARAM
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
- { "listen", "Listen for incoming connections", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E },
- { "timeout", "Connection timeout (in milliseconds)", OFFSET(timeout), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, .flags = D|E },
- { "listen_timeout", "Bind timeout (in milliseconds)", OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, INT_MIN, INT_MAX, .flags = D|E },
+ { "listen", "Listen for incoming connections", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, .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 (in milliseconds)", OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
+ { "send_buffer_size", "Socket send buffer size (in bytes)", OFFSET(send_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
+ { "recv_buffer_size", "Socket receive buffer size (in bytes)", OFFSET(recv_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
{ NULL }
};
@@ -66,6 +73,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,19 +85,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->timeout)
- s->timeout = h->rw_timeout ? h->rw_timeout / 1000 : 10000;
- if (h->rw_timeout && s->listen_timeout < 0)
- s->listen_timeout = h->rw_timeout / 1000;
+ 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);
@@ -109,6 +122,16 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
cur_ai = ai;
restart:
+#if HAVE_STRUCT_SOCKADDR_IN6
+ // workaround for IOS9 getaddrinfo in IPv6 only network use hardcode IPv4 address can not resolve port number.
+ if (cur_ai->ai_family == AF_INET6){
+ struct sockaddr_in6 * sockaddr_v6 = (struct sockaddr_in6 *)cur_ai->ai_addr;
+ if (!sockaddr_v6->sin6_port){
+ sockaddr_v6->sin6_port = htons(port);
+ }
+ }
+#endif
+
fd = ff_socket(cur_ai->ai_family,
cur_ai->ai_socktype,
cur_ai->ai_protocol);
@@ -117,15 +140,29 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
goto fail;
}
- if (s->listen) {
+ /* Set the socket's send or receive buffer sizes, if specified.
+ If unspecified or setting fails, system default is used. */
+ if (s->recv_buffer_size > 0) {
+ setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &s->recv_buffer_size, sizeof (s->recv_buffer_size));
+ }
+ if (s->send_buffer_size > 0) {
+ setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &s->send_buffer_size, sizeof (s->send_buffer_size));
+ }
+
+ if (s->listen == 2) {
+ // multi-client
+ if ((ret = ff_listen(fd, cur_ai->ai_addr, cur_ai->ai_addrlen)) < 0)
+ goto fail1;
+ } else if (s->listen == 1) {
+ // single client
if ((ret = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
- s->listen_timeout, h)) < 0) {
+ s->listen_timeout, h)) < 0)
goto fail1;
- }
+ // Socket descriptor already closed here. Safe to overwrite to client one.
fd = ret;
} 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;
@@ -136,6 +173,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
h->is_streamed = 1;
s->fd = fd;
+
freeaddrinfo(ai);
return 0;
@@ -155,14 +193,30 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
return ret;
}
+static int tcp_accept(URLContext *s, URLContext **c)
+{
+ TCPContext *sc = s->priv_data;
+ TCPContext *cc;
+ int ret;
+ av_assert0(sc->listen);
+ if ((ret = ffurl_alloc(c, s->filename, s->flags, &s->interrupt_callback)) < 0)
+ return ret;
+ cc = (*c)->priv_data;
+ ret = ff_accept(sc->fd, sc->listen_timeout, s);
+ if (ret < 0)
+ return ret;
+ cc->fd = ret;
+ return 0;
+}
+
static int tcp_read(URLContext *h, uint8_t *buf, int size)
{
TCPContext *s = h->priv_data;
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);
@@ -175,8 +229,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);
@@ -212,13 +266,35 @@ static int tcp_get_file_handle(URLContext *h)
return s->fd;
}
+static int tcp_get_window_size(URLContext *h)
+{
+ TCPContext *s = h->priv_data;
+ int avail;
+ int avail_len = sizeof(avail);
+
+#if HAVE_WINSOCK2_H
+ /* SO_RCVBUF with winsock only reports the actual TCP window size when
+ auto-tuning has been disabled via setting SO_RCVBUF */
+ if (s->recv_buffer_size < 0) {
+ return AVERROR(ENOSYS);
+ }
+#endif
+
+ if (getsockopt(s->fd, SOL_SOCKET, SO_RCVBUF, &avail, &avail_len)) {
+ return ff_neterrno();
+ }
+ return avail;
+}
+
const URLProtocol ff_tcp_protocol = {
.name = "tcp",
.url_open = tcp_open,
+ .url_accept = tcp_accept,
.url_read = tcp_read,
.url_write = tcp_write,
.url_close = tcp_close,
.url_get_file_handle = tcp_get_file_handle,
+ .url_get_short_seek = tcp_get_window_size,
.url_shutdown = tcp_shutdown,
.priv_data_size = sizeof(TCPContext),
.flags = URL_PROTOCOL_FLAG_NETWORK,
diff --git a/libavformat/tedcaptionsdec.c b/libavformat/tedcaptionsdec.c
new file mode 100644
index 0000000..774d499
--- /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), AV_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(avf, &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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->codec_id = AV_CODEC_ID_TEXT;
+ avpriv_set_pts_info(st, 64, 1, 1000);
+ st->probe_packets = 0;
+ st->start_time = 0;
+ st->duration = last->pts + last->duration;
+ st->cur_dts = 0;
+
+ return 0;
+}
+
+static int tedcaptions_read_packet(AVFormatContext *avf, AVPacket *packet)
+{
+ TEDCaptionsDemuxer *tc = avf->priv_data;
+
+ return ff_subtitles_queue_read_packet(&tc->subs, packet);
+}
+
+static int tedcaptions_read_close(AVFormatContext *avf)
+{
+ TEDCaptionsDemuxer *tc = avf->priv_data;
+
+ ff_subtitles_queue_clean(&tc->subs);
+ return 0;
+}
+
+static av_cold int tedcaptions_read_probe(AVProbeData *p)
+{
+ static const char *const tags[] = {
+ "\"captions\"", "\"duration\"", "\"content\"",
+ "\"startOfParagraph\"", "\"startTime\"",
+ };
+ unsigned i, count = 0;
+ const char *t;
+
+ if (p->buf[strspn(p->buf, " \t\r\n")] != '{')
+ return 0;
+ for (i = 0; i < FF_ARRAY_ELEMS(tags); i++) {
+ if (!(t = strstr(p->buf, tags[i])))
+ continue;
+ t += strlen(tags[i]);
+ t += strspn(t, " \t\r\n");
+ if (*t == ':')
+ count++;
+ }
+ return count == FF_ARRAY_ELEMS(tags) ? AVPROBE_SCORE_MAX :
+ count ? AVPROBE_SCORE_EXTENSION : 0;
+}
+
+static int tedcaptions_read_seek(AVFormatContext *avf, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts,
+ int flags)
+{
+ TEDCaptionsDemuxer *tc = avf->priv_data;
+ return ff_subtitles_queue_seek(&tc->subs, avf, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+AVInputFormat ff_tedcaptions_demuxer = {
+ .name = "tedcaptions",
+ .long_name = NULL_IF_CONFIG_SMALL("TED Talks captions"),
+ .priv_data_size = sizeof(TEDCaptionsDemuxer),
+ .priv_class = &tedcaptions_demuxer_class,
+ .read_header = tedcaptions_read_header,
+ .read_packet = tedcaptions_read_packet,
+ .read_close = tedcaptions_read_close,
+ .read_probe = tedcaptions_read_probe,
+ .read_seek2 = tedcaptions_read_seek,
+};
diff --git a/libavformat/tee.c b/libavformat/tee.c
new file mode 100644
index 0000000..dd1844a
--- /dev/null
+++ b/libavformat/tee.c
@@ -0,0 +1,618 @@
+/*
+ * 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 "internal.h"
+#include "avformat.h"
+#include "avio_internal.h"
+#include "tee_common.h"
+
+typedef enum {
+ ON_SLAVE_FAILURE_ABORT = 1,
+ ON_SLAVE_FAILURE_IGNORE = 2
+} SlaveFailurePolicy;
+
+#define DEFAULT_SLAVE_FAILURE_POLICY ON_SLAVE_FAILURE_ABORT
+
+typedef struct {
+ AVFormatContext *avf;
+ AVBSFContext **bsfs; ///< bitstream filters per stream
+
+ SlaveFailurePolicy on_fail;
+ int use_fifo;
+ AVDictionary *fifo_options;
+
+ /** map from input to output streams indexes,
+ * disabled output streams are set to -1 */
+ int *stream_map;
+ int header_written;
+} TeeSlave;
+
+typedef struct TeeContext {
+ const AVClass *class;
+ unsigned nb_slaves;
+ unsigned nb_alive;
+ TeeSlave *slaves;
+ int use_fifo;
+ AVDictionary *fifo_options;
+ char *fifo_options_str;
+} TeeContext;
+
+static const char *const slave_delim = "|";
+static const char *const slave_bsfs_spec_sep = "/";
+static const char *const slave_select_sep = ",";
+
+#define OFFSET(x) offsetof(TeeContext, x)
+static const AVOption options[] = {
+ {"use_fifo", "Use fifo pseudo-muxer to separate actual muxers from encoder",
+ OFFSET(use_fifo), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
+ {"fifo_options", "fifo pseudo-muxer options", OFFSET(fifo_options_str),
+ AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM},
+ {NULL}
+};
+
+static const AVClass tee_muxer_class = {
+ .class_name = "Tee muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave)
+{
+ if (!opt) {
+ tee_slave->on_fail = DEFAULT_SLAVE_FAILURE_POLICY;
+ return 0;
+ } else if (!av_strcasecmp("abort", opt)) {
+ tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT;
+ return 0;
+ } else if (!av_strcasecmp("ignore", opt)) {
+ tee_slave->on_fail = ON_SLAVE_FAILURE_IGNORE;
+ return 0;
+ }
+ /* Set failure behaviour to abort, so invalid option error will not be ignored */
+ tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT;
+ return AVERROR(EINVAL);
+}
+
+static int parse_slave_fifo_options(const char *use_fifo,
+ const char *fifo_options, TeeSlave *tee_slave)
+{
+ int ret = 0;
+
+ if (use_fifo) {
+ /*TODO - change this to use proper function for parsing boolean
+ * options when there is one */
+ if (av_match_name(use_fifo, "true,y,yes,enable,enabled,on,1")) {
+ tee_slave->use_fifo = 1;
+ } else if (av_match_name(use_fifo, "false,n,no,disable,disabled,off,0")) {
+ tee_slave->use_fifo = 0;
+ } else {
+ return AVERROR(EINVAL);
+ }
+ }
+
+ if (fifo_options)
+ ret = av_dict_parse_string(&tee_slave->fifo_options, fifo_options, "=", ":", 0);
+
+ return ret;
+}
+
+static int close_slave(TeeSlave *tee_slave)
+{
+ AVFormatContext *avf;
+ unsigned i;
+ int ret = 0;
+
+ avf = tee_slave->avf;
+ if (!avf)
+ return 0;
+
+ if (tee_slave->header_written)
+ ret = av_write_trailer(avf);
+
+ if (tee_slave->bsfs) {
+ for (i = 0; i < avf->nb_streams; ++i)
+ av_bsf_free(&tee_slave->bsfs[i]);
+ }
+ av_freep(&tee_slave->stream_map);
+ av_freep(&tee_slave->bsfs);
+
+ ff_format_io_close(avf, &avf->pb);
+ avformat_free_context(avf);
+ tee_slave->avf = NULL;
+ return ret;
+}
+
+static void close_slaves(AVFormatContext *avf)
+{
+ TeeContext *tee = avf->priv_data;
+ unsigned i;
+
+ for (i = 0; i < tee->nb_slaves; i++) {
+ close_slave(&tee->slaves[i]);
+ }
+ av_freep(&tee->slaves);
+}
+
+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, *on_fail = NULL;
+ char *use_fifo = NULL, *fifo_options_str = NULL;
+ AVFormatContext *avf2 = NULL;
+ AVStream *st, *st2;
+ int stream_count;
+ int fullret;
+ char *subselect = NULL, *next_subselect = NULL, *first_subselect = NULL, *tmp_select = NULL;
+
+ if ((ret = ff_tee_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);
+ STEAL_OPTION("onfail", on_fail);
+ STEAL_OPTION("use_fifo", use_fifo);
+ STEAL_OPTION("fifo_options", fifo_options_str);
+
+ ret = parse_slave_failure_policy_option(on_fail, tee_slave);
+ if (ret < 0) {
+ av_log(avf, AV_LOG_ERROR,
+ "Invalid onfail option value, valid options are 'abort' and 'ignore'\n");
+ goto end;
+ }
+
+ ret = parse_slave_fifo_options(use_fifo, fifo_options_str, tee_slave);
+ if (ret < 0) {
+ av_log(avf, AV_LOG_ERROR, "Error parsing fifo options: %s\n", av_err2str(ret));
+ goto end;
+ }
+
+ if (tee_slave->use_fifo) {
+
+ if (options) {
+ char *format_options_str = NULL;
+ ret = av_dict_get_string(options, &format_options_str, '=', ':');
+ if (ret < 0)
+ goto end;
+
+ ret = av_dict_set(&tee_slave->fifo_options, "format_opts", format_options_str,
+ AV_DICT_DONT_STRDUP_VAL);
+ if (ret < 0)
+ goto end;
+ }
+
+ if (format) {
+ ret = av_dict_set(&tee_slave->fifo_options, "fifo_format", format,
+ AV_DICT_DONT_STRDUP_VAL);
+ format = NULL;
+ if (ret < 0)
+ goto end;
+ }
+
+ av_dict_free(&options);
+ options = tee_slave->fifo_options;
+ }
+ ret = avformat_alloc_output_context2(&avf2, NULL,
+ tee_slave->use_fifo ? "fifo" :format, filename);
+ if (ret < 0)
+ goto end;
+ tee_slave->avf = avf2;
+ av_dict_copy(&avf2->metadata, avf->metadata, 0);
+ avf2->opaque = avf->opaque;
+ avf2->io_open = avf->io_open;
+ avf2->io_close = avf->io_close;
+ avf2->interrupt_callback = avf->interrupt_callback;
+ avf2->flags = avf->flags;
+
+ 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) {
+ tmp_select = av_strdup(select); // av_strtok is destructive so we regenerate it in each loop
+ if (!tmp_select) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ fullret = 0;
+ first_subselect = tmp_select;
+ next_subselect = NULL;
+ while (subselect = av_strtok(first_subselect, slave_select_sep, &next_subselect)) {
+ first_subselect = NULL;
+
+ ret = avformat_match_stream_specifier(avf, avf->streams[i], subselect);
+ if (ret < 0) {
+ av_log(avf, AV_LOG_ERROR,
+ "Invalid stream specifier '%s' for output '%s'\n",
+ subselect, slave);
+ goto end;
+ }
+ if (ret != 0) {
+ fullret = 1; // match
+ break;
+ }
+ }
+ av_freep(&tmp_select);
+
+ if (fullret == 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;
+ }
+
+ ret = ff_stream_encode_params_copy(st2, st);
+ if (ret < 0)
+ goto end;
+ }
+
+ ret = ff_format_output_open(avf2, filename, NULL);
+ if (ret < 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->header_written = 1;
+
+ tee_slave->bsfs = av_calloc(avf2->nb_streams, sizeof(*tee_slave->bsfs));
+ 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);
+ ret = AVERROR(EINVAL);
+ goto end;
+ }
+ 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 = av_bsf_list_parse_str(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);
+ }
+
+ for (i = 0; i < avf->nb_streams; i++){
+ int target_stream = tee_slave->stream_map[i];
+ if (target_stream < 0)
+ continue;
+
+ if (!tee_slave->bsfs[target_stream]) {
+ /* Add pass-through bitstream filter */
+ ret = av_bsf_get_null_filter(&tee_slave->bsfs[target_stream]);
+ if (ret < 0) {
+ av_log(avf, AV_LOG_ERROR,
+ "Failed to create pass-through bitstream filter: %s\n",
+ av_err2str(ret));
+ goto end;
+ }
+ }
+
+ tee_slave->bsfs[target_stream]->time_base_in = avf->streams[i]->time_base;
+ ret = avcodec_parameters_copy(tee_slave->bsfs[target_stream]->par_in,
+ avf->streams[i]->codecpar);
+ if (ret < 0)
+ goto end;
+
+ ret = av_bsf_init(tee_slave->bsfs[target_stream]);
+ if (ret < 0) {
+ av_log(avf, AV_LOG_ERROR,
+ "Failed to initialize bitstream filter(s): %s\n",
+ av_err2str(ret));
+ goto end;
+ }
+ }
+
+ 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_free(on_fail);
+ av_dict_free(&options);
+ av_freep(&tmp_select);
+ return ret;
+}
+
+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];
+ AVBSFContext *bsf = slave->bsfs[i];
+ const char *bsf_name;
+
+ av_log(log_ctx, log_level, " stream:%d codec:%s type:%s",
+ i, avcodec_get_name(st->codecpar->codec_id),
+ av_get_media_type_string(st->codecpar->codec_type));
+
+ bsf_name = bsf->filter->priv_class ?
+ bsf->filter->priv_class->item_name(bsf) : bsf->filter->name;
+ av_log(log_ctx, log_level, " bsfs: %s\n", bsf_name);
+ }
+}
+
+static int tee_process_slave_failure(AVFormatContext *avf, unsigned slave_idx, int err_n)
+{
+ TeeContext *tee = avf->priv_data;
+ TeeSlave *tee_slave = &tee->slaves[slave_idx];
+
+ tee->nb_alive--;
+
+ close_slave(tee_slave);
+
+ if (!tee->nb_alive) {
+ av_log(avf, AV_LOG_ERROR, "All tee outputs failed.\n");
+ return err_n;
+ } else if (tee_slave->on_fail == ON_SLAVE_FAILURE_ABORT) {
+ av_log(avf, AV_LOG_ERROR, "Slave muxer #%u failed, aborting.\n", slave_idx);
+ return err_n;
+ } else {
+ av_log(avf, AV_LOG_ERROR, "Slave muxer #%u failed: %s, continuing with %u/%u slaves.\n",
+ slave_idx, av_err2str(err_n), tee->nb_alive, tee->nb_slaves);
+ return 0;
+ }
+}
+
+static int tee_write_header(AVFormatContext *avf)
+{
+ TeeContext *tee = avf->priv_data;
+ unsigned nb_slaves = 0, i;
+ const char *filename = avf->filename;
+ char **slaves = NULL;
+ int ret;
+
+ while (*filename) {
+ char *slave = av_get_token(&filename, slave_delim);
+ if (!slave) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ ret = av_dynarray_add_nofree(&slaves, &nb_slaves, slave);
+ if (ret < 0) {
+ av_free(slave);
+ goto fail;
+ }
+ if (strspn(filename, slave_delim))
+ filename++;
+ }
+
+ if (tee->fifo_options_str) {
+ ret = av_dict_parse_string(&tee->fifo_options, tee->fifo_options_str, "=", ":", 0);
+ if (ret < 0)
+ goto fail;
+ }
+
+ if (!(tee->slaves = av_mallocz_array(nb_slaves, sizeof(*tee->slaves)))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ tee->nb_slaves = tee->nb_alive = nb_slaves;
+
+ for (i = 0; i < nb_slaves; i++) {
+
+ tee->slaves[i].use_fifo = tee->use_fifo;
+ ret = av_dict_copy(&tee->slaves[i].fifo_options, tee->fifo_options, 0);
+ if (ret < 0)
+ goto fail;
+
+ if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0) {
+ ret = tee_process_slave_failure(avf, i, ret);
+ if (ret < 0)
+ goto fail;
+ } else {
+ log_slave(&tee->slaves[i], avf, AV_LOG_VERBOSE);
+ }
+ av_freep(&slaves[i]);
+ }
+
+ for (i = 0; i < avf->nb_streams; i++) {
+ int j, mapped = 0;
+ for (j = 0; j < tee->nb_slaves; j++)
+ if (tee->slaves[j].avf)
+ 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);
+ }
+ av_free(slaves);
+ return 0;
+
+fail:
+ for (i = 0; i < nb_slaves; i++)
+ av_freep(&slaves[i]);
+ close_slaves(avf);
+ av_free(slaves);
+ return ret;
+}
+
+static int tee_write_trailer(AVFormatContext *avf)
+{
+ TeeContext *tee = avf->priv_data;
+ int ret_all = 0, ret;
+ unsigned i;
+
+ for (i = 0; i < tee->nb_slaves; i++) {
+ if ((ret = close_slave(&tee->slaves[i])) < 0) {
+ ret = tee_process_slave_failure(avf, i, ret);
+ if (!ret_all && ret < 0)
+ ret_all = ret;
+ }
+ }
+ av_freep(&tee->slaves);
+ return ret_all;
+}
+
+static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt)
+{
+ TeeContext *tee = avf->priv_data;
+ AVFormatContext *avf2;
+ AVBSFContext *bsfs;
+ AVPacket pkt2;
+ int ret_all = 0, ret;
+ unsigned i, s;
+ int s2;
+
+ for (i = 0; i < tee->nb_slaves; i++) {
+ if (!(avf2 = tee->slaves[i].avf))
+ continue;
+
+ /* Flush slave if pkt is NULL*/
+ if (!pkt) {
+ ret = av_interleaved_write_frame(avf2, NULL);
+ if (ret < 0) {
+ ret = tee_process_slave_failure(avf, i, ret);
+ if (!ret_all && ret < 0)
+ ret_all = ret;
+ }
+ continue;
+ }
+
+ s = pkt->stream_index;
+ s2 = tee->slaves[i].stream_map[s];
+ if (s2 < 0)
+ continue;
+
+ memset(&pkt2, 0, sizeof(AVPacket));
+ if ((ret = av_packet_ref(&pkt2, pkt)) < 0)
+ if (!ret_all) {
+ ret_all = ret;
+ continue;
+ }
+ bsfs = tee->slaves[i].bsfs[s2];
+ pkt2.stream_index = s2;
+
+ ret = av_bsf_send_packet(bsfs, &pkt2);
+ if (ret < 0) {
+ av_log(avf, AV_LOG_ERROR, "Error while sending packet to bitstream filter: %s\n",
+ av_err2str(ret));
+ ret = tee_process_slave_failure(avf, i, ret);
+ if (!ret_all && ret < 0)
+ ret_all = ret;
+ }
+
+ while(1) {
+ ret = av_bsf_receive_packet(bsfs, &pkt2);
+ if (ret == AVERROR(EAGAIN)) {
+ ret = 0;
+ break;
+ } else if (ret < 0) {
+ break;
+ }
+
+ av_packet_rescale_ts(&pkt2, bsfs->time_base_out,
+ avf2->streams[s2]->time_base);
+ ret = av_interleaved_write_frame(avf2, &pkt2);
+ if (ret < 0)
+ break;
+ };
+
+ if (ret < 0) {
+ ret = tee_process_slave_failure(avf, i, ret);
+ if (!ret_all && ret < 0)
+ 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 | AVFMT_ALLOW_FLUSH,
+};
diff --git a/libavformat/tee_common.c b/libavformat/tee_common.c
new file mode 100644
index 0000000..a960887
--- /dev/null
+++ b/libavformat/tee_common.c
@@ -0,0 +1,68 @@
+/*
+ * Tee common code
+ * 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 "tee_common.h"
+
+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 */
+
+int ff_tee_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;
+}
diff --git a/libavformat/tee_common.h b/libavformat/tee_common.h
new file mode 100644
index 0000000..dcd7f63
--- /dev/null
+++ b/libavformat/tee_common.h
@@ -0,0 +1,30 @@
+/*
+ * Tee common code
+ * Copyright (c) 2016 Michael Niedermayer <michael@niedermayer.cc>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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_TEE_COMMON_H
+#define AVFORMAT_TEE_COMMON_H
+
+#include "libavutil/dict.h"
+
+int ff_tee_parse_slave_options(void *log, char *slave,
+ AVDictionary **options, char **filename);
+
+#endif
diff --git a/libavformat/teeproto.c b/libavformat/teeproto.c
new file mode 100644
index 0000000..e532bc7
--- /dev/null
+++ b/libavformat/teeproto.c
@@ -0,0 +1,147 @@
+/*
+ * Tee output protocol
+ * Copyright (c) 2016 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/avstring.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "avio_internal.h"
+#include "tee_common.h"
+
+typedef struct ChildContext {
+ URLContext *url_context;
+} ChildContext;
+
+typedef struct TeeContext {
+ const AVClass *class;
+ int child_count;
+ ChildContext *child;
+} TeeContext;
+
+static const AVOption tee_options[] = {
+ { NULL }
+};
+
+static const AVClass tee_class = {
+ .class_name = "tee",
+ .item_name = av_default_item_name,
+ .option = tee_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+static const char *const child_delim = "|";
+
+static int tee_write(URLContext *h, const unsigned char *buf, int size)
+{
+ TeeContext *c = h->priv_data;
+ int i;
+ int main_ret = size;
+
+ for (i=0; i<c->child_count; i++) {
+ int ret = ffurl_write(c->child[i].url_context, buf, size);
+ if (ret < 0)
+ main_ret = ret;
+ }
+ return main_ret;
+}
+
+static int tee_close(URLContext *h)
+{
+ TeeContext *c = h->priv_data;
+ int i;
+ int main_ret = 0;
+
+ for (i=0; i<c->child_count; i++) {
+ int ret = ffurl_closep(&c->child[i].url_context);
+ if (ret < 0)
+ main_ret = ret;
+ }
+
+ av_freep(&c->child);
+ c->child_count = 0;
+ return main_ret;
+}
+
+static int tee_open(URLContext *h, const char *filename, int flags)
+{
+ TeeContext *c = h->priv_data;
+ int ret, i;
+
+ av_strstart(filename, "tee:", &filename);
+
+ if (flags & AVIO_FLAG_READ)
+ return AVERROR(ENOSYS);
+
+ while (*filename) {
+ char *child_string = av_get_token(&filename, child_delim);
+ char *child_name = NULL;
+ void *tmp;
+ AVDictionary *options = NULL;
+ if (!child_string) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ tmp = av_realloc_array(c->child, c->child_count + 1, sizeof(*c->child));
+ if (!tmp) {
+ ret = AVERROR(ENOMEM);
+ goto loop_fail;
+ }
+ c->child = tmp;
+ memset(&c->child[c->child_count], 0, sizeof(c->child[c->child_count]));
+
+ ret = ff_tee_parse_slave_options(h, child_string, &options, &child_name);
+ if (ret < 0)
+ goto loop_fail;
+
+ ret = ffurl_open_whitelist(&c->child[c->child_count].url_context, child_name, flags,
+ &h->interrupt_callback, &options,
+ h->protocol_whitelist, h->protocol_blacklist,
+ h);
+loop_fail:
+ av_freep(&child_string);
+ av_dict_free(&options);
+ if (ret < 0)
+ goto fail;
+ c->child_count++;
+
+ if (strspn(filename, child_delim))
+ filename++;
+ }
+
+ h->is_streamed = 0;
+ for (i=0; i<c->child_count; i++) {
+ h->is_streamed |= c->child[i].url_context->is_streamed;
+ }
+
+ return 0;
+fail:
+ tee_close(h);
+ return ret;
+}
+const URLProtocol ff_tee_protocol = {
+ .name = "tee",
+ .url_open = tee_open,
+ .url_write = tee_write,
+ .url_close = tee_close,
+ .priv_data_size = sizeof(TeeContext),
+ .priv_data_class = &tee_class,
+ .default_whitelist = "crypto,file,http,https,httpproxy,rtmp,tcp,tls"
+};
diff --git a/libavformat/tests/.gitignore b/libavformat/tests/.gitignore
index cfd51f2..7ceb7a3 100644
--- a/libavformat/tests/.gitignore
+++ b/libavformat/tests/.gitignore
@@ -1,5 +1,7 @@
+/fifo_muxer
/movenc
/noproxy
+/rtmpdh
/seek
/srtp
/url
diff --git a/libavformat/tests/fifo_muxer.c b/libavformat/tests/fifo_muxer.c
new file mode 100644
index 0000000..e20bd6e
--- /dev/null
+++ b/libavformat/tests/fifo_muxer.c
@@ -0,0 +1,447 @@
+/*
+ * FIFO pseudo-muxer
+ * Copyright (c) 2016 Jan Sebechlebsky
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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 <stdlib.h>
+#include "libavutil/opt.h"
+#include "libavutil/time.h"
+#include "libavutil/avassert.h"
+#include "libavformat/avformat.h"
+#include "libavformat/url.h"
+#include "libavformat/network.h"
+
+#define MAX_TST_PACKETS 128
+#define SLEEPTIME_50_MS 50000
+#define SLEEPTIME_10_MS 10000
+
+/* Implementation of mock muxer to simulate real muxer failures */
+
+/* This is structure of data sent in packets to
+ * failing muxer */
+typedef struct FailingMuxerPacketData {
+ int ret; /* return value of write_packet call*/
+ int recover_after; /* set ret to zero after this number of recovery attempts */
+ unsigned sleep_time; /* sleep for this long in write_packet to simulate long I/O operation */
+} FailingMuxerPacketData;
+
+
+typedef struct FailingMuxerContext {
+ AVClass *class;
+ int write_header_ret;
+ int write_trailer_ret;
+ /* If non-zero, summary of processed packets will be printed in deinit */
+ int print_deinit_summary;
+
+ int flush_count;
+ int pts_written[MAX_TST_PACKETS];
+ int pts_written_nr;
+} FailingMuxerContext;
+
+static int failing_write_header(AVFormatContext *avf)
+{
+ FailingMuxerContext *ctx = avf->priv_data;
+ return ctx->write_header_ret;
+}
+
+static int failing_write_packet(AVFormatContext *avf, AVPacket *pkt)
+{
+ FailingMuxerContext *ctx = avf->priv_data;
+ int ret = 0;
+ if (!pkt) {
+ ctx->flush_count++;
+ } else {
+ FailingMuxerPacketData *data = (FailingMuxerPacketData*) pkt->data;
+
+ if (!data->recover_after) {
+ data->ret = 0;
+ } else {
+ data->recover_after--;
+ }
+
+ ret = data->ret;
+
+ if (data->sleep_time) {
+ int64_t slept = 0;
+ while (slept < data->sleep_time) {
+ if (ff_check_interrupt(&avf->interrupt_callback))
+ return AVERROR_EXIT;
+ av_usleep(SLEEPTIME_10_MS);
+ slept += SLEEPTIME_10_MS;
+ }
+ }
+
+ if (!ret) {
+ ctx->pts_written[ctx->pts_written_nr++] = pkt->pts;
+ av_packet_unref(pkt);
+ }
+ }
+ return ret;
+}
+
+static int failing_write_trailer(AVFormatContext *avf)
+{
+ FailingMuxerContext *ctx = avf->priv_data;
+ return ctx->write_trailer_ret;
+}
+
+static void failing_deinit(AVFormatContext *avf)
+{
+ int i;
+ FailingMuxerContext *ctx = avf->priv_data;
+
+ if (!ctx->print_deinit_summary)
+ return;
+
+ printf("flush count: %d\n", ctx->flush_count);
+ printf("pts seen nr: %d\n", ctx->pts_written_nr);
+ printf("pts seen: ");
+ for (i = 0; i < ctx->pts_written_nr; ++i ) {
+ printf(i ? ",%d" : "%d", ctx->pts_written[i]);
+ }
+ printf("\n");
+}
+#define OFFSET(x) offsetof(FailingMuxerContext, x)
+static const AVOption options[] = {
+ {"write_header_ret", "write_header() return value", OFFSET(write_header_ret),
+ AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
+ {"write_trailer_ret", "write_trailer() return value", OFFSET(write_trailer_ret),
+ AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
+ {"print_deinit_summary", "print summary when deinitializing muxer", OFFSET(print_deinit_summary),
+ AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
+ {NULL}
+ };
+
+static const AVClass failing_muxer_class = {
+ .class_name = "Failing test muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVOutputFormat tst_failing_muxer = {
+ .name = "fail",
+ .long_name = NULL_IF_CONFIG_SMALL("Failing test muxer"),
+ .priv_data_size = sizeof(FailingMuxerContext),
+ .write_header = failing_write_header,
+ .write_packet = failing_write_packet,
+ .write_trailer = failing_write_trailer,
+ .deinit = failing_deinit,
+ .priv_class = &failing_muxer_class,
+ .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH,
+};
+
+static int prepare_packet(AVPacket *pkt,const FailingMuxerPacketData *pkt_data, int64_t pts)
+{
+ int ret;
+ FailingMuxerPacketData *data = av_malloc(sizeof(*data));
+ if (!data) {
+ return AVERROR(ENOMEM);
+ }
+ memcpy(data, pkt_data, sizeof(FailingMuxerPacketData));
+ ret = av_packet_from_data(pkt, (uint8_t*) data, sizeof(*data));
+
+ pkt->pts = pkt->dts = pts;
+ pkt->duration = 1;
+
+ return ret;
+}
+
+static int initialize_fifo_tst_muxer_chain(AVFormatContext **oc)
+{
+ int ret = 0;
+ AVStream *s;
+
+ ret = avformat_alloc_output_context2(oc, NULL, "fifo", "-");
+ if (ret) {
+ fprintf(stderr, "Failed to create format context: %s\n",
+ av_err2str(ret));
+ return EXIT_FAILURE;
+ }
+
+ s = avformat_new_stream(*oc, NULL);
+ if (!s) {
+ fprintf(stderr, "Failed to create stream: %s\n",
+ av_err2str(ret));
+ ret = AVERROR(ENOMEM);
+ }
+
+ return ret;
+}
+
+static int fifo_basic_test(AVFormatContext *oc, AVDictionary **opts,
+ const FailingMuxerPacketData *pkt_data)
+{
+ int ret = 0, i;
+ AVPacket pkt;
+
+ av_init_packet(&pkt);
+
+
+ ret = avformat_write_header(oc, opts);
+ if (ret) {
+ fprintf(stderr, "Unexpected write_header failure: %s\n",
+ av_err2str(ret));
+ goto fail;
+ }
+
+ for (i = 0; i < 15; i++ ) {
+ ret = prepare_packet(&pkt, pkt_data, i);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to prepare test packet: %s\n",
+ av_err2str(ret));
+ goto write_trailer_and_fail;
+ }
+ ret = av_write_frame(oc, &pkt);
+ av_packet_unref(&pkt);
+ if (ret < 0) {
+ fprintf(stderr, "Unexpected write_frame error: %s\n",
+ av_err2str(ret));
+ goto write_trailer_and_fail;
+ }
+ }
+
+ ret = av_write_frame(oc, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "Unexpected write_frame error during flushing: %s\n",
+ av_err2str(ret));
+ goto write_trailer_and_fail;
+ }
+
+ ret = av_write_trailer(oc);
+ if (ret < 0) {
+ fprintf(stderr, "Unexpected write_trailer error during flushing: %s\n",
+ av_err2str(ret));
+ goto fail;
+ }
+
+ return ret;
+write_trailer_and_fail:
+ av_write_trailer(oc);
+fail:
+ return ret;
+}
+
+static int fifo_write_header_err_tst(AVFormatContext *oc, AVDictionary **opts,
+ const FailingMuxerPacketData *pkt_data)
+{
+ int ret = 0, i;
+ AVPacket pkt;
+
+ av_init_packet(&pkt);
+
+ ret = avformat_write_header(oc, opts);
+ if (ret) {
+ fprintf(stderr, "Unexpected write_header failure: %s\n",
+ av_err2str(ret));
+ goto fail;
+ }
+
+ for (i = 0; i < MAX_TST_PACKETS; i++ ) {
+ ret = prepare_packet(&pkt, pkt_data, i);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to prepare test packet: %s\n",
+ av_err2str(ret));
+ goto write_trailer_and_fail;
+ }
+ ret = av_write_frame(oc, &pkt);
+ av_packet_unref(&pkt);
+ if (ret < 0) {
+ break;
+ }
+ }
+
+ if (!ret) {
+ fprintf(stderr, "write_packet not failed when supposed to.\n");
+ goto fail;
+ } else if (ret != -1) {
+ fprintf(stderr, "Unexpected write_packet error: %s\n", av_err2str(ret));
+ goto fail;
+ }
+
+ ret = av_write_trailer(oc);
+ if (ret < 0)
+ fprintf(stderr, "Unexpected write_trailer error: %s\n", av_err2str(ret));
+
+ return ret;
+write_trailer_and_fail:
+ av_write_trailer(oc);
+fail:
+ return ret;
+}
+
+static int fifo_overflow_drop_test(AVFormatContext *oc, AVDictionary **opts,
+ const FailingMuxerPacketData *data)
+{
+ int ret = 0, i;
+ int64_t write_pkt_start, write_pkt_end, duration;
+ AVPacket pkt;
+
+ av_init_packet(&pkt);
+
+ ret = avformat_write_header(oc, opts);
+ if (ret) {
+ fprintf(stderr, "Unexpected write_header failure: %s\n",
+ av_err2str(ret));
+ return ret;
+ }
+
+ write_pkt_start = av_gettime_relative();
+ for (i = 0; i < 6; i++ ) {
+ ret = prepare_packet(&pkt, data, i);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to prepare test packet: %s\n",
+ av_err2str(ret));
+ goto fail;
+ }
+ ret = av_write_frame(oc, &pkt);
+ av_packet_unref(&pkt);
+ if (ret < 0) {
+ break;
+ }
+ }
+ write_pkt_end = av_gettime_relative();
+ duration = write_pkt_end - write_pkt_start;
+ if (duration > (SLEEPTIME_50_MS*6)/2) {
+ fprintf(stderr, "Writing packets to fifo muxer took too much time while testing"
+ "buffer overflow with drop_pkts_on_overflow was on.\n");
+ ret = AVERROR_BUG;
+ goto fail;
+ }
+
+ if (ret) {
+ fprintf(stderr, "Unexpected write_packet error: %s\n", av_err2str(ret));
+ goto fail;
+ }
+
+ ret = av_write_trailer(oc);
+ if (ret < 0)
+ fprintf(stderr, "Unexpected write_trailer error: %s\n", av_err2str(ret));
+
+ return ret;
+fail:
+ av_write_trailer(oc);
+ return ret;
+}
+
+typedef struct TestCase {
+ int (*test_func)(AVFormatContext *, AVDictionary **,const FailingMuxerPacketData *pkt_data);
+ const char *test_name;
+ const char *options;
+
+ uint8_t print_summary_on_deinit;
+ int write_header_ret;
+ int write_trailer_ret;
+
+ FailingMuxerPacketData pkt_data;
+} TestCase;
+
+
+#define BUFFER_SIZE 64
+
+static int run_test(const TestCase *test)
+{
+ AVDictionary *opts = NULL;
+ AVFormatContext *oc = NULL;
+ char buffer[BUFFER_SIZE];
+ int ret, ret1;
+
+ ret = initialize_fifo_tst_muxer_chain(&oc);
+ if (ret < 0) {
+ fprintf(stderr, "Muxer initialization failed: %s\n", av_err2str(ret));
+ goto end;
+ }
+
+ if (test->options) {
+ ret = av_dict_parse_string(&opts, test->options, "=", ":", 0);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to parse options: %s\n", av_err2str(ret));
+ goto end;
+ }
+ }
+
+ snprintf(buffer, BUFFER_SIZE,
+ "print_deinit_summary=%d:write_header_ret=%d:write_trailer_ret=%d",
+ (int)test->print_summary_on_deinit, test->write_header_ret,
+ test->write_trailer_ret);
+ ret = av_dict_set(&opts, "format_opts", buffer, 0);
+ ret1 = av_dict_set(&opts, "fifo_format", "fail", 0);
+ if (ret < 0 || ret1 < 0) {
+ fprintf(stderr, "Failed to set options for test muxer: %s\n",
+ av_err2str(ret));
+ goto end;
+ }
+
+ ret = test->test_func(oc, &opts, &test->pkt_data);
+
+end:
+ printf("%s: %s\n", test->test_name, ret < 0 ? "fail" : "ok");
+ avformat_free_context(oc);
+ av_dict_free(&opts);
+ return ret;
+}
+
+
+const TestCase tests[] = {
+ /* Simple test in packet-non-dropping mode, we expect to get on the output
+ * exactly what was on input */
+ {fifo_basic_test, "nonfail test", NULL,1, 0, 0, {0, 0, 0}},
+
+ /* Test that we receive delayed write_header error from one of the write_packet
+ * calls. */
+ {fifo_write_header_err_tst, "write header error test", NULL, 0, -1, 0, {0, 0, 0}},
+
+ /* Each write_packet will fail 3 times before operation is successful. If recovery
+ * Since recovery is on, fifo muxer should not return any errors. */
+ {fifo_basic_test, "recovery test", "attempt_recovery=1:recovery_wait_time=0",
+ 0, 0, 0, {AVERROR(ETIMEDOUT), 3, 0}},
+
+ /* By setting low queue_size and sending packets with longer processing time,
+ * this test will cause queue to overflow, since drop_pkts_on_overflow is off
+ * by default, all packets should be processed and fifo should block on full
+ * queue. */
+ {fifo_basic_test, "overflow without packet dropping","queue_size=3",
+ 1, 0, 0, {0, 0, SLEEPTIME_10_MS}},
+
+ /* The test as the upper one, except that drop_on_overflow is turned on. In this case
+ * fifo should not block when the queue is full and slow down producer, so the test
+ * measures time producer spends on write_packet calls which should be significantly
+ * less than number_of_pkts * 50 MS.
+ */
+ {fifo_overflow_drop_test, "overflow with packet dropping", "queue_size=3:drop_pkts_on_overflow=1",
+ 0, 0, 0, {0, 0, SLEEPTIME_50_MS}},
+
+ {NULL}
+};
+
+int main(int argc, char *argv[])
+{
+ int i, ret, ret_all = 0;
+
+ av_register_all();
+ av_register_output_format(&tst_failing_muxer);
+
+ for (i = 0; tests[i].test_func; i++) {
+ ret = run_test(&tests[i]);
+ if (!ret_all && ret < 0)
+ ret_all = ret;
+ }
+
+ return ret;
+}
diff --git a/libavformat/tests/movenc.c b/libavformat/tests/movenc.c
index 583a8d9..8e59b74 100644
--- a/libavformat/tests/movenc.c
+++ b/libavformat/tests/movenc.c
@@ -1,20 +1,20 @@
/*
* Copyright (c) 2015 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
*/
@@ -44,7 +44,7 @@ static const uint8_t aac_extradata[] = {
};
-const char *format = "mp4";
+static const char *format = "mp4";
AVFormatContext *ctx;
uint8_t iobuf[32768];
AVDictionary *opts;
@@ -243,12 +243,12 @@ static void init(int bf, int audio_preroll)
init_fps(bf, audio_preroll, 30);
}
-static void mux_frames(int n)
+static void mux_frames(int n, int c)
{
int end_frames = frames + n;
while (1) {
AVPacket pkt;
- uint8_t pktdata[4];
+ uint8_t pktdata[8] = { 0 };
av_init_packet(&pkt);
if (av_compare_ts(audio_dts, audio_st->time_base, video_dts, video_st->time_base) < 0) {
@@ -292,13 +292,19 @@ static void mux_frames(int n)
if (clear_duration)
pkt.duration = 0;
- AV_WB32(pktdata, pkt.pts);
+ AV_WB32(pktdata + 4, pkt.pts);
pkt.data = pktdata;
- pkt.size = 4;
+ pkt.size = 8;
if (skip_write)
continue;
if (skip_write_audio && pkt.stream_index == 1)
continue;
+
+ if (c) {
+ pkt.pts += (1LL<<32);
+ pkt.dts += (1LL<<32);
+ }
+
if (do_interleave)
av_interleaved_write_frame(ctx, &pkt);
else
@@ -308,7 +314,7 @@ static void mux_frames(int n)
static void mux_gops(int n)
{
- mux_frames(gop_size * n);
+ mux_frames(gop_size * n, 0);
}
static void skip_gops(int n)
@@ -424,6 +430,7 @@ int main(int argc, char **argv)
// moof+mdat pairs.
init_out("empty-moov");
av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0);
+ av_dict_set(&opts, "use_editlist", "0", 0);
init(0, 0);
mux_gops(2);
finish();
@@ -460,6 +467,7 @@ int main(int argc, char **argv)
// simple input
init_out("delay-moov");
av_dict_set(&opts, "movflags", "frag_keyframe+delay_moov", 0);
+ av_dict_set(&opts, "use_editlist", "0", 0);
init(0, 0);
mux_gops(2);
finish();
@@ -511,6 +519,7 @@ int main(int argc, char **argv)
// is identical to the one by empty_moov.
init_out("empty-moov-header");
av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0);
+ av_dict_set(&opts, "use_editlist", "0", 0);
init(0, 0);
close_out();
memcpy(header, hash, HASH_SIZE);
@@ -533,6 +542,7 @@ int main(int argc, char **argv)
init_out("delay-moov-header");
av_dict_set(&opts, "movflags", "frag_custom+delay_moov", 0);
+ av_dict_set(&opts, "use_editlist", "0", 0);
init(0, 0);
check(out_size == 0, "Output written during init with delay_moov");
mux_gops(1); // Write 1 second of content
@@ -661,6 +671,21 @@ int main(int argc, char **argv)
finish();
+ // Test muxing discontinuous fragments with very large (> (1<<31)) timestamps.
+ av_dict_set(&opts, "movflags", "frag_custom+delay_moov+dash+frag_discont", 0);
+ av_dict_set(&opts, "fragment_index", "2", 0);
+ init(1, 1);
+ signal_init_ts();
+ skip_gops(1);
+ mux_frames(gop_size, 1); // Write the second fragment
+ init_out("delay-moov-elst-signal-init-discont-largets");
+ av_write_frame(ctx, NULL); // Output the moov
+ close_out();
+ init_out("delay-moov-elst-signal-second-frag-discont-largets");
+ av_write_frame(ctx, NULL); // Output the second fragment
+ close_out();
+ finish();
+
// Test VFR content, with sidx atoms (which declare the pts duration
// of a fragment, forcing overriding the start pts of the next one).
// Here, the fragment duration in pts is significantly different from
@@ -676,9 +701,9 @@ int main(int argc, char **argv)
init_out("vfr");
av_dict_set(&opts, "movflags", "frag_keyframe+delay_moov+dash", 0);
init_fps(1, 1, 3);
- mux_frames(gop_size/2);
+ mux_frames(gop_size/2, 0);
duration /= 10;
- mux_frames(gop_size/2);
+ mux_frames(gop_size/2, 0);
mux_gops(1);
finish();
close_out();
@@ -695,9 +720,9 @@ int main(int argc, char **argv)
init_out("vfr-noduration");
av_dict_set(&opts, "movflags", "frag_keyframe+delay_moov+dash", 0);
init_fps(1, 1, 3);
- mux_frames(gop_size/2);
+ mux_frames(gop_size/2, 0);
duration /= 10;
- mux_frames(gop_size/2);
+ mux_frames(gop_size/2, 0);
mux_gops(1);
finish();
close_out();
@@ -725,22 +750,41 @@ int main(int argc, char **argv)
av_dict_set(&opts, "movflags", "frag_keyframe+delay_moov", 0);
av_dict_set(&opts, "frag_duration", "650000", 0);
init_fps(1, 1, 30);
- mux_frames(gop_size/2);
+ mux_frames(gop_size/2, 0);
// Pretend that the packet duration is the normal, even if
// we actually skip a bunch of frames. (I.e., simulate that
// we don't know of the framedrop in advance.)
fake_pkt_duration = duration;
duration *= 10;
- mux_frames(1);
+ mux_frames(1, 0);
fake_pkt_duration = 0;
duration /= 10;
- mux_frames(gop_size/2 - 1);
+ mux_frames(gop_size/2 - 1, 0);
mux_gops(1);
finish();
close_out();
clear_duration = 0;
do_interleave = 0;
+ // Write a fragmented file with b-frames and audio preroll,
+ // with negative cts values, removing the edit list for the
+ // video track.
+ init_out("delay-moov-elst-neg-cts");
+ av_dict_set(&opts, "movflags", "frag_keyframe+delay_moov+negative_cts_offsets", 0);
+ init(1, 1);
+ mux_gops(2);
+ finish();
+ close_out();
+
+ // Write a fragmented file with b-frames without audio preroll,
+ // with negative cts values, avoiding any edit lists, allowing
+ // to use empty_moov instead of delay_moov.
+ init_out("empty-moov-neg-cts");
+ av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov+negative_cts_offsets", 0);
+ init(1, 0);
+ mux_gops(2);
+ finish();
+ close_out();
av_free(md5);
diff --git a/libavformat/tests/noproxy.c b/libavformat/tests/noproxy.c
index 9daee05..782356c 100644
--- a/libavformat/tests/noproxy.c
+++ b/libavformat/tests/noproxy.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/tests/rtmpdh.c b/libavformat/tests/rtmpdh.c
index cf567fb..77139a5 100644
--- a/libavformat/tests/rtmpdh.c
+++ b/libavformat/tests/rtmpdh.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/tests/seek.c b/libavformat/tests/seek.c
index 82da374..5cf3a12 100644
--- a/libavformat/tests/seek.c
+++ b/libavformat/tests/seek.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
*/
@@ -46,22 +46,49 @@ 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)
{
const char *filename;
- AVFormatContext *ic = NULL;
+ AVFormatContext *ic = avformat_alloc_context();
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;
+
+ ic->flags |= AVFMT_FLAG_KEEP_SIDE_DATA;
+
+ 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 if(!strcmp(argv[i], "-fastseek")) {
+ if (atoi(argv[i+1])) {
+ ic->flags |= AVFMT_FLAG_FAST_SEEK;
+ }
+ } else if(argv[i][0] == '-' && argv[i+1]) {
+ av_dict_set(&format_opts, argv[i] + 1, argv[i+1], 0);
+ } else {
+ argc = 1;
+ }
+ }
av_dict_set(&format_opts, "channels", "1", 0);
av_dict_set(&format_opts, "sample_rate", "22050", 0);
@@ -69,7 +96,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;
@@ -90,12 +117,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];
@@ -107,12 +139,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/tests/srtp.c b/libavformat/tests/srtp.c
index 1703193..89450ce 100644
--- a/libavformat/tests/srtp.c
+++ b/libavformat/tests/srtp.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/tests/url.c b/libavformat/tests/url.c
index ec33173..1623179 100644
--- a/libavformat/tests/url.c
+++ b/libavformat/tests/url.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/thp.c b/libavformat/thp.c
index c9ec5c1..76b9b38 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. */
@@ -107,6 +115,8 @@ static int thp_read_header(AVFormatContext *s)
st->codecpar->width = avio_rb32(pb);
st->codecpar->height = avio_rb32(pb);
st->codecpar->sample_rate = av_q2d(thp->fps);
+ st->nb_frames =
+ st->duration = thp->framecnt;
thp->vst = st;
thp->video_stream_index = st->index;
@@ -143,18 +153,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. */
@@ -168,6 +178,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_packet_unref(pkt);
return AVERROR(EIO);
@@ -176,6 +188,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_packet_unref(pkt);
return AVERROR(EIO);
diff --git a/libavformat/tiertexseq.c b/libavformat/tiertexseq.c
index 8427f92..6e00692 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 fab243e..10e0792 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
*/
@@ -29,6 +29,30 @@
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
+static void set_options(TLSShared *c, const char *uri)
+{
+ 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);
+}
+
int ff_tls_open_underlying(TLSShared *c, URLContext *parent, const char *uri, AVDictionary **options)
{
int port;
@@ -38,10 +62,12 @@ int ff_tls_open_underlying(TLSShared *c, URLContext *parent, const char *uri, AV
const char *proxy_path;
int use_proxy;
+ set_options(c, uri);
+
if (c->listen)
snprintf(opts, sizeof(opts), "?listen=1");
- av_url_split(NULL, 0, NULL, 0, c->host, sizeof(c->host), &port, NULL, 0, uri);
+ av_url_split(NULL, 0, NULL, 0, c->underlying_host, sizeof(c->underlying_host), &port, NULL, 0, uri);
p = strchr(uri, '?');
@@ -52,16 +78,19 @@ int ff_tls_open_underlying(TLSShared *c, URLContext *parent, const char *uri, AV
c->listen = 1;
}
- ff_url_join(buf, sizeof(buf), "tcp", NULL, c->host, port, "%s", p);
+ ff_url_join(buf, sizeof(buf), "tcp", NULL, c->underlying_host, port, "%s", p);
hints.ai_flags = AI_NUMERICHOST;
- if (!getaddrinfo(c->host, NULL, &hints, &ai)) {
+ if (!getaddrinfo(c->underlying_host, NULL, &hints, &ai)) {
c->numerichost = 1;
freeaddrinfo(ai);
}
+ if (!c->host && !(c->host = av_strdup(c->underlying_host)))
+ return AVERROR(ENOMEM);
+
proxy_path = getenv("http_proxy");
- use_proxy = !ff_http_match_no_proxy(getenv("no_proxy"), c->host) &&
+ use_proxy = !ff_http_match_no_proxy(getenv("no_proxy"), c->underlying_host) &&
proxy_path && av_strstart(proxy_path, "http://", NULL);
if (use_proxy) {
@@ -70,11 +99,12 @@ int ff_tls_open_underlying(TLSShared *c, URLContext *parent, const char *uri, AV
av_url_split(NULL, 0, proxy_auth, sizeof(proxy_auth),
proxy_host, sizeof(proxy_host), &proxy_port, NULL, 0,
proxy_path);
- ff_url_join(dest, sizeof(dest), NULL, NULL, c->host, port, NULL);
+ ff_url_join(dest, sizeof(dest), NULL, NULL, c->underlying_host, port, NULL);
ff_url_join(buf, sizeof(buf), "httpproxy", proxy_auth, proxy_host,
proxy_port, "/%s", dest);
}
- return ffurl_open(&c->tcp, buf, AVIO_FLAG_READ_WRITE,
- &parent->interrupt_callback, options, parent->protocols, parent);
+ return ffurl_open_whitelist(&c->tcp, buf, AVIO_FLAG_READ_WRITE,
+ &parent->interrupt_callback, options,
+ parent->protocol_whitelist, parent->protocol_blacklist, parent);
}
diff --git a/libavformat/tls.h b/libavformat/tls.h
index 22cb625..0326ef7 100644
--- a/libavformat/tls.h
+++ b/libavformat/tls.h
@@ -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
*/
@@ -26,7 +26,7 @@
#include "url.h"
#include "libavutil/opt.h"
-#define CONFIG_TLS_PROTOCOL (CONFIG_TLS_GNUTLS_PROTOCOL | CONFIG_TLS_OPENSSL_PROTOCOL)
+#define CONFIG_TLS_PROTOCOL (CONFIG_TLS_GNUTLS_PROTOCOL | CONFIG_TLS_OPENSSL_PROTOCOL | CONFIG_TLS_SECURETRANSPORT_PROTOCOL | CONFIG_TLS_SCHANNEL_PROTOCOL)
typedef struct TLSShared {
char *ca_file;
@@ -35,7 +35,9 @@ typedef struct TLSShared {
char *key_file;
int listen;
- char host[200];
+ char *host;
+
+ char underlying_host[200];
int numerichost;
URLContext *tcp;
@@ -44,17 +46,19 @@ typedef struct TLSShared {
#define TLS_OPTFL (AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
#define TLS_COMMON_OPTIONS(pstruct, options_field) \
{"ca_file", "Certificate Authority database file", offsetof(pstruct, options_field . ca_file), AV_OPT_TYPE_STRING, .flags = TLS_OPTFL }, \
+ {"cafile", "Certificate Authority database file", offsetof(pstruct, options_field . ca_file), AV_OPT_TYPE_STRING, .flags = TLS_OPTFL }, \
{"tls_verify", "Verify the peer certificate", offsetof(pstruct, options_field . verify), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = TLS_OPTFL }, \
{"cert_file", "Certificate file", offsetof(pstruct, options_field . cert_file), AV_OPT_TYPE_STRING, .flags = TLS_OPTFL }, \
{"key_file", "Private key file", offsetof(pstruct, options_field . key_file), AV_OPT_TYPE_STRING, .flags = TLS_OPTFL }, \
- {"listen", "Listen for incoming connections", offsetof(pstruct, options_field . listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = TLS_OPTFL }
+ {"listen", "Listen for incoming connections", offsetof(pstruct, options_field . listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = TLS_OPTFL }, \
+ {"verifyhost", "Verify against a specific hostname", offsetof(pstruct, options_field . host), AV_OPT_TYPE_STRING, .flags = TLS_OPTFL }
int ff_tls_open_underlying(TLSShared *c, URLContext *parent, const char *uri, AVDictionary **options);
void ff_gnutls_init(void);
void ff_gnutls_deinit(void);
-void ff_openssl_init(void);
+int ff_openssl_init(void);
void ff_openssl_deinit(void);
#endif /* AVFORMAT_TLS_H */
diff --git a/libavformat/tls_gnutls.c b/libavformat/tls_gnutls.c
index f8a612a..5ce6c3d 100644
--- a/libavformat/tls_gnutls.c
+++ b/libavformat/tls_gnutls.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
*/
@@ -35,6 +35,16 @@
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
+#ifndef GNUTLS_VERSION_NUMBER
+#define GNUTLS_VERSION_NUMBER LIBGNUTLS_VERSION_NUMBER
+#endif
+
+#if HAVE_THREADS && GNUTLS_VERSION_NUMBER <= 0x020b00
+#include <gcrypt.h>
+#include "libavutil/thread.h"
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif
+
typedef struct TLSContext {
const AVClass *class;
TLSShared tls_shared;
@@ -46,6 +56,10 @@ typedef struct TLSContext {
void ff_gnutls_init(void)
{
avpriv_lock_avformat();
+#if HAVE_THREADS && GNUTLS_VERSION_NUMBER < 0x020b00
+ if (gcry_control(GCRYCTL_ANY_INITIALIZATION_P) == 0)
+ gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+#endif
gnutls_global_init();
avpriv_unlock_avformat();
}
@@ -62,6 +76,9 @@ static int print_tls_error(URLContext *h, int ret)
switch (ret) {
case GNUTLS_E_AGAIN:
case GNUTLS_E_INTERRUPTED:
+#ifdef GNUTLS_E_PREMATURE_TERMINATION
+ case GNUTLS_E_PREMATURE_TERMINATION:
+#endif
break;
case GNUTLS_E_WARNING_ALERT_RECEIVED:
av_log(h, AV_LOG_WARNING, "%s\n", gnutls_strerror(ret));
@@ -129,9 +146,12 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op
if (!c->listen && !c->numerichost)
gnutls_server_name_set(p->session, GNUTLS_NAME_DNS, c->host, strlen(c->host));
gnutls_certificate_allocate_credentials(&p->cred);
- if (c->ca_file)
- gnutls_certificate_set_x509_trust_file(p->cred, c->ca_file, GNUTLS_X509_FMT_PEM);
-#if GNUTLS_VERSION_MAJOR >= 3
+ if (c->ca_file) {
+ ret = gnutls_certificate_set_x509_trust_file(p->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_NUMBER >= 0x030020
else
gnutls_certificate_set_x509_system_trust(p->cred);
#endif
@@ -148,7 +168,8 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op
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(p->session, GNUTLS_CRD_CERTIFICATE, p->cred);
gnutls_transport_set_pull_function(p->session, gnutls_url_pull);
gnutls_transport_set_push_function(p->session, gnutls_url_push);
@@ -221,6 +242,12 @@ static int tls_write(URLContext *h, const uint8_t *buf, int size)
return print_tls_error(h, ret);
}
+static int tls_get_file_handle(URLContext *h)
+{
+ TLSContext *c = h->priv_data;
+ return ffurl_get_file_handle(c->tls_shared.tcp);
+}
+
static const AVOption options[] = {
TLS_COMMON_OPTIONS(TLSContext, tls_shared),
{ NULL }
@@ -239,6 +266,7 @@ const URLProtocol ff_tls_gnutls_protocol = {
.url_read = tls_read,
.url_write = tls_write,
.url_close = tls_close,
+ .url_get_file_handle = tls_get_file_handle,
.priv_data_size = sizeof(TLSContext),
.flags = URL_PROTOCOL_FLAG_NETWORK,
.priv_data_class = &tls_class,
diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c
index 0abccf0..38af8a2 100644
--- a/libavformat/tls_openssl.c
+++ b/libavformat/tls_openssl.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
*/
@@ -66,72 +66,6 @@ static unsigned long openssl_thread_id(void)
#endif
#endif
-void ff_openssl_init(void)
-{
- avpriv_lock_avformat();
- if (!openssl_init) {
- SSL_library_init();
- SSL_load_error_strings();
-#if HAVE_THREADS
- if (!CRYPTO_get_locking_callback()) {
- int i;
- openssl_mutexes = av_malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
- for (i = 0; i < CRYPTO_num_locks(); i++)
- pthread_mutex_init(&openssl_mutexes[i], NULL);
- CRYPTO_set_locking_callback(openssl_lock);
-#if !defined(WIN32) && OPENSSL_VERSION_NUMBER < 0x10000000
- CRYPTO_set_id_callback(openssl_thread_id);
-#endif
- }
-#endif
- }
- openssl_init++;
- avpriv_unlock_avformat();
-}
-
-void ff_openssl_deinit(void)
-{
- avpriv_lock_avformat();
- openssl_init--;
- if (!openssl_init) {
-#if HAVE_THREADS
- if (CRYPTO_get_locking_callback() == openssl_lock) {
- int i;
- CRYPTO_set_locking_callback(NULL);
- for (i = 0; i < CRYPTO_num_locks(); i++)
- pthread_mutex_destroy(&openssl_mutexes[i]);
- av_free(openssl_mutexes);
- }
-#endif
- }
- avpriv_unlock_avformat();
-}
-
-static int print_tls_error(URLContext *h, int ret)
-{
- av_log(h, AV_LOG_ERROR, "%s\n", ERR_error_string(ERR_get_error(), NULL));
- return AVERROR(EIO);
-}
-
-static int tls_close(URLContext *h)
-{
- TLSContext *c = h->priv_data;
- if (c->ssl) {
- SSL_shutdown(c->ssl);
- SSL_free(c->ssl);
- }
- if (c->ctx)
- SSL_CTX_free(c->ctx);
- if (c->tls_shared.tcp)
- ffurl_close(c->tls_shared.tcp);
-#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
- if (c->url_bio_method)
- BIO_meth_free(c->url_bio_method);
-#endif
- ff_openssl_deinit();
- return 0;
-}
-
static int url_bio_create(BIO *b)
{
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
@@ -209,6 +143,79 @@ static BIO_METHOD url_bio_method = {
};
#endif
+int ff_openssl_init(void)
+{
+ avpriv_lock_avformat();
+ if (!openssl_init) {
+ SSL_library_init();
+ SSL_load_error_strings();
+#if HAVE_THREADS
+ if (!CRYPTO_get_locking_callback()) {
+ int i;
+ openssl_mutexes = av_malloc_array(sizeof(pthread_mutex_t), CRYPTO_num_locks());
+ if (!openssl_mutexes) {
+ avpriv_unlock_avformat();
+ return AVERROR(ENOMEM);
+ }
+
+ for (i = 0; i < CRYPTO_num_locks(); i++)
+ pthread_mutex_init(&openssl_mutexes[i], NULL);
+ CRYPTO_set_locking_callback(openssl_lock);
+#if !defined(WIN32) && OPENSSL_VERSION_NUMBER < 0x10000000
+ CRYPTO_set_id_callback(openssl_thread_id);
+#endif
+ }
+#endif
+ }
+ openssl_init++;
+ avpriv_unlock_avformat();
+
+ return 0;
+}
+
+void ff_openssl_deinit(void)
+{
+ avpriv_lock_avformat();
+ openssl_init--;
+ if (!openssl_init) {
+#if HAVE_THREADS
+ if (CRYPTO_get_locking_callback() == openssl_lock) {
+ int i;
+ CRYPTO_set_locking_callback(NULL);
+ for (i = 0; i < CRYPTO_num_locks(); i++)
+ pthread_mutex_destroy(&openssl_mutexes[i]);
+ av_free(openssl_mutexes);
+ }
+#endif
+ }
+ avpriv_unlock_avformat();
+}
+
+static int print_tls_error(URLContext *h, int ret)
+{
+ av_log(h, AV_LOG_ERROR, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+ return AVERROR(EIO);
+}
+
+static int tls_close(URLContext *h)
+{
+ TLSContext *c = h->priv_data;
+ if (c->ssl) {
+ SSL_shutdown(c->ssl);
+ SSL_free(c->ssl);
+ }
+ if (c->ctx)
+ SSL_CTX_free(c->ctx);
+ if (c->tls_shared.tcp)
+ ffurl_close(c->tls_shared.tcp);
+#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
+ if (c->url_bio_method)
+ BIO_meth_free(c->url_bio_method);
+#endif
+ ff_openssl_deinit();
+ return 0;
+}
+
static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
{
TLSContext *p = h->priv_data;
@@ -216,7 +223,8 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op
BIO *bio;
int ret;
- ff_openssl_init();
+ if ((ret = ff_openssl_init()) < 0)
+ return ret;
if ((ret = ff_tls_open_underlying(c, h, uri, options)) < 0)
goto fail;
@@ -232,8 +240,10 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op
goto fail;
}
SSL_CTX_set_options(p->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
- if (c->ca_file)
- SSL_CTX_load_verify_locations(p->ctx, c->ca_file, NULL);
+ if (c->ca_file) {
+ if (!SSL_CTX_load_verify_locations(p->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(p->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));
@@ -249,7 +259,7 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op
// Note, this doesn't check that the peer certificate actually matches
// the requested hostname.
if (c->verify)
- SSL_CTX_set_verify(p->ctx, SSL_VERIFY_PEER, NULL);
+ SSL_CTX_set_verify(p->ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
p->ssl = SSL_new(p->ctx);
if (!p->ssl) {
av_log(h, AV_LOG_ERROR, "%s\n", ERR_error_string(ERR_get_error(), NULL));
@@ -311,6 +321,12 @@ static int tls_write(URLContext *h, const uint8_t *buf, int size)
return print_tls_error(h, ret);
}
+static int tls_get_file_handle(URLContext *h)
+{
+ TLSContext *c = h->priv_data;
+ return ffurl_get_file_handle(c->tls_shared.tcp);
+}
+
static const AVOption options[] = {
TLS_COMMON_OPTIONS(TLSContext, tls_shared),
{ NULL }
@@ -329,6 +345,7 @@ const URLProtocol ff_tls_openssl_protocol = {
.url_read = tls_read,
.url_write = tls_write,
.url_close = tls_close,
+ .url_get_file_handle = tls_get_file_handle,
.priv_data_size = sizeof(TLSContext),
.flags = URL_PROTOCOL_FLAG_NETWORK,
.priv_data_class = &tls_class,
diff --git a/libavformat/tls_schannel.c b/libavformat/tls_schannel.c
new file mode 100644
index 0000000..9f1c088
--- /dev/null
+++ b/libavformat/tls_schannel.c
@@ -0,0 +1,608 @@
+/*
+ * Copyright (c) 2015 Hendrik Leppkes
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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 the CURL SChannel module */
+
+#include "avformat.h"
+#include "internal.h"
+#include "network.h"
+#include "os_support.h"
+#include "url.h"
+#include "tls.h"
+
+#define SECURITY_WIN32
+#include <windows.h>
+#include <security.h>
+#include <schnlsp.h>
+
+#define SCHANNEL_INITIAL_BUFFER_SIZE 4096
+#define SCHANNEL_FREE_BUFFER_SIZE 1024
+
+/* mingw does not define this symbol */
+#ifndef SECBUFFER_ALERT
+#define SECBUFFER_ALERT 17
+#endif
+
+typedef struct TLSContext {
+ const AVClass *class;
+ TLSShared tls_shared;
+
+ CredHandle cred_handle;
+ TimeStamp cred_timestamp;
+
+ CtxtHandle ctxt_handle;
+ TimeStamp ctxt_timestamp;
+
+ ULONG request_flags;
+ ULONG context_flags;
+
+ uint8_t *enc_buf;
+ int enc_buf_size;
+ int enc_buf_offset;
+
+ uint8_t *dec_buf;
+ int dec_buf_size;
+ int dec_buf_offset;
+
+ SecPkgContext_StreamSizes sizes;
+
+ int connected;
+ int connection_closed;
+ int sspi_close_notify;
+} TLSContext;
+
+static void init_sec_buffer(SecBuffer *buffer, unsigned long type,
+ void *data, unsigned long size)
+{
+ buffer->cbBuffer = size;
+ buffer->BufferType = type;
+ buffer->pvBuffer = data;
+}
+
+static void init_sec_buffer_desc(SecBufferDesc *desc, SecBuffer *buffers,
+ unsigned long buffer_count)
+{
+ desc->ulVersion = SECBUFFER_VERSION;
+ desc->pBuffers = buffers;
+ desc->cBuffers = buffer_count;
+}
+
+static int tls_shutdown_client(URLContext *h)
+{
+ TLSContext *c = h->priv_data;
+ TLSShared *s = &c->tls_shared;
+ int ret;
+
+ if (c->connected) {
+ SecBufferDesc BuffDesc;
+ SecBuffer Buffer;
+ SECURITY_STATUS sspi_ret;
+ SecBuffer outbuf;
+ SecBufferDesc outbuf_desc;
+
+ DWORD dwshut = SCHANNEL_SHUTDOWN;
+ init_sec_buffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
+ init_sec_buffer_desc(&BuffDesc, &Buffer, 1);
+
+ sspi_ret = ApplyControlToken(&c->ctxt_handle, &BuffDesc);
+ if (sspi_ret != SEC_E_OK)
+ av_log(h, AV_LOG_ERROR, "ApplyControlToken failed\n");
+
+ init_sec_buffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
+ init_sec_buffer_desc(&outbuf_desc, &outbuf, 1);
+
+ sspi_ret = InitializeSecurityContext(&c->cred_handle, &c->ctxt_handle, s->host,
+ c->request_flags, 0, 0, NULL, 0, &c->ctxt_handle,
+ &outbuf_desc, &c->context_flags, &c->ctxt_timestamp);
+ if (sspi_ret == SEC_E_OK || sspi_ret == SEC_I_CONTEXT_EXPIRED) {
+ ret = ffurl_write(s->tcp, outbuf.pvBuffer, outbuf.cbBuffer);
+ FreeContextBuffer(outbuf.pvBuffer);
+ if (ret < 0 || ret != outbuf.cbBuffer)
+ av_log(h, AV_LOG_ERROR, "Failed to send close message\n");
+ }
+
+ c->connected = 0;
+ }
+ return 0;
+}
+
+static int tls_close(URLContext *h)
+{
+ TLSContext *c = h->priv_data;
+
+ tls_shutdown_client(h);
+
+ DeleteSecurityContext(&c->ctxt_handle);
+ FreeCredentialsHandle(&c->cred_handle);
+
+ av_freep(&c->enc_buf);
+ c->enc_buf_size = c->enc_buf_offset = 0;
+
+ av_freep(&c->dec_buf);
+ c->dec_buf_size = c->dec_buf_offset = 0;
+
+ if (c->tls_shared.tcp)
+ ffurl_close(c->tls_shared.tcp);
+ return 0;
+}
+
+static int tls_client_handshake_loop(URLContext *h, int initial)
+{
+ TLSContext *c = h->priv_data;
+ TLSShared *s = &c->tls_shared;
+ SECURITY_STATUS sspi_ret;
+ SecBuffer outbuf[3];
+ SecBufferDesc outbuf_desc;
+ SecBuffer inbuf[2];
+ SecBufferDesc inbuf_desc;
+ int i, ret = 0, read_data = initial;
+
+ if (c->enc_buf == NULL) {
+ c->enc_buf_offset = 0;
+ ret = av_reallocp(&c->enc_buf, SCHANNEL_INITIAL_BUFFER_SIZE);
+ if (ret < 0)
+ goto fail;
+ c->enc_buf_size = SCHANNEL_INITIAL_BUFFER_SIZE;
+ }
+
+ if (c->dec_buf == NULL) {
+ c->dec_buf_offset = 0;
+ ret = av_reallocp(&c->dec_buf, SCHANNEL_INITIAL_BUFFER_SIZE);
+ if (ret < 0)
+ goto fail;
+ c->dec_buf_size = SCHANNEL_INITIAL_BUFFER_SIZE;
+ }
+
+ while (1) {
+ if (c->enc_buf_size - c->enc_buf_offset < SCHANNEL_FREE_BUFFER_SIZE) {
+ c->enc_buf_size = c->enc_buf_offset + SCHANNEL_FREE_BUFFER_SIZE;
+ ret = av_reallocp(&c->enc_buf, c->enc_buf_size);
+ if (ret < 0) {
+ c->enc_buf_size = c->enc_buf_offset = 0;
+ goto fail;
+ }
+ }
+
+ if (read_data) {
+ ret = ffurl_read(c->tls_shared.tcp, c->enc_buf + c->enc_buf_offset,
+ c->enc_buf_size - c->enc_buf_offset);
+ if (ret < 0) {
+ av_log(h, AV_LOG_ERROR, "Failed to read handshake response\n");
+ goto fail;
+ }
+ c->enc_buf_offset += ret;
+ }
+
+ /* input buffers */
+ init_sec_buffer(&inbuf[0], SECBUFFER_TOKEN, av_malloc(c->enc_buf_offset), c->enc_buf_offset);
+ init_sec_buffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
+ init_sec_buffer_desc(&inbuf_desc, inbuf, 2);
+
+ if (inbuf[0].pvBuffer == NULL) {
+ av_log(h, AV_LOG_ERROR, "Failed to allocate input buffer\n");
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ memcpy(inbuf[0].pvBuffer, c->enc_buf, c->enc_buf_offset);
+
+ /* output buffers */
+ init_sec_buffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
+ init_sec_buffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
+ init_sec_buffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0);
+ init_sec_buffer_desc(&outbuf_desc, outbuf, 3);
+
+ sspi_ret = InitializeSecurityContext(&c->cred_handle, &c->ctxt_handle, s->host, c->request_flags,
+ 0, 0, &inbuf_desc, 0, NULL, &outbuf_desc, &c->context_flags,
+ &c->ctxt_timestamp);
+ av_freep(&inbuf[0].pvBuffer);
+
+ if (sspi_ret == SEC_E_INCOMPLETE_MESSAGE) {
+ av_log(h, AV_LOG_DEBUG, "Received incomplete handshake, need more data\n");
+ read_data = 1;
+ continue;
+ }
+
+ /* remote requests a client certificate - attempt to continue without one anyway */
+ if (sspi_ret == SEC_I_INCOMPLETE_CREDENTIALS &&
+ !(c->request_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
+ av_log(h, AV_LOG_VERBOSE, "Client certificate has been requested, ignoring\n");
+ c->request_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
+ read_data = 0;
+ continue;
+ }
+
+ /* continue handshake */
+ if (sspi_ret == SEC_I_CONTINUE_NEEDED || sspi_ret == SEC_E_OK) {
+ for (i = 0; i < 3; i++) {
+ if (outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
+ ret = ffurl_write(c->tls_shared.tcp, outbuf[i].pvBuffer, outbuf[i].cbBuffer);
+ if (ret < 0 || ret != outbuf[i].cbBuffer) {
+ av_log(h, AV_LOG_VERBOSE, "Failed to send handshake data\n");
+ ret = AVERROR(EIO);
+ goto fail;
+ }
+ }
+
+ if (outbuf[i].pvBuffer != NULL) {
+ FreeContextBuffer(outbuf[i].pvBuffer);
+ outbuf[i].pvBuffer = NULL;
+ }
+ }
+ } else {
+ if (sspi_ret == SEC_E_WRONG_PRINCIPAL)
+ av_log(h, AV_LOG_ERROR, "SNI or certificate check failed\n");
+ else
+ av_log(h, AV_LOG_ERROR, "Creating security context failed (0x%lx)\n", sspi_ret);
+ ret = AVERROR_UNKNOWN;
+ goto fail;
+ }
+
+ if (inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
+ if (c->enc_buf_offset > inbuf[1].cbBuffer) {
+ memmove(c->enc_buf, (c->enc_buf + c->enc_buf_offset) - inbuf[1].cbBuffer,
+ inbuf[1].cbBuffer);
+ c->enc_buf_offset = inbuf[1].cbBuffer;
+ if (sspi_ret == SEC_I_CONTINUE_NEEDED) {
+ read_data = 0;
+ continue;
+ }
+ }
+ } else {
+ c->enc_buf_offset = 0;
+ }
+
+ if (sspi_ret == SEC_I_CONTINUE_NEEDED) {
+ read_data = 1;
+ continue;
+ }
+
+ break;
+ }
+
+ return 0;
+
+fail:
+ /* free any remaining output data */
+ for (i = 0; i < 3; i++) {
+ if (outbuf[i].pvBuffer != NULL) {
+ FreeContextBuffer(outbuf[i].pvBuffer);
+ outbuf[i].pvBuffer = NULL;
+ }
+ }
+
+ return ret;
+}
+
+static int tls_client_handshake(URLContext *h)
+{
+ TLSContext *c = h->priv_data;
+ TLSShared *s = &c->tls_shared;
+ SecBuffer outbuf;
+ SecBufferDesc outbuf_desc;
+ SECURITY_STATUS sspi_ret;
+ int ret;
+
+ init_sec_buffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
+ init_sec_buffer_desc(&outbuf_desc, &outbuf, 1);
+
+ c->request_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ sspi_ret = InitializeSecurityContext(&c->cred_handle, NULL, s->host, c->request_flags, 0, 0,
+ NULL, 0, &c->ctxt_handle, &outbuf_desc, &c->context_flags,
+ &c->ctxt_timestamp);
+ if (sspi_ret != SEC_I_CONTINUE_NEEDED) {
+ av_log(h, AV_LOG_ERROR, "Unable to create initial security context (0x%lx)\n", sspi_ret);
+ ret = AVERROR_UNKNOWN;
+ goto fail;
+ }
+
+ ret = ffurl_write(s->tcp, outbuf.pvBuffer, outbuf.cbBuffer);
+ FreeContextBuffer(outbuf.pvBuffer);
+ if (ret < 0 || ret != outbuf.cbBuffer) {
+ av_log(h, AV_LOG_ERROR, "Failed to send initial handshake data\n");
+ ret = AVERROR(EIO);
+ goto fail;
+ }
+
+ return tls_client_handshake_loop(h, 1);
+
+fail:
+ DeleteSecurityContext(&c->ctxt_handle);
+ return ret;
+}
+
+static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
+{
+ TLSContext *c = h->priv_data;
+ TLSShared *s = &c->tls_shared;
+ SECURITY_STATUS sspi_ret;
+ SCHANNEL_CRED schannel_cred = { 0 };
+ int ret;
+
+ if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
+ goto fail;
+
+ if (s->listen) {
+ av_log(h, AV_LOG_ERROR, "TLS Listen Sockets with SChannel is not implemented.\n");
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+
+ /* SChannel Options */
+ schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
+
+ if (s->verify)
+ schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
+ SCH_CRED_REVOCATION_CHECK_CHAIN;
+ else
+ schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
+ SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+ SCH_CRED_IGNORE_REVOCATION_OFFLINE;
+
+ /* Get credential handle */
+ sspi_ret = AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME, SECPKG_CRED_OUTBOUND,
+ NULL, &schannel_cred, NULL, NULL, &c->cred_handle,
+ &c->cred_timestamp);
+ if (sspi_ret != SEC_E_OK) {
+ av_log(h, AV_LOG_ERROR, "Unable to acquire security credentials (0x%lx)\n", sspi_ret);
+ ret = AVERROR_UNKNOWN;
+ goto fail;
+ }
+
+ ret = tls_client_handshake(h);
+ if (ret < 0)
+ goto fail;
+
+ c->connected = 1;
+
+ return 0;
+
+fail:
+ tls_close(h);
+ return ret;
+}
+
+static int tls_read(URLContext *h, uint8_t *buf, int len)
+{
+ TLSContext *c = h->priv_data;
+ TLSShared *s = &c->tls_shared;
+ SECURITY_STATUS sspi_ret = SEC_E_OK;
+ SecBuffer inbuf[4];
+ SecBufferDesc inbuf_desc;
+ int size, ret;
+ int min_enc_buf_size = len + SCHANNEL_FREE_BUFFER_SIZE;
+
+ if (len <= c->dec_buf_offset)
+ goto cleanup;
+
+ if (c->sspi_close_notify)
+ goto cleanup;
+
+ if (!c->connection_closed) {
+ size = c->enc_buf_size - c->enc_buf_offset;
+ if (size < SCHANNEL_FREE_BUFFER_SIZE || c->enc_buf_size < min_enc_buf_size) {
+ c->enc_buf_size = c->enc_buf_offset + SCHANNEL_FREE_BUFFER_SIZE;
+ if (c->enc_buf_size < min_enc_buf_size)
+ c->enc_buf_size = min_enc_buf_size;
+ ret = av_reallocp(&c->enc_buf, c->enc_buf_size);
+ if (ret < 0) {
+ c->enc_buf_size = c->enc_buf_offset = 0;
+ return ret;
+ }
+ }
+
+ ret = ffurl_read(s->tcp, c->enc_buf + c->enc_buf_offset,
+ c->enc_buf_size - c->enc_buf_offset);
+ if (ret < 0) {
+ av_log(h, AV_LOG_ERROR, "Unable to read from socket\n");
+ return ret;
+ } else if (ret == 0)
+ c->connection_closed = 1;
+
+ c->enc_buf_offset += ret;
+ }
+
+ while (c->enc_buf_offset > 0 && sspi_ret == SEC_E_OK && c->dec_buf_offset < len) {
+ /* input buffer */
+ init_sec_buffer(&inbuf[0], SECBUFFER_DATA, c->enc_buf, c->enc_buf_offset);
+
+ /* additional buffers for possible output */
+ init_sec_buffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
+ init_sec_buffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0);
+ init_sec_buffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0);
+ init_sec_buffer_desc(&inbuf_desc, inbuf, 4);
+
+ sspi_ret = DecryptMessage(&c->ctxt_handle, &inbuf_desc, 0, NULL);
+ if (sspi_ret == SEC_E_OK || sspi_ret == SEC_I_RENEGOTIATE ||
+ sspi_ret == SEC_I_CONTEXT_EXPIRED) {
+ /* handle decrypted data */
+ if (inbuf[1].BufferType == SECBUFFER_DATA) {
+ /* grow buffer if needed */
+ size = inbuf[1].cbBuffer > SCHANNEL_FREE_BUFFER_SIZE ?
+ inbuf[1].cbBuffer : SCHANNEL_FREE_BUFFER_SIZE;
+ if (c->dec_buf_size - c->dec_buf_offset < size || c->dec_buf_size < len) {
+ c->dec_buf_size = c->dec_buf_offset + size;
+ if (c->dec_buf_size < len)
+ c->dec_buf_size = len;
+ ret = av_reallocp(&c->dec_buf, c->dec_buf_size);
+ if (ret < 0) {
+ c->dec_buf_size = c->dec_buf_offset = 0;
+ return ret;
+ }
+ }
+
+ /* copy decrypted data to buffer */
+ size = inbuf[1].cbBuffer;
+ if (size) {
+ memcpy(c->dec_buf + c->dec_buf_offset, inbuf[1].pvBuffer, size);
+ c->dec_buf_offset += size;
+ }
+ }
+ if (inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
+ if (c->enc_buf_offset > inbuf[3].cbBuffer) {
+ memmove(c->enc_buf, (c->enc_buf + c->enc_buf_offset) - inbuf[3].cbBuffer,
+ inbuf[3].cbBuffer);
+ c->enc_buf_offset = inbuf[3].cbBuffer;
+ }
+ } else
+ c->enc_buf_offset = 0;
+
+ if (sspi_ret == SEC_I_RENEGOTIATE) {
+ if (c->enc_buf_offset) {
+ av_log(h, AV_LOG_ERROR, "Cannot renegotiate, encrypted data buffer not empty\n");
+ ret = AVERROR_UNKNOWN;
+ goto cleanup;
+ }
+
+ av_log(h, AV_LOG_VERBOSE, "Re-negotiating security context\n");
+ ret = tls_client_handshake_loop(h, 0);
+ if (ret < 0) {
+ goto cleanup;
+ }
+ sspi_ret = SEC_E_OK;
+ continue;
+ } else if (sspi_ret == SEC_I_CONTEXT_EXPIRED) {
+ c->sspi_close_notify = 1;
+ if (!c->connection_closed) {
+ c->connection_closed = 1;
+ av_log(h, AV_LOG_VERBOSE, "Server closed the connection\n");
+ }
+ ret = 0;
+ goto cleanup;
+ }
+ } else if (sspi_ret == SEC_E_INCOMPLETE_MESSAGE) {
+ ret = AVERROR(EAGAIN);
+ goto cleanup;
+ } else {
+ av_log(h, AV_LOG_ERROR, "Unable to decrypt message (error 0x%x)\n", (unsigned)sspi_ret);
+ ret = AVERROR(EIO);
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+
+cleanup:
+ size = FFMIN(len, c->dec_buf_offset);
+ if (size) {
+ memcpy(buf, c->dec_buf, size);
+ memmove(c->dec_buf, c->dec_buf + size, c->dec_buf_offset - size);
+ c->dec_buf_offset -= size;
+
+ return size;
+ }
+
+ if (ret == 0 && !c->connection_closed)
+ ret = AVERROR(EAGAIN);
+
+ return ret < 0 ? ret : 0;
+}
+
+static int tls_write(URLContext *h, const uint8_t *buf, int len)
+{
+ TLSContext *c = h->priv_data;
+ TLSShared *s = &c->tls_shared;
+ SECURITY_STATUS sspi_ret;
+ int ret = 0, data_size;
+ uint8_t *data = NULL;
+ SecBuffer outbuf[4];
+ SecBufferDesc outbuf_desc;
+
+ if (c->sizes.cbMaximumMessage == 0) {
+ sspi_ret = QueryContextAttributes(&c->ctxt_handle, SECPKG_ATTR_STREAM_SIZES, &c->sizes);
+ if (sspi_ret != SEC_E_OK)
+ return AVERROR_UNKNOWN;
+ }
+
+ /* limit how much data we can consume */
+ len = FFMIN(len, c->sizes.cbMaximumMessage);
+
+ data_size = c->sizes.cbHeader + len + c->sizes.cbTrailer;
+ data = av_malloc(data_size);
+ if (data == NULL)
+ return AVERROR(ENOMEM);
+
+ init_sec_buffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
+ data, c->sizes.cbHeader);
+ init_sec_buffer(&outbuf[1], SECBUFFER_DATA,
+ data + c->sizes.cbHeader, len);
+ init_sec_buffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
+ data + c->sizes.cbHeader + len,
+ c->sizes.cbTrailer);
+ init_sec_buffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0);
+ init_sec_buffer_desc(&outbuf_desc, outbuf, 4);
+
+ memcpy(outbuf[1].pvBuffer, buf, len);
+
+ sspi_ret = EncryptMessage(&c->ctxt_handle, 0, &outbuf_desc, 0);
+ if (sspi_ret == SEC_E_OK) {
+ len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
+ ret = ffurl_write(s->tcp, data, len);
+ if (ret < 0 || ret != len) {
+ ret = AVERROR(EIO);
+ av_log(h, AV_LOG_ERROR, "Writing encrypted data to socket failed\n");
+ goto done;
+ }
+ } else {
+ av_log(h, AV_LOG_ERROR, "Encrypting data failed\n");
+ if (sspi_ret == SEC_E_INSUFFICIENT_MEMORY)
+ ret = AVERROR(ENOMEM);
+ else
+ ret = AVERROR(EIO);
+ goto done;
+ }
+
+done:
+ av_freep(&data);
+ return ret < 0 ? ret : outbuf[1].cbBuffer;
+}
+
+static int tls_get_file_handle(URLContext *h)
+{
+ TLSContext *c = h->priv_data;
+ return ffurl_get_file_handle(c->tls_shared.tcp);
+}
+
+static const AVOption options[] = {
+ TLS_COMMON_OPTIONS(TLSContext, tls_shared),
+ { NULL }
+};
+
+static const AVClass tls_class = {
+ .class_name = "tls",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const URLProtocol ff_tls_schannel_protocol = {
+ .name = "tls",
+ .url_open2 = tls_open,
+ .url_read = tls_read,
+ .url_write = tls_write,
+ .url_close = tls_close,
+ .url_get_file_handle = tls_get_file_handle,
+ .priv_data_size = sizeof(TLSContext),
+ .flags = URL_PROTOCOL_FLAG_NETWORK,
+ .priv_data_class = &tls_class,
+};
diff --git a/libavformat/tls_securetransport.c b/libavformat/tls_securetransport.c
new file mode 100644
index 0000000..bc8a320
--- /dev/null
+++ b/libavformat/tls_securetransport.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 2015 Rodger Combs
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General 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 <errno.h>
+
+
+#include "avformat.h"
+#include "avio_internal.h"
+#include "internal.h"
+#include "network.h"
+#include "os_support.h"
+#include "url.h"
+#include "tls.h"
+#include "libavcodec/internal.h"
+#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
+
+#include <Security/Security.h>
+#include <Security/SecureTransport.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+// We use a private API call here; it's good enough for WebKit.
+SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator, SecCertificateRef certificate, SecKeyRef privateKey);
+#define ioErr -36
+
+typedef struct TLSContext {
+ const AVClass *class;
+ TLSShared tls_shared;
+ SSLContextRef ssl_context;
+ CFArrayRef ca_array;
+ int lastErr;
+} TLSContext;
+
+static int print_tls_error(URLContext *h, int ret)
+{
+ TLSContext *c = h->priv_data;
+ switch (ret) {
+ case errSSLWouldBlock:
+ break;
+ case errSSLXCertChainInvalid:
+ av_log(h, AV_LOG_ERROR, "Invalid certificate chain\n");
+ return AVERROR(EIO);
+ case ioErr:
+ return c->lastErr;
+ default:
+ av_log(h, AV_LOG_ERROR, "IO Error: %i\n", ret);
+ return AVERROR(EIO);
+ }
+ return AVERROR(EIO);
+}
+
+static int import_pem(URLContext *h, char *path, CFArrayRef *array)
+{
+ AVIOContext *s = NULL;
+ CFDataRef data = NULL;
+ int64_t ret = 0;
+ char *buf = NULL;
+ SecExternalFormat format = kSecFormatPEMSequence;
+ SecExternalFormat type = kSecItemTypeAggregate;
+ CFStringRef pathStr = CFStringCreateWithCString(NULL, path, 0x08000100);
+ if (!pathStr) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+
+ if ((ret = ffio_open_whitelist(&s, path, AVIO_FLAG_READ,
+ &h->interrupt_callback, NULL,
+ h->protocol_whitelist, h->protocol_blacklist)) < 0)
+ goto end;
+
+ if ((ret = avio_size(s)) < 0)
+ goto end;
+
+ if (ret == 0) {
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+
+ if (!(buf = av_malloc(ret))) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+
+ if ((ret = avio_read(s, buf, ret)) < 0)
+ goto end;
+
+ data = CFDataCreate(kCFAllocatorDefault, buf, ret);
+
+ if (SecItemImport(data, pathStr, &format, &type,
+ 0, NULL, NULL, array) != noErr || !array) {
+ ret = AVERROR_UNKNOWN;
+ goto end;
+ }
+
+ if (CFArrayGetCount(*array) == 0) {
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+
+end:
+ av_free(buf);
+ if (pathStr)
+ CFRelease(pathStr);
+ if (data)
+ CFRelease(data);
+ if (s)
+ avio_close(s);
+ return ret;
+}
+
+static int load_ca(URLContext *h)
+{
+ TLSContext *c = h->priv_data;
+ int ret = 0;
+ CFArrayRef array = NULL;
+
+ if ((ret = import_pem(h, c->tls_shared.ca_file, &array)) < 0)
+ goto end;
+
+ if (!(c->ca_array = CFRetain(array))) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+
+end:
+ if (array)
+ CFRelease(array);
+ return ret;
+}
+
+static int load_cert(URLContext *h)
+{
+ TLSContext *c = h->priv_data;
+ int ret = 0;
+ CFArrayRef certArray = NULL;
+ CFArrayRef keyArray = NULL;
+ SecIdentityRef id = NULL;
+ CFMutableArrayRef outArray = NULL;
+
+ if ((ret = import_pem(h, c->tls_shared.cert_file, &certArray)) < 0)
+ goto end;
+
+ if ((ret = import_pem(h, c->tls_shared.key_file, &keyArray)) < 0)
+ goto end;
+
+ if (!(id = SecIdentityCreate(kCFAllocatorDefault,
+ (SecCertificateRef)CFArrayGetValueAtIndex(certArray, 0),
+ (SecKeyRef)CFArrayGetValueAtIndex(keyArray, 0)))) {
+ ret = AVERROR_UNKNOWN;
+ goto end;
+ }
+
+ if (!(outArray = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, certArray))) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+
+ CFArraySetValueAtIndex(outArray, 0, id);
+
+ SSLSetCertificate(c->ssl_context, outArray);
+
+end:
+ if (certArray)
+ CFRelease(certArray);
+ if (keyArray)
+ CFRelease(keyArray);
+ if (outArray)
+ CFRelease(outArray);
+ if (id)
+ CFRelease(id);
+ return ret;
+}
+
+static OSStatus tls_read_cb(SSLConnectionRef connection, void *data, size_t *dataLength)
+{
+ URLContext *h = (URLContext*)connection;
+ TLSContext *c = h->priv_data;
+ int read = ffurl_read_complete(c->tls_shared.tcp, data, *dataLength);
+ if (read <= 0) {
+ *dataLength = 0;
+ switch(AVUNERROR(read)) {
+ case ENOENT:
+ case 0:
+ return errSSLClosedGraceful;
+ case ECONNRESET:
+ return errSSLClosedAbort;
+ case EAGAIN:
+ return errSSLWouldBlock;
+ default:
+ c->lastErr = read;
+ return ioErr;
+ }
+ } else {
+ *dataLength = read;
+ return noErr;
+ }
+}
+
+static OSStatus tls_write_cb(SSLConnectionRef connection, const void *data, size_t *dataLength)
+{
+ URLContext *h = (URLContext*)connection;
+ TLSContext *c = h->priv_data;
+ int written = ffurl_write(c->tls_shared.tcp, data, *dataLength);
+ if (written <= 0) {
+ *dataLength = 0;
+ switch(AVUNERROR(written)) {
+ case EAGAIN:
+ return errSSLWouldBlock;
+ default:
+ c->lastErr = written;
+ return ioErr;
+ }
+ } else {
+ *dataLength = written;
+ return noErr;
+ }
+}
+
+static int tls_close(URLContext *h)
+{
+ TLSContext *c = h->priv_data;
+ if (c->ssl_context) {
+ SSLClose(c->ssl_context);
+ CFRelease(c->ssl_context);
+ }
+ if (c->ca_array)
+ CFRelease(c->ca_array);
+ if (c->tls_shared.tcp)
+ ffurl_close(c->tls_shared.tcp);
+ return 0;
+}
+
+#define CHECK_ERROR(func, ...) do { \
+ OSStatus status = func(__VA_ARGS__); \
+ if (status != noErr) { \
+ ret = AVERROR_UNKNOWN; \
+ av_log(h, AV_LOG_ERROR, #func ": Error %i\n", (int)status); \
+ goto fail; \
+ } \
+ } while (0)
+
+static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
+{
+ TLSContext *c = h->priv_data;
+ TLSShared *s = &c->tls_shared;
+ int ret;
+
+ if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
+ goto fail;
+
+ c->ssl_context = SSLCreateContext(NULL, s->listen ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
+ if (!c->ssl_context) {
+ av_log(h, AV_LOG_ERROR, "Unable to create SSL context\n");
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ if (s->ca_file) {
+ if ((ret = load_ca(h)) < 0)
+ goto fail;
+ }
+ if (s->ca_file || !s->verify)
+ CHECK_ERROR(SSLSetSessionOption, c->ssl_context, kSSLSessionOptionBreakOnServerAuth, true);
+ if (s->cert_file)
+ if ((ret = load_cert(h)) < 0)
+ goto fail;
+ CHECK_ERROR(SSLSetPeerDomainName, c->ssl_context, s->host, strlen(s->host));
+ CHECK_ERROR(SSLSetIOFuncs, c->ssl_context, tls_read_cb, tls_write_cb);
+ CHECK_ERROR(SSLSetConnection, c->ssl_context, h);
+ while (1) {
+ OSStatus status = SSLHandshake(c->ssl_context);
+ if (status == errSSLServerAuthCompleted) {
+ SecTrustRef peerTrust;
+ SecTrustResultType trustResult;
+ if (!s->verify)
+ continue;
+
+ if (SSLCopyPeerTrust(c->ssl_context, &peerTrust) != noErr) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ if (SecTrustSetAnchorCertificates(peerTrust, c->ca_array) != noErr) {
+ ret = AVERROR_UNKNOWN;
+ goto fail;
+ }
+
+ if (SecTrustEvaluate(peerTrust, &trustResult) != noErr) {
+ ret = AVERROR_UNKNOWN;
+ goto fail;
+ }
+
+ if (trustResult == kSecTrustResultProceed ||
+ trustResult == kSecTrustResultUnspecified) {
+ // certificate is trusted
+ status = errSSLWouldBlock; // so we call SSLHandshake again
+ } else if (trustResult == kSecTrustResultRecoverableTrustFailure) {
+ // not trusted, for some reason other than being expired
+ status = errSSLXCertChainInvalid;
+ } else {
+ // cannot use this certificate (fatal)
+ status = errSSLBadCert;
+ }
+
+ if (peerTrust)
+ CFRelease(peerTrust);
+ }
+ if (status == noErr)
+ break;
+
+ av_log(h, AV_LOG_ERROR, "Unable to negotiate TLS/SSL session: %i\n", (int)status);
+ ret = AVERROR(EIO);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ tls_close(h);
+ return ret;
+}
+
+static int map_ssl_error(OSStatus status, size_t processed)
+{
+ switch (status) {
+ case noErr:
+ return processed;
+ case errSSLClosedGraceful:
+ case errSSLClosedNoNotify:
+ return 0;
+ default:
+ return (int)status;
+ }
+}
+
+static int tls_read(URLContext *h, uint8_t *buf, int size)
+{
+ TLSContext *c = h->priv_data;
+ size_t processed = 0;
+ int ret = SSLRead(c->ssl_context, buf, size, &processed);
+ ret = map_ssl_error(ret, processed);
+ if (ret > 0)
+ return ret;
+ if (ret == 0)
+ return AVERROR_EOF;
+ return print_tls_error(h, ret);
+}
+
+static int tls_write(URLContext *h, const uint8_t *buf, int size)
+{
+ TLSContext *c = h->priv_data;
+ size_t processed = 0;
+ int ret = SSLWrite(c->ssl_context, buf, size, &processed);
+ ret = map_ssl_error(ret, processed);
+ if (ret > 0)
+ return ret;
+ if (ret == 0)
+ return AVERROR_EOF;
+ return print_tls_error(h, ret);
+}
+
+static int tls_get_file_handle(URLContext *h)
+{
+ TLSContext *c = h->priv_data;
+ return ffurl_get_file_handle(c->tls_shared.tcp);
+}
+
+static const AVOption options[] = {
+ TLS_COMMON_OPTIONS(TLSContext, tls_shared),
+ { NULL }
+};
+
+static const AVClass tls_class = {
+ .class_name = "tls",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const URLProtocol ff_tls_securetransport_protocol = {
+ .name = "tls",
+ .url_open2 = tls_open,
+ .url_read = tls_read,
+ .url_write = tls_write,
+ .url_close = tls_close,
+ .url_get_file_handle = tls_get_file_handle,
+ .priv_data_size = sizeof(TLSContext),
+ .flags = URL_PROTOCOL_FLAG_NETWORK,
+ .priv_data_class = &tls_class,
+};
diff --git a/libavformat/tmv.c b/libavformat/tmv.c
index 0283dda..2e35171 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 091b502..ae90a85 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 "libavutil/crc.h"
#include "libavutil/dict.h"
#include "libavutil/intreadwrite.h"
+#include "apetag.h"
#include "avformat.h"
+#include "avio_internal.h"
#include "internal.h"
#include "id3v1.h"
@@ -34,9 +37,11 @@ typedef struct TTAContext {
static int tta_probe(AVProbeData *p)
{
- const uint8_t *d = p->buf;
-
- if (d[0] == 'T' && d[1] == 'T' && d[2] == 'A' && d[3] == '1')
+ 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;
}
@@ -45,17 +50,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, ff_crcEDB88320_update, 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);
@@ -63,27 +69,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);
@@ -92,20 +102,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->codecpar, avio_tell(s->pb) - start_offset))
+ return AVERROR(ENOMEM);
+
+ avio_seek(s->pb, start_offset, SEEK_SET);
+ avio_read(s->pb, st->codecpar->extradata, st->codecpar->extradata_size);
+
+ ffio_init_checksum(s->pb, ff_crcEDB88320_update, 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->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = AV_CODEC_ID_TTA;
@@ -113,19 +136,11 @@ static int tta_read_header(AVFormatContext *s)
st->codecpar->sample_rate = samplerate;
st->codecpar->bits_per_coded_sample = bps;
- st->codecpar->extradata_size = avio_tell(s->pb) - start_offset;
- if (st->codecpar->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE <= (unsigned)st->codecpar->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->codecpar->extradata = av_mallocz(st->codecpar->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata) {
- st->codecpar->extradata_size = 0;
- return AVERROR(ENOMEM);
+ if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) {
+ 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->codecpar->extradata, st->codecpar->extradata_size);
return 0;
}
@@ -140,6 +155,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/ttaenc.c b/libavformat/ttaenc.c
new file mode 100644
index 0000000..fdce1e3
--- /dev/null
+++ b/libavformat/ttaenc.c
@@ -0,0 +1,149 @@
+/*
+ * True Audio (TTA) muxer
+ * Copyright (c) 2016 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/crc.h"
+#include "libavutil/intreadwrite.h"
+
+#include "apetag.h"
+#include "avformat.h"
+#include "avio_internal.h"
+#include "internal.h"
+
+typedef struct TTAMuxContext {
+ AVIOContext *seek_table;
+ AVIOContext *data;
+ uint32_t nb_samples;
+ int frame_size;
+ int last_frame;
+} TTAMuxContext;
+
+static int tta_write_header(AVFormatContext *s)
+{
+ TTAMuxContext *tta = s->priv_data;
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ int ret;
+
+ if (s->nb_streams != 1) {
+ av_log(s, AV_LOG_ERROR, "Only one stream is supported\n");
+ return AVERROR(EINVAL);
+ }
+ if (par->codec_id != AV_CODEC_ID_TTA) {
+ av_log(s, AV_LOG_ERROR, "Unsupported codec\n");
+ return AVERROR(EINVAL);
+ }
+ if (par->extradata && par->extradata_size < 22) {
+ av_log(s, AV_LOG_ERROR, "Invalid TTA extradata\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if ((ret = avio_open_dyn_buf(&tta->seek_table)) < 0)
+ return ret;
+ if ((ret = avio_open_dyn_buf(&tta->data)) < 0) {
+ ffio_free_dyn_buf(&tta->seek_table);
+ return ret;
+ }
+
+ /* Ignore most extradata information if present. It can be innacurate
+ if for example remuxing from Matroska */
+ ffio_init_checksum(s->pb, ff_crcEDB88320_update, UINT32_MAX);
+ ffio_init_checksum(tta->seek_table, ff_crcEDB88320_update, UINT32_MAX);
+ avio_write(s->pb, "TTA1", 4);
+ avio_wl16(s->pb, par->extradata ? AV_RL16(par->extradata + 4) : 1);
+ avio_wl16(s->pb, par->channels);
+ avio_wl16(s->pb, par->bits_per_raw_sample);
+ avio_wl32(s->pb, par->sample_rate);
+ /* Prevent overflow */
+ if (par->sample_rate > 0x7FFFFFu) {
+ av_log(s, AV_LOG_ERROR, "Sample rate too large\n");
+ return AVERROR(EINVAL);
+ }
+ tta->frame_size = par->sample_rate * 256 / 245;
+ avpriv_set_pts_info(s->streams[0], 64, 1, par->sample_rate);
+
+ return 0;
+}
+
+static int tta_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ TTAMuxContext *tta = s->priv_data;
+
+ avio_write(tta->data, pkt->data, pkt->size);
+ avio_wl32(tta->seek_table, pkt->size);
+ tta->nb_samples += pkt->duration;
+
+ if (tta->frame_size != pkt->duration) {
+ if (tta->last_frame) {
+ /* Two frames with a different duration than the default frame
+ size means the TTA stream comes from a faulty container, and
+ there's no way the last frame duration will be correct. */
+ av_log(s, AV_LOG_ERROR, "Invalid frame durations\n");
+
+ return AVERROR_INVALIDDATA;
+ }
+ /* First frame with a different duration than the default frame size.
+ Assume it's the last frame in the stream and continue. */
+ tta->last_frame++;
+ }
+
+ return 0;
+}
+
+static int tta_write_trailer(AVFormatContext *s)
+{
+ TTAMuxContext *tta = s->priv_data;
+ uint8_t *ptr;
+ unsigned int crc;
+ int size;
+
+ avio_wl32(s->pb, tta->nb_samples);
+ crc = ffio_get_checksum(s->pb) ^ UINT32_MAX;
+ avio_wl32(s->pb, crc);
+
+ /* Write Seek table */
+ crc = ffio_get_checksum(tta->seek_table) ^ UINT32_MAX;
+ avio_wl32(tta->seek_table, crc);
+ size = avio_close_dyn_buf(tta->seek_table, &ptr);
+ avio_write(s->pb, ptr, size);
+ av_free(ptr);
+
+ /* Write audio data */
+ size = avio_close_dyn_buf(tta->data, &ptr);
+ avio_write(s->pb, ptr, size);
+ av_free(ptr);
+
+ ff_ape_write_tag(s);
+ avio_flush(s->pb);
+
+ return 0;
+}
+
+AVOutputFormat ff_tta_muxer = {
+ .name = "tta",
+ .long_name = NULL_IF_CONFIG_SMALL("TTA (True Audio)"),
+ .mime_type = "audio/x-tta",
+ .extensions = "tta",
+ .priv_data_size = sizeof(TTAMuxContext),
+ .audio_codec = AV_CODEC_ID_TTA,
+ .video_codec = AV_CODEC_ID_NONE,
+ .write_header = tta_write_header,
+ .write_packet = tta_write_packet,
+ .write_trailer = tta_write_trailer,
+};
diff --git a/libavformat/tty.c b/libavformat/tty.c
index f4535bb..8d48f2c 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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->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->codecpar->width = width;
- st->codecpar->height = height;
- avpriv_set_pts_info(st, 60, framerate.den, framerate.num);
- st->avg_frame_rate = framerate;
+ st->codecpar->width = s->width;
+ st->codecpar->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, INT_MAX, DEC },
{ NULL },
};
diff --git a/libavformat/txd.c b/libavformat/txd.c
index 809ec33..18c9683 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 9d4c130..3835f98 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
*/
@@ -24,54 +24,117 @@
* UDP protocol
*/
+#define _DEFAULT_SOURCE
#define _BSD_SOURCE /* Needed for using struct ip_mreq with recent glibc */
#include "avformat.h"
#include "avio_internal.h"
+#include "libavutil/avassert.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"
+#ifdef __APPLE__
+#include "TargetConditionals.h"
+#endif
+
+#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 pkt_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;
+ int64_t bitrate; /* number of bits to send per second */
+ int64_t burst_bits;
+ int close_req;
+#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 *localaddr;
+ int timeout;
+ struct sockaddr_storage local_addr_storage;
char *sources;
char *block;
} 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[] = {
- { "ttl", "Time to live (in milliseconds, multicast only)", OFFSET(ttl), AV_OPT_TYPE_INT, { .i64 = 16 }, 0, INT_MAX, .flags = D|E },
{ "buffer_size", "System data size (in bytes)", OFFSET(buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
+ { "bitrate", "Bits to send per second", OFFSET(bitrate), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, .flags = E },
+ { "burst_bits", "Max length of bursts in bits (when using bitrate)", OFFSET(burst_bits), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, .flags = E },
+ { "localport", "Local port", OFFSET(local_port), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, D|E },
{ "local_port", "Local port", OFFSET(local_port), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
- { "reuse_socket", "Reuse socket", OFFSET(reuse_socket), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, .flags = D|E },
- { "connect", "Connect socket", OFFSET(is_connected), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E },
- { "pkt_size", "Maximum packet size", OFFSET(pkt_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
{ "localaddr", "Local address", OFFSET(localaddr), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = 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", "Maximum UDP packet size", OFFSET(pkt_size), AV_OPT_TYPE_INT, { .i64 = 1472 }, -1, INT_MAX, .flags = D|E },
+ { "reuse", "explicitly allow reusing UDP sockets", OFFSET(reuse_socket), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, D|E },
+ { "reuse_socket", "explicitly allow reusing UDP sockets", OFFSET(reuse_socket), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, .flags = D|E },
+ { "broadcast", "explicitly allow or disallow broadcast destination", OFFSET(is_broadcast), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
+ { "ttl", "Time to live (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_BOOL, { .i64 = 0 }, 0, 1, .flags = D|E },
+ { "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_BOOL, {.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 },
{ "sources", "Source list", OFFSET(sources), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E },
{ "block", "Block list", OFFSET(block), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E },
{ NULL }
@@ -84,6 +147,13 @@ static const AVClass udp_class = {
.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)
{
char errbuf[100];
@@ -113,14 +183,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;
@@ -142,14 +215,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;
@@ -194,7 +270,7 @@ static struct addrinfo *udp_resolve_host(URLContext *h,
res = NULL;
av_log(h, AV_LOG_ERROR, "getaddrinfo(%s, %s): %s\n",
node ? node : "unknown",
- service ? service : "unknown",
+ service,
gai_strerror(error));
}
@@ -206,7 +282,7 @@ static int udp_set_multicast_sources(URLContext *h,
int addr_len, char **sources,
int nb_sources, int include)
{
-#if HAVE_STRUCT_GROUP_SOURCE_REQ && defined(MCAST_BLOCK_SOURCE) && !defined(_WIN32)
+#if HAVE_STRUCT_GROUP_SOURCE_REQ && defined(MCAST_BLOCK_SOURCE) && !defined(_WIN32) && (!defined(TARGET_OS_TV) || !TARGET_OS_TV)
/* These ones are available in the microsoft SDK, but don't seem to work
* as on linux, so just prefer the v4-only approach there for now. */
int i;
@@ -283,7 +359,7 @@ static int udp_set_url(URLContext *h,
int addr_len;
res0 = udp_resolve_host(h, 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);
@@ -296,7 +372,7 @@ static int udp_socket_create(URLContext *h, struct sockaddr_storage *addr,
{
UDPContext *s = h->priv_data;
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)
@@ -304,10 +380,13 @@ static int udp_socket_create(URLContext *h, struct sockaddr_storage *addr,
res0 = udp_resolve_host(h, (localaddr && 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");
}
@@ -354,6 +433,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
@@ -415,6 +495,168 @@ static int udp_get_file_handle(URLContext *h)
return s->udp_fd;
}
+#if HAVE_PTHREAD_CANCEL
+static void *circular_buffer_task_rx( 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;
+}
+
+static void *circular_buffer_task_tx( void *_URLContext)
+{
+ URLContext *h = _URLContext;
+ UDPContext *s = h->priv_data;
+ int old_cancelstate;
+ int64_t target_timestamp = av_gettime_relative();
+ int64_t start_timestamp = av_gettime_relative();
+ int64_t sent_bits = 0;
+ int64_t burst_interval = s->bitrate ? (s->burst_bits * 1000000 / s->bitrate) : 0;
+ int64_t max_delay = s->bitrate ? ((int64_t)h->max_packet_size * 8 * 1000000 / s->bitrate + 1) : 0;
+
+ 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;
+ }
+
+ for(;;) {
+ int len;
+ const uint8_t *p;
+ uint8_t tmp[4];
+ int64_t timestamp;
+
+ len=av_fifo_size(s->fifo);
+
+ while (len<4) {
+ if (s->close_req)
+ goto end;
+ if (pthread_cond_wait(&s->cond, &s->mutex) < 0) {
+ goto end;
+ }
+ len=av_fifo_size(s->fifo);
+ }
+
+ av_fifo_generic_read(s->fifo, tmp, 4, NULL);
+ len=AV_RL32(tmp);
+
+ av_assert0(len >= 0);
+ av_assert0(len <= sizeof(s->tmp));
+
+ av_fifo_generic_read(s->fifo, s->tmp, len, NULL);
+
+ pthread_mutex_unlock(&s->mutex);
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelstate);
+
+ if (s->bitrate) {
+ timestamp = av_gettime_relative();
+ if (timestamp < target_timestamp) {
+ int64_t delay = target_timestamp - timestamp;
+ if (delay > max_delay) {
+ delay = max_delay;
+ start_timestamp = timestamp + delay;
+ sent_bits = 0;
+ }
+ av_usleep(delay);
+ } else {
+ if (timestamp - burst_interval > target_timestamp) {
+ start_timestamp = timestamp - burst_interval;
+ sent_bits = 0;
+ }
+ }
+ sent_bits += len * 8;
+ target_timestamp = start_timestamp + sent_bits * 1000000 / s->bitrate;
+ }
+
+ p = s->tmp;
+ while (len) {
+ int ret;
+ av_assert0(len > 0);
+ if (!s->is_connected) {
+ ret = sendto (s->udp_fd, p, len, 0,
+ (struct sockaddr *) &s->dest_addr,
+ s->dest_addr_len);
+ } else
+ ret = send(s->udp_fd, p, len, 0);
+ if (ret >= 0) {
+ len -= ret;
+ p += ret;
+ } else {
+ ret = ff_neterrno();
+ if (ret != AVERROR(EAGAIN) && ret != AVERROR(EINTR)) {
+ pthread_mutex_lock(&s->mutex);
+ s->circular_buffer_error = ret;
+ pthread_mutex_unlock(&s->mutex);
+ return NULL;
+ }
+ }
+ }
+
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate);
+ pthread_mutex_lock(&s->mutex);
+ }
+
+end:
+ pthread_mutex_unlock(&s->mutex);
+ return NULL;
+}
+
+
+#endif
+
static int parse_source_list(char *buf, char **sources, int *num_sources,
int max_sources)
{
@@ -441,7 +683,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;
@@ -452,10 +694,8 @@ 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);
-
if (s->buffer_size < 0)
s->buffer_size = is_output ? UDP_TX_BUF_SIZE : UDP_MAX_PKT_SIZE;
@@ -484,14 +724,28 @@ static int udp_open(URLContext *h, const char *uri, int flags)
if (buf == endptr)
s->reuse_socket = 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->pkt_size = strtol(buf, NULL, 10);
}
if (av_find_info_tag(buf, sizeof(buf), "buffer_size", p)) {
s->buffer_size = strtol(buf, NULL, 10);
@@ -499,6 +753,26 @@ 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), "bitrate", p)) {
+ s->bitrate = strtoll(buf, NULL, 10);
+ if (!HAVE_PTHREAD_CANCEL)
+ av_log(h, AV_LOG_WARNING,
+ "'bitrate' option was set but it is not supported "
+ "on this build (pthread support is required)\n");
+ }
+ if (av_find_info_tag(buf, sizeof(buf), "burst_bits", p)) {
+ s->burst_bits = strtoll(buf, NULL, 10);
+ }
if (av_find_info_tag(buf, sizeof(buf), "localaddr", p)) {
av_strlcpy(localaddr, buf, sizeof(localaddr));
}
@@ -512,7 +786,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->pkt_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);
@@ -527,7 +813,7 @@ static int udp_open(URLContext *h, const char *uri, int flags)
goto fail;
}
- if ((s->is_multicast || s->local_port < 0) && (h->flags & AVIO_FLAG_READ))
+ if ((s->is_multicast || s->local_port <= 0) && (h->flags & AVIO_FLAG_READ))
s->local_port = port;
if (localaddr[0])
@@ -537,6 +823,8 @@ static int udp_open(URLContext *h, const char *uri, int flags)
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.
*/
@@ -546,6 +834,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
@@ -585,7 +898,7 @@ static int udp_open(URLContext *h, const char *uri, int flags)
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) {
@@ -607,12 +920,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);
}
@@ -629,10 +950,54 @@ 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
+ /*
+ Create thread in case of:
+ 1. Input and circular_buffer_size is set
+ 2. Output and bitrate and circular_buffer_size is set
+ */
+
+ if (is_output && s->bitrate && !s->circular_buffer_size) {
+ /* Warn user in case of 'circular_buffer_size' is not set */
+ av_log(h, AV_LOG_WARNING,"'bitrate' option was set but 'circular_buffer_size' is not, but required\n");
+ }
+
+ if ((!is_output && s->circular_buffer_size) || (is_output && s->bitrate && 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, is_output?circular_buffer_task_tx:circular_buffer_task_rx, 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++)
@@ -640,10 +1005,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);
@@ -651,6 +1070,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;
}
@@ -659,6 +1079,35 @@ static int udp_write(URLContext *h, const uint8_t *buf, int size)
UDPContext *s = h->priv_data;
int ret;
+#if HAVE_PTHREAD_CANCEL
+ if (s->fifo) {
+ uint8_t tmp[4];
+
+ pthread_mutex_lock(&s->mutex);
+
+ /*
+ Return error if last tx failed.
+ Here we can't know on which packet error was, but it needs to know that error exists.
+ */
+ if (s->circular_buffer_error<0) {
+ int err=s->circular_buffer_error;
+ pthread_mutex_unlock(&s->mutex);
+ return err;
+ }
+
+ if(av_fifo_space(s->fifo) < size + 4) {
+ /* What about a partial packet tx ? */
+ pthread_mutex_unlock(&s->mutex);
+ return AVERROR(ENOMEM);
+ }
+ AV_WL32(tmp, size);
+ av_fifo_generic_write(s->fifo, tmp, 4, NULL); /* size of packet */
+ av_fifo_generic_write(s->fifo, (uint8_t *)buf, size, NULL); /* the data */
+ pthread_cond_signal(&s->cond);
+ pthread_mutex_unlock(&s->mutex);
+ return size;
+ }
+#endif
if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
ret = ff_network_wait_fd(s->udp_fd, 1);
if (ret < 0)
@@ -679,9 +1128,33 @@ static int udp_close(URLContext *h)
{
UDPContext *s = h->priv_data;
+#if HAVE_PTHREAD_CANCEL
+ // Request close once writing is finished
+ if (s->thread_started && !(h->flags & AVIO_FLAG_READ)) {
+ pthread_mutex_lock(&s->mutex);
+ s->close_req = 1;
+ pthread_cond_signal(&s->cond);
+ pthread_mutex_unlock(&s->mutex);
+ }
+#endif
+
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);
+#if HAVE_PTHREAD_CANCEL
+ if (s->thread_started) {
+ int ret;
+ // Cancel only read, as write has been signaled as success to the user
+ if (h->flags & AVIO_FLAG_READ)
+ 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
closesocket(s->udp_fd);
+ av_fifo_freep(&s->fifo);
return 0;
}
@@ -693,6 +1166,18 @@ const URLProtocol ff_udp_protocol = {
.url_close = udp_close,
.url_get_file_handle = udp_get_file_handle,
.priv_data_size = sizeof(UDPContext),
- .flags = URL_PROTOCOL_FLAG_NETWORK,
.priv_data_class = &udp_class,
+ .flags = URL_PROTOCOL_FLAG_NETWORK,
+};
+
+const 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 0000000..2f1a14c
--- /dev/null
+++ b/libavformat/uncodedframecrcenc.c
@@ -0,0 +1,177 @@
+/*
+* 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 = AV_CEIL_RSHIFT(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 = frame->channels;
+ 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%08"PRIx32, cksum);
+ }
+}
+
+static int write_header(struct AVFormatContext *s)
+{
+ return ff_framehash_write_header(s);
+}
+
+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]->codecpar->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 = 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 647e7e8..4f01d14 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
*/
@@ -25,12 +25,11 @@
* Unix socket url_protocol
*/
-#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 {
@@ -45,7 +44,7 @@ typedef struct UnixContext {
#define OFFSET(x) offsetof(UnixContext, x)
#define ED AV_OPT_FLAG_DECODING_PARAM|AV_OPT_FLAG_ENCODING_PARAM
static const AVOption unix_options[] = {
- { "listen", "Open socket for listening", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, ED },
+ { "listen", "Open socket for listening", OFFSET(listen), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, ED },
{ "timeout", "Timeout in ms", OFFSET(timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, ED },
{ "type", "Socket type", OFFSET(type), AV_OPT_TYPE_INT, { .i64 = SOCK_STREAM }, INT_MIN, INT_MAX, ED, "type" },
{ "stream", "Stream (reliable stream-oriented)", 0, AV_OPT_TYPE_CONST, { .i64 = SOCK_STREAM }, INT_MIN, INT_MAX, ED, "type" },
diff --git a/libavformat/url.c b/libavformat/url.c
index 92cd5f1..596fb49 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
*/
@@ -68,7 +68,7 @@ int ff_url_join(char *str, int size, const char *proto,
av_strlcatf(str, size, ":%d", port);
if (fmt) {
va_list vl;
- int len = strlen(str);
+ size_t len = strlen(str);
va_start(vl, fmt);
vsnprintf(str + len, size > len ? size - len : 0, fmt, vl);
@@ -145,3 +145,19 @@ void ff_make_absolute_url(char *buf, int size, const char *base,
}
av_strlcat(buf, rel, size);
}
+
+AVIODirEntry *ff_alloc_dir_entry(void)
+{
+ AVIODirEntry *entry = av_mallocz(sizeof(AVIODirEntry));
+ if (entry) {
+ entry->type = AVIO_ENTRY_UNKNOWN;
+ entry->size = -1;
+ entry->modification_timestamp = -1;
+ entry->access_timestamp = -1;
+ entry->status_change_timestamp = -1;
+ entry->user_id = -1;
+ entry->group_id = -1;
+ entry->filemode = -1;
+ }
+ return entry;
+}
diff --git a/libavformat/url.h b/libavformat/url.h
index 5853ffe..4750bff 100644
--- a/libavformat/url.h
+++ b/libavformat/url.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
*/
@@ -38,10 +38,6 @@ extern const AVClass ffurl_context_class;
typedef struct URLContext {
const AVClass *av_class; /**< information for av_log(). Set by url_open(). */
const struct URLProtocol *prot;
- /**
- * A NULL-terminated list of protocols usable by the child contexts.
- */
- const struct URLProtocol **protocols;
void *priv_data;
char *filename; /**< specified URL */
int flags;
@@ -49,7 +45,10 @@ 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 microseconds */
+ int64_t rw_timeout; /**< maximum time to wait for (network) read/write operation completion, in mcs */
+ const char *protocol_whitelist;
+ const char *protocol_blacklist;
+ int min_packet_size; /**< if non zero, the stream is packetized with this min packet size */
} URLContext;
typedef struct URLProtocol {
@@ -61,6 +60,8 @@ typedef struct URLProtocol {
* for those nested protocols.
*/
int (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
+ int (*url_accept)(URLContext *s, URLContext **c);
+ int (*url_handshake)(URLContext *c);
/**
* Read data from the protocol.
@@ -84,11 +85,18 @@ typedef struct URLProtocol {
int (*url_get_file_handle)(URLContext *h);
int (*url_get_multi_file_handle)(URLContext *h, int **handles,
int *numhandles);
+ int (*url_get_short_seek)(URLContext *h);
int (*url_shutdown)(URLContext *h, int flags);
int priv_data_size;
const AVClass *priv_data_class;
int flags;
int (*url_check)(URLContext *h, int mask);
+ int (*url_open_dir)(URLContext *h);
+ int (*url_read_dir)(URLContext *h, AVIODirEntry **next);
+ int (*url_close_dir)(URLContext *h);
+ int (*url_delete)(URLContext *h);
+ int (*url_move)(URLContext *h_src, URLContext *h_dst);
+ const char *default_whitelist;
} URLProtocol;
/**
@@ -101,15 +109,11 @@ typedef struct URLProtocol {
* is to be opened
* @param int_cb interrupt callback to use for the URLContext, may be
* NULL
- * @param protocols a NULL-terminate list of protocols available for use by
- * this context and its children. The caller must ensure this
- * list remains valid until the context is closed.
- * @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,
- const AVIOInterruptCB *int_cb,
- const URLProtocol **protocols);
+ const AVIOInterruptCB *int_cb);
/**
* Connect an URLContext that has been allocated by ffurl_alloc
@@ -134,17 +138,41 @@ 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.
- * @param protocols a NULL-terminate list of protocols available for use by
- * this context and its children. The caller must ensure this
- * list remains valid until the context is closed.
* @param parent An enclosing URLContext, whose generic options should
* be applied to this URLContext as well.
- * @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,
+int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options,
- const URLProtocol **protocols, URLContext *parent);
+ const char *whitelist, const char* blacklist,
+ URLContext *parent);
+
+int ffurl_open(URLContext **puc, const char *filename, int flags,
+ const AVIOInterruptCB *int_cb, AVDictionary **options);
+
+/**
+ * Accept an URLContext c on an URLContext s
+ *
+ * @param s server context
+ * @param c client context, must be unallocated.
+ * @return >= 0 on success, ff_neterrno() on failure.
+ */
+int ffurl_accept(URLContext *s, URLContext **c);
+
+/**
+ * Perform one step of the protocol handshake to accept a new client.
+ * See avio_handshake() for details.
+ * Implementations should try to return decreasing values.
+ * If the protocol uses an underlying protocol, the underlying handshake is
+ * usually the first step, and the return value can be:
+ * (largest value for this protocol) + (return value from other protocol)
+ *
+ * @param c the client context
+ * @return >= 0 on success or a negative value corresponding
+ * to an AVERROR code on failure
+ */
+int ffurl_handshake(URLContext *c);
/**
* Read up to size bytes from the resource accessed by h, and store
@@ -192,11 +220,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);
/**
@@ -222,6 +251,13 @@ int ffurl_get_file_handle(URLContext *h);
int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles);
/**
+ * Return the current short seek threshold value for this URL.
+ *
+ * @return threshold (>0) on success or <=0 on error.
+ */
+int ffurl_get_short_seek(URLContext *h);
+
+/**
* Signal the URLContext that we are done reading or writing the stream.
*
* @param h pointer to the resource
@@ -268,7 +304,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
@@ -279,6 +315,13 @@ int ff_url_join(char *str, int size, const char *proto,
void ff_make_absolute_url(char *buf, int size, const char *base,
const char *rel);
+/**
+ * Allocate directory entry with default values.
+ *
+ * @return entry or NULL on error
+ */
+AVIODirEntry *ff_alloc_dir_entry(void);
+
const AVClass *ff_urlcontext_child_class_next(const AVClass *prev);
/**
diff --git a/libavformat/urldecode.c b/libavformat/urldecode.c
index 49af9ba..283d912 100644
--- a/libavformat/urldecode.c
+++ b/libavformat/urldecode.c
@@ -9,20 +9,20 @@
* based on http://www.icosaedro.it/apache/urldecode.c
* from Umberto Salsi (salsi@icosaedro.it)
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/urldecode.h b/libavformat/urldecode.h
index b43f319..cb81ebc 100644
--- a/libavformat/urldecode.h
+++ b/libavformat/urldecode.h
@@ -1,18 +1,18 @@
/*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/utils.c b/libavformat/utils.c
index 8fa89eb..1a7996c 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,16 @@
#include "libavutil/parseutils.h"
#include "libavutil/pixdesc.h"
#include "libavutil/time.h"
+#include "libavutil/time_internal.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 +52,204 @@
#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(const 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)
+#if FF_API_OLD_OPEN_CALLBACKS
+FF_DISABLE_DEPRECATION_WARNINGS
+MAKE_ACCESSORS(AVFormatContext, format, AVOpenCallback, open_cb)
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+int64_t av_stream_get_end_pts(const AVStream *st)
+{
+ if (st->priv_pts) {
+ return st->priv_pts->val;
+ } else
+ return AV_NOPTS_VALUE;
+}
+
+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_whiteblacklists(AVFormatContext *dst, const AVFormatContext *src)
+{
+ av_assert0(!dst->codec_whitelist &&
+ !dst->format_whitelist &&
+ !dst->protocol_whitelist &&
+ !dst->protocol_blacklist);
+ dst-> codec_whitelist = av_strdup(src->codec_whitelist);
+ dst->format_whitelist = av_strdup(src->format_whitelist);
+ dst->protocol_whitelist = av_strdup(src->protocol_whitelist);
+ dst->protocol_blacklist = av_strdup(src->protocol_blacklist);
+ if ( (src-> codec_whitelist && !dst-> codec_whitelist)
+ || (src-> format_whitelist && !dst-> format_whitelist)
+ || (src->protocol_whitelist && !dst->protocol_whitelist)
+ || (src->protocol_blacklist && !dst->protocol_blacklist)) {
+ av_log(dst, AV_LOG_ERROR, "Failed to duplicate black/whitelist\n");
+ return AVERROR(ENOMEM);
+ }
+ return 0;
+}
+
+static const AVCodec *find_decoder(AVFormatContext *s, const AVStream *st, enum AVCodecID codec_id)
+{
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ if (st->codec->codec)
+ return st->codec->codec;
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+ switch (st->codecpar->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);
+}
+
+static const AVCodec *find_probe_decoder(AVFormatContext *s, const AVStream *st, enum AVCodecID codec_id)
+{
+ const AVCodec *codec;
+
+#if CONFIG_H264_DECODER
+ /* Other parts of the code assume this decoder to be used for h264,
+ * so force it if possible. */
+ if (codec_id == AV_CODEC_ID_H264)
+ return avcodec_find_decoder_by_name("h264");
+#endif
+
+ codec = find_decoder(s, st, codec_id);
+ if (!codec)
+ return NULL;
+
+ if (codec->capabilities & AV_CODEC_CAP_AVOID_PROBING) {
+ const AVCodec *probe_codec = NULL;
+ while (probe_codec = av_codec_next(probe_codec)) {
+ if (probe_codec->id == codec_id &&
+ av_codec_is_decoder(probe_codec) &&
+ !(probe_codec->capabilities & (AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_EXPERIMENTAL))) {
+ return probe_codec;
+ }
+ }
+ }
+
+ return codec;
+}
+
+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 +257,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 +277,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 +311,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,15 +321,21 @@ 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 },
+ { "dvbtxt", AV_CODEC_ID_DVB_TELETEXT,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 },
+ { "mjpeg_2000",AV_CODEC_ID_JPEG2000, AVMEDIA_TYPE_VIDEO },
{ "mp3", AV_CODEC_ID_MP3, AVMEDIA_TYPE_AUDIO },
{ "mpegvideo", AV_CODEC_ID_MPEG2VIDEO, AVMEDIA_TYPE_VIDEO },
+ { "truehd", AV_CODEC_ID_TRUEHD, AVMEDIA_TYPE_AUDIO },
{ 0 }
};
- AVInputFormat *fmt = av_probe_input_format2(pd, 1, &score);
+ int score;
+ AVInputFormat *fmt = av_probe_input_format3(pd, 1, &score);
if (fmt) {
int i;
@@ -167,52 +345,81 @@ static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st,
fmt->name, score);
for (i = 0; fmt_id_type[i].name; i++) {
if (!strcmp(fmt->name, fmt_id_type[i].name)) {
+ if (fmt_id_type[i].type != AVMEDIA_TYPE_AUDIO &&
+ st->codecpar->sample_rate)
+ continue;
+ if (st->request_probe > score &&
+ st->codecpar->codec_id != fmt_id_type[i].id)
+ continue;
st->codecpar->codec_id = fmt_id_type[i].id;
st->codecpar->codec_type = fmt_id_type[i].type;
+ st->internal->need_context_update = 1;
#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS
st->codec->codec_type = st->codecpar->codec_type;
st->codec->codec_id = st->codecpar->codec_id;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
- 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 \'%s\'\n", ic->format_whitelist);
+ 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;
- ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ, options);
- if (ret < 0)
+ if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, 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 int add_to_pktbuf(AVPacketList **packet_buffer, AVPacket *pkt,
@@ -243,12 +450,18 @@ static int add_to_pktbuf(AVPacketList **packet_buffer, AVPacket *pkt,
return 0;
}
-static int queue_attached_pictures(AVFormatContext *s)
+int avformat_queue_attached_pictures(AVFormatContext *s)
{
int i, ret;
for (i = 0; i < s->nb_streams; i++)
if (s->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC &&
s->streams[i]->discard < AVDISCARD_ALL) {
+ if (s->streams[i]->attached_pic.size <= 0) {
+ av_log(s, AV_LOG_WARNING,
+ "Attached picture on stream %d has invalid size, "
+ "ignoring\n", i);
+ continue;
+ }
ret = add_to_pktbuf(&s->internal->raw_packet_buffer,
&s->streams[i]->attached_pic,
@@ -259,27 +472,40 @@ static int queue_attached_pictures(AVFormatContext *s)
return 0;
}
-#if FF_API_LAVF_AVCTX
-FF_DISABLE_DEPRECATION_WARNINGS
static int update_stream_avctx(AVFormatContext *s)
{
int i, ret;
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
- if (!st->internal->need_codec_update)
+ if (!st->internal->need_context_update)
continue;
+ /* close parser, because it depends on the codec */
+ if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
+ av_parser_close(st->parser);
+ st->parser = NULL;
+ }
+
+ /* update internal codec context, for the parser */
+ ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
+ if (ret < 0)
+ return ret;
+
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ /* update deprecated public codec context */
ret = avcodec_parameters_to_context(st->codec, st->codecpar);
if (ret < 0)
return ret;
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
- st->internal->need_codec_update = 0;
+ st->internal->need_context_update = 0;
}
return 0;
}
-FF_ENABLE_DEPRECATION_WARNINGS
-#endif
+
int avformat_open_input(AVFormatContext **ps, const char *filename,
AVInputFormat *fmt, AVDictionary **options)
@@ -291,17 +517,50 @@ 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;
if (options)
av_dict_copy(&tmp, *options, 0);
+ if (s->pb) // must be before any goto fail
+ s->flags |= AVFMT_FLAG_CUSTOM_IO;
+
if ((ret = av_opt_set_dict(s, &tmp)) < 0)
goto fail;
+ av_strlcpy(s->filename, filename ? filename : "", sizeof(s->filename));
if ((ret = init_input(s, filename, &tmp)) < 0)
goto fail;
+ s->probe_score = ret;
+
+ if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {
+ s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);
+ if (!s->protocol_whitelist) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ }
+
+ if (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {
+ s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);
+ if (!s->protocol_blacklist) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ }
+
+ if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
+ av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
+ 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) {
@@ -312,7 +571,6 @@ int avformat_open_input(AVFormatContext **ps, const char *filename,
}
s->duration = s->start_time = AV_NOPTS_VALUE;
- av_strlcpy(s->filename, filename ? filename : "", sizeof(s->filename));
/* Allocate private data. */
if (s->iformat->priv_data_size > 0) {
@@ -330,28 +588,47 @@ 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_dict(s->pb, &s->internal->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);
- if (s->iformat->read_header)
+
+ if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
if ((ret = s->iformat->read_header(s)) < 0)
goto fail;
- if (id3v2_extra_meta &&
- (ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0)
- goto fail;
+ if (!s->metadata) {
+ s->metadata = s->internal->id3v2_meta;
+ s->internal->id3v2_meta = NULL;
+ } else if (s->internal->id3v2_meta) {
+ int level = AV_LOG_WARNING;
+ if (s->error_recognition & AV_EF_COMPLIANT)
+ level = AV_LOG_ERROR;
+ av_log(s, level, "Discarding ID3 tags because more suitable tags were found.\n");
+ av_dict_free(&s->internal->id3v2_meta);
+ if (s->error_recognition & AV_EF_EXPLODE)
+ return AVERROR_INVALIDDATA;
+ }
+
+ 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;
+ if ((ret = ff_id3v2_parse_chapters(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;
-#if FF_API_LAVF_AVCTX
update_stream_avctx(s);
-#endif
for (i = 0; i < s->nb_streams; i++)
s->streams[i]->internal->orig_codec_id = s->streams[i]->codecpar->codec_id;
@@ -367,7 +644,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;
@@ -375,44 +652,144 @@ fail:
/*******************************************************/
+static void force_codec_ids(AVFormatContext *s, AVStream *st)
+{
+ switch (st->codecpar->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ if (s->video_codec_id)
+ st->codecpar->codec_id = s->video_codec_id;
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ if (s->audio_codec_id)
+ st->codecpar->codec_id = s->audio_codec_id;
+ break;
+ case AVMEDIA_TYPE_SUBTITLE:
+ if (s->subtitle_codec_id)
+ st->codecpar->codec_id = s->subtitle_codec_id;
+ break;
+ case AVMEDIA_TYPE_DATA:
+ if (s->data_codec_id)
+ st->codecpar->codec_id = s->data_codec_id;
+ break;
+ }
+}
+
static int probe_codec(AVFormatContext *s, AVStream *st, const AVPacket *pkt)
{
- if (st->codecpar->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->codecpar->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->codecpar->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->codecpar->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;
@@ -424,16 +801,10 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
if (pktl) {
*pkt = pktl->pkt;
st = s->streams[pkt->stream_index];
- if (st->codecpar->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);
@@ -446,13 +817,19 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
av_init_packet(pkt);
ret = s->iformat->read_packet(s, pkt);
if (ret < 0) {
+ /* Some demuxers return FFERROR_REDO when they consume
+ data and discard it (ignored streams, junk, extradata).
+ We must re-call the demuxer to get the real packet. */
+ if (ret == FFERROR_REDO)
+ continue;
if (!pktl || ret == AVERROR(EAGAIN))
return ret;
for (i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
- if (st->probe_packets)
+ if (st->probe_packets || st->request_probe > 0)
if ((err = probe_codec(s, st, NULL)) < 0)
return err;
+ av_assert0(st->request_probe <= 0);
}
continue;
}
@@ -474,25 +851,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->codecpar->codec_type) {
- case AVMEDIA_TYPE_VIDEO:
- if (s->video_codec_id)
- st->codecpar->codec_id = s->video_codec_id;
- break;
- case AVMEDIA_TYPE_AUDIO:
- if (s->audio_codec_id)
- st->codecpar->codec_id = s->audio_codec_id;
- break;
- case AVMEDIA_TYPE_SUBTITLE:
- if (s->subtitle_codec_id)
- st->codecpar->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->codecpar->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;
err = add_to_pktbuf(&s->internal->raw_packet_buffer, pkt,
@@ -506,8 +891,21 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
}
}
+
/**********************************************************/
+static int determinable_frame_size(AVCodecContext *avctx)
+{
+ switch(avctx->codec_id) {
+ case AV_CODEC_ID_MP1:
+ case AV_CODEC_ID_MP2:
+ case AV_CODEC_ID_MP3:
+ return 1;
+ }
+
+ return 0;
+}
+
/**
* Return the frame duration in seconds. Return 0 if not available.
*/
@@ -515,27 +913,39 @@ void ff_compute_frame_duration(AVFormatContext *s, int *pnum, int *pden, AVStrea
AVCodecParserContext *pc, AVPacket *pkt)
{
AVRational codec_framerate = s->iformat ? st->internal->avctx->framerate :
- (AVRational){ 0, 1 };
- int frame_size;
+ av_mul_q(av_inv_q(st->internal->avctx->time_base), (AVRational){1, st->internal->avctx->ticks_per_frame});
+ int frame_size, sample_rate;
+
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ if ((!codec_framerate.den || !codec_framerate.num) && st->codec->time_base.den && st->codec->time_base.num)
+ codec_framerate = av_mul_q(av_inv_q(st->codec->time_base), (AVRational){1, st->codec->ticks_per_frame});
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
*pnum = 0;
*pden = 0;
switch (st->codecpar->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->internal->avctx->ticks_per_frame);
+ av_reduce(pnum, pden,
+ codec_framerate.den,
+ codec_framerate.num * (int64_t)st->internal->avctx->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
@@ -545,11 +955,17 @@ void ff_compute_frame_duration(AVFormatContext *s, int *pnum, int *pden, AVStrea
}
break;
case AVMEDIA_TYPE_AUDIO:
- frame_size = av_get_audio_frame_duration2(st->codecpar, pkt->size);
- if (frame_size <= 0 || st->codecpar->sample_rate <= 0)
+ if (st->internal->avctx_inited) {
+ frame_size = av_get_audio_frame_duration(st->internal->avctx, pkt->size);
+ sample_rate = st->internal->avctx->sample_rate;
+ } else {
+ frame_size = av_get_audio_frame_duration2(st->codecpar, pkt->size);
+ sample_rate = st->codecpar->sample_rate;
+ }
+ if (frame_size <= 0 || sample_rate <= 0)
break;
*pnum = frame_size;
- *pden = st->codecpar->sample_rate;
+ *pden = sample_rate;
break;
default:
break;
@@ -566,46 +982,166 @@ static int is_intra_only(enum AVCodecID id)
return 1;
}
+static int has_decode_delay_been_guessed(AVStream *st)
+{
+ if (st->codecpar->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->internal->avctx->has_b_frames &&
+ avpriv_h264_has_num_reorder_frames(st->internal->avctx) == st->internal->avctx->has_b_frames)
+ return 1;
+#endif
+ if (st->internal->avctx->has_b_frames<3)
+ return st->nb_decoded_frames >= 7;
+ else if (st->internal->avctx->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->codecpar->codec_id != AV_CODEC_ID_H264 &&
+ st->codecpar->codec_id != AV_CODEC_ID_HEVC;
+
+ if(!onein_oneout) {
+ int delay = st->internal->avctx->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;
+}
+
+/**
+ * Updates the dts of packets of a stream in pkt_buffer, by re-ordering the pts
+ * of the packets in a window.
+ */
+static void update_dts_from_pts(AVFormatContext *s, int stream_index,
+ AVPacketList *pkt_buffer)
+{
+ AVStream *st = s->streams[stream_index];
+ int delay = st->internal->avctx->has_b_frames;
+ int i;
+
+ int64_t pts_buffer[MAX_REORDER_DELAY+1];
+
+ for (i = 0; i<MAX_REORDER_DELAY+1; i++)
+ pts_buffer[i] = AV_NOPTS_VALUE;
+
+ for (; pkt_buffer; pkt_buffer = get_next_pkt(s, st, pkt_buffer)) {
+ if (pkt_buffer->pkt.stream_index != stream_index)
+ continue;
+
+ if (pkt_buffer->pkt.pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY) {
+ pts_buffer[0] = pkt_buffer->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]);
+
+ pkt_buffer->pkt.dts = select_from_pts_buffer(st, pts_buffer, pkt_buffer->pkt.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;
+ AVPacketList *pktl_it;
+
+ uint64_t shift;
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;
+ st->first_dts = dts - (st->cur_dts - RELATIVE_TS_BASE);
st->cur_dts = dts;
+ shift = (uint64_t)st->first_dts - RELATIVE_TS_BASE;
- for (; pktl; pktl = pktl->next) {
- if (pktl->pkt.stream_index != stream_index)
+ if (is_relative(pts))
+ pts += shift;
+
+ for (pktl_it = pktl; pktl_it; pktl_it = get_next_pkt(s, st, pktl_it)) {
+ if (pktl_it->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_it->pkt.pts))
+ pktl_it->pkt.pts += shift;
+
+ if (is_relative(pktl_it->pkt.dts))
+ pktl_it->pkt.dts += shift;
- if (pktl->pkt.dts != AV_NOPTS_VALUE)
- pktl->pkt.dts += st->first_dts;
+ if (st->start_time == AV_NOPTS_VALUE && pktl_it->pkt.pts != AV_NOPTS_VALUE) {
+ st->start_time = pktl_it->pkt.pts;
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->sample_rate)
+ st->start_time += av_rescale_q(st->skip_samples, (AVRational){1, st->codecpar->sample_rate}, st->time_base);
+ }
+ }
- if (st->start_time == AV_NOPTS_VALUE && pktl->pkt.pts != AV_NOPTS_VALUE)
- st->start_time = pktl->pkt.pts;
+ if (has_decode_delay_been_guessed(st)) {
+ update_dts_from_pts(s, stream_index, pktl);
}
- if (st->start_time == AV_NOPTS_VALUE)
+
+ if (st->start_time == AV_NOPTS_VALUE) {
st->start_time = pts;
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->sample_rate)
+ st->start_time += av_rescale_q(st->skip_samples, (AVRational){1, st->codecpar->sample_rate}, st->time_base);
+ }
}
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 ||
@@ -614,42 +1150,85 @@ 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 %"PRId64") 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 &&
+ if ((pktl->pkt.pts == pktl->pkt.dts ||
+ pktl->pkt.pts == AV_NOPTS_VALUE) &&
+ (pktl->pkt.dts == AV_NOPTS_VALUE ||
+ pktl->pkt.dts == st->first_dts ||
+ pktl->pkt.dts == RELATIVE_TS_BASE) &&
!pktl->pkt.duration) {
pktl->pkt.dts = cur_dts;
if (!st->internal->avctx->has_b_frames)
pktl->pkt.pts = cur_dts;
- cur_dts += duration;
- if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
+// if (st->codecpar->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->codecpar->codec_id != AV_CODEC_ID_H264 &&
+ st->codecpar->codec_id != AV_CODEC_ID_HEVC;
if (s->flags & AVFMT_FLAG_NOFILLIN)
return;
+ if (st->codecpar->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->internal->avctx->has_b_frames)
+ //FIXME Set low_delay = 0 when has_b_frames = 1
+ st->internal->avctx->has_b_frames = 1;
+
/* do we have a video B-frame ? */
delay = st->internal->avctx->has_b_frames;
presentation_delayed = 0;
@@ -663,7 +1242,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).
@@ -672,23 +1254,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->codecpar->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) {
@@ -706,21 +1292,22 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st,
pkt->pts > pkt->dts)
presentation_delayed = 1;
- av_log(NULL, AV_LOG_TRACE,
- "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
+ if (s->debug & FF_FDEBUG_TS)
+ av_log(s, AV_LOG_TRACE,
+ "IN delayed:%d pts:%s, dts:%s cur_dts:%s st:%d pc:%p duration:%"PRId64" 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->codecpar->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;
@@ -730,41 +1317,32 @@ 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->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
- int duration = pkt->duration;
- if (!duration && st->codecpar->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);
}
}
@@ -772,19 +1350,20 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *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->codecpar->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;
+
+ if(has_decode_delay_been_guessed(st))
+ 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_log(NULL, AV_LOG_TRACE,
- "OUTdelayed:%d/%d pts:%"PRId64", dts:%"PRId64" cur_dts:%"PRId64"\n",
- presentation_delayed, delay, pkt->pts, pkt->dts, st->cur_dts);
+ if (s->debug & FF_FDEBUG_TS)
+ av_log(s, AV_LOG_TRACE, "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->codecpar->codec_id))
@@ -825,10 +1404,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->internal->avctx,
@@ -836,6 +1420,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;
@@ -853,7 +1438,7 @@ static int parse_packet(AVFormatContext *s, AVPacket *pkt, int stream_index)
}
/* set the duration */
- out_pkt.duration = 0;
+ out_pkt.duration = (st->parser->flags & PARSER_FLAG_COMPLETE_FRAMES) ? pkt->duration : 0;
if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
if (st->internal->avctx->sample_rate > 0) {
out_pkt.duration =
@@ -869,26 +1454,24 @@ 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 ((ret = add_to_pktbuf(&s->internal->parse_queue, &out_pkt,
- &s->internal->parse_queue_end,
- 1))) {
- av_packet_unref(&out_pkt);
+ ret = add_to_pktbuf(&s->internal->parse_queue, &out_pkt,
+ &s->internal->parse_queue_end, 1);
+ av_packet_unref(&out_pkt);
+ if (ret < 0)
goto fail;
- }
}
/* end of the stream => close and free the parser */
@@ -917,6 +1500,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->codecpar->sample_rate, st->time_base.den);
+}
+
static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
{
int ret = 0, i, got_packet = 0;
@@ -946,37 +1534,74 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
ret = 0;
st = s->streams[cur_pkt.stream_index];
+ /* update context if required */
+ if (st->internal->need_context_update) {
+ if (avcodec_is_open(st->internal->avctx)) {
+ av_log(s, AV_LOG_DEBUG, "Demuxer context update while decoder is open, closing and trying to re-open\n");
+ avcodec_close(st->internal->avctx);
+ st->info->found_decoder = 0;
+ }
+
+ /* close parser, because it depends on the codec */
+ if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
+ av_parser_close(st->parser);
+ st->parser = NULL;
+ }
+
+ ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
+ if (ret < 0)
+ return ret;
+
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ /* update deprecated public codec context */
+ ret = avcodec_parameters_to_context(st->codec, st->codecpar);
+ if (ret < 0)
+ return ret;
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+ st->internal->need_context_update = 0;
+ }
+
if (cur_pkt.pts != AV_NOPTS_VALUE &&
cur_pkt.dts != AV_NOPTS_VALUE &&
cur_pkt.pts < cur_pkt.dts) {
av_log(s, AV_LOG_WARNING,
- "Invalid timestamps stream=%d, pts=%"PRId64", "
- "dts=%"PRId64", size=%d\n",
- 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=%"PRId64", 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=%"PRId64", 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->codecpar->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->codecpar->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);
@@ -987,15 +1612,80 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
} else if (st->discard < AVDISCARD_ALL) {
if ((ret = parse_packet(s, &cur_pkt, cur_pkt.stream_index)) < 0)
return ret;
+ st->codecpar->sample_rate = st->internal->avctx->sample_rate;
+ st->codecpar->bit_rate = st->internal->avctx->bit_rate;
+ st->codecpar->channels = st->internal->avctx->channels;
+ st->codecpar->channel_layout = st->internal->avctx->channel_layout;
+ st->codecpar->codec_id = st->internal->avctx->codec_id;
} else {
/* free packet */
av_packet_unref(&cur_pkt);
}
+ if (pkt->flags & AV_PKT_FLAG_KEY)
+ st->skip_to_keyframe = 0;
+ if (st->skip_to_keyframe) {
+ av_packet_unref(&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->start_skip_samples && (pkt->pts == 0 || pkt->pts == RELATIVE_TS_BASE))
+ st->skip_samples = st->start_skip_samples;
+ 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 / discard %d\n", st->skip_samples, discard_padding);
+ }
+ 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 FF_API_LAVF_MERGE_SD
+FF_DISABLE_DEPRECATION_WARNINGS
+ if (!(s->flags & AVFMT_FLAG_KEEP_SIDE_DATA))
+ av_packet_merge_side_data(pkt);
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+ }
+
av_opt_get_dict_val(s, "metadata", AV_OPT_SEARCH_CHILDREN, &metadata);
if (metadata) {
s->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
@@ -1010,9 +1700,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=%"PRId64", 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;
@@ -1022,15 +1714,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) {
@@ -1038,23 +1735,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);
@@ -1068,14 +1784,32 @@ int av_read_frame(AVFormatContext *s, AVPacket *pkt)
ret = add_to_pktbuf(&s->internal->packet_buffer, pkt,
&s->internal->packet_buffer_end, 1);
+ av_packet_unref(pkt);
if (ret < 0)
return ret;
}
+
+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);
@@ -1088,23 +1822,39 @@ 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 = INT_MIN;
if (s->nb_streams <= 0)
return -1;
for (i = 0; i < s->nb_streams; i++) {
+ int score = 0;
st = s->streams[i];
- if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
- !(st->disposition & AV_DISPOSITION_ATTACHED_PIC)) {
- return i;
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
+ score -= 400;
+ if (st->codecpar->width && st->codecpar->height)
+ score += 50;
+ score+= 25;
+ }
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ if (st->codecpar->sample_rate)
+ score += 50;
+ }
+ if (st->codec_info_nb_frames)
+ score += 12;
+
+ if (st->discard != AVDISCARD_ALL)
+ score += 200;
+
+ if (score > best_score) {
+ best_score = score;
+ best_stream = i;
}
- if (first_audio_index < 0 &&
- st->codecpar->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. */
@@ -1124,13 +1874,22 @@ 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;
+
+ st->skip_samples = 0;
}
}
@@ -1173,6 +1932,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) *
@@ -1188,7 +1956,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) {
@@ -1214,6 +1982,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);
@@ -1234,6 +2003,16 @@ int ff_index_search_timestamp(const AVIndexEntry *entries, int nb_entries,
while (b - a > 1) {
m = (a + b) >> 1;
+
+ // Search for the next non-discarded packet.
+ while ((entries[m].flags & AVINDEX_DISCARD_FRAME) && m < b && m < nb_entries - 1) {
+ m++;
+ if (m == b && entries[m].timestamp >= wanted_timestamp) {
+ m = b - 1;
+ break;
+ }
+ }
+
timestamp = entries[m].timestamp;
if (timestamp >= wanted_timestamp)
b = m;
@@ -1252,12 +2031,78 @@ int ff_index_search_timestamp(const AVIndexEntry *entries, int nb_entries,
return m;
}
+void ff_configure_buffers_for_index(AVFormatContext *s, int64_t time_tolerance)
+{
+ int ist1, ist2;
+ int64_t pos_delta = 0;
+ int64_t skip = 0;
+ //We could use URLProtocol flags here but as many user applications do not use URLProtocols this would be unreliable
+ const char *proto = avio_find_protocol_name(s->filename);
+
+ if (!proto) {
+ av_log(s, AV_LOG_INFO,
+ "Protocol name not provided, cannot determine if input is local or "
+ "a network protocol, buffers and access patterns cannot be configured "
+ "optimally without knowing the protocol\n");
+ }
+
+ if (proto && !(strcmp(proto, "file") && strcmp(proto, "pipe") && strcmp(proto, "cache")))
+ return;
+
+ for (ist1 = 0; ist1 < s->nb_streams; ist1++) {
+ AVStream *st1 = s->streams[ist1];
+ for (ist2 = 0; ist2 < s->nb_streams; ist2++) {
+ AVStream *st2 = s->streams[ist2];
+ int i1, i2;
+
+ if (ist1 == ist2)
+ continue;
+
+ for (i1 = i2 = 0; i1 < st1->nb_index_entries; i1++) {
+ AVIndexEntry *e1 = &st1->index_entries[i1];
+ int64_t e1_pts = av_rescale_q(e1->timestamp, st1->time_base, AV_TIME_BASE_Q);
+
+ skip = FFMAX(skip, e1->size);
+ for (; i2 < st2->nb_index_entries; i2++) {
+ AVIndexEntry *e2 = &st2->index_entries[i2];
+ int64_t e2_pts = av_rescale_q(e2->timestamp, st2->time_base, AV_TIME_BASE_Q);
+ if (e2_pts - e1_pts < time_tolerance)
+ continue;
+ pos_delta = FFMAX(pos_delta, e1->pos - e2->pos);
+ break;
+ }
+ }
+ }
+ }
+
+ pos_delta *= 2;
+ /* XXX This could be adjusted depending on protocol*/
+ if (s->pb->buffer_size < pos_delta && pos_delta < (1<<24)) {
+ av_log(s, AV_LOG_VERBOSE, "Reconfiguring buffers to size %"PRId64"\n", pos_delta);
+ ffio_set_buf_size(s->pb, pos_delta);
+ s->pb->short_seek_threshold = FFMAX(s->pb->short_seek_threshold, pos_delta/2);
+ }
+
+ if (skip < (1<<23)) {
+ s->pb->short_seek_threshold = FFMAX(s->pb->short_seek_threshold, skip);
+ }
+}
+
int av_index_search_timestamp(AVStream *st, int64_t wanted_timestamp, int flags)
{
return ff_index_search_timestamp(st->index_entries, st->nb_index_entries,
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)
{
@@ -1271,7 +2116,7 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index,
if (stream_index < 0)
return -1;
- av_log(s, AV_LOG_TRACE, "read_seek: %d %"PRId64"\n", stream_index, target_ts);
+ av_log(s, AV_LOG_TRACE, "read_seek: %d %s\n", stream_index, av_ts2str(target_ts));
ts_max =
ts_min = AV_NOPTS_VALUE;
@@ -1291,23 +2136,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_log(s, AV_LOG_TRACE, "using cached pos_min=0x%"PRIx64" dts_min=%"PRId64"\n",
- pos_min, ts_min);
+ av_log(s, AV_LOG_TRACE, "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_log(s, AV_LOG_TRACE, "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));
}
}
@@ -1320,11 +2165,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,
@@ -1333,55 +2217,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_log(s, AV_LOG_TRACE, "gen_seek: %d %"PRId64"\n", stream_index, target_ts);
+ av_log(s, AV_LOG_TRACE, "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_log(s, AV_LOG_TRACE, "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_log(s, AV_LOG_TRACE,
+ "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;
@@ -1404,20 +2276,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_log(s, AV_LOG_TRACE, "%"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_log(s, AV_LOG_TRACE, "%"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;
@@ -1431,12 +2303,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_log(s, AV_LOG_TRACE, "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_log(s, AV_LOG_TRACE, "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;
}
@@ -1456,6 +2330,8 @@ static int seek_frame_byte(AVFormatContext *s, int stream_index,
avio_seek(s->pb, pos, SEEK_SET);
+ s->io_repositioned = 1;
+
return 0;
}
@@ -1477,9 +2353,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;
@@ -1495,10 +2372,18 @@ static int seek_frame_generic(AVFormatContext *s, int stream_index,
} while (read_status == AVERROR(EAGAIN));
if (read_status < 0)
break;
- av_packet_unref(&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) {
+ av_packet_unref(&pkt);
break;
+ }
+ if (nonkey++ > 1000 && st->codecpar->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);
+ av_packet_unref(&pkt);
+ break;
+ }
+ }
+ av_packet_unref(&pkt);
}
index = av_index_search_timestamp(st, timestamp, flags);
}
@@ -1564,10 +2449,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;
}
@@ -1577,15 +2474,34 @@ 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);
+ stream_index = 0;
+ }
+
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;
}
@@ -1595,13 +2511,25 @@ 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
+}
+
+int avformat_flush(AVFormatContext *s)
+{
+ ff_read_frame_flush(s);
+ return 0;
}
/*******************************************************/
@@ -1633,25 +2561,42 @@ 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, end_time_text;
int64_t duration, duration1, filesize;
int i;
AVStream *st;
+ AVProgram *p;
start_time = INT64_MAX;
+ start_time_text = INT64_MAX;
end_time = INT64_MIN;
+ end_time_text = INT64_MIN;
duration = INT64_MIN;
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->start_time != AV_NOPTS_VALUE && st->time_base.den) {
start_time1 = av_rescale_q(st->start_time, st->time_base,
AV_TIME_BASE_Q);
- start_time = FFMIN(start_time, start_time1);
- if (st->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);
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE || st->codecpar->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_rescale_q_rnd(st->duration, st->time_base,
+ AV_TIME_BASE_Q,
+ AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
+ if (end_time1 != AV_NOPTS_VALUE && (end_time1 > 0 ? start_time1 <= INT64_MAX - end_time1 : start_time1 >= INT64_MIN - end_time1)) {
+ end_time1 += start_time1;
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE || st->codecpar->codec_type == AVMEDIA_TYPE_DATA)
+ end_time_text = FFMAX(end_time_text, end_time1);
+ else
+ 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) {
@@ -1660,17 +2605,42 @@ 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 (end_time == INT64_MIN || (end_time < end_time_text && end_time_text - end_time < AV_TIME_BASE)) {
+ end_time = end_time_text;
+ } else if (end_time < end_time_text) {
+ av_log(ic, AV_LOG_VERBOSE, "Ignoring outlier non primary stream endtime %f\n", end_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 > 1) {
+ 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 &&
+ p->end_time - (uint64_t)p->start_time <= INT64_MAX)
+ duration = FFMAX(duration, p->end_time - p->start_time);
+ }
+ } else if (end_time >= start_time && end_time - (uint64_t)start_time <= INT64_MAX) {
+ 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 > 0) {
+ /* compute the bitrate */
+ double bitrate = (double) filesize * 8.0 * AV_TIME_BASE /
+ (double) ic->duration;
+ if (bitrate >= 0 && bitrate <= INT64_MAX)
+ ic->bit_rate = bitrate;
}
}
@@ -1696,20 +2666,27 @@ 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 */
if (ic->bit_rate <= 0) {
- int bit_rate = 0;
+ int64_t bit_rate = 0;
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
+ if (st->codecpar->bit_rate <= 0 && st->internal->avctx->bit_rate > 0)
+ st->codecpar->bit_rate = st->internal->avctx->bit_rate;
if (st->codecpar->bit_rate > 0) {
- if (INT_MAX - st->codecpar->bit_rate < bit_rate) {
+ if (INT64_MAX - st->codecpar->bit_rate < bit_rate) {
bit_rate = 0;
break;
}
bit_rate += st->codecpar->bit_rate;
+ } else if (st->codecpar->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;
@@ -1719,29 +2696,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 6
/* 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;
@@ -1750,9 +2735,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->codecpar->codec_type != AVMEDIA_TYPE_UNKNOWN)
av_log(ic, 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);
@@ -1760,11 +2747,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;
@@ -1785,31 +2773,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_packet_unref(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->codecpar->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->codecpar->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;
}
}
@@ -1830,15 +2863,16 @@ static void estimate_timings(AVFormatContext *ic, int64_t old_offset)
file_size && (ic->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
/* 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);
@@ -1847,46 +2881,63 @@ static void estimate_timings(AVFormatContext *ic, int64_t old_offset)
AVStream av_unused *st;
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
- av_log(ic, AV_LOG_TRACE, "%d: start_time: %0.3f duration: %0.3f\n", i,
- (double) st->start_time / AV_TIME_BASE,
- (double) st->duration / AV_TIME_BASE);
+ av_log(ic, AV_LOG_TRACE, "stream %d: start_time: %0.3f duration: %0.3f\n", i,
+ (double) st->start_time * av_q2d(st->time_base),
+ (double) st->duration * av_q2d(st->time_base));
}
av_log(ic, AV_LOG_TRACE,
- "stream: start_time: %0.3f duration: %0.3f bitrate=%d kb/s\n",
+ "format: start_time: %0.3f duration: %0.3f bitrate=%"PRId64" kb/s\n",
(double) ic->start_time / AV_TIME_BASE,
(double) ic->duration / AV_TIME_BASE,
- ic->bit_rate / 1000);
+ (int64_t)ic->bit_rate / 1000);
}
}
-static int has_codec_parameters(AVStream *st)
+static int has_codec_parameters(AVStream *st, const char **errmsg_ptr)
{
AVCodecContext *avctx = st->internal->avctx;
- 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->codecpar->codec_id == AV_CODEC_ID_RV30 || st->codecpar->codec_id == AV_CODEC_ID_RV40)
+ if (!st->sample_aspect_ratio.num && !st->codecpar->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->internal->avctx->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 */
@@ -1897,25 +2948,23 @@ static int try_decode_frame(AVFormatContext *s, AVStream *st, AVPacket *avpkt,
const AVCodec *codec;
int got_picture = 1, ret = 0;
AVFrame *frame = av_frame_alloc();
+ AVSubtitle subtitle;
AVPacket pkt = *avpkt;
+ int do_skip_frame = 0;
+ enum AVDiscard skip_frame;
if (!frame)
return AVERROR(ENOMEM);
- if (!avcodec_is_open(avctx) && !st->info->found_decoder) {
+ if (!avcodec_is_open(avctx) &&
+ st->info->found_decoder <= 0 &&
+ (st->codecpar->codec_id != -st->info->found_decoder || !st->codecpar->codec_id)) {
AVDictionary *thread_opt = NULL;
-#if FF_API_LAVF_AVCTX
-FF_DISABLE_DEPRECATION_WARNINGS
- codec = st->codec->codec ? st->codec->codec
- : avcodec_find_decoder(st->codecpar->codec_id);
-FF_ENABLE_DEPRECATION_WARNINGS
-#else
- codec = avcodec_find_decoder(st->codecpar->codec_id);
-#endif
+ codec = find_probe_decoder(s, st, st->codecpar->codec_id);
if (!codec) {
- st->info->found_decoder = -1;
+ st->info->found_decoder = -st->codecpar->codec_id;
ret = -1;
goto fail;
}
@@ -1923,11 +2972,13 @@ FF_ENABLE_DEPRECATION_WARNINGS
/* 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(avctx, codec, options ? options : &thread_opt);
if (!options)
av_dict_free(&thread_opt);
if (ret < 0) {
- st->info->found_decoder = -1;
+ st->info->found_decoder = -avctx->codec_id;
goto fail;
}
st->info->found_decoder = 1;
@@ -1939,9 +2990,15 @@ FF_ENABLE_DEPRECATION_WARNINGS
goto fail;
}
+ if (avpriv_codec_get_cap_skip_frame_fill_param(avctx->codec)) {
+ do_skip_frame = 1;
+ skip_frame = avctx->skip_frame;
+ avctx->skip_frame = AVDISCARD_ALL;
+ }
+
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 &&
(avctx->codec->capabilities & AV_CODEC_CAP_CHANNEL_CONF)))) {
got_picture = 0;
@@ -1957,15 +3014,27 @@ FF_ENABLE_DEPRECATION_WARNINGS
got_picture = 1;
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
ret = 0;
+ } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+ ret = avcodec_decode_subtitle2(avctx, &subtitle,
+ &got_picture, &pkt);
+ if (ret >= 0)
+ pkt.size = 0;
}
if (ret >= 0) {
if (got_picture)
- st->info->nb_decoded_frames++;
+ st->nb_decoded_frames++;
ret = got_picture;
}
}
+ if (!pkt.data && !got_picture)
+ ret = -1;
+
fail:
+ if (do_skip_frame) {
+ avctx->skip_frame = skip_frame;
+ }
+
av_frame_free(&frame);
return ret;
}
@@ -1994,6 +3063,9 @@ 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)
{
+ if (bps <= 0 || bps > 64)
+ return AV_CODEC_ID_NONE;
+
if (flt) {
switch (bps) {
case 32:
@@ -2004,6 +3076,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) {
@@ -2015,6 +3088,8 @@ enum AVCodecID ff_get_pcm_codec_id(int bps, int flt, int be, int sflags)
return be ? AV_CODEC_ID_PCM_S24BE : AV_CODEC_ID_PCM_S24LE;
case 4:
return be ? AV_CODEC_ID_PCM_S32BE : AV_CODEC_ID_PCM_S32LE;
+ case 8:
+ return be ? AV_CODEC_ID_PCM_S64BE : AV_CODEC_ID_PCM_S64LE;
default:
return AV_CODEC_ID_NONE;
}
@@ -2037,11 +3112,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;
}
@@ -2060,7 +3149,10 @@ enum AVCodecID av_codec_get_id(const AVCodecTag *const *tags, unsigned int tag)
static void compute_chapters_end(AVFormatContext *s)
{
unsigned int i, j;
- int64_t max_time = s->duration +
+ int64_t max_time = 0;
+
+ if (s->duration > 0 && s->start_time < INT64_MAX - s->duration)
+ max_time = s->duration +
((s->start_time == AV_NOPTS_VALUE) ? 0 : s->start_time);
for (i = 0; i < s->nb_chapters; i++)
@@ -2077,38 +3169,243 @@ static void compute_chapters_end(AVFormatContext *s)
if (j != i && next_start > ch->start && next_start < end)
end = next_start;
}
- ch->end = (end == INT64_MAX) ? ch->start : end;
+ ch->end = (end == INT64_MAX || end < ch->start) ? ch->start : end;
}
}
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 < 30)
+ return (i + 31) * 1001 * 12;
+ i -= 30;
+
+ if (i < 3)
+ return ((const int[]) { 80, 120, 240})[i] * 1001 * 12;
+
+ i -= 3;
+
+ return ((const int[]) { 24, 30, 60, 12, 15, 48 })[i] * 1000 * 12;
}
-static int extract_extradata_init(AVStream *st)
+/* 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_HEVC ||
+ c->codec_id == AV_CODEC_ID_H264)
+ return 1;
+ return 0;
+}
+
+int ff_alloc_extradata(AVCodecParameters *par, int size)
{
- AVStreamInternal *i = st->internal;
- const AVBitStreamFilter *f;
int ret;
+ if (size < 0 || size >= INT32_MAX - AV_INPUT_BUFFER_PADDING_SIZE) {
+ par->extradata = NULL;
+ par->extradata_size = 0;
+ return AVERROR(EINVAL);
+ }
+ par->extradata = av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (par->extradata) {
+ memset(par->extradata + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+ par->extradata_size = size;
+ ret = 0;
+ } else {
+ par->extradata_size = 0;
+ ret = AVERROR(ENOMEM);
+ }
+ return ret;
+}
+
+int ff_get_extradata(AVFormatContext *s, AVCodecParameters *par, AVIOContext *pb, int size)
+{
+ int ret = ff_alloc_extradata(par, size);
+ if (ret < 0)
+ return ret;
+ ret = avio_read(pb, par->extradata, size);
+ if (ret != size) {
+ av_freep(&par->extradata);
+ par->extradata_size = 0;
+ av_log(s, 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->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
+ continue;
+ // the check for tb_unreliable() is not completely correct, since this is not about handling
+ // an unreliable/inexact time base, but a time base that is finer than necessary, as e.g.
+ // ipmovie.c produces.
+ if (tb_unreliable(st->internal->avctx) && 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->internal->avctx)) {
+ 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*11.5)/get_std_framerate(j))
+ continue;
+ if (!st->info->codec_info_duration && get_std_framerate(j) < 1001*12)
+ 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(ic, 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;
+ }
+}
+
+static int extract_extradata_check(AVStream *st)
+{
+ const AVBitStreamFilter *f;
+
f = av_bsf_get_by_name("extract_extradata");
if (!f)
- goto finish;
+ return 0;
- /* check that the codec id is supported */
if (f->codec_ids) {
const enum AVCodecID *ids;
for (ids = f->codec_ids; *ids != AV_CODEC_ID_NONE; ids++)
if (*ids == st->codecpar->codec_id)
- break;
- if (*ids == AV_CODEC_ID_NONE)
- goto finish;
+ return 1;
}
+ return 0;
+}
+
+static int extract_extradata_init(AVStream *st)
+{
+ AVStreamInternal *i = st->internal;
+ const AVBitStreamFilter *f;
+ int ret;
+
+ f = av_bsf_get_by_name("extract_extradata");
+ if (!f)
+ goto finish;
+
+ /* check that the codec id is supported */
+ ret = extract_extradata_check(st);
+ if (!ret)
+ goto finish;
+
i->extract_extradata.pkt = av_packet_alloc();
if (!i->extract_extradata.pkt)
return AVERROR(ENOMEM);
@@ -2199,13 +3496,41 @@ static int extract_extradata(AVStream *st, AVPacket *pkt)
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
{
- int i, count, ret, read_size, j;
+ int i, count = 0, ret = 0, j;
+ int64_t read_size;
AVStream *st;
AVCodecContext *avctx;
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_duration;
+ int64_t max_stream_analyze_duration;
+ int64_t max_subtitle_analyze_duration;
+ int64_t probesize = ic->probesize;
+ int eof_reached = 0;
+ int *missing_streams = av_opt_ptr(ic->iformat->priv_class, ic->priv_data, "missing_streams");
+
+ flush_codecs = probesize > 0;
+
+ av_opt_set(ic, "skip_clear", "1", AV_OPT_SEARCH_CHILDREN);
+
+ max_stream_analyze_duration = max_analyze_duration;
+ max_subtitle_analyze_duration = max_analyze_duration;
+ if (!max_analyze_duration) {
+ max_stream_analyze_duration =
+ max_analyze_duration = 5*AV_TIME_BASE;
+ max_subtitle_analyze_duration = 30*AV_TIME_BASE;
+ if (!strcmp(ic->iformat->name, "flv"))
+ max_stream_analyze_duration = 90*AV_TIME_BASE;
+ if (!strcmp(ic->iformat->name, "mpeg") || !strcmp(ic->iformat->name, "mpegts"))
+ max_stream_analyze_duration = 7*AV_TIME_BASE;
+ }
+
+ if (ic->pb)
+ av_log(ic, AV_LOG_DEBUG, "Before avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d nb_streams:%d\n",
+ avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count, ic->nb_streams);
for (i = 0; i < ic->nb_streams; i++) {
const AVCodec *codec;
@@ -2213,11 +3538,12 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
st = ic->streams[i];
avctx = st->internal->avctx;
- // only for the split stuff
- if (!st->parser && !(ic->flags & AVFMT_FLAG_NOPARSE)) {
- st->parser = av_parser_init(st->codecpar->codec_id);
- if (st->need_parsing == AVSTREAM_PARSE_HEADERS && st->parser)
- st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
+ st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+/* if (!st->time_base.num)
+ st->time_base = */
+ if (!avctx->time_base.num)
+ avctx->time_base = st->time_base;
}
/* check if the caller has overridden the codec id */
@@ -2230,53 +3556,70 @@ FF_DISABLE_DEPRECATION_WARNINGS
}
FF_ENABLE_DEPRECATION_WARNINGS
#endif
+ // only for the split stuff
+ if (!st->parser && !(ic->flags & AVFMT_FLAG_NOPARSE) && st->request_probe <= 0) {
+ st->parser = av_parser_init(st->codecpar->codec_id);
+ 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->codecpar->codec_id));
+ }
+ }
+
if (st->codecpar->codec_id != st->internal->orig_codec_id)
st->internal->orig_codec_id = st->codecpar->codec_id;
ret = avcodec_parameters_to_context(avctx, st->codecpar);
if (ret < 0)
goto find_stream_info_err;
- if (st->codecpar->codec_id != AV_CODEC_ID_PROBE &&
- st->codecpar->codec_id != AV_CODEC_ID_NONE)
+ if (st->request_probe <= 0)
st->internal->avctx_inited = 1;
-#if FF_API_LAVF_AVCTX
-FF_DISABLE_DEPRECATION_WARNINGS
- codec = st->codec->codec ? st->codec->codec
- : avcodec_find_decoder(st->codecpar->codec_id);
-FF_ENABLE_DEPRECATION_WARNINGS
-#else
- codec = avcodec_find_decoder(st->codecpar->codec_id);
-#endif
+ codec = find_probe_decoder(ic, st, st->codecpar->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->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE
- && codec && !avctx->codec)
- avcodec_open2(avctx, codec,
- options ? &options[i] : &thread_opt);
+ && codec && !avctx->codec) {
+ if (avcodec_open2(avctx, codec, options ? &options[i] : &thread_opt) < 0)
+ av_log(ic, AV_LOG_WARNING,
+ "Failed to open codec in %s\n",__FUNCTION__);
+ }
// 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 && !avctx->codec)
- avcodec_open2(avctx, codec,
- options ? &options[i] : &thread_opt);
+ if (avcodec_open2(avctx, codec, options ? &options[i] : &thread_opt) < 0)
+ av_log(ic, AV_LOG_WARNING,
+ "Failed to open codec in %s\n",__FUNCTION__);
}
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;
}
- 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");
@@ -2288,46 +3631,67 @@ FF_ENABLE_DEPRECATION_WARNINGS
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->internal->avctx))
+ 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 &&
- st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
- break;
- if (!st->codecpar->extradata &&
- !st->internal->avctx->extradata &&
+ if (!(st->r_frame_rate.num && st->avg_frame_rate.num) &&
+ st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ int count = (ic->iformat->flags & AVFMT_NOTIMESTAMPS) ?
+ st->info->codec_info_duration_fields/2 :
+ st->info->duration_count;
+ if (count < fps_analyze_framecount)
+ break;
+ }
+ if (!st->internal->avctx->extradata &&
(!st->internal->extract_extradata.inited ||
- st->internal->extract_extradata.bsf))
+ st->internal->extract_extradata.bsf) &&
+ extract_extradata_check(st))
break;
if (st->first_dts == AV_NOPTS_VALUE &&
- st->codec_info_nb_frames < ic->max_ts_probe &&
+ !(ic->iformat->flags & AVFMT_NOTIMESTAMPS) &&
+ st->codec_info_nb_frames < ((st->disposition & AV_DISPOSITION_ATTACHED_PIC) ? 1 : ic->max_ts_probe) &&
(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))
break;
}
+ analyzed_all_streams = 0;
+ if (!missing_streams || !*missing_streams)
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]->codecpar->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;
}
@@ -2339,36 +3703,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
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(ic, 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->internal->avctx, 0);
- av_log(ic, AV_LOG_WARNING,
- "Could not find codec parameters (%s)\n", buf);
- } else {
- ret = 0;
- }
- }
+ eof_reached = 1;
break;
}
@@ -2381,9 +3716,10 @@ FF_ENABLE_DEPRECATION_WARNINGS
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;
+
avctx = st->internal->avctx;
if (!st->internal->avctx_inited) {
ret = avcodec_parameters_to_context(avctx, st->codecpar);
@@ -2396,7 +3732,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
/* 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,
@@ -2430,17 +3766,46 @@ FF_ENABLE_DEPRECATION_WARNINGS
}
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;
+ int64_t limit;
+
+ 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 (analyzed_all_streams) limit = max_analyze_duration;
+ else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) limit = max_subtitle_analyze_duration;
+ else limit = max_stream_analyze_duration;
+
+ if (t >= limit) {
+ av_log(ic, AV_LOG_VERBOSE, "max_analyze_duration %"PRId64" reached at %"PRId64" microseconds st:%d\n",
+ limit,
+ t, pkt->stream_index);
if (ic->flags & AVFMT_FLAG_NOBUFFER)
av_packet_unref(pkt);
break;
}
+ if (pkt->duration) {
+ if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE && pkt->pts != AV_NOPTS_VALUE && pkt->pts >= st->start_time) {
+ st->info->codec_info_duration = FFMIN(pkt->pts - st->start_time, st->info->codec_info_duration + pkt->duration);
+ } else
+ st->info->codec_info_duration += pkt->duration;
+ st->info->codec_info_duration_fields += st->parser && st->need_parsing && avctx->ticks_per_frame ==2 ? st->parser->repeat_pict + 1 : 2;
+ }
}
+#if FF_API_R_FRAME_RATE
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+ ff_rfps_add_frame(ic, st, pkt->dts);
+#endif
if (!st->internal->avctx->extradata) {
ret = extract_extradata(st, pkt);
if (ret < 0)
@@ -2466,32 +3831,90 @@ FF_ENABLE_DEPRECATION_WARNINGS
count++;
}
+ if (eof_reached) {
+ int stream_index;
+ for (stream_index = 0; stream_index < ic->nb_streams; stream_index++) {
+ st = ic->streams[stream_index];
+ avctx = st->internal->avctx;
+ if (!has_codec_parameters(st, NULL)) {
+ const AVCodec *codec = find_probe_decoder(ic, st, st->codecpar->codec_id);
+ if (codec && !avctx->codec) {
+ AVDictionary *opts = NULL;
+ if (ic->codec_whitelist)
+ av_dict_set(&opts, "codec_whitelist", ic->codec_whitelist, 0);
+ if (avcodec_open2(avctx, codec, (options && stream_index < orig_nb_streams) ? &options[stream_index] : &opts) < 0)
+ av_log(ic, AV_LOG_WARNING,
+ "Failed to open codec in %s\n",__FUNCTION__);
+ av_dict_free(&opts);
+ }
+ }
+
+ // EOF already reached while reading the stream above.
+ // So continue with reoordering DTS with whatever delay we have.
+ if (ic->internal->packet_buffer && !has_decode_delay_been_guessed(st)) {
+ update_dts_from_pts(ic, stream_index, ic->internal->packet_buffer);
+ }
+ }
+ }
+
+ 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->internal->avctx);
}
+
+ ff_rfps_calculate(ic);
+
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
avctx = st->internal->avctx;
if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (avctx->codec_id == AV_CODEC_ID_RAWVIDEO && !avctx->codec_tag && !avctx->bits_per_coded_sample) {
+ uint32_t tag= avcodec_pix_fmt_to_codec_tag(avctx->pix_fmt);
+ if (avpriv_find_pix_fmt(avpriv_get_raw_pix_fmt_tags(), tag) == avctx->pix_fmt)
+ avctx->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;
+ AVRational codec_frame_rate = avctx->framerate;
- 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. */
@@ -2504,11 +3927,36 @@ FF_ENABLE_DEPRECATION_WARNINGS
best_error = error;
best_fps = std_fps.num;
}
+
+ if (ic->internal->prefer_codec_framerate && codec_frame_rate.num > 0 && codec_frame_rate.den > 0) {
+ error = fabs(av_q2d(codec_frame_rate) /
+ av_q2d(std_fps) - 1);
+ if (error < best_error) {
+ best_error = error;
+ best_fps = std_fps.num;
+ }
+ }
}
if (best_fps)
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 ( avctx->time_base.den * (int64_t) st->time_base.num
+ <= avctx->time_base.num * avctx->ticks_per_frame * (int64_t) st->time_base.den) {
+ av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den,
+ avctx->time_base.den, (int64_t)avctx->time_base.num * avctx->ticks_per_frame, INT_MAX);
+ } 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 = { avctx->height, avctx->width };
+ st->sample_aspect_ratio = av_mul_q(st->display_aspect_ratio,
+ hw_ratio);
+ }
} else if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) {
if (!avctx->bits_per_coded_sample)
avctx->bits_per_coded_sample =
@@ -2534,17 +3982,57 @@ FF_ENABLE_DEPRECATION_WARNINGS
}
}
+ 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 no packet was ever seen, update context now for has_codec_parameters */
+ if (!st->internal->avctx_inited) {
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
+ st->codecpar->format == AV_SAMPLE_FMT_NONE)
+ st->codecpar->format = st->internal->avctx->sample_fmt;
+ ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
+ if (ret < 0)
+ goto find_stream_info_err;
+ }
+ if (!has_codec_parameters(st, &errmsg)) {
+ char buf[256];
+ avcodec_string(buf, sizeof(buf), st->internal->avctx, 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);
/* update the stream parameters from the internal codec contexts */
for (i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
- if (!st->internal->avctx_inited)
- continue;
- ret = avcodec_parameters_from_context(st->codecpar, st->internal->avctx);
- if (ret < 0)
- goto find_stream_info_err;
+ if (st->internal->avctx_inited) {
+ int orig_w = st->codecpar->width;
+ int orig_h = st->codecpar->height;
+ ret = avcodec_parameters_from_context(st->codecpar, st->internal->avctx);
+ if (ret < 0)
+ goto find_stream_info_err;
+ // The decoder might reduce the video size by the lowres factor.
+ if (av_codec_get_lowres(st->internal->avctx) && orig_w) {
+ st->codecpar->width = orig_w;
+ st->codecpar->height = orig_h;
+ }
+ }
#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS
@@ -2552,6 +4040,20 @@ FF_DISABLE_DEPRECATION_WARNINGS
if (ret < 0)
goto find_stream_info_err;
+ // The old API (AVStream.codec) "requires" the resolution to be adjusted
+ // by the lowres factor.
+ if (av_codec_get_lowres(st->internal->avctx) && st->internal->avctx->width) {
+ av_codec_set_lowres(st->codec, av_codec_get_lowres(st->internal->avctx));
+ st->codec->width = st->internal->avctx->width;
+ st->codec->height = st->internal->avctx->height;
+ }
+
+ if (st->codec->codec_tag != MKTAG('t','m','c','d')) {
+ st->codec->time_base = st->internal->avctx->time_base;
+ st->codec->ticks_per_frame = st->internal->avctx->ticks_per_frame;
+ }
+ st->codec->framerate = st->avg_frame_rate;
+
if (st->internal->avctx->subtitle_header) {
st->codec->subtitle_header = av_malloc(st->internal->avctx->subtitle_header_size);
if (!st->codec->subtitle_header)
@@ -2560,31 +4062,46 @@ FF_DISABLE_DEPRECATION_WARNINGS
memcpy(st->codec->subtitle_header, st->internal->avctx->subtitle_header,
st->codec->subtitle_header_size);
}
+
+ // Fields unavailable in AVCodecParameters
+ st->codec->coded_width = st->internal->avctx->coded_width;
+ st->codec->coded_height = st->internal->avctx->coded_height;
+ st->codec->properties = st->internal->avctx->properties;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
st->internal->avctx_inited = 0;
}
- estimate_timings(ic, old_offset);
-
find_stream_info_err:
for (i = 0; i < ic->nb_streams; i++) {
+ st = ic->streams[i];
+ if (st->info)
+ av_freep(&st->info->duration_error);
av_freep(&ic->streams[i]->info);
av_bsf_free(&ic->streams[i]->internal->extract_extradata.bsf);
av_packet_free(&ic->streams[i]->internal->extract_extradata.pkt);
}
+ 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;
}
@@ -2593,12 +4110,16 @@ 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;
+ int best_count = -1, best_multiframe = -1, best_disposition = -1;
+ int count, multiframe, disposition;
+ int64_t best_bitrate = -1;
+ int64_t bitrate;
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;
@@ -2612,20 +4133,29 @@ 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 |
- AV_DISPOSITION_VISUAL_IMPAIRED))
+ if (type == AVMEDIA_TYPE_AUDIO && !(par->channels && par->sample_rate))
continue;
if (decoder_ret) {
- decoder = avcodec_find_decoder(par->codec_id);
+ decoder = find_decoder(ic, st, par->codec_id);
if (!decoder) {
if (ret < 0)
ret = AVERROR_DECODER_NOT_FOUND;
continue;
}
}
- if (best_count >= st->codec_info_nb_frames)
+ disposition = !(st->disposition & (AV_DISPOSITION_HEARING_IMPAIRED | AV_DISPOSITION_VISUAL_IMPAIRED));
+ count = st->codec_info_nb_frames;
+ bitrate = par->bit_rate;
+ multiframe = FFMIN(5, count);
+ if ((best_disposition > disposition) ||
+ (best_disposition == disposition && best_multiframe > multiframe) ||
+ (best_disposition == disposition && best_multiframe == multiframe && best_bitrate > bitrate) ||
+ (best_disposition == disposition && best_multiframe == multiframe && best_bitrate == bitrate && best_count >= count))
continue;
- best_count = st->codec_info_nb_frames;
+ best_disposition = disposition;
+ best_count = count;
+ best_bitrate = bitrate;
+ best_multiframe = multiframe;
ret = real_stream_index;
best_decoder = decoder;
if (program && i == nb_streams - 1 && ret < 0) {
@@ -2636,7 +4166,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;
}
@@ -2660,6 +4190,63 @@ int av_read_pause(AVFormatContext *s)
return AVERROR(ENOSYS);
}
+int ff_stream_encode_params_copy(AVStream *dst, const AVStream *src)
+{
+ int ret, i;
+
+ dst->id = src->id;
+ dst->time_base = src->time_base;
+ dst->nb_frames = src->nb_frames;
+ dst->disposition = src->disposition;
+ dst->sample_aspect_ratio = src->sample_aspect_ratio;
+ dst->avg_frame_rate = src->avg_frame_rate;
+ dst->r_frame_rate = src->r_frame_rate;
+
+ av_dict_free(&dst->metadata);
+ ret = av_dict_copy(&dst->metadata, src->metadata, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = avcodec_parameters_copy(dst->codecpar, src->codecpar);
+ if (ret < 0)
+ return ret;
+
+ /* Free existing side data*/
+ for (i = 0; i < dst->nb_side_data; i++)
+ av_free(dst->side_data[i].data);
+ av_freep(&dst->side_data);
+ dst->nb_side_data = 0;
+
+ /* Copy side data if present */
+ if (src->nb_side_data) {
+ dst->side_data = av_mallocz_array(src->nb_side_data,
+ sizeof(AVPacketSideData));
+ if (!dst->side_data)
+ return AVERROR(ENOMEM);
+ dst->nb_side_data = src->nb_side_data;
+
+ for (i = 0; i < src->nb_side_data; i++) {
+ uint8_t *data = av_memdup(src->side_data[i].data,
+ src->side_data[i].size);
+ if (!data)
+ return AVERROR(ENOMEM);
+ dst->side_data[i].type = src->side_data[i].type;
+ dst->side_data[i].size = src->side_data[i].size;
+ dst->side_data[i].data = data;
+ }
+ }
+
+ av_freep(&dst->recommended_encoder_configuration);
+ if (src->recommended_encoder_configuration) {
+ const char *conf_str = src->recommended_encoder_configuration;
+ dst->recommended_encoder_configuration = av_strdup(conf_str);
+ if (!dst->recommended_encoder_configuration)
+ return AVERROR(ENOMEM);
+ }
+
+ return 0;
+}
+
static void free_stream(AVStream **pst)
{
AVStream *st = *pst;
@@ -2680,6 +4267,10 @@ static void free_stream(AVStream **pst)
if (st->internal) {
avcodec_free_context(&st->internal->avctx);
+ for (i = 0; i < st->internal->nb_bsfcs; i++) {
+ av_bsf_free(&st->internal->bsfcs[i]);
+ av_freep(&st->internal->bsfcs);
+ }
av_bsf_free(&st->internal->extract_extradata.bsf);
av_packet_free(&st->internal->extract_extradata.pkt);
}
@@ -2688,20 +4279,30 @@ static void free_stream(AVStream **pst)
av_dict_free(&st->metadata);
avcodec_parameters_free(&st->codecpar);
av_freep(&st->probe_data.buf);
- av_free(st->index_entries);
+ av_freep(&st->index_entries);
#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS
- av_free(st->codec->extradata);
- av_free(st->codec->subtitle_header);
- av_free(st->codec);
+ avcodec_free_context(&st->codec);
FF_ENABLE_DEPRECATION_WARNINGS
#endif
- av_free(st->priv_data);
- av_free(st->info);
+ 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(&st->priv_pts);
av_freep(pst);
}
+void ff_free_stream(AVFormatContext *s, AVStream *st)
+{
+ av_assert0(s->nb_streams>0);
+ av_assert0(s->streams[ s->nb_streams - 1 ] == st);
+
+ free_stream(&s->streams[ --s->nb_streams ]);
+}
+
void avformat_free_context(AVFormatContext *s)
{
int i;
@@ -2712,9 +4313,12 @@ 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 = s->nb_streams - 1; i >= 0; i--)
+ ff_free_stream(s, s->streams[i]);
- for (i = 0; i < s->nb_streams; i++)
- free_stream(&s->streams[i]);
for (i = s->nb_programs - 1; i >= 0; i--) {
av_dict_free(&s->programs[i]->metadata);
@@ -2725,21 +4329,29 @@ 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_dict_free(&s->internal->id3v2_meta);
av_freep(&s->streams);
+ flush_packet_queue(s);
av_freep(&s->internal);
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;
@@ -2760,12 +4372,17 @@ 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 >= FFMIN(s->max_streams, INT_MAX/sizeof(*streams))) {
+ if (s->max_streams < INT_MAX/sizeof(*streams))
+ av_log(s, AV_LOG_ERROR, "Number of streams exceeds max_streams parameter (%d), see the documentation if you wish to increase it\n", s->max_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)
@@ -2774,6 +4391,7 @@ AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)
av_free(st);
return NULL;
}
+ st->info->last_dts = AV_NOPTS_VALUE;
#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS
@@ -2790,6 +4408,14 @@ FF_ENABLE_DEPRECATION_WARNINGS
if (!st->internal)
goto fail;
+ st->codecpar = avcodec_parameters_alloc();
+ if (!st->codecpar)
+ goto fail;
+
+ st->internal->avctx = avcodec_alloc_context3(NULL);
+ if (!st->internal->avctx)
+ goto fail;
+
if (s->iformat) {
#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS
@@ -2804,37 +4430,35 @@ FF_ENABLE_DEPRECATION_WARNINGS
* 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 = RELATIVE_TS_BASE;
} else {
st->cur_dts = AV_NOPTS_VALUE;
}
- st->codecpar = avcodec_parameters_alloc();
- if (!st->codecpar)
- goto fail;
-
- st->internal->avctx = avcodec_alloc_context3(NULL);
- if (!st->internal->avctx)
- goto fail;
-
st->index = s->nb_streams;
st->start_time = AV_NOPTS_VALUE;
st->duration = AV_NOPTS_VALUE;
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;
-#if FF_API_LAVF_AVCTX
- st->internal->need_codec_update = 1;
-#endif
+ st->inject_global_side_data = s->internal->inject_global_side_data;
+
+ st->internal->need_context_update = 1;
s->streams[s->nb_streams++] = st;
return st;
@@ -2862,6 +4486,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;
}
@@ -2872,6 +4501,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];
@@ -2891,10 +4525,11 @@ AVChapter *avpriv_new_chapter(AVFormatContext *s, int id, AVRational time_base,
return chapter;
}
-void ff_program_add_stream_index(AVFormatContext *ac, int progid, unsigned idx)
+void av_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);
@@ -2909,12 +4544,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;
}
@@ -2925,7 +4558,7 @@ uint64_t ff_ntp_time(void)
return (av_gettime() / 1000) * 1000 + NTP_OFFSET_US;
}
-int av_get_frame_filename(char *buf, int buf_size, const char *path, int number)
+int av_get_frame_filename2(char *buf, int buf_size, const char *path, int number, int flags)
{
const char *p;
char *q, buf1[20], c;
@@ -2950,9 +4583,11 @@ int av_get_frame_filename(char *buf, int buf_size, const char *path, int number)
case '%':
goto addchar;
case 'd':
- if (percentd_found)
+ if (!(flags & AV_FRAME_FILENAME_FLAGS_MULTIPLE) && 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)
@@ -2978,12 +4613,17 @@ fail:
return -1;
}
+int av_get_frame_filename(char *buf, int buf_size, const char *path, int number)
+{
+ return av_get_frame_filename2(buf, buf_size, path, number, 0);
+}
+
void av_url_split(char *proto, int proto_size,
char *authorization, int authorization_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;
@@ -3012,8 +4652,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
@@ -3022,9 +4665,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 '@' */
}
@@ -3109,11 +4753,18 @@ 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;
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ av_codec_set_pkt_timebase(s->codec, new_tb);
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+ av_codec_set_pkt_timebase(s->internal->avctx, new_tb);
s->pts_wrap_bits = pts_wrap_bits;
}
@@ -3180,29 +4831,19 @@ int ff_find_stream_index(AVFormatContext *s, int id)
return -1;
}
-int64_t ff_iso8601_to_unix_time(const char *datestr)
-{
- struct tm time1 = { 0 }, time2 = { 0 };
- const char *ret1, *ret2;
- ret1 = av_small_strptime(datestr, "%Y - %m - %d %T", &time1);
- ret2 = av_small_strptime(datestr, "%Y - %m - %dT%T", &time2);
- if (ret2 && !ret1)
- return av_timegm(&time2);
- else
- return av_timegm(&time1);
-}
-
int avformat_query_codec(const AVOutputFormat *ofmt, enum AVCodecID codec_id,
int std_compliance)
{
if (ofmt) {
+ unsigned int codec_tag;
if (ofmt->query_codec)
return ofmt->query_codec(codec_id, std_compliance);
else if (ofmt->codec_tag)
- return !!av_codec_get_tag(ofmt->codec_tag, codec_id);
+ return !!av_codec_get_tag2(ofmt->codec_tag, codec_id, &codec_tag);
else if (codec_id == ofmt->video_codec ||
codec_id == ofmt->audio_codec ||
- codec_id == ofmt->subtitle_codec)
+ codec_id == ofmt->subtitle_codec ||
+ codec_id == ofmt->data_codec)
return 1;
}
return AVERROR_PATCHWELCOME;
@@ -3215,7 +4856,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;
}
@@ -3225,6 +4867,7 @@ int avformat_network_deinit(void)
#if CONFIG_NETWORK
ff_network_close();
ff_tls_deinit();
+ ff_network_inited_globally = 0;
#endif
return 0;
}
@@ -3271,6 +4914,207 @@ 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->codecpar ? stream->codecpar->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->internal->avctx->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->internal->avctx->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' || *spec == 'V') { /* opt:[vasdtV] */
+ enum AVMediaType type;
+ int nopic = 0;
+
+ 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;
+ case 'V': type = AVMEDIA_TYPE_VIDEO; nopic = 1; break;
+ default: av_assert0(0);
+ }
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ if (type != st->codecpar->codec_type
+ && (st->codecpar->codec_type != AVMEDIA_TYPE_UNKNOWN || st->codec->codec_type != type))
+ return 0;
+FF_ENABLE_DEPRECATION_WARNINGS
+#else
+ if (type != st->codecpar->codec_type)
+ return 0;
+#endif
+ if (nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC))
+ return 0;
+ if (*spec++ == ':') { /* possibly followed by :index */
+ int i, index = strtol(spec, NULL, 0);
+ for (i = 0; i < s->nb_streams; i++) {
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ if ((s->streams[i]->codecpar->codec_type == type
+ || s->streams[i]->codec->codec_type == type
+ ) &&
+ !(nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC)) &&
+ index-- == 0)
+ return i == st->index;
+FF_ENABLE_DEPRECATION_WARNINGS
+#else
+ if ((s->streams[i]->codecpar->codec_type == type) &&
+ !(nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC)) &&
+ index-- == 0)
+ return i == st->index;
+#endif
+ }
+ 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 == 'u') {
+ AVCodecParameters *par = st->codecpar;
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ AVCodecContext *codec = st->codec;
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+ int val;
+ switch (par->codec_type) {
+ case AVMEDIA_TYPE_AUDIO:
+ val = par->sample_rate && par->channels;
+#if FF_API_LAVF_AVCTX
+ val = val || (codec->sample_rate && codec->channels);
+#endif
+ if (par->format == AV_SAMPLE_FMT_NONE
+#if FF_API_LAVF_AVCTX
+ && codec->sample_fmt == AV_SAMPLE_FMT_NONE
+#endif
+ )
+ return 0;
+ break;
+ case AVMEDIA_TYPE_VIDEO:
+ val = par->width && par->height;
+#if FF_API_LAVF_AVCTX
+ val = val || (codec->width && codec->height);
+#endif
+ if (par->format == AV_PIX_FMT_NONE
+#if FF_API_LAVF_AVCTX
+ && codec->pix_fmt == AV_PIX_FMT_NONE
+#endif
+ )
+ return 0;
+ break;
+ case AVMEDIA_TYPE_UNKNOWN:
+ val = 0;
+ break;
+ default:
+ val = 1;
+ break;
+ }
+#if FF_API_LAVF_AVCTX
+ return (par->codec_id != AV_CODEC_ID_NONE || codec->codec_id != AV_CODEC_ID_NONE) && val != 0;
+#else
+ return par->codec_id != AV_CODEC_ID_NONE && val != 0;
+#endif
+ } 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[] = {
@@ -3300,11 +5144,26 @@ int ff_generate_avci_extradata(AVStream *st)
0x11, 0x12, 0x08, 0xc6, 0x8c, 0x04, 0x41, 0x28,
0x4c, 0x34, 0xf0, 0x1e, 0x01, 0x13, 0xf2, 0xe0,
0x3c, 0x60, 0x20, 0x20, 0x28, 0x00, 0x00, 0x03,
- 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x94, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x94, 0x20,
// PPS
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,
@@ -3315,8 +5174,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,
@@ -3338,6 +5197,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;
@@ -3351,30 +5225,39 @@ int ff_generate_avci_extradata(AVStream *st)
size = sizeof(avci100_1080i_extradata);
}
} else if (st->codecpar->width == 1440) {
- data = avci50_1080i_extradata;
- size = sizeof(avci50_1080i_extradata);
+ if (st->codecpar->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->codecpar->width == 1280) {
data = avci100_720p_extradata;
size = sizeof(avci100_720p_extradata);
+ } else if (st->codecpar->width == 960) {
+ data = avci50_720p_extradata;
+ size = sizeof(avci50_720p_extradata);
}
if (!size)
return 0;
av_freep(&st->codecpar->extradata);
- st->codecpar->extradata_size = 0;
- st->codecpar->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
+ if (ff_alloc_extradata(st->codecpar, size))
return AVERROR(ENOMEM);
-
memcpy(st->codecpar->extradata, data, size);
- st->codecpar->extradata_size = size;
return 0;
}
-uint8_t *av_stream_get_side_data(AVStream *st, enum AVPacketSideDataType type,
- int *size)
+#if FF_API_NOCONST_GET_SIDE_DATA
+uint8_t *av_stream_get_side_data(AVStream *st,
+ enum AVPacketSideDataType type, int *size)
+#else
+uint8_t *av_stream_get_side_data(const AVStream *st,
+ enum AVPacketSideDataType type, int *size)
+#endif
{
int i;
@@ -3405,7 +5288,7 @@ int av_stream_add_side_data(AVStream *st, enum AVPacketSideDataType type,
}
}
- if ((unsigned) st->nb_side_data + 1 >= INT_MAX / sizeof(*st->side_data))
+ if ((unsigned)st->nb_side_data + 1 >= INT_MAX / sizeof(*st->side_data))
return AVERROR(ERANGE);
tmp = av_realloc(st->side_data, (st->nb_side_data + 1) * sizeof(*tmp));
@@ -3442,9 +5325,282 @@ uint8_t *av_stream_new_side_data(AVStream *st, enum AVPacketSideDataType type,
return data;
}
+int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args)
+{
+ int ret;
+ const AVBitStreamFilter *bsf;
+ AVBSFContext *bsfc;
+ AVCodecParameters *in_par;
+
+ if (!(bsf = av_bsf_get_by_name(name))) {
+ av_log(NULL, AV_LOG_ERROR, "Unknown bitstream filter '%s'\n", name);
+ return AVERROR_BSF_NOT_FOUND;
+ }
+
+ if ((ret = av_bsf_alloc(bsf, &bsfc)) < 0)
+ return ret;
+
+ if (st->internal->nb_bsfcs) {
+ in_par = st->internal->bsfcs[st->internal->nb_bsfcs - 1]->par_out;
+ bsfc->time_base_in = st->internal->bsfcs[st->internal->nb_bsfcs - 1]->time_base_out;
+ } else {
+ in_par = st->codecpar;
+ bsfc->time_base_in = st->time_base;
+ }
+
+ if ((ret = avcodec_parameters_copy(bsfc->par_in, in_par)) < 0) {
+ av_bsf_free(&bsfc);
+ return ret;
+ }
+
+ if (args && bsfc->filter->priv_class) {
+ const AVOption *opt = av_opt_next(bsfc->priv_data, NULL);
+ const char * shorthand[2] = {NULL};
+
+ if (opt)
+ shorthand[0] = opt->name;
+
+ if ((ret = av_opt_set_from_string(bsfc->priv_data, args, shorthand, "=", ":")) < 0) {
+ av_bsf_free(&bsfc);
+ return ret;
+ }
+ }
+
+ if ((ret = av_bsf_init(bsfc)) < 0) {
+ av_bsf_free(&bsfc);
+ return ret;
+ }
+
+ if ((ret = av_dynarray_add_nofree(&st->internal->bsfcs, &st->internal->nb_bsfcs, bsfc))) {
+ av_bsf_free(&bsfc);
+ return ret;
+ }
+
+ av_log(NULL, AV_LOG_VERBOSE,
+ "Automatically inserted bitstream filter '%s'; args='%s'\n",
+ name, args ? args : "");
+ return 1;
+}
+
+#if FF_API_OLD_BSF
+FF_DISABLE_DEPRECATION_WARNINGS
+int av_apply_bitstream_filters(AVCodecContext *codec, AVPacket *pkt,
+ AVBitStreamFilterContext *bsfc)
+{
+ int ret = 0;
+ while (bsfc) {
+ AVPacket new_pkt = *pkt;
+ int a = av_bitstream_filter_filter(bsfc, codec, NULL,
+ &new_pkt.data, &new_pkt.size,
+ pkt->data, pkt->size,
+ pkt->flags & AV_PKT_FLAG_KEY);
+ if (a == 0 && new_pkt.size == 0 && new_pkt.side_data_elems == 0) {
+ av_packet_unref(pkt);
+ memset(pkt, 0, sizeof(*pkt));
+ return 0;
+ }
+ if(a == 0 && new_pkt.data != pkt->data) {
+ uint8_t *t = av_malloc(new_pkt.size + AV_INPUT_BUFFER_PADDING_SIZE); //the new should be a subset of the old so cannot overflow
+ if (t) {
+ memcpy(t, new_pkt.data, new_pkt.size);
+ memset(t + new_pkt.size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+ new_pkt.data = t;
+ new_pkt.buf = NULL;
+ a = 1;
+ } else {
+ a = AVERROR(ENOMEM);
+ }
+ }
+ if (a > 0) {
+ new_pkt.buf = av_buffer_create(new_pkt.data, new_pkt.size,
+ av_buffer_default_free, NULL, 0);
+ if (new_pkt.buf) {
+ pkt->side_data = NULL;
+ pkt->side_data_elems = 0;
+ av_packet_unref(pkt);
+ } else {
+ av_freep(&new_pkt.data);
+ a = AVERROR(ENOMEM);
+ }
+ }
+ if (a < 0) {
+ av_log(codec, AV_LOG_ERROR,
+ "Failed to open bitstream filter %s for stream %d with codec %s",
+ bsfc->filter->name, pkt->stream_index,
+ codec->codec ? codec->codec->name : "copy");
+ ret = a;
+ break;
+ }
+ *pkt = new_pkt;
+
+ bsfc = bsfc->next;
+ }
+ return ret;
+}
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+int ff_format_output_open(AVFormatContext *s, const char *url, AVDictionary **options)
+{
+ if (!s->oformat)
+ return AVERROR(EINVAL);
+
+ if (!(s->oformat->flags & AVFMT_NOFILE))
+ return s->io_open(s, &s->pb, url, AVIO_FLAG_WRITE, options);
+ return 0;
+}
+
void ff_format_io_close(AVFormatContext *s, AVIOContext **pb)
{
if (*pb)
s->io_close(s, *pb);
*pb = NULL;
}
+
+int ff_parse_creation_time_metadata(AVFormatContext *s, int64_t *timestamp, int return_seconds)
+{
+ AVDictionaryEntry *entry;
+ int64_t parsed_timestamp;
+ int ret;
+ if ((entry = av_dict_get(s->metadata, "creation_time", NULL, 0))) {
+ if ((ret = av_parse_time(&parsed_timestamp, entry->value, 0)) >= 0) {
+ *timestamp = return_seconds ? parsed_timestamp / 1000000 : parsed_timestamp;
+ return 1;
+ } else {
+ av_log(s, AV_LOG_WARNING, "Failed to parse creation_time %s\n", entry->value);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+int ff_standardize_creation_time(AVFormatContext *s)
+{
+ int64_t timestamp;
+ int ret = ff_parse_creation_time_metadata(s, &timestamp, 0);
+ if (ret == 1)
+ return avpriv_dict_set_timestamp(&s->metadata, "creation_time", timestamp);
+ return ret;
+}
+
+int ff_get_packet_palette(AVFormatContext *s, AVPacket *pkt, int ret, uint32_t *palette)
+{
+ uint8_t *side_data;
+ int size;
+
+ side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_PALETTE, &size);
+ if (side_data) {
+ if (size != AVPALETTE_SIZE) {
+ av_log(s, AV_LOG_ERROR, "Invalid palette side data\n");
+ return AVERROR_INVALIDDATA;
+ }
+ memcpy(palette, side_data, AVPALETTE_SIZE);
+ return 1;
+ }
+
+ if (ret == CONTAINS_PAL) {
+ int i;
+ for (i = 0; i < AVPALETTE_COUNT; i++)
+ palette[i] = AV_RL32(pkt->data + pkt->size - AVPALETTE_SIZE + i*4);
+ return 1;
+ }
+
+ return 0;
+}
+
+int ff_bprint_to_codecpar_extradata(AVCodecParameters *par, struct AVBPrint *buf)
+{
+ int ret;
+ char *str;
+
+ ret = av_bprint_finalize(buf, &str);
+ if (ret < 0)
+ return ret;
+ if (!av_bprint_is_complete(buf)) {
+ av_free(str);
+ return AVERROR(ENOMEM);
+ }
+
+ par->extradata = str;
+ /* Note: the string is NUL terminated (so extradata can be read as a
+ * string), but the ending character is not accounted in the size (in
+ * binary formats you are likely not supposed to mux that character). When
+ * extradata is copied, it is also padded with AV_INPUT_BUFFER_PADDING_SIZE
+ * zeros. */
+ par->extradata_size = buf->len;
+ return 0;
+}
+
+int avformat_transfer_internal_stream_timing_info(const AVOutputFormat *ofmt,
+ AVStream *ost, const AVStream *ist,
+ enum AVTimebaseSource copy_tb)
+{
+ //TODO: use [io]st->internal->avctx
+ const AVCodecContext *dec_ctx = ist->codec;
+ AVCodecContext *enc_ctx = ost->codec;
+
+ enc_ctx->time_base = ist->time_base;
+ /*
+ * Avi is a special case here because it supports variable fps but
+ * having the fps and timebase differe significantly adds quite some
+ * overhead
+ */
+ if (!strcmp(ofmt->name, "avi")) {
+#if FF_API_R_FRAME_RATE
+ if (copy_tb == AVFMT_TBCF_AUTO && ist->r_frame_rate.num
+ && av_q2d(ist->r_frame_rate) >= av_q2d(ist->avg_frame_rate)
+ && 0.5/av_q2d(ist->r_frame_rate) > av_q2d(ist->time_base)
+ && 0.5/av_q2d(ist->r_frame_rate) > av_q2d(dec_ctx->time_base)
+ && av_q2d(ist->time_base) < 1.0/500 && av_q2d(dec_ctx->time_base) < 1.0/500
+ || copy_tb == AVFMT_TBCF_R_FRAMERATE) {
+ enc_ctx->time_base.num = ist->r_frame_rate.den;
+ enc_ctx->time_base.den = 2*ist->r_frame_rate.num;
+ enc_ctx->ticks_per_frame = 2;
+ } else
+#endif
+ if (copy_tb == AVFMT_TBCF_AUTO && av_q2d(dec_ctx->time_base)*dec_ctx->ticks_per_frame > 2*av_q2d(ist->time_base)
+ && av_q2d(ist->time_base) < 1.0/500
+ || copy_tb == AVFMT_TBCF_DECODER) {
+ enc_ctx->time_base = dec_ctx->time_base;
+ enc_ctx->time_base.num *= dec_ctx->ticks_per_frame;
+ enc_ctx->time_base.den *= 2;
+ enc_ctx->ticks_per_frame = 2;
+ }
+ } else if (!(ofmt->flags & AVFMT_VARIABLE_FPS)
+ && !av_match_name(ofmt->name, "mov,mp4,3gp,3g2,psp,ipod,ismv,f4v")) {
+ if (copy_tb == AVFMT_TBCF_AUTO && dec_ctx->time_base.den
+ && av_q2d(dec_ctx->time_base)*dec_ctx->ticks_per_frame > av_q2d(ist->time_base)
+ && av_q2d(ist->time_base) < 1.0/500
+ || copy_tb == AVFMT_TBCF_DECODER) {
+ enc_ctx->time_base = dec_ctx->time_base;
+ enc_ctx->time_base.num *= dec_ctx->ticks_per_frame;
+ }
+ }
+
+ if ((enc_ctx->codec_tag == AV_RL32("tmcd") || ost->codecpar->codec_tag == AV_RL32("tmcd"))
+ && dec_ctx->time_base.num < dec_ctx->time_base.den
+ && dec_ctx->time_base.num > 0
+ && 121LL*dec_ctx->time_base.num > dec_ctx->time_base.den) {
+ enc_ctx->time_base = dec_ctx->time_base;
+ }
+
+ if (ost->avg_frame_rate.num)
+ enc_ctx->time_base = av_inv_q(ost->avg_frame_rate);
+
+ av_reduce(&enc_ctx->time_base.num, &enc_ctx->time_base.den,
+ enc_ctx->time_base.num, enc_ctx->time_base.den, INT_MAX);
+
+ return 0;
+}
+
+AVRational av_stream_get_codec_timebase(const AVStream *st)
+{
+ // See avformat_transfer_internal_stream_timing_info() TODO.
+#if FF_API_LAVF_AVCTX
+FF_DISABLE_DEPRECATION_WARNINGS
+ return st->codec->time_base;
+FF_ENABLE_DEPRECATION_WARNINGS
+#else
+ return st->internal->avctx->time_base;
+#endif
+}
diff --git a/libavformat/v210.c b/libavformat/v210.c
new file mode 100644
index 0000000..a90bd36
--- /dev/null
+++ b/libavformat/v210.c
@@ -0,0 +1,130 @@
+/*
+ * Raw v210 video demuxer
+ * Copyright (c) 2015 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 "libavutil/imgutils.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "avformat.h"
+
+typedef struct V210DemuxerContext {
+ const AVClass *class; /**< Class for private options. */
+ int width, height; /**< Integers describing video size, set by a private option. */
+ AVRational framerate; /**< AVRational describing framerate, set by a private option. */
+} V210DemuxerContext;
+
+// v210 frame width is padded to multiples of 48
+#define GET_PACKET_SIZE(w, h) (((w + 47) / 48) * 48 * h * 8 / 3)
+
+static int v210_read_header(AVFormatContext *ctx)
+{
+ V210DemuxerContext *s = ctx->priv_data;
+ AVStream *st;
+ int ret;
+
+ st = avformat_new_stream(ctx, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+
+ st->codecpar->codec_id = ctx->iformat->raw_codec_id;
+
+ avpriv_set_pts_info(st, 64, s->framerate.den, s->framerate.num);
+
+ ret = av_image_check_size(s->width, s->height, 0, ctx);
+ if (ret < 0)
+ return ret;
+ st->codecpar->width = s->width;
+ st->codecpar->height = s->height;
+ st->codecpar->format = ctx->iformat->raw_codec_id == AV_CODEC_ID_V210 ?
+ AV_PIX_FMT_YUV422P10 : AV_PIX_FMT_YUV422P16;
+ ctx->packet_size = GET_PACKET_SIZE(s->width, s->height);
+ st->codecpar->bit_rate = av_rescale_q(ctx->packet_size,
+ (AVRational){8,1}, st->time_base);
+
+ return 0;
+}
+
+
+static int v210_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ int ret;
+
+ ret = av_get_packet(s->pb, pkt, s->packet_size);
+ pkt->pts = pkt->dts = pkt->pos / s->packet_size;
+
+ pkt->stream_index = 0;
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+#define OFFSET(x) offsetof(V210DemuxerContext, x)
+#define DEC AV_OPT_FLAG_DECODING_PARAM
+static const AVOption v210_options[] = {
+ { "video_size", "set frame size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC },
+ { "framerate", "set frame rate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, DEC },
+ { NULL },
+};
+
+#if CONFIG_V210_DEMUXER
+static const AVClass v210_demuxer_class = {
+ .class_name = "v210 demuxer",
+ .item_name = av_default_item_name,
+ .option = v210_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_v210_demuxer = {
+ .name = "v210",
+ .long_name = NULL_IF_CONFIG_SMALL("Uncompressed 4:2:2 10-bit"),
+ .priv_data_size = sizeof(V210DemuxerContext),
+ .read_header = v210_read_header,
+ .read_packet = v210_read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+ .extensions = "v210",
+ .raw_codec_id = AV_CODEC_ID_V210,
+ .priv_class = &v210_demuxer_class,
+};
+#endif // CONFIG_V210_DEMUXER
+
+#if CONFIG_V210X_DEMUXER
+static const AVClass v210x_demuxer_class = {
+ .class_name = "v210x demuxer",
+ .item_name = av_default_item_name,
+ .option = v210_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_v210x_demuxer = {
+ .name = "v210x",
+ .long_name = NULL_IF_CONFIG_SMALL("Uncompressed 4:2:2 10-bit"),
+ .priv_data_size = sizeof(V210DemuxerContext),
+ .read_header = v210_read_header,
+ .read_packet = v210_read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+ .extensions = "yuv10",
+ .raw_codec_id = AV_CODEC_ID_V210X,
+ .priv_class = &v210x_demuxer_class,
+};
+#endif // CONFIG_V210X_DEMUXER
diff --git a/libavformat/vag.c b/libavformat/vag.c
new file mode 100644
index 0000000..e8ebcaf
--- /dev/null
+++ b/libavformat/vag.c
@@ -0,0 +1,83 @@
+/*
+ * VAG demuxer
+ * Copyright (c) 2015 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"
+
+static int vag_probe(AVProbeData *p)
+{
+ if (memcmp(p->buf, "VAGp\0\0\0", 7))
+ return 0;
+
+ return AVPROBE_SCORE_MAX;
+}
+
+static int vag_read_header(AVFormatContext *s)
+{
+ AVStream *st;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avio_skip(s->pb, 4);
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_PSX;
+ st->codecpar->channels = 1 + (avio_rb32(s->pb) == 0x00000004);
+ avio_skip(s->pb, 4);
+ if (st->codecpar->channels > 1) {
+ st->duration = avio_rb32(s->pb);
+ } else {
+ st->duration = avio_rb32(s->pb) / 16 * 28;
+ }
+ st->codecpar->sample_rate = avio_rb32(s->pb);
+ if (st->codecpar->sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+ avio_seek(s->pb, 0x1000, SEEK_SET);
+ if (avio_rl32(s->pb) == MKTAG('V','A','G','p')) {
+ st->codecpar->block_align = 0x1000 * st->codecpar->channels;
+ avio_seek(s->pb, 0, SEEK_SET);
+ st->duration = st->duration / 16 * 28;
+ } else {
+ st->codecpar->block_align = 16 * st->codecpar->channels;
+ avio_seek(s->pb, st->codecpar->channels > 1 ? 0x80 : 0x30, SEEK_SET);
+ }
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ return 0;
+}
+
+static int vag_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+
+ return av_get_packet(s->pb, pkt, par->block_align);
+}
+
+AVInputFormat ff_vag_demuxer = {
+ .name = "vag",
+ .long_name = NULL_IF_CONFIG_SMALL("Sony PS2 VAG"),
+ .read_probe = vag_probe,
+ .read_header = vag_read_header,
+ .read_packet = vag_read_packet,
+ .extensions = "vag",
+};
diff --git a/libavformat/vc1dec.c b/libavformat/vc1dec.c
new file mode 100644
index 0000000..33f8465
--- /dev/null
+++ b/libavformat/vc1dec.c
@@ -0,0 +1,81 @@
+/*
+ * VC-1 demuxer
+ * Copyright (c) 2015 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 "rawdec.h"
+#include "libavutil/intreadwrite.h"
+#include "libavcodec/vc1_common.h"
+
+static int vc1_probe(AVProbeData *p)
+{
+ int seq = 0, entry = 0, frame = 0, i;
+
+ for (i = 0; i < p->buf_size + 5; i++) {
+ uint32_t code = AV_RB32(p->buf + i);
+ if ((code & 0xffffffe0) == 0x100) {
+ int type = code & 0x11f;
+ i += 4;
+ switch (type) {
+ case VC1_CODE_SEQHDR: {
+ int profile, level, chromaformat;
+ profile = (p->buf[i] & 0xc0) >> 6;
+ if (profile != PROFILE_ADVANCED) {
+ seq = 0;
+ continue;
+ }
+ level = (p->buf[i] & 0x38) >> 3;
+ if (level >= 5) {
+ seq = 0;
+ continue;
+ }
+ chromaformat = (p->buf[i] & 0x6) >> 1;
+ if (chromaformat != 1) {
+ seq = 0;
+ continue;
+ }
+ seq++;
+ i += 6;
+ break;
+ }
+ case VC1_CODE_ENTRYPOINT:
+ if (!seq)
+ continue;
+ entry++;
+ i += 2;
+ break;
+ case VC1_CODE_FRAME:
+ case VC1_CODE_FIELD:
+ case VC1_CODE_SLICE:
+ if (seq && entry)
+ frame++;
+ break;
+ }
+ }
+ }
+
+ if (frame > 1)
+ return AVPROBE_SCORE_EXTENSION / 2 + 1;
+ if (frame == 1)
+ return AVPROBE_SCORE_EXTENSION / 4;
+ return 0;
+}
+
+FF_DEF_RAWVIDEO_DEMUXER2(vc1, "raw VC-1", vc1_probe, "vc1", AV_CODEC_ID_VC1, AVFMT_GENERIC_INDEX|AVFMT_NOTIMESTAMPS)
diff --git a/libavformat/vc1test.c b/libavformat/vc1test.c
index e127941..a801f4b 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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->codec_id = AV_CODEC_ID_WMV3;
- st->codecpar->extradata = av_malloc(VC1_EXTRADATA_SIZE);
- if (!st->codecpar->extradata)
+ if (ff_get_extradata(s, st->codecpar, pb, VC1_EXTRADATA_SIZE) < 0)
return AVERROR(ENOMEM);
- st->codecpar->extradata_size = VC1_EXTRADATA_SIZE;
- avio_read(pb, st->codecpar->extradata, VC1_EXTRADATA_SIZE);
st->codecpar->height = avio_rl32(pb);
st->codecpar->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 c286f66..cf95d1d 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 34650f6..22f82a3 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
*/
@@ -29,9 +29,11 @@
#include "libavutil/version.h"
-#define LIBAVFORMAT_VERSION_MAJOR 57
-#define LIBAVFORMAT_VERSION_MINOR 11
-#define LIBAVFORMAT_VERSION_MICRO 4
+// Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium)
+// Also please add any ticket numbers that you believe might be affected here
+#define LIBAVFORMAT_VERSION_MAJOR 57
+#define LIBAVFORMAT_VERSION_MINOR 84
+#define LIBAVFORMAT_VERSION_MICRO 101
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
LIBAVFORMAT_VERSION_MINOR, \
@@ -47,6 +49,11 @@
* FF_API_* defines may be placed below to indicate public API that will be
* dropped at a future version bump. The defines themselves are not part of
* the public API and may change, break or disappear at any time.
+ *
+ * @note, when bumping the major version it is recommended to manually
+ * disable each FF_API_* in its own commit instead of disabling them all
+ * at once through the bump. This improves the git bisect-ability of the change.
+ *
*/
#ifndef FF_API_LAVF_BITEXACT
#define FF_API_LAVF_BITEXACT (LIBAVFORMAT_VERSION_MAJOR < 58)
@@ -57,14 +64,42 @@
#ifndef FF_API_LAVF_CODEC_TB
#define FF_API_LAVF_CODEC_TB (LIBAVFORMAT_VERSION_MAJOR < 58)
#endif
+#ifndef FF_API_URL_FEOF
+#define FF_API_URL_FEOF (LIBAVFORMAT_VERSION_MAJOR < 58)
+#endif
#ifndef FF_API_LAVF_FMT_RAWPICTURE
#define FF_API_LAVF_FMT_RAWPICTURE (LIBAVFORMAT_VERSION_MAJOR < 58)
#endif
#ifndef FF_API_COMPUTE_PKT_FIELDS2
#define FF_API_COMPUTE_PKT_FIELDS2 (LIBAVFORMAT_VERSION_MAJOR < 58)
#endif
+#ifndef FF_API_OLD_OPEN_CALLBACKS
+#define FF_API_OLD_OPEN_CALLBACKS (LIBAVFORMAT_VERSION_MAJOR < 58)
+#endif
#ifndef FF_API_LAVF_AVCTX
#define FF_API_LAVF_AVCTX (LIBAVFORMAT_VERSION_MAJOR < 58)
#endif
+#ifndef FF_API_NOCONST_GET_SIDE_DATA
+#define FF_API_NOCONST_GET_SIDE_DATA (LIBAVFORMAT_VERSION_MAJOR < 58)
+#endif
+#ifndef FF_API_HTTP_USER_AGENT
+#define FF_API_HTTP_USER_AGENT (LIBAVFORMAT_VERSION_MAJOR < 58)
+#endif
+#ifndef FF_API_HLS_WRAP
+#define FF_API_HLS_WRAP (LIBAVFORMAT_VERSION_MAJOR < 58)
+#endif
+#ifndef FF_API_LAVF_MERGE_SD
+#define FF_API_LAVF_MERGE_SD (LIBAVFORMAT_VERSION_MAJOR < 58)
+#endif
+#ifndef FF_API_LAVF_KEEPSIDE_FLAG
+#define FF_API_LAVF_KEEPSIDE_FLAG (LIBAVFORMAT_VERSION_MAJOR < 58)
+#endif
+#ifndef FF_API_OLD_ROTATE_API
+#define FF_API_OLD_ROTATE_API (LIBAVFORMAT_VERSION_MAJOR < 58)
+#endif
+
+#ifndef FF_API_R_FRAME_RATE
+#define FF_API_R_FRAME_RATE 1
+#endif
#endif /* AVFORMAT_VERSION_H */
diff --git a/libavformat/vivo.c b/libavformat/vivo.c
new file mode 100644
index 0000000..c9e9c37
--- /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->codecpar->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->codecpar->width = value_int;
+ } else if (!strcmp(key, "Height")) {
+ vst->codecpar->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->codecpar->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->codecpar->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->codecpar->codec_tag = 0;
+ vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+
+ if (vivo->version == 1) {
+ vst->codecpar->codec_id = AV_CODEC_ID_H263;
+ ast->codecpar->codec_id = AV_CODEC_ID_G723_1;
+ ast->codecpar->bits_per_coded_sample = 8;
+ ast->codecpar->block_align = 24;
+ ast->codecpar->bit_rate = 6400;
+ }
+
+ ast->start_time = 0;
+ ast->codecpar->codec_tag = 0;
+ ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ ast->codecpar->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_packet_unref(pkt);
+ return ret;
+}
+
+AVInputFormat ff_vivo_demuxer = {
+ .name = "vivo",
+ .long_name = NULL_IF_CONFIG_SMALL("Vivo"),
+ .priv_data_size = sizeof(VivoContext),
+ .read_probe = vivo_probe,
+ .read_header = vivo_read_header,
+ .read_packet = vivo_read_packet,
+ .extensions = "viv",
+};
diff --git a/libavformat/voc.c b/libavformat/voc.c
index 1b7d499..2a97234 100644
--- a/libavformat/voc.c
+++ b/libavformat/voc.c
@@ -2,20 +2,20 @@
* Creative Voice File common data.
* Copyright (c) 2006 Aurelien Jacobs <aurel@gnuage.org>
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/voc.h b/libavformat/voc.h
index d2c0cb4..1f9a8be 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/voc_packet.c b/libavformat/voc_packet.c
index 3865075..1e2e19e 100644
--- a/libavformat/voc_packet.c
+++ b/libavformat/voc_packet.c
@@ -1,21 +1,24 @@
/*
- * This file is part of Libav.
+ * Copyright (c) 2006 Aurelien Jacobs <aurel@gnuage.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
*/
+#include "libavutil/intreadwrite.h"
#include "avformat.h"
#include "internal.h"
#include "voc.h"
@@ -30,11 +33,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 & AVIO_SEEKABLE_NORMAL))
@@ -94,6 +106,11 @@ ff_voc_get_packet(AVFormatContext *s, AVPacket *pkt, AVStream *st, int max_size)
}
}
+ if (par->sample_rate <= 0) {
+ av_log(s, AV_LOG_ERROR, "Invalid sample rate %d\n", par->sample_rate);
+ return AVERROR_INVALIDDATA;
+ }
+
if (tmp_codec >= 0) {
tmp_codec = ff_codec_get_id(ff_voc_codec_tags, tmp_codec);
if (par->codec_id == AV_CODEC_ID_NONE)
@@ -109,11 +126,21 @@ ff_voc_get_packet(AVFormatContext *s, AVPacket *pkt, AVStream *st, int max_size)
}
}
- par->bit_rate = par->sample_rate * par->bits_per_coded_sample;
+ par->bit_rate = (int64_t)par->sample_rate * par->channels * par->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_duration2(st->codecpar, size);
+ if (duration > 0 && voc->pts != AV_NOPTS_VALUE)
+ voc->pts += duration;
+ else
+ voc->pts = AV_NOPTS_VALUE;
+
+ return ret;
}
diff --git a/libavformat/vocdec.c b/libavformat/vocdec.c
index 5ed7117..10df28b 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
*/
@@ -68,6 +68,36 @@ 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;
+ int index;
+
+ if (s->nb_streams < 1) {
+ av_log(s, AV_LOG_ERROR, "cannot seek while no stream was found yet\n");
+ return AVERROR(EINVAL);
+ }
+
+ st = s->streams[stream_index];
+ 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"),
@@ -75,5 +105,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 033fc56..321b113 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;
+ AVCodecParameters *par = s->streams[0]->codecpar;
const int header_size = 26;
const int version = 0x0114;
@@ -37,6 +38,11 @@ static int voc_write_header(AVFormatContext *s)
|| s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
return AVERROR_PATCHWELCOME;
+ if (!par->codec_tag && par->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 (par->codec_tag > 0xFF) {
+ if (par->codec_tag > 3) {
avio_w8(pb, VOC_TYPE_NEW_VOICE_DATA);
avio_wl24(pb, pkt->size + 12);
avio_wl32(pb, par->sample_rate);
@@ -64,13 +70,13 @@ static int voc_write_packet(AVFormatContext *s, AVPacket *pkt)
if (s->streams[0]->codecpar->channels > 1) {
avio_w8(pb, VOC_TYPE_EXTENDED);
avio_wl24(pb, 4);
- avio_wl16(pb, 65536-256000000/(par->sample_rate*par->channels));
+ avio_wl16(pb, 65536-(256000000 + par->sample_rate*par->channels/2)/(par->sample_rate*par->channels));
avio_w8(pb, par->codec_tag);
avio_w8(pb, par->channels - 1);
}
avio_w8(pb, VOC_TYPE_VOICE_DATA);
avio_wl24(pb, pkt->size + 2);
- avio_w8(pb, 256 - 1000000 / par->sample_rate);
+ avio_w8(pb, 256 - (1000000 + par->sample_rate/2) / par->sample_rate);
avio_w8(pb, par->codec_tag);
}
voc->param_written = 1;
@@ -95,7 +101,7 @@ AVOutputFormat ff_voc_muxer = {
.mime_type = "audio/x-voc",
.extensions = "voc",
.priv_data_size = sizeof(VocEncContext),
- .audio_codec = AV_CODEC_ID_PCM_U8,
+ .audio_codec = AV_CODEC_ID_PCM_S16LE,
.video_codec = AV_CODEC_ID_NONE,
.write_header = voc_write_header,
.write_packet = voc_write_packet,
diff --git a/libavformat/vorbiscomment.c b/libavformat/vorbiscomment.c
index ee06a57..575dd13 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,12 +34,13 @@ const AVMetadataConv ff_vorbiscomment_metadata_conv[] = {
{ "ALBUMARTIST", "album_artist"},
{ "TRACKNUMBER", "track" },
{ "DISCNUMBER", "disc" },
+ { "DESCRIPTION", "comment" },
{ 0 }
};
-int ff_vorbiscomment_length(AVDictionary *m, const char *vendor_string)
+int64_t ff_vorbiscomment_length(AVDictionary *m, const char *vendor_string)
{
- int len = 8;
+ int64_t len = 8;
len += strlen(vendor_string);
if (m) {
AVDictionaryEntry *tag = NULL;
@@ -60,8 +61,10 @@ int ff_vorbiscomment_write(uint8_t **p, AVDictionary **m,
AVDictionaryEntry *tag = NULL;
bytestream_put_le32(p, count);
while ((tag = av_dict_get(*m, "", tag, AV_DICT_IGNORE_SUFFIX))) {
- unsigned int len1 = strlen(tag->key);
- unsigned int len2 = strlen(tag->value);
+ int64_t len1 = strlen(tag->key);
+ int64_t len2 = strlen(tag->value);
+ if (len1+1+len2 > UINT32_MAX)
+ return AVERROR(EINVAL);
bytestream_put_le32(p, len1+1+len2);
bytestream_put_buffer(p, tag->key, len1);
bytestream_put_byte(p, '=');
diff --git a/libavformat/vorbiscomment.h b/libavformat/vorbiscomment.h
index d9ec099..e0d30b1 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
*/
@@ -34,7 +34,7 @@
* For no string, set to an empty string.
* @return The length in bytes.
*/
-int ff_vorbiscomment_length(AVDictionary *m, const char *vendor_string);
+int64_t ff_vorbiscomment_length(AVDictionary *m, const char *vendor_string);
/**
* Write a VorbisComment into a buffer. The buffer, p, must have enough
diff --git a/libavformat/vpcc.c b/libavformat/vpcc.c
new file mode 100644
index 0000000..df08de5
--- /dev/null
+++ b/libavformat/vpcc.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016 Google Inc.
+ * Copyright (c) 2016 KongQun Yang (kqyang@google.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/pixdesc.h"
+#include "libavutil/pixfmt.h"
+#include "vpcc.h"
+
+enum VPX_CHROMA_SUBSAMPLING
+{
+ VPX_SUBSAMPLING_420_VERTICAL = 0,
+ VPX_SUBSAMPLING_420_COLLOCATED_WITH_LUMA = 1,
+ VPX_SUBSAMPLING_422 = 2,
+ VPX_SUBSAMPLING_444 = 3,
+};
+
+static int get_vpx_chroma_subsampling(AVFormatContext *s,
+ enum AVPixelFormat pixel_format,
+ enum AVChromaLocation chroma_location)
+{
+ int chroma_w, chroma_h;
+ if (av_pix_fmt_get_chroma_sub_sample(pixel_format, &chroma_w, &chroma_h) == 0) {
+ if (chroma_w == 1 && chroma_h == 1) {
+ return (chroma_location == AVCHROMA_LOC_LEFT)
+ ? VPX_SUBSAMPLING_420_VERTICAL
+ : VPX_SUBSAMPLING_420_COLLOCATED_WITH_LUMA;
+ } else if (chroma_w == 1 && chroma_h == 0) {
+ return VPX_SUBSAMPLING_422;
+ } else if (chroma_w == 0 && chroma_h == 0) {
+ return VPX_SUBSAMPLING_444;
+ }
+ }
+ av_log(s, AV_LOG_ERROR, "Unsupported pixel format (%d)\n", pixel_format);
+ return -1;
+}
+
+static int get_bit_depth(AVFormatContext *s, enum AVPixelFormat pixel_format)
+{
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pixel_format);
+ if (desc == NULL) {
+ av_log(s, AV_LOG_ERROR, "Unsupported pixel format (%d)\n",
+ pixel_format);
+ return -1;
+ }
+ return desc->comp[0].depth;
+}
+
+static int get_vpx_video_full_range_flag(enum AVColorRange color_range)
+{
+ return color_range == AVCOL_RANGE_JPEG;
+}
+
+int ff_isom_write_vpcc(AVFormatContext *s, AVIOContext *pb,
+ AVCodecParameters *par)
+{
+ int profile = par->profile;
+ int level = par->level == FF_LEVEL_UNKNOWN ? 0 : par->level;
+ int bit_depth = get_bit_depth(s, par->format);
+ int vpx_chroma_subsampling =
+ get_vpx_chroma_subsampling(s, par->format, par->chroma_location);
+ int vpx_video_full_range_flag =
+ get_vpx_video_full_range_flag(par->color_range);
+
+ if (bit_depth < 0 || vpx_chroma_subsampling < 0)
+ return AVERROR_INVALIDDATA;
+
+ if (profile == FF_PROFILE_UNKNOWN) {
+ if (vpx_chroma_subsampling == VPX_SUBSAMPLING_420_VERTICAL ||
+ vpx_chroma_subsampling == VPX_SUBSAMPLING_420_COLLOCATED_WITH_LUMA) {
+ profile = (bit_depth == 8) ? FF_PROFILE_VP9_0 : FF_PROFILE_VP9_2;
+ } else {
+ profile = (bit_depth == 8) ? FF_PROFILE_VP9_1 : FF_PROFILE_VP9_3;
+ }
+ }
+
+ avio_w8(pb, profile);
+ avio_w8(pb, level);
+ avio_w8(pb, (bit_depth << 4) | (vpx_chroma_subsampling << 1) | vpx_video_full_range_flag);
+ avio_w8(pb, par->color_primaries);
+ avio_w8(pb, par->color_trc);
+ avio_w8(pb, par->color_space);
+
+ // vp9 does not have codec initialization data.
+ avio_wb16(pb, 0);
+ return 0;
+}
diff --git a/libavformat/vpcc.h b/libavformat/vpcc.h
new file mode 100644
index 0000000..184e857
--- /dev/null
+++ b/libavformat/vpcc.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016 Google Inc.
+ * Copyright (c) 2016 KongQun Yang (kqyang@google.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
+ * internal header for VPx codec configuration utilities.
+ */
+
+#ifndef AVFORMAT_VPCC_H
+#define AVFORMAT_VPCC_H
+
+#include <stdint.h>
+#include "avio.h"
+#include "avformat.h"
+#include "libavcodec/avcodec.h"
+
+/**
+ * Writes VP codec configuration to the provided AVIOContext.
+ *
+ * @param s address of the AVFormatContext for the logging context.
+ * @param pb address of the AVIOContext where the vpcC shall be written.
+ * @param par address of the AVCodecParameters which contains codec information.
+ * @return >=0 in case of success, a negative value corresponding to an AVERROR
+ * code in case of failure
+ */
+int ff_isom_write_vpcc(AVFormatContext *s, AVIOContext *pb,
+ AVCodecParameters *par);
+
+#endif /* AVFORMAT_VPCC_H */
diff --git a/libavformat/vpk.c b/libavformat/vpk.c
new file mode 100644
index 0000000..bb9eabb
--- /dev/null
+++ b/libavformat/vpk.c
@@ -0,0 +1,117 @@
+/*
+ * VPK demuxer
+ * Copyright (c) 2015 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"
+
+typedef struct VPKDemuxContext {
+ unsigned block_count;
+ unsigned current_block;
+ unsigned last_block_size;
+} VPKDemuxContext;
+
+static int vpk_probe(AVProbeData *p)
+{
+ if (AV_RL32(p->buf) != MKBETAG('V','P','K',' '))
+ return 0;
+
+ return AVPROBE_SCORE_MAX / 3 * 2;
+}
+
+static int vpk_read_header(AVFormatContext *s)
+{
+ VPKDemuxContext *vpk = s->priv_data;
+ unsigned offset;
+ unsigned samples_per_block;
+ AVStream *st;
+
+ vpk->current_block = 0;
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avio_skip(s->pb, 4);
+ st->duration = avio_rl32(s->pb) * 28 / 16;
+ offset = avio_rl32(s->pb);
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_PSX;
+ st->codecpar->block_align = avio_rl32(s->pb);
+ st->codecpar->sample_rate = avio_rl32(s->pb);
+ if (st->codecpar->sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+ st->codecpar->channels = avio_rl32(s->pb);
+ if (st->codecpar->channels <= 0)
+ return AVERROR_INVALIDDATA;
+ samples_per_block = ((st->codecpar->block_align / st->codecpar->channels) * 28) / 16;
+ if (samples_per_block <= 0)
+ return AVERROR_INVALIDDATA;
+ vpk->block_count = (st->duration + (samples_per_block - 1)) / samples_per_block;
+ vpk->last_block_size = (st->duration % samples_per_block) * 16 * st->codecpar->channels / 28;
+ avio_skip(s->pb, offset - avio_tell(s->pb));
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ return 0;
+}
+
+static int vpk_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ VPKDemuxContext *vpk = s->priv_data;
+ int ret, i;
+
+ vpk->current_block++;
+ if (vpk->current_block == vpk->block_count) {
+ unsigned size = vpk->last_block_size / par->channels;
+ unsigned skip = (par->block_align - vpk->last_block_size) / par->channels;
+
+ ret = av_new_packet(pkt, vpk->last_block_size);
+ if (ret < 0)
+ return ret;
+ for (i = 0; i < par->channels; i++) {
+ ret = avio_read(s->pb, pkt->data + i * size, size);
+ avio_skip(s->pb, skip);
+ if (ret != size) {
+ av_packet_unref(pkt);
+ ret = AVERROR(EIO);
+ break;
+ }
+ }
+ pkt->stream_index = 0;
+ } else if (vpk->current_block < vpk->block_count) {
+ ret = av_get_packet(s->pb, pkt, par->block_align);
+ pkt->stream_index = 0;
+ } else {
+ return AVERROR_EOF;
+ }
+
+ return ret;
+}
+
+AVInputFormat ff_vpk_demuxer = {
+ .name = "vpk",
+ .long_name = NULL_IF_CONFIG_SMALL("Sony PS2 VPK"),
+ .priv_data_size = sizeof(VPKDemuxContext),
+ .read_probe = vpk_probe,
+ .read_header = vpk_read_header,
+ .read_packet = vpk_read_packet,
+ .extensions = "vpk",
+};
diff --git a/libavformat/vplayerdec.c b/libavformat/vplayerdec.c
new file mode 100644
index 0000000..49943d0
--- /dev/null
+++ b/libavformat/vplayerdec.c
@@ -0,0 +1,129 @@
+/*
+ * 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, "%*3d:%*2d:%*2d.%*2d%c", &c) == 1 ||
+ sscanf(ptr, "%*3d:%*2d:%*2d%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, n, len;
+
+ if (((n = sscanf(*line, "%d:%d:%d.%d%c%n", &hh, &mm, &ss, &ms, &c, &len)) >= 5 ||
+ (n = sscanf(*line, "%d:%d:%d%c%n", &hh, &mm, &ss, &c, &len)) >= 4) && strchr(": =", c)) {
+ *line += len;
+ return (hh*3600LL + mm*60LL + ss) * 100LL + (n < 5 ? 0 : 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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->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(s, &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 c3a6a97..d00fa5e 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);
- st->codecpar->bit_rate = read_bitrate * 1000;
+ if (st->codecpar->channels <= 0) {
+ av_log(s, AV_LOG_ERROR, "Invalid number of channels\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ st->codecpar->bit_rate = (int64_t)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:
@@ -207,7 +211,7 @@ static int vqf_read_header(AVFormatContext *s)
size = 2048;
break;
default:
- av_log(s, AV_LOG_ERROR, "Mode not suported: %d Hz, %d kb/s.\n",
+ av_log(s, AV_LOG_ERROR, "Mode not supported: %d Hz, %"PRId64" kb/s.\n",
st->codecpar->sample_rate, st->codecpar->bit_rate);
return -1;
}
@@ -215,9 +219,8 @@ static int vqf_read_header(AVFormatContext *s)
avpriv_set_pts_info(st, 64, size, st->codecpar->sample_rate);
/* put first 12 bytes of COMM chunk in extradata */
- if (!(st->codecpar->extradata = av_malloc(12 + AV_INPUT_BUFFER_PADDING_SIZE)))
+ if (ff_alloc_extradata(st->codecpar, 12))
return AVERROR(ENOMEM);
- st->codecpar->extradata_size = 12;
memcpy(st->codecpar->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_packet_unref(pkt);
return AVERROR(EIO);
}
@@ -258,7 +261,7 @@ static int vqf_read_seek(AVFormatContext *s,
{
VqfContext *c = s->priv_data;
AVStream *st;
- int ret;
+ int64_t ret;
int64_t pos;
st = s->streams[stream_index];
diff --git a/libavformat/w64.c b/libavformat/w64.c
new file mode 100644
index 0000000..ef2d90a
--- /dev/null
+++ b/libavformat/w64.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009 Daniel Verkamp
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "w64.h"
+
+const uint8_t ff_w64_guid_riff[16] = {
+ 'r', 'i', 'f', 'f',
+ 0x2E, 0x91, 0xCF, 0x11, 0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00
+};
+
+const uint8_t ff_w64_guid_wave[16] = {
+ 'w', 'a', 'v', 'e',
+ 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
+};
+
+const uint8_t ff_w64_guid_fmt [16] = {
+ 'f', 'm', 't', ' ',
+ 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
+};
+
+const uint8_t ff_w64_guid_fact[16] = { 'f', 'a', 'c', 't',
+ 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
+};
+
+const uint8_t ff_w64_guid_data[16] = {
+ 'd', 'a', 't', 'a',
+ 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
+};
+
+const uint8_t ff_w64_guid_summarylist[16] = {
+ 0xBC, 0x94, 0x5F, 0x92,
+ 0x5A, 0x52, 0xD2, 0x11, 0x86, 0xDC, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
+};
diff --git a/libavformat/w64.h b/libavformat/w64.h
new file mode 100644
index 0000000..0ec3fa9
--- /dev/null
+++ b/libavformat/w64.h
@@ -0,0 +1,31 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_W64_H
+#define AVFORMAT_W64_H
+
+#include <stdint.h>
+
+extern const uint8_t ff_w64_guid_riff[16];
+extern const uint8_t ff_w64_guid_wave[16];
+extern const uint8_t ff_w64_guid_fmt [16];
+extern const uint8_t ff_w64_guid_fact[16];
+extern const uint8_t ff_w64_guid_data[16];
+extern const uint8_t ff_w64_guid_summarylist[16];
+
+#endif /* AVFORMAT_W64_H */
diff --git a/libavformat/wavdec.c b/libavformat/wavdec.c
index 8c78666..b016185 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,92 @@
#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;
+static void set_spdif(AVFormatContext *s, WAVDemuxContext *wav)
+{
+ if (CONFIG_SPDIF_DEMUXER && s->streams[0]->codecpar->codec_tag == 1) {
+ enum AVCodecID codec;
+ int len = 1<<16;
+ int ret = ffio_ensure_seekback(s->pb, len);
+
+ if (ret >= 0) {
+ uint8_t *buf = av_malloc(len);
+ if (!buf) {
+ ret = AVERROR(ENOMEM);
+ } else {
+ int64_t pos = avio_tell(s->pb);
+ len = ret = avio_read(s->pb, buf, len);
+ if (len >= 0) {
+ ret = ff_spdif_probe(buf, len, &codec);
+ if (ret > AVPROBE_SCORE_EXTENSION) {
+ s->streams[0]->codecpar->codec_id = codec;
+ wav->spdif = 1;
+ }
+ }
+ avio_seek(s->pb, pos, SEEK_SET);
+ av_free(buf);
+ }
+ }
+
+ if (ret < 0)
+ av_log(s, AV_LOG_WARNING, "Cannot check for SPDIF\n");
+ }
+}
+
#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 +134,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 +146,18 @@ static int wav_probe(AVProbeData *p)
return 0;
}
+static void handle_stream_probing(AVStream *st)
+{
+ if (st->codecpar->codec_id == AV_CODEC_ID_PCM_S16LE) {
+ st->request_probe = AVPROBE_SCORE_EXTENSION;
+ st->probe_packets = FFMIN(st->probe_packets, 32);
+ }
+}
+
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,16 +165,67 @@ static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream **st)
if (!*st)
return AVERROR(ENOMEM);
- ret = ff_get_wav_header(s, pb, (*st)->codecpar, size);
+ ret = ff_get_wav_header(s, pb, (*st)->codecpar, 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)->codecpar->sample_rate);
return 0;
}
+static int wav_parse_xma2_tag(AVFormatContext *s, int64_t size, AVStream **st)
+{
+ AVIOContext *pb = s->pb;
+ int version, num_streams, i, channels = 0;
+
+ if (size < 36)
+ return AVERROR_INVALIDDATA;
+
+ *st = avformat_new_stream(s, NULL);
+ if (!*st)
+ return AVERROR(ENOMEM);
+
+ (*st)->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ (*st)->codecpar->codec_id = AV_CODEC_ID_XMA2;
+ (*st)->need_parsing = AVSTREAM_PARSE_FULL_RAW;
+
+ version = avio_r8(pb);
+ if (version != 3 && version != 4)
+ return AVERROR_INVALIDDATA;
+ num_streams = avio_r8(pb);
+ if (size != (32 + ((version==3)?0:8) + 4*num_streams))
+ return AVERROR_INVALIDDATA;
+ avio_skip(pb, 10);
+ (*st)->codecpar->sample_rate = avio_rb32(pb);
+ if (version == 4)
+ avio_skip(pb, 8);
+ avio_skip(pb, 4);
+ (*st)->duration = avio_rb32(pb);
+ avio_skip(pb, 8);
+
+ for (i = 0; i < num_streams; i++) {
+ channels += avio_r8(pb);
+ avio_skip(pb, 3);
+ }
+ (*st)->codecpar->channels = channels;
+
+ if ((*st)->codecpar->channels <= 0 || (*st)->codecpar->sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+
+ avpriv_set_pts_info(*st, 64, 1, (*st)->codecpar->sample_rate);
+
+ avio_seek(pb, -size, SEEK_CUR);
+ av_freep(&(*st)->codecpar->extradata);
+ if (ff_get_extradata(s, (*st)->codecpar, pb, size) < 0)
+ return AVERROR(ENOMEM);
+
+ return 0;
+}
+
static inline int wav_parse_bext_string(AVFormatContext *s, const char *key,
int length)
{
@@ -167,7 +281,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,30 +328,49 @@ static int wav_read_header(AVFormatContext *s)
{
int64_t size, av_uninit(data_size);
int64_t sample_count = 0;
- int rf64;
+ int rf64 = 0;
uint32_t tag;
AVIOContext *pb = s->pb;
AVStream *st = NULL;
WAVDemuxContext *wav = s->priv_data;
- int ret, got_fmt = 0;
+ int ret, got_fmt = 0, got_xma2 = 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_log(s, AV_LOG_ERROR, "invalid start code %s in RIFF header\n",
+ av_fourcc2str(tag));
+ 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,28 +383,39 @@ 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_xma2 && !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");
got_fmt = 1;
break;
+ case MKTAG('X', 'M', 'A', '2'):
+ /* only parse the first 'XMA2' tag found */
+ if (!got_fmt && !got_xma2 && (ret = wav_parse_xma2_tag(s, size, &st)) < 0) {
+ return ret;
+ } else if (got_xma2)
+ av_log(s, AV_LOG_WARNING, "found more than one 'XMA2' tag\n");
+
+ got_xma2 = 1;
+ break;
case MKTAG('d', 'a', 't', 'a'):
- if (!got_fmt) {
+ if (!(pb->seekable & AVIO_SEEKABLE_NORMAL) && !got_fmt && !got_xma2) {
av_log(s, AV_LOG_ERROR,
"found no 'fmt ' tag before the 'data' tag\n");
return AVERROR_INVALIDDATA;
@@ -279,9 +423,14 @@ static int wav_read_header(AVFormatContext *s)
if (rf64) {
next_tag_ofs = wav->data_end = avio_tell(pb) + data_size;
- } else {
+ } else if (size != 0xFFFFFFFF) {
data_size = size;
next_tag_ofs = wav->data_end = size ? next_tag_ofs : INT64_MAX;
+ } else {
+ av_log(s, AV_LOG_WARNING, "Ignoring maximum wav data size, "
+ "file may be invalid\n");
+ data_size = 0;
+ next_tag_ofs = wav->data_end = INT64_MAX;
}
data_ofs = avio_tell(pb);
@@ -294,33 +443,78 @@ 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->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ vst->codecpar->codec_id = AV_CODEC_ID_SMVJPEG;
+ vst->codecpar->width = avio_rl24(pb);
+ vst->codecpar->height = avio_rl24(pb);
+ if (ff_alloc_extradata(vst->codecpar, 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->codecpar->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;
}
}
break_loop:
+ if (!got_fmt && !got_xma2) {
+ av_log(s, AV_LOG_ERROR, "no 'fmt ' or 'XMA2' tag found\n");
+ return AVERROR_INVALIDDATA;
+ }
+
if (data_ofs < 0) {
av_log(s, AV_LOG_ERROR, "no 'data' tag found\n");
return AVERROR_INVALIDDATA;
@@ -328,17 +522,66 @@ break_loop:
avio_seek(pb, data_ofs, SEEK_SET);
- if (!sample_count && st->codecpar->channels &&
- av_get_bits_per_sample(st->codecpar->codec_id))
- sample_count = (data_size << 3) /
- (st->codecpar->channels *
- (uint64_t)av_get_bits_per_sample(st->codecpar->codec_id));
+ if (data_size > (INT64_MAX>>3)) {
+ av_log(s, AV_LOG_WARNING, "Data size %"PRId64" is too large\n", data_size);
+ data_size = 0;
+ }
+
+ if ( st->codecpar->bit_rate > 0 && data_size > 0
+ && st->codecpar->sample_rate > 0
+ && sample_count > 0 && st->codecpar->channels > 1
+ && sample_count % st->codecpar->channels == 0) {
+ if (fabs(8.0 * data_size * st->codecpar->channels * st->codecpar->sample_rate /
+ sample_count /st->codecpar->bit_rate - 1.0) < 0.3)
+ sample_count /= st->codecpar->channels;
+ }
+
+ if ( data_size > 0 && sample_count && st->codecpar->channels
+ && (data_size << 3) / sample_count / st->codecpar->channels > st->codecpar->bits_per_coded_sample + 1) {
+ av_log(s, AV_LOG_WARNING, "ignoring wrong sample_count %"PRId64"\n", sample_count);
+ sample_count = 0;
+ }
+
+ /* G.729 hack (for Ticket4577)
+ * FIXME: Come up with cleaner, more general solution */
+ if (st->codecpar->codec_id == AV_CODEC_ID_G729 && sample_count && (data_size << 3) > sample_count) {
+ 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->codecpar->codec_id) > 0)
+ if ( st->codecpar->channels
+ && data_size
+ && av_get_bits_per_sample(st->codecpar->codec_id)
+ && wav->data_end <= avio_size(pb))
+ sample_count = (data_size << 3)
+ /
+ (st->codecpar->channels * (uint64_t)av_get_bits_per_sample(st->codecpar->codec_id));
+
if (sample_count)
st->duration = sample_count;
+ if (st->codecpar->codec_id == AV_CODEC_ID_PCM_S32LE &&
+ st->codecpar->block_align == st->codecpar->channels * 4 &&
+ st->codecpar->bits_per_coded_sample == 32 &&
+ st->codecpar->extradata_size == 2 &&
+ AV_RL16(st->codecpar->extradata) == 1) {
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_F16LE;
+ st->codecpar->bits_per_coded_sample = 16;
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_PCM_S24LE &&
+ st->codecpar->block_align == st->codecpar->channels * 4 &&
+ st->codecpar->bits_per_coded_sample == 24) {
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_F24LE;
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_XMA1 ||
+ st->codecpar->codec_id == AV_CODEC_ID_XMA2) {
+ st->codecpar->block_align = 2048;
+ }
+
ff_metadata_conv_ctx(s, NULL, wav_metadata_conv);
ff_metadata_conv_ctx(s, NULL, ff_riff_info_conv);
+ set_spdif(s, wav);
+
return 0;
}
@@ -351,23 +594,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 +615,72 @@ static int wav_read_packet(AVFormatContext *s, AVPacket *pkt)
AVStream *st;
WAVDemuxContext *wav = s->priv_data;
+ 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 +702,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->codecpar->codec_id) {
@@ -416,6 +724,7 @@ static int wav_read_seek(AVFormatContext *s,
case AV_CODEC_ID_MP3:
case AV_CODEC_ID_AC3:
case AV_CODEC_ID_DTS:
+ case AV_CODEC_ID_XMA2:
/* use generic seeking with dynamically generated indexes */
return -1;
default:
@@ -424,6 +733,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_BOOL, { .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 +756,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 +774,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 +782,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 +790,95 @@ 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(s, pb, st->codecpar, 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(s, pb, st->codecpar, 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->codecpar->sample_rate);
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->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 & AVIO_SEEKABLE_NORMAL))
+ 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);
+ if (chunk_size == UINT32_MAX)
+ return AVERROR_INVALIDDATA;
+
+ 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);
+
+ set_spdif(s, wav);
return 0;
}
diff --git a/libavformat/wavenc.c b/libavformat/wavenc.c
index fc7d49e..adb20cb 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,20 +46,53 @@
#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;
+ int 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;
- int len = 0;
+ size_t len = 0;
if (tag = av_dict_get(s->metadata, key, NULL, 0)) {
len = strlen(tag->value);
@@ -74,11 +120,11 @@ static void bwf_write_bext_chunk(AVFormatContext *s)
avio_wl64(s->pb, time_reference);
avio_wl16(s->pb, 1); // set version to 1
- if (tmp_tag = av_dict_get(s->metadata, "umid", NULL, 0)) {
+ if ((tmp_tag = av_dict_get(s->metadata, "umid", NULL, 0)) && strlen(tmp_tag->value) > 2) {
unsigned char umidpart_str[17] = {0};
- int i;
+ int64_t i;
uint64_t umidpart;
- int len = strlen(tmp_tag->value+2);
+ size_t len = strlen(tmp_tag->value+2);
for (i = 0; i < len/16; i++) {
memcpy(umidpart_str, tmp_tag->value + 2 + (i*16), 16);
@@ -97,25 +143,205 @@ 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;
+ AVCodecParameters *par = s->streams[0]->codecpar;
+
+ if (par->codec_id != AV_CODEC_ID_PCM_S8 &&
+ par->codec_id != AV_CODEC_ID_PCM_S16LE &&
+ par->codec_id != AV_CODEC_ID_PCM_U8 &&
+ par->codec_id != AV_CODEC_ID_PCM_U16LE) {
+ AVCodec *codec = avcodec_find_decoder(s->streams[0]->codecpar->codec_id);
+ av_log(s, AV_LOG_ERROR, "%s codec not supported for Peak Chunk\n",
+ codec ? codec->name : "NONE");
+ return -1;
+ }
+
+ wav->peak_bps = av_get_bits_per_sample(par->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(par->channels, sizeof(*wav->peak_maxpos));
+ wav->peak_maxneg = av_mallocz_array(par->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;
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ int peak_of_peaks;
+ int c;
+
+ if (!wav->peak_output)
+ return;
+
+ for (c = 0; c < par->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;
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ 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, par->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(s, pb, s->streams[0]->codecpar) < 0) {
- const AVCodecDescriptor *desc = avcodec_descriptor_get(s->streams[0]->codecpar->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 != PEAK_ONLY) {
+ /* format header */
+ fmt = ff_start_tag(pb, "fmt ");
+ if (ff_put_wav_header(s, pb, s->streams[0]->codecpar, 0) < 0) {
+ const AVCodecDescriptor *desc = avcodec_descriptor_get(s->streams[0]->codecpar->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]->codecpar->codec_tag != 0x01 /* hence for all other than PCM */
&& (s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
@@ -127,15 +353,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]->codecpar->sample_rate);
wav->maxpts = wav->last_duration = 0;
wav->minpts = INT64_MAX;
- /* info header */
- ff_riff_write_info(s);
+ if (wav->write_peak != PEAK_ONLY) {
+ /* 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 +380,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 != PEAK_ONLY)
+ 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]->codecpar->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 +418,103 @@ 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 & AVIO_SEEKABLE_NORMAL) {
- ff_end_tag(pb, wav->data);
+ if (wav->write_peak != PEAK_ONLY && avio_tell(pb) - wav->data < UINT32_MAX) {
+ 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 if (file_size - 8 <= UINT32_MAX) {
+ 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);
+ } else {
+ av_log(s, AV_LOG_ERROR,
+ "Filesize %"PRId64" invalid for wav, output file will be broken\n",
+ file_size);
+ }
+
+ number_of_samples = av_rescale(wav->maxpts - wav->minpts + wav->last_duration,
+ s->streams[0]->codecpar->sample_rate * (int64_t)s->streams[0]->time_base.num,
+ s->streams[0]->time_base.den);
if(s->streams[0]->codecpar->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]->codecpar->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_bext", "Write BEXT chunk.", OFFSET(write_bext), AV_OPT_TYPE_BOOL, { .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 +540,102 @@ 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(s, pb, s->streams[0]->codecpar, 0)) < 0) {
+ AVCodec *codec = avcodec_find_decoder(s->streams[0]->codecpar->codec_id);
+ av_log(s, AV_LOG_ERROR, "%s codec not supported\n",
+ codec ? codec->name : "NONE");
+ return ret;
+ }
+ end_guid(pb, start);
+
+ if (s->streams[0]->codecpar->codec_tag != 0x01 /* hence for all other than PCM */
+ && (s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
+ 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 & AVIO_SEEKABLE_NORMAL) {
+ 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]->codecpar->codec_tag != 0x01) {
+ int64_t number_of_samples;
+
+ number_of_samples = av_rescale(wav->maxpts - wav->minpts + wav->last_duration,
+ s->streams[0]->codecpar->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 f46b8c2..cb4d4d9 100644
--- a/libavformat/wc3movie.c
+++ b/libavformat/wc3movie.c
@@ -2,20 +2,20 @@
* Wing Commander III Movie (.mve) File Demuxer
* Copyright (c) 2003 The FFmpeg project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -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"
@@ -149,16 +150,15 @@ static int wc3_read_header(AVFormatContext *s)
break;
default:
- av_log(s, AV_LOG_ERROR, " unrecognized WC3 chunk: %c%c%c%c (0x%02X%02X%02X%02X)\n",
- (uint8_t)fourcc_tag, (uint8_t)(fourcc_tag >> 8), (uint8_t)(fourcc_tag >> 16), (uint8_t)(fourcc_tag >> 24),
- (uint8_t)fourcc_tag, (uint8_t)(fourcc_tag >> 8), (uint8_t)(fourcc_tag >> 16), (uint8_t)(fourcc_tag >> 24));
+ av_log(s, AV_LOG_ERROR, "unrecognized WC3 chunk: %s\n",
+ av_fourcc2str(fourcc_tag));
return AVERROR_INVALIDDATA;
}
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 +210,7 @@ static int wc3_read_packet(AVFormatContext *s,
fourcc_tag = avio_rl32(pb);
/* chunk sizes are 16-bit aligned */
size = (avio_rb32(pb) + 1) & (~1);
- if (pb->eof_reached)
+ if (avio_feof(pb))
return AVERROR(EIO);
switch (fourcc_tag) {
@@ -246,10 +246,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]);
}
break;
@@ -267,9 +273,8 @@ static int wc3_read_packet(AVFormatContext *s,
break;
default:
- av_log (s, AV_LOG_ERROR, " unrecognized WC3 chunk: %c%c%c%c (0x%02X%02X%02X%02X)\n",
- (uint8_t)fourcc_tag, (uint8_t)(fourcc_tag >> 8), (uint8_t)(fourcc_tag >> 16), (uint8_t)(fourcc_tag >> 24),
- (uint8_t)fourcc_tag, (uint8_t)(fourcc_tag >> 8), (uint8_t)(fourcc_tag >> 16), (uint8_t)(fourcc_tag >> 24));
+ av_log(s, AV_LOG_ERROR, "unrecognized WC3 chunk: %s\n",
+ av_fourcc2str(fourcc_tag));
ret = AVERROR_INVALIDDATA;
packet_read = 1;
break;
diff --git a/libavformat/webm_chunk.c b/libavformat/webm_chunk.c
new file mode 100644
index 0000000..f8dbaa3
--- /dev/null
+++ b/libavformat/webm_chunk.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2015, 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
+ */
+
+/**
+ * @file WebM Chunk Muxer
+ * The chunk muxer enables writing WebM Live chunks where there is a header
+ * chunk, followed by data chunks where each Cluster is written out as a Chunk.
+ */
+
+#include <float.h>
+#include <time.h>
+
+#include "avformat.h"
+#include "avio.h"
+#include "avio_internal.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"
+
+#define MAX_FILENAME_SIZE 1024
+
+typedef struct WebMChunkContext {
+ const AVClass *class;
+ int chunk_start_index;
+ char *header_filename;
+ int chunk_duration;
+ int chunk_index;
+ char *http_method;
+ uint64_t duration_written;
+ int prev_pts;
+ AVOutputFormat *oformat;
+ AVFormatContext *avf;
+} WebMChunkContext;
+
+static int chunk_mux_init(AVFormatContext *s)
+{
+ WebMChunkContext *wc = s->priv_data;
+ AVFormatContext *oc;
+ int ret;
+
+ ret = avformat_alloc_output_context2(&wc->avf, wc->oformat, NULL, NULL);
+ if (ret < 0)
+ return ret;
+ oc = wc->avf;
+
+ oc->interrupt_callback = s->interrupt_callback;
+ oc->max_delay = s->max_delay;
+ av_dict_copy(&oc->metadata, s->metadata, 0);
+
+ *(const AVClass**)oc->priv_data = oc->oformat->priv_class;
+ av_opt_set_defaults(oc->priv_data);
+ av_opt_set_int(oc->priv_data, "dash", 1, 0);
+ av_opt_set_int(oc->priv_data, "cluster_time_limit", wc->chunk_duration, 0);
+ av_opt_set_int(oc->priv_data, "live", 1, 0);
+
+ oc->streams = s->streams;
+ oc->nb_streams = s->nb_streams;
+
+ return 0;
+}
+
+static int get_chunk_filename(AVFormatContext *s, int is_header, char *filename)
+{
+ WebMChunkContext *wc = s->priv_data;
+ AVFormatContext *oc = wc->avf;
+ if (!filename) {
+ return AVERROR(EINVAL);
+ }
+ if (is_header) {
+ if (!wc->header_filename) {
+ av_log(oc, AV_LOG_ERROR, "No header filename provided\n");
+ return AVERROR(EINVAL);
+ }
+ av_strlcpy(filename, wc->header_filename, strlen(wc->header_filename) + 1);
+ } else {
+ if (av_get_frame_filename(filename, MAX_FILENAME_SIZE,
+ s->filename, wc->chunk_index - 1) < 0) {
+ av_log(oc, AV_LOG_ERROR, "Invalid chunk filename template '%s'\n", s->filename);
+ return AVERROR(EINVAL);
+ }
+ }
+ return 0;
+}
+
+static int webm_chunk_write_header(AVFormatContext *s)
+{
+ WebMChunkContext *wc = s->priv_data;
+ AVFormatContext *oc = NULL;
+ int ret;
+ int i;
+ AVDictionary *options = NULL;
+
+ // DASH Streams can only have either one track per file.
+ if (s->nb_streams != 1) { return AVERROR_INVALIDDATA; }
+
+ wc->chunk_index = wc->chunk_start_index;
+ wc->oformat = av_guess_format("webm", s->filename, "video/webm");
+ if (!wc->oformat)
+ return AVERROR_MUXER_NOT_FOUND;
+
+ ret = chunk_mux_init(s);
+ if (ret < 0)
+ return ret;
+ oc = wc->avf;
+ ret = get_chunk_filename(s, 1, oc->filename);
+ if (ret < 0)
+ return ret;
+ if (wc->http_method)
+ av_dict_set(&options, "method", wc->http_method, 0);
+ ret = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, &options);
+ av_dict_free(&options);
+ if (ret < 0)
+ return ret;
+
+ oc->pb->seekable = 0;
+ ret = oc->oformat->write_header(oc);
+ if (ret < 0)
+ return ret;
+ ff_format_io_close(s, &oc->pb);
+ for (i = 0; i < s->nb_streams; i++) {
+ // ms precision is the de-facto standard timescale for mkv files.
+ avpriv_set_pts_info(s->streams[i], 64, 1, 1000);
+ }
+ return 0;
+}
+
+static int chunk_start(AVFormatContext *s)
+{
+ WebMChunkContext *wc = s->priv_data;
+ AVFormatContext *oc = wc->avf;
+ int ret;
+
+ ret = avio_open_dyn_buf(&oc->pb);
+ if (ret < 0)
+ return ret;
+ wc->chunk_index++;
+ return 0;
+}
+
+static int chunk_end(AVFormatContext *s)
+{
+ WebMChunkContext *wc = s->priv_data;
+ AVFormatContext *oc = wc->avf;
+ int ret;
+ int buffer_size;
+ uint8_t *buffer;
+ AVIOContext *pb;
+ char filename[MAX_FILENAME_SIZE];
+ AVDictionary *options = NULL;
+
+ if (wc->chunk_start_index == wc->chunk_index)
+ return 0;
+ // Flush the cluster in WebM muxer.
+ oc->oformat->write_packet(oc, NULL);
+ buffer_size = avio_close_dyn_buf(oc->pb, &buffer);
+ ret = get_chunk_filename(s, 0, filename);
+ if (ret < 0)
+ goto fail;
+ if (wc->http_method)
+ av_dict_set(&options, "method", wc->http_method, 0);
+ ret = s->io_open(s, &pb, filename, AVIO_FLAG_WRITE, &options);
+ if (ret < 0)
+ goto fail;
+ avio_write(pb, buffer, buffer_size);
+ ff_format_io_close(s, &pb);
+ oc->pb = NULL;
+fail:
+ av_dict_free(&options);
+ av_free(buffer);
+ return (ret < 0) ? ret : 0;
+}
+
+static int webm_chunk_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ WebMChunkContext *wc = s->priv_data;
+ AVFormatContext *oc = wc->avf;
+ AVStream *st = s->streams[pkt->stream_index];
+ int ret;
+
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ wc->duration_written += av_rescale_q(pkt->pts - wc->prev_pts,
+ st->time_base,
+ (AVRational) {1, 1000});
+ wc->prev_pts = pkt->pts;
+ }
+
+ // For video, a new chunk is started only on key frames. For audio, a new
+ // chunk is started based on chunk_duration.
+ if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
+ (pkt->flags & AV_PKT_FLAG_KEY)) ||
+ (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
+ (pkt->pts == 0 || wc->duration_written >= wc->chunk_duration))) {
+ wc->duration_written = 0;
+ if ((ret = chunk_end(s)) < 0 || (ret = chunk_start(s)) < 0) {
+ goto fail;
+ }
+ }
+
+ ret = oc->oformat->write_packet(oc, pkt);
+ if (ret < 0)
+ goto fail;
+
+fail:
+ if (ret < 0) {
+ oc->streams = NULL;
+ oc->nb_streams = 0;
+ avformat_free_context(oc);
+ }
+
+ return ret;
+}
+
+static int webm_chunk_write_trailer(AVFormatContext *s)
+{
+ WebMChunkContext *wc = s->priv_data;
+ AVFormatContext *oc = wc->avf;
+ oc->oformat->write_trailer(oc);
+ chunk_end(s);
+ oc->streams = NULL;
+ oc->nb_streams = 0;
+ avformat_free_context(oc);
+ return 0;
+}
+
+#define OFFSET(x) offsetof(WebMChunkContext, x)
+static const AVOption options[] = {
+ { "chunk_start_index", "start index of the chunk", OFFSET(chunk_start_index), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "header", "filename of the header where the initialization data will be written", OFFSET(header_filename), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
+ { "audio_chunk_duration", "duration of each chunk in milliseconds", OFFSET(chunk_duration), AV_OPT_TYPE_INT, {.i64 = 5000}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "method", "set the HTTP method", OFFSET(http_method), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
+ { NULL },
+};
+
+#if CONFIG_WEBM_CHUNK_MUXER
+static const AVClass webm_chunk_class = {
+ .class_name = "WebM Chunk Muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVOutputFormat ff_webm_chunk_muxer = {
+ .name = "webm_chunk",
+ .long_name = NULL_IF_CONFIG_SMALL("WebM Chunk Muxer"),
+ .mime_type = "video/webm",
+ .extensions = "chk",
+ .flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_NEEDNUMBER |
+ AVFMT_TS_NONSTRICT,
+ .priv_data_size = sizeof(WebMChunkContext),
+ .write_header = webm_chunk_write_header,
+ .write_packet = webm_chunk_write_packet,
+ .write_trailer = webm_chunk_write_trailer,
+ .priv_class = &webm_chunk_class,
+};
+#endif
diff --git a/libavformat/webmdashenc.c b/libavformat/webmdashenc.c
new file mode 100644
index 0000000..1280d8a
--- /dev/null
+++ b/libavformat/webmdashenc.c
@@ -0,0 +1,585 @@
+/*
+ * 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
+ * ISO DASH Specification:
+ * http://standards.iso.org/ittf/PubliclyAvailableStandards/c065274_ISO_IEC_23009-1_2014.zip
+ */
+
+#include <float.h>
+#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"
+#include "libavutil/time_internal.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;
+ int is_live;
+ int chunk_start_index;
+ int chunk_duration;
+ char *utc_timing_url;
+ double time_shift_buffer_depth;
+ int minimum_update_period;
+ int debug_mode;
+} 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 int write_header(AVFormatContext *s)
+{
+ WebMDashMuxContext *w = s->priv_data;
+ 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=\"%s\"\n", w->is_live ? "dynamic" : "static");
+ if (!w->is_live) {
+ 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=\"%s\"%s",
+ w->is_live ? "urn:mpeg:dash:profile:isoff-live:2011" : "urn:webm:dash:profile:webm-on-demand:2012",
+ w->is_live ? "\n" : ">\n");
+ if (w->is_live) {
+ time_t local_time = time(NULL);
+ struct tm gmt_buffer;
+ struct tm *gmt = gmtime_r(&local_time, &gmt_buffer);
+ char gmt_iso[21];
+ if (!strftime(gmt_iso, 21, "%Y-%m-%dT%H:%M:%SZ", gmt)) {
+ return AVERROR_UNKNOWN;
+ }
+ if (w->debug_mode) {
+ av_strlcpy(gmt_iso, "", 1);
+ }
+ avio_printf(s->pb, " availabilityStartTime=\"%s\"\n", gmt_iso);
+ avio_printf(s->pb, " timeShiftBufferDepth=\"PT%gS\"\n", w->time_shift_buffer_depth);
+ avio_printf(s->pb, " minimumUpdatePeriod=\"PT%dS\"", w->minimum_update_period);
+ avio_printf(s->pb, ">\n");
+ if (w->utc_timing_url) {
+ avio_printf(s->pb, "<UTCTiming\n");
+ avio_printf(s->pb, " schemeIdUri=\"urn:mpeg:dash:utc:http-iso:2014\"\n");
+ avio_printf(s->pb, " value=\"%s\"/>\n", w->utc_timing_url);
+ }
+ }
+ return 0;
+}
+
+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);
+ AVCodecParameters *gold_par = s->streams[as->streams[0]]->codecpar;
+ 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);
+ AVCodecParameters *par = s->streams[as->streams[i]]->codecpar;
+ if (!track_num ||
+ strncmp(gold_track_num->value, track_num->value, strlen(gold_track_num->value)) ||
+ gold_par->codec_id != par->codec_id ||
+ gold_par->extradata_size != par->extradata_size ||
+ memcmp(gold_par->extradata, par->extradata, par->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, char *id,
+ int output_width, int output_height,
+ int output_sample_rate) {
+ WebMDashMuxContext *w = s->priv_data;
+ 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);
+ const char *bandwidth_str;
+ if ((w->is_live && (!filename)) ||
+ (!w->is_live && (!irange || !cues_start || !cues_end || !filename || !bandwidth))) {
+ return AVERROR_INVALIDDATA;
+ }
+ avio_printf(s->pb, "<Representation id=\"%s\"", id);
+ // if bandwidth for live was not provided, use a default
+ if (w->is_live && !bandwidth) {
+ bandwidth_str = (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) ? "128000" : "1000000";
+ } else {
+ bandwidth_str = bandwidth->value;
+ }
+ avio_printf(s->pb, " bandwidth=\"%s\"", bandwidth_str);
+ if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && output_width)
+ avio_printf(s->pb, " width=\"%d\"", stream->codecpar->width);
+ if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && output_height)
+ avio_printf(s->pb, " height=\"%d\"", stream->codecpar->height);
+ if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && output_sample_rate)
+ avio_printf(s->pb, " audioSamplingRate=\"%d\"", stream->codecpar->sample_rate);
+ if (w->is_live) {
+ // For live streams, Codec and Mime Type always go in the Representation tag.
+ avio_printf(s->pb, " codecs=\"%s\"", get_codec_name(stream->codecpar->codec_id));
+ avio_printf(s->pb, " mimeType=\"%s/webm\"",
+ stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? "video" : "audio");
+ // For live streams, subsegments always start with key frames. So this
+ // is always 1.
+ avio_printf(s->pb, " startsWithSAP=\"1\"");
+ avio_printf(s->pb, ">");
+ } else {
+ 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]]->codecpar->width;
+ for (i = 1; i < as->nb_streams; i++)
+ if (first_width != s->streams[as->streams[i]]->codecpar->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]]->codecpar->height;
+ for (i = 1; i < as->nb_streams; i++)
+ if (first_height != s->streams[as->streams[i]]->codecpar->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]]->codecpar->sample_rate;
+ for (i = 1; i < as->nb_streams; i++)
+ if (first_sample_rate != s->streams[as->streams[i]]->codecpar->sample_rate)
+ return 0;
+ return 1;
+}
+
+static void free_adaptation_sets(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);
+ w->nb_as = 0;
+}
+
+/*
+ * Parses a live header filename and computes the representation id,
+ * initialization pattern and the media pattern. Pass NULL if you don't want to
+ * compute any of those 3. Returns 0 on success and non-zero on failure.
+ *
+ * Name of the header file should conform to the following pattern:
+ * <file_description>_<representation_id>.hdr where <file_description> can be
+ * anything. The chunks should be named according to the following pattern:
+ * <file_description>_<representation_id>_<chunk_number>.chk
+ */
+static int parse_filename(char *filename, char **representation_id,
+ char **initialization_pattern, char **media_pattern) {
+ char *underscore_pos = NULL;
+ char *period_pos = NULL;
+ char *temp_pos = NULL;
+ char *filename_str = av_strdup(filename);
+ int ret = 0;
+
+ if (!filename_str) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ temp_pos = av_stristr(filename_str, "_");
+ while (temp_pos) {
+ underscore_pos = temp_pos + 1;
+ temp_pos = av_stristr(temp_pos + 1, "_");
+ }
+ if (!underscore_pos) {
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+ period_pos = av_stristr(underscore_pos, ".");
+ if (!period_pos) {
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+ *(underscore_pos - 1) = 0;
+ if (representation_id) {
+ *representation_id = av_malloc(period_pos - underscore_pos + 1);
+ if (!(*representation_id)) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ av_strlcpy(*representation_id, underscore_pos, period_pos - underscore_pos + 1);
+ }
+ if (initialization_pattern) {
+ *initialization_pattern = av_asprintf("%s_$RepresentationID$.hdr",
+ filename_str);
+ if (!(*initialization_pattern)) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ }
+ if (media_pattern) {
+ *media_pattern = av_asprintf("%s_$RepresentationID$_$Number$.chk",
+ filename_str);
+ if (!(*media_pattern)) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ }
+
+end:
+ av_freep(&filename_str);
+ return ret;
+}
+
+/*
+ * 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];
+ AVCodecParameters *par = s->streams[as->streams[0]]->codecpar;
+ 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. For live streams, they always go
+ // in the Representation tag.
+ int width_in_as = 1, height_in_as = 1, sample_rate_in_as = 1;
+ if (par->codec_type == AVMEDIA_TYPE_VIDEO) {
+ width_in_as = !w->is_live && check_matching_width(s, as);
+ height_in_as = !w->is_live && check_matching_height(s, as);
+ } else {
+ sample_rate_in_as = !w->is_live && check_matching_sample_rate(s, as);
+ }
+
+ avio_printf(s->pb, "<AdaptationSet id=\"%s\"", as->id);
+ avio_printf(s->pb, " mimeType=\"%s/webm\"",
+ par->codec_type == AVMEDIA_TYPE_VIDEO ? "video" : "audio");
+ avio_printf(s->pb, " codecs=\"%s\"", get_codec_name(par->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 (par->codec_type == AVMEDIA_TYPE_VIDEO && width_in_as)
+ avio_printf(s->pb, " width=\"%d\"", par->width);
+ if (par->codec_type == AVMEDIA_TYPE_VIDEO && height_in_as)
+ avio_printf(s->pb, " height=\"%d\"", par->height);
+ if (par->codec_type == AVMEDIA_TYPE_AUDIO && sample_rate_in_as)
+ avio_printf(s->pb, " audioSamplingRate=\"%d\"", par->sample_rate);
+
+ avio_printf(s->pb, " bitstreamSwitching=\"%s\"",
+ boolean[bitstream_switching(s, as)]);
+ avio_printf(s->pb, " subsegmentAlignment=\"%s\"",
+ boolean[w->is_live || 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 (!w->is_live && (!kf || !strncmp(kf->value, "0", 1))) subsegmentStartsWithSAP = 0;
+ }
+ avio_printf(s->pb, " subsegmentStartsWithSAP=\"%d\"", subsegmentStartsWithSAP);
+ avio_printf(s->pb, ">\n");
+
+ if (w->is_live) {
+ AVDictionaryEntry *filename =
+ av_dict_get(s->streams[as->streams[0]]->metadata, FILENAME, NULL, 0);
+ char *initialization_pattern = NULL;
+ char *media_pattern = NULL;
+ int ret = parse_filename(filename->value, NULL, &initialization_pattern,
+ &media_pattern);
+ if (ret) return ret;
+ avio_printf(s->pb, "<ContentComponent id=\"1\" type=\"%s\"/>\n",
+ par->codec_type == AVMEDIA_TYPE_VIDEO ? "video" : "audio");
+ avio_printf(s->pb, "<SegmentTemplate");
+ avio_printf(s->pb, " timescale=\"1000\"");
+ avio_printf(s->pb, " duration=\"%d\"", w->chunk_duration);
+ avio_printf(s->pb, " media=\"%s\"", media_pattern);
+ avio_printf(s->pb, " startNumber=\"%d\"", w->chunk_start_index);
+ avio_printf(s->pb, " initialization=\"%s\"", initialization_pattern);
+ avio_printf(s->pb, "/>\n");
+ av_free(initialization_pattern);
+ av_free(media_pattern);
+ }
+
+ for (i = 0; i < as->nb_streams; i++) {
+ char *representation_id = NULL;
+ int ret;
+ if (w->is_live) {
+ AVDictionaryEntry *filename =
+ av_dict_get(s->streams[as->streams[i]]->metadata, FILENAME, NULL, 0);
+ if (!filename)
+ return AVERROR(EINVAL);
+ if (ret = parse_filename(filename->value, &representation_id, NULL, NULL))
+ return ret;
+ } else {
+ representation_id = av_asprintf("%d", w->representation_id++);
+ if (!representation_id) return AVERROR(ENOMEM);
+ }
+ ret = write_representation(s, s->streams[as->streams[i]],
+ representation_id, !width_in_as,
+ !height_in_as, !sample_rate_in_as);
+ av_free(representation_id);
+ if (ret) return ret;
+ }
+ 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 AVERROR(ENOMEM);
+ 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;
+ if (!w->adaptation_sets) {
+ av_log(s, AV_LOG_ERROR, "The 'adaptation_sets' option must be set.\n");
+ return AVERROR(EINVAL);
+ }
+ // 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)) {
+ void *mem = av_realloc(w->as, sizeof(*w->as) * (w->nb_as + 1));
+ if (mem == NULL)
+ return AVERROR(ENOMEM);
+ w->as = mem;
+ ++w->nb_as;
+ 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 AVERROR(ENOMEM);
+ as->streams[as->nb_streams - 1] = to_integer(p, q - p + 1);
+ if (as->streams[as->nb_streams - 1] < 0 ||
+ as->streams[as->nb_streams - 1] >= s->nb_streams) {
+ av_log(s, AV_LOG_ERROR, "Invalid value for 'streams' in adapation_sets.\n");
+ return AVERROR(EINVAL);
+ }
+ 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;
+ int ret;
+ WebMDashMuxContext *w = s->priv_data;
+ ret = parse_adaptation_sets(s);
+ if (ret < 0) {
+ goto fail;
+ }
+ ret = write_header(s);
+ if (ret < 0) {
+ goto fail;
+ }
+ avio_printf(s->pb, "<Period id=\"0\"");
+ avio_printf(s->pb, " start=\"PT%gS\"", start);
+ if (!w->is_live) {
+ avio_printf(s->pb, " duration=\"PT%gS\"", get_duration(s));
+ }
+ avio_printf(s->pb, " >\n");
+
+ for (i = 0; i < w->nb_as; i++) {
+ ret = write_adaptation_set(s, i);
+ if (ret < 0) {
+ goto fail;
+ }
+ }
+
+ avio_printf(s->pb, "</Period>\n");
+ write_footer(s);
+fail:
+ free_adaptation_sets(s);
+ return ret < 0 ? ret : 0;
+}
+
+static int webm_dash_manifest_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ return AVERROR_EOF;
+}
+
+static int webm_dash_manifest_write_trailer(AVFormatContext *s)
+{
+ free_adaptation_sets(s);
+ 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 },
+ { "debug_mode", "[private option - users should never set this]. Create deterministic output", OFFSET(debug_mode), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
+ { "live", "create a live stream manifest", OFFSET(is_live), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
+ { "chunk_start_index", "start index of the chunk", OFFSET(chunk_start_index), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "chunk_duration_ms", "duration of each chunk (in milliseconds)", OFFSET(chunk_duration), AV_OPT_TYPE_INT, {.i64 = 1000}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "utc_timing_url", "URL of the page that will return the UTC timestamp in ISO format", OFFSET(utc_timing_url), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
+ { "time_shift_buffer_depth", "Smallest time (in seconds) shifting buffer for which any Representation is guaranteed to be available.", OFFSET(time_shift_buffer_depth), AV_OPT_TYPE_DOUBLE, { .dbl = 60.0 }, 1.0, DBL_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "minimum_update_period", "Minimum Update Period (in seconds) of the manifest.", OFFSET(minimum_update_period), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, 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 0000000..9fb4722
--- /dev/null
+++ b/libavformat/webpenc.c
@@ -0,0 +1,218 @@
+/*
+ * 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;
+ int wrote_webp_header;
+ int using_webp_anim_encoder;
+} 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->codecpar->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);
+
+ return 0;
+}
+
+static int is_animated_webp_packet(AVPacket *pkt)
+{
+ if (pkt->size) {
+ int skip = 0;
+ unsigned flags = 0;
+
+ if (pkt->size < 4)
+ return 0;
+ if (AV_RL32(pkt->data) == AV_RL32("RIFF"))
+ skip = 12;
+
+ if (pkt->size < skip + 4)
+ return 0;
+ if (AV_RL32(pkt->data + skip) == AV_RL32("VP8X")) {
+ flags |= pkt->data[skip + 4 + 4];
+ }
+
+ if (flags & 2) // ANIMATION_FLAG is on
+ return 1;
+ }
+ 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 (w->last_pkt.size < 4)
+ return 0;
+ if (AV_RL32(w->last_pkt.data) == AV_RL32("RIFF"))
+ skip = 12;
+
+ if (w->last_pkt.size < skip + 4)
+ return 0; // Safe to do this as a valid WebP bitstream is >=30 bytes.
+ 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;
+ }
+
+ if (!w->wrote_webp_header) {
+ avio_write(s->pb, "RIFF\0\0\0\0WEBP", 12);
+ w->wrote_webp_header = 1;
+ if (w->frame_count > 1) // first non-empty packet
+ w->frame_count = 1; // so we don't count previous empty packets.
+ }
+
+ 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->codecpar->width - 1);
+ avio_wl24(s->pb, st->codecpar->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->codecpar->width - 1);
+ avio_wl24(s->pb, st->codecpar->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_packet_unref(&w->last_pkt);
+ }
+
+ return 0;
+}
+
+static int webp_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ WebpContext *w = s->priv_data;
+ w->using_webp_anim_encoder |= is_animated_webp_packet(pkt);
+
+ if (w->using_webp_anim_encoder) {
+ avio_write(s->pb, pkt->data, pkt->size);
+ w->wrote_webp_header = 1; // for good measure
+ } else {
+ int ret;
+ if ((ret = flush(s, 0, pkt->pts)) < 0)
+ return ret;
+ av_packet_ref(&w->last_pkt, pkt);
+ }
+ ++w->frame_count;
+
+ return 0;
+}
+
+static int webp_write_trailer(AVFormatContext *s)
+{
+ unsigned filesize;
+ WebpContext *w = s->priv_data;
+
+ if (w->using_webp_anim_encoder) {
+ if ((w->frame_count > 1) && w->loop) { // Write loop count.
+ avio_seek(s->pb, 42, SEEK_SET);
+ avio_wl16(s->pb, w->loop);
+ }
+ } else {
+ 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);
+ // Note: without the following, avio only writes 8 bytes to the file.
+ avio_seek(s->pb, filesize, SEEK_SET);
+ }
+
+ 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 0000000..0aeb8a6
--- /dev/null
+++ b/libavformat/webvttdec.c
@@ -0,0 +1,224 @@
+/*
+ * 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->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ st->codecpar->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) ||
+ !strncmp(p, "NOTE", 4))
+ 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(s, &webvtt->q);
+
+end:
+ av_bprint_finalize(&cue, NULL);
+ av_bprint_finalize(&header, NULL);
+ return res;
+}
+
+static int webvtt_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ WebVTTContext *webvtt = s->priv_data;
+ return ff_subtitles_queue_read_packet(&webvtt->q, pkt);
+}
+
+static int webvtt_read_seek(AVFormatContext *s, int stream_index,
+ int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
+{
+ WebVTTContext *webvtt = s->priv_data;
+ return ff_subtitles_queue_seek(&webvtt->q, s, stream_index,
+ min_ts, ts, max_ts, flags);
+}
+
+static int webvtt_read_close(AVFormatContext *s)
+{
+ WebVTTContext *webvtt = s->priv_data;
+ ff_subtitles_queue_clean(&webvtt->q);
+ return 0;
+}
+
+#define OFFSET(x) offsetof(WebVTTContext, x)
+#define KIND_FLAGS AV_OPT_FLAG_SUBTITLE_PARAM
+
+static const AVOption options[] = {
+ { "kind", "Set kind of WebVTT track", OFFSET(kind), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, KIND_FLAGS, "webvtt_kind" },
+ { "subtitles", "WebVTT subtitles kind", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, 0, "webvtt_kind" },
+ { "captions", "WebVTT captions kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CAPTIONS }, INT_MIN, INT_MAX, 0, "webvtt_kind" },
+ { "descriptions", "WebVTT descriptions kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DESCRIPTIONS }, INT_MIN, INT_MAX, 0, "webvtt_kind" },
+ { "metadata", "WebVTT metadata kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_METADATA }, INT_MIN, INT_MAX, 0, "webvtt_kind" },
+ { NULL }
+};
+
+static const AVClass webvtt_demuxer_class = {
+ .class_name = "WebVTT demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_webvtt_demuxer = {
+ .name = "webvtt",
+ .long_name = NULL_IF_CONFIG_SMALL("WebVTT subtitle"),
+ .priv_data_size = sizeof(WebVTTContext),
+ .read_probe = webvtt_probe,
+ .read_header = webvtt_read_header,
+ .read_packet = webvtt_read_packet,
+ .read_seek2 = webvtt_read_seek,
+ .read_close = webvtt_read_close,
+ .extensions = "vtt",
+ .priv_class = &webvtt_demuxer_class,
+};
diff --git a/libavformat/webvttenc.c b/libavformat/webvttenc.c
new file mode 100644
index 0000000..4827de0
--- /dev/null
+++ b/libavformat/webvttenc.c
@@ -0,0 +1,106 @@
+/*
+ * 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];
+ AVCodecParameters *par = ctx->streams[0]->codecpar;
+ AVIOContext *pb = ctx->pb;
+
+ if (ctx->nb_streams != 1 || par->codec_id != AV_CODEC_ID_WEBVTT) {
+ av_log(ctx, AV_LOG_ERROR, "Exactly one WebVTT stream is needed.\n");
+ return AVERROR(EINVAL);
+ }
+
+ 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 1bfc010..9c2d35c 100644
--- a/libavformat/westwood_aud.c
+++ b/libavformat/westwood_aud.c
@@ -2,20 +2,20 @@
* Westwood Studios AUD Format Demuxer
* Copyright (c) 2003 The FFmpeg project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -69,8 +69,6 @@ static int wsaud_probe(AVProbeData *p)
if (p->buf[10] & 0xFC)
return 0;
- /* note: only check for WS IMA (type 99) right now since there is no
- * support for type 1 */
if (p->buf[11] != 99 && p->buf[11] != 1)
return 0;
@@ -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);
@@ -166,6 +164,12 @@ static int wsaud_read_packet(AVFormatContext *s,
if (ret != chunk_size)
return AVERROR(EIO);
+ if (st->codecpar->channels <= 0) {
+ av_log(s, AV_LOG_ERROR, "invalid number of channels %d\n",
+ st->codecpar->channels);
+ return AVERROR_INVALIDDATA;
+ }
+
/* 2 samples/byte, 1 or 2 samples per frame depending on stereo */
pkt->duration = (chunk_size * 2) / st->codecpar->channels;
}
diff --git a/libavformat/westwood_vqa.c b/libavformat/westwood_vqa.c
index 8bd647e..efb9847 100644
--- a/libavformat/westwood_vqa.c
+++ b/libavformat/westwood_vqa.c
@@ -2,20 +2,20 @@
* Westwood Studios VQA Format Demuxer
* Copyright (c) 2003 The FFmpeg project
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -81,10 +81,10 @@ static int wsvqa_read_header(AVFormatContext *s)
WsVqaDemuxContext *wsvqa = s->priv_data;
AVIOContext *pb = s->pb;
AVStream *st;
- unsigned char *header;
- unsigned char scratch[VQA_PREAMBLE_SIZE];
- unsigned int chunk_tag;
- unsigned int chunk_size;
+ uint8_t *header;
+ uint8_t scratch[VQA_PREAMBLE_SIZE];
+ uint32_t chunk_tag;
+ uint32_t chunk_size;
int fps;
/* initialize the video decoder stream */
@@ -101,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->codecpar->extradata_size = VQA_HEADER_SIZE;
- st->codecpar->extradata = av_mallocz(VQA_HEADER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
- header = (unsigned char *)st->codecpar->extradata;
- if (avio_read(pb, st->codecpar->extradata, VQA_HEADER_SIZE) !=
- VQA_HEADER_SIZE) {
- return AVERROR(EIO);
- }
+ if (ff_get_extradata(s, st->codecpar, pb, VQA_HEADER_SIZE) < 0)
+ return AVERROR(ENOMEM);
+ header = st->codecpar->extradata;
st->codecpar->width = AV_RL16(&header[6]);
st->codecpar->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]);
@@ -149,9 +144,8 @@ static int wsvqa_read_header(AVFormatContext *s)
break;
default:
- av_log (s, AV_LOG_ERROR, " note: unknown chunk seen (%c%c%c%c)\n",
- scratch[0], scratch[1],
- scratch[2], scratch[3]);
+ av_log(s, AV_LOG_ERROR, " note: unknown chunk seen (%s)\n",
+ av_fourcc2str(chunk_tag));
break;
}
@@ -167,26 +161,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_packet_unref(pkt);
+ ret= av_get_packet(pb, pkt, chunk_size);
+ if (ret<0)
return AVERROR(EIO);
- }
switch (chunk_type) {
case SND0_TAG:
@@ -223,9 +214,7 @@ static int wsvqa_read_packet(AVFormatContext *s,
break;
case SND2_TAG:
st->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_WS;
- st->codecpar->extradata_size = 2;
- st->codecpar->extradata = av_mallocz(2 + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
+ if (ff_alloc_extradata(st->codecpar, 2))
return AVERROR(ENOMEM);
AV_WL16(st->codecpar->extradata, wsvqa->version);
break;
@@ -236,7 +225,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 */
@@ -260,7 +250,8 @@ static int wsvqa_read_packet(AVFormatContext *s,
case CMDS_TAG:
break;
default:
- av_log(s, AV_LOG_INFO, "Skipping unknown chunk 0x%08X\n", chunk_type);
+ av_log(s, AV_LOG_INFO, "Skipping unknown chunk %s\n",
+ av_fourcc2str(av_bswap32(chunk_type)));
}
avio_skip(pb, chunk_size + skip_byte);
}
diff --git a/libavformat/wsddec.c b/libavformat/wsddec.c
new file mode 100644
index 0000000..81a4dcc
--- /dev/null
+++ b/libavformat/wsddec.c
@@ -0,0 +1,173 @@
+/*
+ * Wideband Single-bit Data (WSD) 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 "libavutil/timecode.h"
+#include "avformat.h"
+#include "internal.h"
+#include "rawdec.h"
+
+static int wsd_probe(AVProbeData *p)
+{
+ if (p->buf_size < 45 || memcmp(p->buf, "1bit", 4) ||
+ !AV_RB32(p->buf + 36) || !p->buf[44] ||
+ (p->buf[0] >= 0x10 && (AV_RB32(p->buf + 20) < 0x80 || AV_RB32(p->buf + 24) < 0x80)))
+ return 0;
+ return AVPROBE_SCORE_MAX;
+}
+
+static int empty_string(const char *buf, unsigned size)
+{
+ while (size--) {
+ if (*buf++ != ' ')
+ return 0;
+ }
+ return 1;
+}
+
+static int wsd_to_av_channel_layoyt(AVFormatContext *s, int bit)
+{
+ switch (bit) {
+ case 2: return AV_CH_BACK_RIGHT;
+ case 3:
+ avpriv_request_sample(s, "Rr-middle");
+ break;
+ case 4: return AV_CH_BACK_CENTER;
+ case 5:
+ avpriv_request_sample(s, "Lr-middle");
+ break;
+ case 6: return AV_CH_BACK_LEFT;
+ case 24: return AV_CH_LOW_FREQUENCY;
+ case 26: return AV_CH_FRONT_RIGHT;
+ case 27: return AV_CH_FRONT_RIGHT_OF_CENTER;
+ case 28: return AV_CH_FRONT_CENTER;
+ case 29: return AV_CH_FRONT_LEFT_OF_CENTER;
+ case 30: return AV_CH_FRONT_LEFT;
+ default:
+ av_log(s, AV_LOG_WARNING, "reserved channel assignment\n");
+ break;
+ }
+ return 0;
+}
+
+static int get_metadata(AVFormatContext *s, const char *const tag, const unsigned size)
+{
+ uint8_t *buf;
+ if (!(size + 1))
+ return AVERROR(ENOMEM);
+
+ buf = av_malloc(size + 1);
+ if (!buf)
+ return AVERROR(ENOMEM);
+
+ if (avio_read(s->pb, buf, size) != size) {
+ av_free(buf);
+ return AVERROR(EIO);
+ }
+
+ if (empty_string(buf, size)) {
+ av_free(buf);
+ return 0;
+ }
+
+ buf[size] = 0;
+ av_dict_set(&s->metadata, tag, buf, AV_DICT_DONT_STRDUP_VAL);
+ return 0;
+}
+
+static int wsd_read_header(AVFormatContext *s)
+{
+ AVIOContext *pb = s->pb;
+ AVStream *st;
+ int version;
+ uint32_t text_offset, data_offset, channel_assign;
+ char playback_time[AV_TIMECODE_STR_SIZE];
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avio_skip(pb, 8);
+ version = avio_r8(pb);
+ av_log(s, AV_LOG_DEBUG, "version: %i.%i\n", version >> 4, version & 0xF);
+ avio_skip(pb, 11);
+
+ if (version < 0x10) {
+ text_offset = 0x80;
+ data_offset = 0x800;
+ avio_skip(pb, 8);
+ } else {
+ text_offset = avio_rb32(pb);
+ data_offset = avio_rb32(pb);
+ }
+
+ avio_skip(pb, 4);
+ av_timecode_make_smpte_tc_string(playback_time, avio_rb32(pb), 0);
+ av_dict_set(&s->metadata, "playback_time", playback_time, 0);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = s->iformat->raw_codec_id;
+ st->codecpar->sample_rate = avio_rb32(pb) / 8;
+ avio_skip(pb, 4);
+ st->codecpar->channels = avio_r8(pb) & 0xF;
+ st->codecpar->bit_rate = (int64_t)st->codecpar->channels * st->codecpar->sample_rate * 8LL;
+ if (!st->codecpar->channels)
+ return AVERROR_INVALIDDATA;
+
+ avio_skip(pb, 3);
+ channel_assign = avio_rb32(pb);
+ if (!(channel_assign & 1)) {
+ int i;
+ for (i = 1; i < 32; i++)
+ if (channel_assign & (1 << i))
+ st->codecpar->channel_layout |= wsd_to_av_channel_layoyt(s, i);
+ }
+
+ avio_skip(pb, 16);
+ if (avio_rb32(pb))
+ avpriv_request_sample(s, "emphasis");
+
+ if (avio_seek(pb, text_offset, SEEK_SET) >= 0) {
+ get_metadata(s, "title", 128);
+ get_metadata(s, "composer", 128);
+ get_metadata(s, "song_writer", 128);
+ get_metadata(s, "artist", 128);
+ get_metadata(s, "album", 128);
+ get_metadata(s, "genre", 32);
+ get_metadata(s, "date", 32);
+ get_metadata(s, "location", 32);
+ get_metadata(s, "comment", 512);
+ get_metadata(s, "user", 512);
+ }
+
+ return avio_seek(pb, data_offset, SEEK_SET);
+}
+
+AVInputFormat ff_wsd_demuxer = {
+ .name = "wsd",
+ .long_name = NULL_IF_CONFIG_SMALL("Wideband Single-bit Data (WSD)"),
+ .read_probe = wsd_probe,
+ .read_header = wsd_read_header,
+ .read_packet = ff_raw_read_partial_packet,
+ .extensions = "wsd",
+ .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK,
+ .raw_codec_id = AV_CODEC_ID_DSD_MSBF,
+};
diff --git a/libavformat/wtv.h b/libavformat/wtv.h
new file mode 100644
index 0000000..f26ad5e
--- /dev/null
+++ b/libavformat/wtv.h
@@ -0,0 +1,60 @@
+/*
+ * Windows Television (WTV)
+ * Copyright (c) 2010-2011 Peter Ross <pross@xvid.org>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_WTV_H
+#define AVFORMAT_WTV_H
+
+#include "riff.h"
+#include "asf.h"
+
+#define WTV_SECTOR_BITS 12
+#define WTV_SECTOR_SIZE (1 << WTV_SECTOR_BITS)
+#define WTV_BIGSECTOR_BITS 18
+#define WTV_PAD8(x) (((x) + 7) & ~7)
+
+extern const uint8_t ff_timeline_le16[16];
+extern const uint8_t ff_timeline_table_0_entries_Events_le16[62];
+extern const uint8_t ff_table_0_entries_legacy_attrib_le16[58];
+extern const uint8_t ff_table_0_entries_time_le16[40];
+
+extern const ff_asf_guid ff_dir_entry_guid;
+extern const ff_asf_guid ff_wtv_guid;
+extern const ff_asf_guid ff_timestamp_guid;
+extern const ff_asf_guid ff_data_guid;
+extern const ff_asf_guid ff_SBE2_STREAM_DESC_EVENT;
+extern const ff_asf_guid ff_stream1_guid;
+extern const ff_asf_guid ff_sync_guid;
+extern const ff_asf_guid ff_index_guid;
+extern const ff_asf_guid ff_mediatype_audio;
+extern const ff_asf_guid ff_mediatype_video;
+extern const ff_asf_guid ff_format_none;
+extern const AVCodecGuid ff_video_guids[];
+
+extern const ff_asf_guid ff_DSATTRIB_TRANSPORT_PROPERTIES;
+extern const ff_asf_guid ff_metadata_guid;
+extern const ff_asf_guid ff_stream2_guid;
+extern const ff_asf_guid ff_mediasubtype_cpfilters_processed;
+extern const ff_asf_guid ff_format_cpfilters_processed;
+extern const ff_asf_guid ff_format_waveformatex;
+extern const ff_asf_guid ff_format_mpeg2_video;
+extern const ff_asf_guid ff_format_videoinfo2;
+
+#endif /* AVFORMAT_WTV_H */
diff --git a/libavformat/wtv_common.c b/libavformat/wtv_common.c
new file mode 100644
index 0000000..ce4349d
--- /dev/null
+++ b/libavformat/wtv_common.c
@@ -0,0 +1,84 @@
+/*
+ * Windows Television (WTV)
+ * Copyright (c) 2010-2011 Peter Ross <pross@xvid.org>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "wtv.h"
+
+/* WTV GUIDs*/
+const ff_asf_guid ff_dir_entry_guid =
+ {0x92,0xB7,0x74,0x91,0x59,0x70,0x70,0x44,0x88,0xDF,0x06,0x3B,0x82,0xCC,0x21,0x3D};
+const ff_asf_guid ff_wtv_guid =
+ {0xB7,0xD8,0x00,0x20,0x37,0x49,0xDA,0x11,0xA6,0x4E,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
+const ff_asf_guid ff_timestamp_guid =
+ {0x5B,0x05,0xE6,0x1B,0x97,0xA9,0x49,0x43,0x88,0x17,0x1A,0x65,0x5A,0x29,0x8A,0x97};
+const ff_asf_guid ff_data_guid =
+ {0x95,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
+const ff_asf_guid ff_SBE2_STREAM_DESC_EVENT =
+ {0xED,0xA4,0x13,0x23,0x2D,0xBF,0x4F,0x45,0xAD,0x8A,0xD9,0x5B,0xA7,0xF9,0x1F,0xEE};
+const ff_asf_guid ff_stream1_guid =
+ {0xA1,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
+const ff_asf_guid ff_sync_guid =
+ {0x97,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
+const ff_asf_guid ff_index_guid =
+ {0x96,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
+const ff_asf_guid ff_mediatype_audio =
+ {'a','u','d','s',FF_MEDIASUBTYPE_BASE_GUID};
+const ff_asf_guid ff_mediatype_video =
+ {'v','i','d','s',FF_MEDIASUBTYPE_BASE_GUID};
+const ff_asf_guid ff_format_none =
+ {0xD6,0x17,0x64,0x0F,0x18,0xC3,0xD0,0x11,0xA4,0x3F,0x00,0xA0,0xC9,0x22,0x31,0x96};
+
+/* declare utf16le strings */
+#define _ , 0,
+const uint8_t ff_timeline_le16[] =
+ {'t'_'i'_'m'_'e'_'l'_'i'_'n'_'e', 0};
+const uint8_t ff_timeline_table_0_entries_Events_le16[] =
+ {'t'_'i'_'m'_'e'_'l'_'i'_'n'_'e'_'.'_'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'E'_'v'_'e'_'n'_'t'_'s', 0};
+const uint8_t ff_table_0_entries_legacy_attrib_le16[] =
+ {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0};
+const uint8_t ff_table_0_entries_time_le16[] =
+ {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'e'_'n'_'t'_'r'_'i'_'e'_'s'_'.'_'t'_'i'_'m'_'e', 0};
+#undef _
+
+const ff_asf_guid ff_DSATTRIB_TRANSPORT_PROPERTIES =
+ {0x12,0xF6,0x22,0xB6,0xAD,0x47,0x71,0x46,0xAD,0x6C,0x05,0xA9,0x8E,0x65,0xDE,0x3A};
+const ff_asf_guid ff_metadata_guid =
+ {0x5A,0xFE,0xD7,0x6D,0xC8,0x1D,0x8F,0x4A,0x99,0x22,0xFA,0xB1,0x1C,0x38,0x14,0x53};
+const ff_asf_guid ff_stream2_guid =
+ {0xA2,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
+
+/* Media subtypes */
+const ff_asf_guid ff_mediasubtype_cpfilters_processed =
+ {0x28,0xBD,0xAD,0x46,0xD0,0x6F,0x96,0x47,0x93,0xB2,0x15,0x5C,0x51,0xDC,0x04,0x8D};
+
+/* Formats */
+const ff_asf_guid ff_format_cpfilters_processed =
+ {0x6F,0xB3,0x39,0x67,0x5F,0x1D,0xC2,0x4A,0x81,0x92,0x28,0xBB,0x0E,0x73,0xD1,0x6A};
+const ff_asf_guid ff_format_waveformatex =
+ {0x81,0x9F,0x58,0x05,0x56,0xC3,0xCE,0x11,0xBF,0x01,0x00,0xAA,0x00,0x55,0x59,0x5A};
+const ff_asf_guid ff_format_mpeg2_video =
+ {0xE3,0x80,0x6D,0xE0,0x46,0xDB,0xCF,0x11,0xB4,0xD1,0x00,0x80,0x5F,0x6C,0xBB,0xEA};
+const ff_asf_guid ff_format_videoinfo2 =
+ {0xA0,0x76,0x2A,0xF7,0x0A,0xEB,0xD0,0x11,0xAC,0xE4,0x00,0x00,0xC0,0xCC,0x16,0xBA};
+
+const AVCodecGuid ff_video_guids[] = {
+ {AV_CODEC_ID_MPEG2VIDEO, {0x26,0x80,0x6D,0xE0,0x46,0xDB,0xCF,0x11,0xB4,0xD1,0x00,0x80,0x5F,0x6C,0xBB,0xEA}},
+ {AV_CODEC_ID_NONE}
+};
diff --git a/libavformat/wtv.c b/libavformat/wtvdec.c
index d750cef..301163b 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 formatting GUIDs */
@@ -49,18 +47,12 @@
* File system routines
*/
-#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;
@@ -69,11 +61,11 @@ 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);
}
/**
- * @return bytes read, 0 on end of file, or <0 on error
+ * @return bytes read, AVERROR_EOF on end of file, or <0 on error
*/
static int wtvfile_read_packet(void *opaque, uint8_t *buf, int buf_size)
{
@@ -83,8 +75,8 @@ 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)
- return 0;
+ if (wf->position >= wf->length || avio_feof(pb))
+ return AVERROR_EOF;
buf_size = FFMIN(buf_size, wf->length - wf->position);
while(nread < buf_size) {
@@ -162,6 +154,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;
@@ -178,7 +171,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) {
@@ -186,13 +178,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;
@@ -203,19 +194,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)) {
@@ -227,32 +222,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
@@ -269,7 +261,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;
@@ -282,7 +274,7 @@ static AVIOContext * wtvfile_open2(AVFormatContext *s, const uint8_t *buf, int b
"bad filename length, remaining directory entries ignored\n");
break;
}
- if (48 + name_size > buf_end - buf) {
+ if (48 + (int64_t)name_size > buf_end - buf) {
av_log(s, AV_LOG_ERROR, "filename exceeds buffer size; remaining directory entries ignored\n");
break;
}
@@ -310,9 +302,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);
avio_context_free(&pb);
}
@@ -325,10 +317,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 interactive seeking */
+ int64_t pts; /**< pts for next data chunk */
+ int64_t last_valid_pts; /**< latest valid pts, used for interactive seeking */
/* maintain private seek index, as the AVIndexEntry->pos is relative to the
start of the 'timeline' file, not the file system (AVFormatContext->pb) */
@@ -338,18 +330,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 =
@@ -372,10 +352,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 =
@@ -386,8 +362,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 =
@@ -397,71 +371,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)
@@ -470,6 +430,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));
@@ -486,22 +447,31 @@ static void get_attachment(AVFormatContext *s, AVIOContext *pb, int length)
if (!st)
goto done;
av_dict_set(&st->metadata, "title", description, 0);
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->codec_id = AV_CODEC_ID_MJPEG;
- st->codecpar->codec_type = AVMEDIA_TYPE_ATTACHMENT;
- st->codecpar->extradata = av_mallocz(filesize);
st->id = -1;
- if (!st->codecpar->extradata)
+ ret = av_get_packet(pb, &st->attached_pic, filesize);
+ if (ret < 0)
goto done;
- st->codecpar->extradata_size = filesize;
- avio_read(pb, st->codecpar->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;
@@ -518,14 +488,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);
@@ -557,14 +536,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;
@@ -586,7 +565,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, NULL);
+ st->codecpar->codec_tag = ff_get_bmp_header(pb, st, NULL);
return 72 + 40;
}
@@ -659,12 +638,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;
@@ -682,16 +661,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(s, pb, st->codecpar, size);
+ if (!ff_guidcmp(formattype, ff_format_waveformatex)) {
+ int ret = ff_get_wav_header(s, pb, st->codecpar, 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);
}
@@ -709,18 +688,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);
}
@@ -728,7 +708,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->codecpar->codec_id = ff_codec_get_id(ff_codec_bmp_tags, AV_RL32(subtype));
} else {
- st->codecpar->codec_id = ff_codec_guid_get_id(video_guids, subtype);
+ st->codecpar->codec_id = ff_codec_guid_get_id(ff_video_guids, subtype);
}
if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
av_log(s, AV_LOG_WARNING, "unknown subtype:"FF_PRI_GUID"\n", FF_ARG_GUID(subtype));
@@ -738,7 +718,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->codecpar->codec_id = AV_CODEC_ID_DVB_SUBTITLE;
@@ -748,14 +728,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->codecpar->codec_id = AV_CODEC_ID_DVB_TELETEXT;
+ st->codecpar->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;
@@ -774,6 +754,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) {
+ int64_t 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
@@ -784,19 +784,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;
@@ -809,9 +816,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);
@@ -885,7 +892,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);
@@ -898,15 +905,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) {
@@ -914,10 +920,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}) ||
@@ -930,9 +941,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));
@@ -942,18 +954,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;
@@ -961,7 +961,7 @@ static int read_header(AVFormatContext *s)
uint8_t root[WTV_SECTOR_SIZE];
AVIOContext *pb;
int64_t timeline_pos;
- int ret;
+ int64_t ret;
wtv->epoch =
wtv->pts =
@@ -985,7 +985,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;
@@ -999,21 +999,23 @@ 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);
}
+ s->ctx_flags |= AVFMTCTX_NOHEADER; // Needed for noStreams.wtv
+
/* 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);
@@ -1021,24 +1023,25 @@ 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;
+ AVIndexEntry *e = wtv->index_entries;
+ AVIndexEntry *e_end = wtv->index_entries + wtv->nb_index_entries - 1;
+ uint64_t last_position = 0;
while (1) {
uint64_t frame_nb = avio_rl64(pb);
uint64_t position = avio_rl64(pb);
- if (pb->eof_reached)
- break;
- for (i = wtv->nb_index_entries - 1; i >= 0; i--) {
- AVIndexEntry *e = wtv->index_entries + i;
- if (frame_nb > e->size)
- break;
- if (position > e->pos)
- e->pos = position;
+ while (e <= e_end && frame_nb > e->size) {
+ e->pos = last_position;
+ e++;
}
+ if (avio_feof(pb))
+ break;
+ last_position = position;
}
+ e_end->pos = last_position;
wtvfile_close(pb);
- st->duration = wtv->index_entries[wtv->nb_index_entries - 1].timestamp;
+ st->duration = e_end->timestamp;
}
}
}
@@ -1087,26 +1090,30 @@ static int read_seek(AVFormatContext *s, int stream_index,
i = ff_index_search_timestamp(wtv->index_entries, wtv->nb_index_entries, ts_relative, flags);
if (i < 0) {
- if (wtv->last_valid_pts == AV_NOPTS_VALUE || ts < wtv->last_valid_pts)
- avio_seek(pb, 0, SEEK_SET);
- else if (st->duration != AV_NOPTS_VALUE && ts_relative > st->duration && wtv->nb_index_entries)
- avio_seek(pb, wtv->index_entries[wtv->nb_index_entries - 1].pos, SEEK_SET);
+ if (wtv->last_valid_pts == AV_NOPTS_VALUE || ts < wtv->last_valid_pts) {
+ if (avio_seek(pb, 0, SEEK_SET) < 0)
+ return -1;
+ } else if (st->duration != AV_NOPTS_VALUE && ts_relative > st->duration && wtv->nb_index_entries) {
+ if (avio_seek(pb, wtv->index_entries[wtv->nb_index_entries - 1].pos, SEEK_SET) < 0)
+ return -1;
+ }
if (parse_chunks(s, SEEK_TO_PTS, ts, 0) < 0)
return AVERROR(ERANGE);
return 0;
}
+ if (avio_seek(pb, wtv->index_entries[i].pos, SEEK_SET) < 0)
+ return -1;
wtv->pts = wtv->index_entries[i].timestamp;
if (wtv->epoch != AV_NOPTS_VALUE)
wtv->pts += wtv->epoch;
wtv->last_valid_pts = wtv->pts;
- avio_seek(pb, wtv->index_entries[i].pos, SEEK_SET);
return 0;
}
static int read_close(AVFormatContext *s)
{
WtvContext *wtv = s->priv_data;
- av_free(wtv->index_entries);
+ av_freep(&wtv->index_entries);
wtvfile_close(wtv->pb);
return 0;
}
diff --git a/libavformat/wtvenc.c b/libavformat/wtvenc.c
new file mode 100644
index 0000000..4a68b81
--- /dev/null
+++ b/libavformat/wtvenc.c
@@ -0,0 +1,846 @@
+/*
+ * 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_array(*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->codecpar->width, st->codecpar->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->codecpar->width);
+ avio_wl32(pb, st->codecpar->height);
+
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+ avio_wl32(pb, 0);
+
+ avio_wl32(pb, st->codecpar->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->codecpar, 0, 1);
+
+ if (st->codecpar->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
+ int padding = (st->codecpar->extradata_size & 3) ? 4 - (st->codecpar->extradata_size & 3) : 0;
+ /* MPEG2VIDEOINFO */
+ avio_wl32(pb, 0);
+ avio_wl32(pb, st->codecpar->extradata_size + padding);
+ avio_wl32(pb, -1);
+ avio_wl32(pb, -1);
+ avio_wl32(pb, 0);
+ avio_write(pb, st->codecpar->extradata, st->codecpar->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->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ g = ff_get_codec_guid(st->codecpar->codec_id, ff_video_guids);
+ media_type = &ff_mediatype_video;
+ format_type = st->codecpar->codec_id == AV_CODEC_ID_MPEG2VIDEO ? &ff_format_mpeg2_video : &ff_format_videoinfo2;
+ tags = ff_codec_bmp_tags;
+ } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ g = ff_get_codec_guid(st->codecpar->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->codecpar->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->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ put_videoinfoheader2(pb, st);
+ } else {
+ if (ff_put_wav_header(s, pb, st->codecpar, 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->codecpar->codec_id);
+ if (!tag) {
+ av_log(s, AV_LOG_ERROR, "unsupported codec_id (0x%x)\n", st->codecpar->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->codecpar->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->codecpar->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->codecpar->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->codecpar->codec_type);
+ return -1;
+ }
+ if (!i)
+ write_sync(s);
+ }
+
+ for (i = 0; i < s->nb_streams; i++) {
+ st = s->streams[i];
+ if (st->codecpar->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->codecpar->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;
+ AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar;
+
+ 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, par->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->codecpar->codec_id == AV_CODEC_ID_MJPEG && !wctx->thumbnail.size) {
+ av_packet_ref(&wctx->thumbnail, pkt);
+ return 0;
+ } else if (st->codecpar->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;
+
+ ff_standardize_creation_time(s);
+ //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_packet_unref(&wctx->thumbnail);
+ return 0;
+}
+
+AVOutputFormat ff_wtv_muxer = {
+ .name = "wtv",
+ .long_name = NULL_IF_CONFIG_SMALL("Windows Television (WTV)"),
+ .extensions = "wtv",
+ .priv_data_size = sizeof(WtvContext),
+ .audio_codec = AV_CODEC_ID_AC3,
+ .video_codec = AV_CODEC_ID_MPEG2VIDEO,
+ .write_header = write_header,
+ .write_packet = write_packet,
+ .write_trailer = write_trailer,
+ .codec_tag = (const AVCodecTag* const []){ ff_codec_bmp_tags,
+ ff_codec_wav_tags, 0 },
+};
diff --git a/libavformat/wv.c b/libavformat/wv.c
index 724256b..0f4f807 100644
--- a/libavformat/wv.c
+++ b/libavformat/wv.c
@@ -1,20 +1,20 @@
/*
* WavPack shared functions
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/wv.h b/libavformat/wv.h
index ef285d2..07d2e1b 100644
--- a/libavformat/wv.h
+++ b/libavformat/wv.h
@@ -1,20 +1,20 @@
/*
* WavPack shared functions
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
diff --git a/libavformat/wvdec.c b/libavformat/wvdec.c
index 51aae08..8252656 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;
@@ -122,7 +125,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);
@@ -236,7 +239,8 @@ static int wv_read_header(AVFormatContext *s)
st->codecpar->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 & AVIO_SEEKABLE_NORMAL) {
int64_t cur = avio_tell(s->pb);
@@ -257,7 +261,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)
@@ -293,6 +297,7 @@ static int wv_read_packet(AVFormatContext *s, AVPacket *pkt)
}
}
pkt->stream_index = 0;
+ pkt->pos = pos;
wc->block_parsed = 1;
pkt->pts = wc->header.block_idx;
block_samples = wc->header.samples;
@@ -302,41 +307,6 @@ static int wv_read_packet(AVFormatContext *s, AVPacket *pkt)
else
pkt->duration = block_samples;
- av_add_index_entry(s->streams[0], pos, pkt->pts, 0, 0, AVINDEX_KEYFRAME);
- return 0;
-}
-
-static int wv_read_seek(AVFormatContext *s, int stream_index,
- int64_t timestamp, int flags)
-{
- AVStream *st = s->streams[stream_index];
- WVContext *wc = s->priv_data;
- AVPacket pkt1, *pkt = &pkt1;
- int ret;
- int index = av_index_search_timestamp(st, timestamp, flags);
- int64_t pos, pts;
-
- /* if found, seek there */
- if (index >= 0 &&
- timestamp <= st->index_entries[st->nb_index_entries - 1].timestamp) {
- wc->block_parsed = 1;
- avio_seek(s->pb, st->index_entries[index].pos, SEEK_SET);
- return 0;
- }
- /* if timestamp is out of bounds, return error */
- if (timestamp < 0 || timestamp >= s->duration)
- return AVERROR(EINVAL);
-
- pos = avio_tell(s->pb);
- do {
- ret = av_read_frame(s, pkt);
- if (ret < 0) {
- avio_seek(s->pb, pos, SEEK_SET);
- return ret;
- }
- pts = pkt->pts;
- av_packet_unref(pkt);
- } while(pts < timestamp);
return 0;
}
@@ -347,5 +317,5 @@ AVInputFormat ff_wv_demuxer = {
.read_probe = wv_probe,
.read_header = wv_read_header,
.read_packet = wv_read_packet,
- .read_seek = wv_read_seek,
+ .flags = AVFMT_GENERIC_INDEX,
};
diff --git a/libavformat/wvedec.c b/libavformat/wvedec.c
new file mode 100644
index 0000000..89c0001
--- /dev/null
+++ b/libavformat/wvedec.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015 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 wve_probe(AVProbeData *p)
+{
+ if (memcmp(p->buf, "ALawSoundFile**\0\017\020", 18) ||
+ memcmp(p->buf + 22, "\0\0\0\1\0\0\0\0\0\0", 10))
+ return 0;
+ return AVPROBE_SCORE_MAX;
+}
+
+static int wve_read_header(AVFormatContext *s)
+{
+ AVStream *st;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avio_skip(s->pb, 18);
+ st->duration = avio_rb32(s->pb);
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_ALAW;
+ st->codecpar->sample_rate = 8000;
+ st->codecpar->channels = 1;
+ st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id);
+ st->codecpar->block_align = st->codecpar->bits_per_coded_sample * st->codecpar->channels / 8;
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+ avio_skip(s->pb, 10);
+
+ return 0;
+}
+
+AVInputFormat ff_wve_demuxer = {
+ .name = "wve",
+ .long_name = NULL_IF_CONFIG_SMALL("Psion 3 audio"),
+ .read_probe = wve_probe,
+ .read_header = wve_read_header,
+ .read_packet = ff_pcm_read_packet,
+ .read_seek = ff_pcm_read_seek,
+};
diff --git a/libavformat/wvenc.c b/libavformat/wvenc.c
index 63f1ea6..8743739 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 06b9c3f..810e0c0 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,8 +84,11 @@ static int xa_read_header(AVFormatContext *s)
avio_skip(pb, 2); /* Skip block align */
avio_skip(pb, 2); /* Skip bits-per-sample */
+ if (!st->codecpar->channels || !st->codecpar->sample_rate)
+ return AVERROR_INVALIDDATA;
+
st->codecpar->bit_rate = av_clip(15LL * st->codecpar->channels * 8 *
- st->codecpar->sample_rate / 28, 0, INT_MAX);
+ st->codecpar->sample_rate / 28, 0, INT_MAX);
avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
st->start_time = 0;
diff --git a/libavformat/xmv.c b/libavformat/xmv.c
index b2112b0..b974e5a 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,87 @@
#include "avformat.h"
#include "internal.h"
#include "riff.h"
+#include "libavutil/avassert.h"
+/** The min size of an XMV header. */
#define XMV_MIN_HEADER_SIZE 36
+/** Audio flag: ADPCM'd 5.1 stream, front left / right channels */
#define XMV_AUDIO_ADPCM51_FRONTLEFTRIGHT 1
+/** Audio flag: ADPCM'd 5.1 stream, front center / low frequency channels */
#define XMV_AUDIO_ADPCM51_FRONTCENTERLOW 2
+/** Audio flag: ADPCM'd 5.1 stream, rear left / right channels */
#define XMV_AUDIO_ADPCM51_REARLEFTRIGHT 4
+/** Audio flag: Any of the ADPCM'd 5.1 stream flags. */
#define XMV_AUDIO_ADPCM51 (XMV_AUDIO_ADPCM51_FRONTLEFTRIGHT | \
XMV_AUDIO_ADPCM51_FRONTCENTERLOW | \
XMV_AUDIO_ADPCM51_REARLEFTRIGHT)
#define XMV_BLOCK_ALIGN_SIZE 36
-typedef struct XMVAudioTrack {
- uint16_t compression;
- uint16_t channels;
- uint32_t sample_rate;
- uint16_t bits_per_sample;
- uint32_t bit_rate;
- uint16_t flags;
- uint16_t block_align;
- uint16_t block_samples;
-
- enum AVCodecID codec_id;
-} XMVAudioTrack;
-
+/** A video packet with an XMV file. */
typedef struct XMVVideoPacket {
- /* The decoder stream index for this video packet. */
- int stream_index;
-
- uint32_t data_size;
- uint32_t data_offset;
+ int created;
+ int stream_index; ///< The decoder stream index for this video packet.
- uint32_t current_frame;
- uint32_t frame_count;
+ uint32_t data_size; ///< The size of the remaining video data.
+ uint64_t data_offset; ///< The offset of the video data within the file.
- /* Does the video packet contain extra data? */
- int has_extradata;
+ uint32_t current_frame; ///< The current frame within this video packet.
+ uint32_t frame_count; ///< The amount of frames within this video packet.
- /* Extra data */
- uint8_t extradata[4];
+ int has_extradata; ///< Does the video packet contain extra data?
+ uint8_t extradata[4]; ///< The extra data
- int64_t last_pts;
- int64_t pts;
+ int64_t last_pts; ///< PTS of the last video frame.
+ int64_t pts; ///< PTS of the most current video frame.
} XMVVideoPacket;
+/** An audio packet with an XMV file. */
typedef struct XMVAudioPacket {
- /* The decoder stream index for this audio packet. */
- int stream_index;
+ int created;
+ 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.
+ int32_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; ///< Size of the current packet.
+ uint32_t next_packet_size; ///< Size of the next packet.
- uint32_t this_packet_size;
- uint32_t next_packet_size;
+ uint64_t this_packet_offset; ///< Offset of the current packet.
+ uint64_t next_packet_offset; ///< Offset of the next packet.
- uint32_t this_packet_offset;
- uint32_t next_packet_offset;
+ uint16_t current_stream; ///< The index of the stream currently handling.
+ uint16_t stream_count; ///< The number of streams in this file.
- uint16_t current_stream;
- uint16_t stream_count;
+ uint32_t video_duration;
+ uint32_t video_width;
+ uint32_t video_height;
- 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 +136,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;
}
@@ -142,13 +145,14 @@ static int xmv_read_header(AVFormatContext *s)
{
XMVDemuxContext *xmv = s->priv_data;
AVIOContext *pb = s->pb;
- AVStream *vst = NULL;
uint32_t file_version;
uint32_t this_packet_size;
uint16_t audio_track;
int ret;
+ s->ctx_flags |= AVFMTCTX_NOHEADER;
+
avio_skip(pb, 4); /* Next packet size */
this_packet_size = avio_rl32(pb);
@@ -160,24 +164,11 @@ static int xmv_read_header(AVFormatContext *s)
if ((file_version != 4) && (file_version != 2))
avpriv_request_sample(s, "Uncommon version %"PRIu32"", file_version);
+ /* Video tracks */
- /* Video track */
-
- vst = avformat_new_stream(s, NULL);
- if (!vst)
- return AVERROR(ENOMEM);
-
- avpriv_set_pts_info(vst, 32, 1, 1000);
-
- vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
- vst->codecpar->codec_id = AV_CODEC_ID_WMV2;
- vst->codecpar->codec_tag = MKBETAG('W', 'M', 'V', '2');
- vst->codecpar->width = avio_rl32(pb);
- vst->codecpar->height = avio_rl32(pb);
-
- vst->duration = avio_rl32(pb);
-
- xmv->video.stream_index = vst->index;
+ xmv->video_width = avio_rl32(pb);
+ xmv->video_height = avio_rl32(pb);
+ xmv->video_duration = avio_rl32(pb);
/* Audio tracks */
@@ -185,36 +176,29 @@ 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_mallocz_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];
- 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;
+ XMVAudioPacket *packet = &xmv->audio[audio_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,42 +206,21 @@ 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 <= 0 ||
+ 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;
goto fail;
}
-
- ast = avformat_new_stream(s, NULL);
- if (!ast) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
-
- ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
- ast->codecpar->codec_id = track->codec_id;
- ast->codecpar->codec_tag = track->compression;
- ast->codecpar->channels = track->channels;
- ast->codecpar->sample_rate = track->sample_rate;
- ast->codecpar->bits_per_coded_sample = track->bits_per_sample;
- ast->codecpar->bit_rate = track->bit_rate;
- ast->codecpar->block_align = 36 * track->channels;
-
- avpriv_set_pts_info(ast, 32, track->block_samples, track->sample_rate);
-
- packet->stream_index = ast->index;
-
- ast->duration = vst->duration;
}
- /** 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 +266,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);
@@ -323,6 +287,26 @@ static int xmv_process_packet_header(AVFormatContext *s)
xmv->video.has_extradata = (data[3] & 0x80) != 0;
+ if (!xmv->video.created) {
+ AVStream *vst = avformat_new_stream(s, NULL);
+ if (!vst)
+ return AVERROR(ENOMEM);
+
+ avpriv_set_pts_info(vst, 32, 1, 1000);
+
+ vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ vst->codecpar->codec_id = AV_CODEC_ID_WMV2;
+ vst->codecpar->codec_tag = MKBETAG('W', 'M', 'V', '2');
+ vst->codecpar->width = xmv->video_width;
+ vst->codecpar->height = xmv->video_height;
+
+ vst->duration = xmv->video_duration;
+
+ xmv->video.stream_index = vst->index;
+
+ xmv->video.created = 1;
+ }
+
/* Adding the audio data sizes and the video data size keeps you 4 bytes
* short for every audio track. But as playing around with XMV files with
* ADPCM audio showed, taking the extra 4 bytes from the audio data gives
@@ -337,7 +321,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 */
@@ -348,6 +332,29 @@ static int xmv_process_packet_header(AVFormatContext *s)
if (avio_read(pb, data, 4) != 4)
return AVERROR(EIO);
+ if (!packet->created) {
+ AVStream *ast = avformat_new_stream(s, NULL);
+ if (!ast)
+ return AVERROR(ENOMEM);
+
+ ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ ast->codecpar->codec_id = packet->codec_id;
+ ast->codecpar->codec_tag = packet->compression;
+ ast->codecpar->channels = packet->channels;
+ ast->codecpar->sample_rate = packet->sample_rate;
+ ast->codecpar->bits_per_coded_sample = packet->bits_per_sample;
+ ast->codecpar->bit_rate = packet->bit_rate;
+ ast->codecpar->block_align = 36 * packet->channels;
+
+ avpriv_set_pts_info(ast, 32, packet->block_samples, packet->sample_rate);
+
+ packet->stream_index = ast->index;
+
+ ast->duration = xmv->video_duration;
+
+ packet->created = 1;
+ }
+
packet->data_size = AV_RL32(data) & 0x007FFFFF;
if ((packet->data_size == 0) && (audio_track != 0))
/* This happens when I create an XMV with several identical audio
@@ -357,9 +364,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 +394,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->codecpar->extradata_size < 4) {
- av_free(vst->codecpar->extradata);
+ av_freep(&vst->codecpar->extradata);
- vst->codecpar->extradata =
- av_malloc(4 + AV_INPUT_BUFFER_PADDING_SIZE);
- vst->codecpar->extradata_size = 4;
+ if ((ret = ff_alloc_extradata(vst->codecpar, 4)) < 0)
+ return ret;
}
memcpy(vst->codecpar->extradata, xmv->video.extradata, 4);
@@ -411,6 +417,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 +472,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 +497,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 +512,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;
@@ -555,16 +564,17 @@ static int xmv_read_packet(AVFormatContext *s,
/* Fetch a video frame */
result = xmv_fetch_video_packet(s, pkt);
- if (result)
- return result;
-
} else {
/* Fetch an audio frame */
result = xmv_fetch_audio_packet(s, pkt, xmv->current_stream - 1);
- if (result)
- return result;
}
+ if (result) {
+ xmv->current_stream = 0;
+ xmv->video.current_frame = xmv->video.frame_count;
+ return result;
+ }
+
/* Increase our counters */
if (++xmv->current_stream >= xmv->stream_count) {
@@ -578,6 +588,7 @@ static int xmv_read_packet(AVFormatContext *s,
AVInputFormat ff_xmv_demuxer = {
.name = "xmv",
.long_name = NULL_IF_CONFIG_SMALL("Microsoft XMV"),
+ .extensions = "xmv",
.priv_data_size = sizeof(XMVDemuxContext),
.read_probe = xmv_probe,
.read_header = xmv_read_header,
diff --git a/libavformat/xvag.c b/libavformat/xvag.c
new file mode 100644
index 0000000..22e4f1e
--- /dev/null
+++ b/libavformat/xvag.c
@@ -0,0 +1,113 @@
+/*
+ * XVAG demuxer
+ * Copyright (c) 2015 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/bswap.h"
+#include "libavcodec/internal.h"
+#include "avformat.h"
+#include "internal.h"
+
+static int xvag_probe(AVProbeData *p)
+{
+ if (memcmp(p->buf, "XVAG", 4) ||
+ memcmp(p->buf+32, "fmat", 4))
+ return 0;
+
+ return AVPROBE_SCORE_MAX;
+}
+
+static int xvag_read_header(AVFormatContext *s)
+{
+ unsigned offset, big_endian, codec;
+ AVStream *st;
+
+ avio_skip(s->pb, 4);
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+
+ offset = avio_rl32(s->pb);
+ big_endian = offset > av_bswap32(offset);
+ if (big_endian) {
+ offset = av_bswap32(offset);
+ avio_skip(s->pb, 28);
+ codec = avio_rb32(s->pb);
+ st->codecpar->channels = avio_rb32(s->pb);
+ avio_skip(s->pb, 4);
+ st->duration = avio_rb32(s->pb);
+ avio_skip(s->pb, 8);
+ st->codecpar->sample_rate = avio_rb32(s->pb);
+ } else {
+ avio_skip(s->pb, 28);
+ codec = avio_rl32(s->pb);
+ st->codecpar->channels = avio_rl32(s->pb);
+ avio_skip(s->pb, 4);
+ st->duration = avio_rl32(s->pb);
+ avio_skip(s->pb, 8);
+ st->codecpar->sample_rate = avio_rl32(s->pb);
+ }
+
+ if (st->codecpar->sample_rate <= 0)
+ return AVERROR_INVALIDDATA;
+ if (st->codecpar->channels <= 0 || st->codecpar->channels > FF_SANE_NB_CHANNELS)
+ return AVERROR_INVALIDDATA;
+
+ switch (codec) {
+ case 0x1c:
+ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_PSX;
+ st->codecpar->block_align = 16 * st->codecpar->channels;
+ break;
+ default:
+ avpriv_request_sample(s, "codec %X", codec);
+ return AVERROR_PATCHWELCOME;
+ };
+
+ avio_skip(s->pb, offset - avio_tell(s->pb));
+
+ if (avio_rb16(s->pb) == 0xFFFB) {
+ st->codecpar->codec_id = AV_CODEC_ID_MP3;
+ st->codecpar->block_align = 0x1000;
+ st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
+ }
+
+ avio_skip(s->pb, -2);
+ avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
+
+ return 0;
+}
+
+static int xvag_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVCodecParameters *par = s->streams[0]->codecpar;
+
+ return av_get_packet(s->pb, pkt, par->block_align);
+}
+
+AVInputFormat ff_xvag_demuxer = {
+ .name = "xvag",
+ .long_name = NULL_IF_CONFIG_SMALL("Sony PS3 XVAG"),
+ .read_probe = xvag_probe,
+ .read_header = xvag_read_header,
+ .read_packet = xvag_read_packet,
+ .extensions = "xvag",
+};
diff --git a/libavformat/xwma.c b/libavformat/xwma.c
index 006f60d..1c18772 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(s, pb, st->codecpar, size);
+ ret = ff_get_wav_header(s, pb, st->codecpar, size, 0);
if (ret < 0)
return ret;
st->need_parsing = AVSTREAM_PARSE_NONE;
@@ -85,9 +85,11 @@ static int xwma_read_header(AVFormatContext *s)
* extradata for that. Thus, ask the user for feedback, but try to go on
* anyway.
*/
- if (st->codecpar->codec_id != AV_CODEC_ID_WMAV2) {
- avpriv_request_sample(s, "Unexpected codec (tag 0x04%"PRIx32"; id %d)",
- st->codecpar->codec_tag, st->codecpar->codec_id);
+ if (st->codecpar->codec_id != AV_CODEC_ID_WMAV2 &&
+ st->codecpar->codec_id != AV_CODEC_ID_WMAPRO) {
+ avpriv_request_sample(s, "Unexpected codec (tag %s; id %d)",
+ av_fourcc2str(st->codecpar->codec_tag),
+ st->codecpar->codec_id);
} else {
/* In all xWMA files I have seen, there is no extradata. But the WMA
* codecs require extradata, so we provide our own fake extradata.
@@ -103,12 +105,18 @@ static int xwma_read_header(AVFormatContext *s)
*/
avpriv_request_sample(s, "Unexpected extradata (%d bytes)",
st->codecpar->extradata_size);
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_WMAPRO) {
+ if (ff_alloc_extradata(st->codecpar, 18))
+ return AVERROR(ENOMEM);
+
+ memset(st->codecpar->extradata, 0, st->codecpar->extradata_size);
+ st->codecpar->extradata[ 0] = st->codecpar->bits_per_coded_sample;
+ st->codecpar->extradata[14] = 224;
} else {
- st->codecpar->extradata_size = 6;
- st->codecpar->extradata = av_mallocz(6 + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!st->codecpar->extradata)
+ if (ff_alloc_extradata(st->codecpar, 6))
return AVERROR(ENOMEM);
+ memset(st->codecpar->extradata, 0, st->codecpar->extradata_size);
/* setup extradata with our experimentally obtained value */
st->codecpar->extradata[4] = 31;
}
@@ -131,7 +139,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,13 +175,13 @@ 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.
* for processing later on.
*/
- dpds_table = av_malloc(dpds_table_size * sizeof(uint32_t));
+ dpds_table = av_malloc_array(dpds_table_size, sizeof(uint32_t));
if (!dpds_table) {
return AVERROR(ENOMEM);
}
diff --git a/libavformat/yop.c b/libavformat/yop.c
index c782ecd..e6fd896 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->codecpar->extradata = av_mallocz(8 + AV_INPUT_BUFFER_PADDING_SIZE);
-
- if (!video_stream->codecpar->extradata)
+ if (ff_alloc_extradata(video_stream->codecpar, 8))
return AVERROR(ENOMEM);
- video_stream->codecpar->extradata_size = 8;
// Audio
audio_par = audio_stream->codecpar;
@@ -98,6 +101,8 @@ static int yop_read_header(AVFormatContext *s)
yop->palette_size = video_par->extradata[0] * 3 + 4;
yop->audio_block_length = AV_RL16(video_par->extradata + 6);
+ video_par->bit_rate = 8 * (yop->frame_size - yop->audio_block_length) * frame_rate;
+
// 1840 samples per frame, 1 nibble per sample; hence 1840/2 = 920
if (yop->audio_block_length < 920 ||
yop->audio_block_length + yop->palette_size >= yop->frame_size) {
diff --git a/libavformat/yuv4mpeg.h b/libavformat/yuv4mpeg.h
index 7891ed6..eba7337 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
*/
@@ -23,5 +23,6 @@
#define Y4M_MAGIC "YUV4MPEG2"
#define Y4M_FRAME_MAGIC "FRAME"
+#define Y4M_FRAME_MAGIC_LEN 6
#endif /* AVFORMAT_YUV4MPEG_H */
diff --git a/libavformat/yuv4mpegdec.c b/libavformat/yuv4mpegdec.c
index f30e188..462b823 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
*/
@@ -81,20 +81,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 "
@@ -121,10 +153,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
@@ -147,6 +178,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)
@@ -190,13 +251,19 @@ static int yuv4_read_header(AVFormatContext *s)
st->codecpar->height = height;
av_reduce(&raten, &rated, raten, rated, (1UL << 31) - 1);
avpriv_set_pts_info(st, 64, rated, raten);
- st->avg_frame_rate = av_inv_q(st->time_base);
- st->codecpar->format = pix_fmt;
- st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
- st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
- st->sample_aspect_ratio = (AVRational){ aspectn, aspectd };
- st->codecpar->chroma_location = chroma_sample_location;
- st->codecpar->field_order = field_order;
+ st->avg_frame_rate = av_inv_q(st->time_base);
+ st->codecpar->format = pix_fmt;
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
+ st->sample_aspect_ratio = (AVRational){ aspectn, aspectd };
+ st->codecpar->chroma_location = chroma_sample_location;
+ st->codecpar->field_order = field_order;
+ s->packet_size = av_image_get_buffer_size(st->codecpar->format, width, height, 1) + Y4M_FRAME_MAGIC_LEN;
+ if ((int) s->packet_size < 0)
+ return s->packet_size;
+ s->internal->data_offset = avio_tell(pb);
+
+ st->duration = (avio_size(pb) - avio_tell(pb)) / s->packet_size;
return 0;
}
@@ -205,8 +272,8 @@ static int yuv4_read_packet(AVFormatContext *s, AVPacket *pkt)
{
int i;
char header[MAX_FRAME_HEADER+1];
- int packet_size, width, height, ret;
- AVStream *st = s->streams[0];
+ int ret;
+ int64_t off = avio_tell(s->pb);
for (i = 0; i < MAX_FRAME_HEADER; i++) {
header[i] = avio_r8(s->pb);
@@ -225,21 +292,24 @@ static int yuv4_read_packet(AVFormatContext *s, AVPacket *pkt)
if (strncmp(header, Y4M_FRAME_MAGIC, strlen(Y4M_FRAME_MAGIC)))
return AVERROR_INVALIDDATA;
- width = st->codecpar->width;
- height = st->codecpar->height;
-
- packet_size = av_image_get_buffer_size(st->codecpar->format,
- width, height, 1);
- if (packet_size < 0)
- return packet_size;
-
- ret = av_get_packet(s->pb, pkt, packet_size);
+ ret = av_get_packet(s->pb, pkt, s->packet_size - Y4M_FRAME_MAGIC_LEN);
if (ret < 0)
return ret;
- else if (ret != packet_size)
+ else if (ret != s->packet_size - Y4M_FRAME_MAGIC_LEN) {
+ av_packet_unref(pkt);
return s->pb->eof_reached ? AVERROR_EOF : AVERROR(EIO);
-
+ }
pkt->stream_index = 0;
+ pkt->pts = (off - s->internal->data_offset) / s->packet_size;
+ pkt->duration = 1;
+ return 0;
+}
+
+static int yuv4_read_seek(AVFormatContext *s, int stream_index,
+ int64_t pts, int flags)
+{
+ if (avio_seek(s->pb, pts * s->packet_size + s->internal->data_offset, SEEK_SET) < 0)
+ return -1;
return 0;
}
@@ -258,5 +328,6 @@ AVInputFormat ff_yuv4mpegpipe_demuxer = {
.read_probe = yuv4_probe,
.read_header = yuv4_read_header,
.read_packet = yuv4_read_packet,
+ .read_seek = yuv4_read_seek,
.extensions = "y4m",
};
diff --git a/libavformat/yuv4mpegenc.c b/libavformat/yuv4mpegenc.c
index 8721c21..b4dc6e9 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
*/
@@ -33,10 +33,12 @@ static int yuv4_generate_header(AVFormatContext *s, char* buf)
int raten, rated, aspectn, aspectd, n;
char inter;
const char *colorspace = "";
+ int field_order;
st = s->streams[0];
width = st->codecpar->width;
height = st->codecpar->height;
+ field_order = st->codecpar->field_order;
// TODO: should be avg_frame_rate
av_reduce(&raten, &rated, st->time_base.den,
@@ -48,8 +50,17 @@ static int yuv4_generate_header(AVFormatContext *s, char* buf)
if (aspectn == 0 && aspectd == 1)
aspectd = 0; // 0:0 means unknown
- switch (st->codecpar->field_order) {
+#if FF_API_LAVF_AVCTX
+ FF_DISABLE_DEPRECATION_WARNINGS
+ if (field_order != st->codec->field_order && st->codec->field_order != AV_FIELD_UNKNOWN)
+ field_order = st->codec->field_order;
+ FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+ switch (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 +69,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 +88,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 */
@@ -93,7 +152,6 @@ static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt)
int width, height, h_chroma_shift, v_chroma_shift;
int i;
char buf2[Y4M_LINE_MAX + 1];
- char buf1[20];
uint8_t *ptr, *ptr1, *ptr2;
frame = (AVFrame *)pkt->data;
@@ -112,24 +170,56 @@ static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt)
/* construct frame header */
- snprintf(buf1, sizeof(buf1), "%s\n", Y4M_FRAME_MAGIC);
- avio_write(pb, buf1, strlen(buf1));
+ avio_printf(s->pb, "%s\n", Y4M_FRAME_MAGIC);
width = st->codecpar->width;
height = st->codecpar->height;
ptr = frame->data[0];
+
+ switch (st->codecpar->format) {
+ 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->codecpar->format));
+ return AVERROR(EINVAL);
+ }
+
for (i = 0; i < height; i++) {
avio_write(pb, ptr, width);
ptr += frame->linesize[0];
}
- if (st->codecpar->format != AV_PIX_FMT_GRAY8) {
+ if (st->codecpar->format != AV_PIX_FMT_GRAY8 &&
+ st->codecpar->format != AV_PIX_FMT_GRAY16) {
// Adjust for smaller Cb and Cr planes
av_pix_fmt_get_chroma_sub_sample(st->codecpar->format, &h_chroma_shift,
&v_chroma_shift);
// Shift right, rounding up
- width = AV_CEIL_RSHIFT(width, h_chroma_shift);
+ width = AV_CEIL_RSHIFT(width, h_chroma_shift);
height = AV_CEIL_RSHIFT(height, v_chroma_shift);
ptr1 = frame->data[1];
@@ -143,6 +233,7 @@ static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt)
ptr2 += frame->linesize[2];
}
}
+
return 0;
}
@@ -158,15 +249,50 @@ static int yuv4_write_header(AVFormatContext *s)
return AVERROR_INVALIDDATA;
}
- if (s->streams[0]->codecpar->format == AV_PIX_FMT_YUV411P) {
- av_log(s, AV_LOG_ERROR, "Warning: generating rarely used 4:1:1 YUV "
+ switch (s->streams[0]->codecpar->format) {
+ 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]->codecpar->format != AV_PIX_FMT_YUV420P) &&
- (s->streams[0]->codecpar->format != AV_PIX_FMT_YUV422P) &&
- (s->streams[0]->codecpar->format != AV_PIX_FMT_GRAY8) &&
- (s->streams[0]->codecpar->format != 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 an official yuv4mpegpipe pixel format. "
+ "Use '-strict -1' to encode to this pixel format.\n",
+ av_get_pix_fmt_name(s->streams[0]->codecpar->format));
+ 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 +304,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,
OpenPOWER on IntegriCloud