mirror of https://github.com/FFmpeg/FFmpeg.git
It has not been properly maintained for years and there is little hope of that changing in the future. It appears simpler to write a new replacement from scratch than unbreaking it.pull/76/merge
parent
f2ce63246f
commit
894682a973
18 changed files with 7 additions and 6199 deletions
File diff suppressed because it is too large
Load Diff
@ -1,372 +0,0 @@ |
||||
# Port on which the server is listening. You must select a different |
||||
# port from your standard HTTP web server if it is running on the same |
||||
# computer. |
||||
Port 8090 |
||||
|
||||
# Address on which the server is bound. Only useful if you have |
||||
# several network interfaces. |
||||
BindAddress 0.0.0.0 |
||||
|
||||
# Number of simultaneous HTTP connections that can be handled. It has |
||||
# to be defined *before* the MaxClients parameter, since it defines the |
||||
# MaxClients maximum limit. |
||||
MaxHTTPConnections 2000 |
||||
|
||||
# Number of simultaneous requests that can be handled. Since AVServer |
||||
# is very fast, it is more likely that you will want to leave this high |
||||
# and use MaxBandwidth, below. |
||||
MaxClients 1000 |
||||
|
||||
# This the maximum amount of kbit/sec that you are prepared to |
||||
# consume when streaming to clients. |
||||
MaxBandwidth 1000 |
||||
|
||||
# Access log file (uses standard Apache log file format) |
||||
# '-' is the standard output. |
||||
CustomLog - |
||||
|
||||
|
||||
################################################################## |
||||
# Definition of the live feeds. Each live feed contains one video |
||||
# and/or audio sequence coming from an avconv encoder or another |
||||
# avserver. This sequence may be encoded simultaneously with several |
||||
# codecs at several resolutions. |
||||
|
||||
<Feed feed1.ffm> |
||||
|
||||
# You must use 'avconv' to send a live feed to avserver. In this |
||||
# example, you can type: |
||||
# |
||||
# avconv http://localhost:8090/feed1.ffm |
||||
|
||||
# avserver can also do time shifting. It means that it can stream any |
||||
# previously recorded live stream. The request should contain: |
||||
# "http://xxxx?date=[YYYY-MM-DDT][[HH:]MM:]SS[.m...]".You must specify |
||||
# a path where the feed is stored on disk. You also specify the |
||||
# maximum size of the feed, where zero means unlimited. Default: |
||||
# File=/tmp/feed_name.ffm FileMaxSize=5M |
||||
File /tmp/feed1.ffm |
||||
FileMaxSize 200K |
||||
|
||||
# You could specify |
||||
# ReadOnlyFile /saved/specialvideo.ffm |
||||
# This marks the file as readonly and it will not be deleted or updated. |
||||
|
||||
# Specify launch in order to start avconv automatically. |
||||
# First avconv must be defined with an appropriate path if needed, |
||||
# after that options can follow, but avoid adding the http:// field |
||||
#Launch avconv |
||||
|
||||
# Only allow connections from localhost to the feed. |
||||
ACL allow 127.0.0.1 |
||||
|
||||
</Feed> |
||||
|
||||
|
||||
################################################################## |
||||
# Now you can define each stream which will be generated from the |
||||
# original audio and video stream. Each format has a filename (here |
||||
# 'test1.mpg'). AVServer will send this stream when answering a |
||||
# request containing this filename. |
||||
|
||||
<Stream test1.mpg> |
||||
|
||||
# coming from live feed 'feed1' |
||||
Feed feed1.ffm |
||||
|
||||
# Format of the stream : you can choose among: |
||||
# mpeg : MPEG-1 multiplexed video and audio |
||||
# mpegvideo : only MPEG-1 video |
||||
# mp2 : MPEG-2 audio (use AudioCodec to select layer 2 and 3 codec) |
||||
# ogg : Ogg format (Vorbis audio codec) |
||||
# rm : RealNetworks-compatible stream. Multiplexed audio and video. |
||||
# ra : RealNetworks-compatible stream. Audio only. |
||||
# mpjpeg : Multipart JPEG (works with Netscape without any plugin) |
||||
# jpeg : Generate a single JPEG image. |
||||
# asf : ASF compatible streaming (Windows Media Player format). |
||||
# swf : Macromedia Flash compatible stream |
||||
# avi : AVI format (MPEG-4 video, MPEG audio sound) |
||||
Format mpeg |
||||
|
||||
# Bitrate for the audio stream. Codecs usually support only a few |
||||
# different bitrates. |
||||
AudioBitRate 32 |
||||
|
||||
# Number of audio channels: 1 = mono, 2 = stereo |
||||
AudioChannels 1 |
||||
|
||||
# Sampling frequency for audio. When using low bitrates, you should |
||||
# lower this frequency to 22050 or 11025. The supported frequencies |
||||
# depend on the selected audio codec. |
||||
AudioSampleRate 44100 |
||||
|
||||
# Bitrate for the video stream |
||||
VideoBitRate 64 |
||||
|
||||
# Ratecontrol buffer size |
||||
VideoBufferSize 40 |
||||
|
||||
# Number of frames per second |
||||
VideoFrameRate 3 |
||||
|
||||
# Size of the video frame: WxH (default: 160x128) |
||||
# The following abbreviations are defined: sqcif, qcif, cif, 4cif, qqvga, |
||||
# qvga, vga, svga, xga, uxga, qxga, sxga, qsxga, hsxga, wvga, wxga, wsxga, |
||||
# wuxga, woxga, wqsxga, wquxga, whsxga, whuxga, cga, ega, hd480, hd720, |
||||
# hd1080 |
||||
VideoSize 160x128 |
||||
|
||||
# Transmit only intra frames (useful for low bitrates, but kills frame rate). |
||||
#VideoIntraOnly |
||||
|
||||
# If non-intra only, an intra frame is transmitted every VideoGopSize |
||||
# frames. Video synchronization can only begin at an intra frame. |
||||
VideoGopSize 12 |
||||
|
||||
# More MPEG-4 parameters |
||||
# VideoHighQuality |
||||
# Video4MotionVector |
||||
|
||||
# Choose your codecs: |
||||
#AudioCodec mp2 |
||||
#VideoCodec mpeg1video |
||||
|
||||
# Suppress audio |
||||
#NoAudio |
||||
|
||||
# Suppress video |
||||
#NoVideo |
||||
|
||||
#VideoQMin 3 |
||||
#VideoQMax 31 |
||||
|
||||
# Set this to the number of seconds backwards in time to start. Note that |
||||
# most players will buffer 5-10 seconds of video, and also you need to allow |
||||
# for a keyframe to appear in the data stream. |
||||
#Preroll 15 |
||||
|
||||
# ACL: |
||||
|
||||
# You can allow ranges of addresses (or single addresses) |
||||
#ACL ALLOW <first address> <last address> |
||||
|
||||
# You can deny ranges of addresses (or single addresses) |
||||
#ACL DENY <first address> <last address> |
||||
|
||||
# You can repeat the ACL allow/deny as often as you like. It is on a per |
||||
# stream basis. The first match defines the action. If there are no matches, |
||||
# then the default is the inverse of the last ACL statement. |
||||
# |
||||
# Thus 'ACL allow localhost' only allows access from localhost. |
||||
# 'ACL deny 1.0.0.0 1.255.255.255' would deny the whole of network 1 and |
||||
# allow everybody else. |
||||
|
||||
</Stream> |
||||
|
||||
|
||||
################################################################## |
||||
# Example streams |
||||
|
||||
|
||||
# Multipart JPEG |
||||
|
||||
#<Stream test.mjpg> |
||||
#Feed feed1.ffm |
||||
#Format mpjpeg |
||||
#VideoFrameRate 2 |
||||
#VideoIntraOnly |
||||
#NoAudio |
||||
#Strict -1 |
||||
#</Stream> |
||||
|
||||
|
||||
# Single JPEG |
||||
|
||||
#<Stream test.jpg> |
||||
#Feed feed1.ffm |
||||
#Format jpeg |
||||
#VideoFrameRate 2 |
||||
#VideoIntraOnly |
||||
##VideoSize 352x240 |
||||
#NoAudio |
||||
#Strict -1 |
||||
#</Stream> |
||||
|
||||
|
||||
# Flash |
||||
|
||||
#<Stream test.swf> |
||||
#Feed feed1.ffm |
||||
#Format swf |
||||
#VideoFrameRate 2 |
||||
#VideoIntraOnly |
||||
#NoAudio |
||||
#</Stream> |
||||
|
||||
|
||||
# ASF compatible |
||||
|
||||
<Stream test.asf> |
||||
Feed feed1.ffm |
||||
Format asf |
||||
VideoFrameRate 15 |
||||
VideoSize 352x240 |
||||
VideoBitRate 256 |
||||
VideoBufferSize 40 |
||||
VideoGopSize 30 |
||||
AudioBitRate 64 |
||||
StartSendOnKey |
||||
</Stream> |
||||
|
||||
|
||||
# MP3 audio |
||||
|
||||
#<Stream test.mp3> |
||||
#Feed feed1.ffm |
||||
#Format mp2 |
||||
#AudioCodec mp3 |
||||
#AudioBitRate 64 |
||||
#AudioChannels 1 |
||||
#AudioSampleRate 44100 |
||||
#NoVideo |
||||
#</Stream> |
||||
|
||||
|
||||
# Ogg Vorbis audio |
||||
|
||||
#<Stream test.ogg> |
||||
#Feed feed1.ffm |
||||
#Title "Stream title" |
||||
#AudioBitRate 64 |
||||
#AudioChannels 2 |
||||
#AudioSampleRate 44100 |
||||
#NoVideo |
||||
#</Stream> |
||||
|
||||
|
||||
# Real with audio only at 32 kbits |
||||
|
||||
#<Stream test.ra> |
||||
#Feed feed1.ffm |
||||
#Format rm |
||||
#AudioBitRate 32 |
||||
#NoVideo |
||||
#NoAudio |
||||
#</Stream> |
||||
|
||||
|
||||
# Real with audio and video at 64 kbits |
||||
|
||||
#<Stream test.rm> |
||||
#Feed feed1.ffm |
||||
#Format rm |
||||
#AudioBitRate 32 |
||||
#VideoBitRate 128 |
||||
#VideoFrameRate 25 |
||||
#VideoGopSize 25 |
||||
#NoAudio |
||||
#</Stream> |
||||
|
||||
|
||||
################################################################## |
||||
# A stream coming from a file: you only need to set the input |
||||
# filename and optionally a new format. Supported conversions: |
||||
# AVI -> ASF |
||||
|
||||
#<Stream file.rm> |
||||
#File "/usr/local/httpd/htdocs/tlive.rm" |
||||
#NoAudio |
||||
#</Stream> |
||||
|
||||
#<Stream file.asf> |
||||
#File "/usr/local/httpd/htdocs/test.asf" |
||||
#NoAudio |
||||
#Author "Me" |
||||
#Copyright "Super MegaCorp" |
||||
#Title "Test stream from disk" |
||||
#Comment "Test comment" |
||||
#</Stream> |
||||
|
||||
|
||||
################################################################## |
||||
# RTSP examples |
||||
# |
||||
# You can access this stream with the RTSP URL: |
||||
# rtsp://localhost:5454/test1-rtsp.mpg |
||||
# |
||||
# A non-standard RTSP redirector is also created. Its URL is: |
||||
# http://localhost:8090/test1-rtsp.rtsp |
||||
|
||||
#<Stream test1-rtsp.mpg> |
||||
#Format rtp |
||||
#File "/usr/local/httpd/htdocs/test1.mpg" |
||||
#</Stream> |
||||
|
||||
|
||||
# Transcode an incoming live feed to another live feed, |
||||
# using libx264 and video presets |
||||
|
||||
#<Stream live.h264> |
||||
#Format rtp |
||||
#Feed feed1.ffm |
||||
#VideoCodec libx264 |
||||
#VideoFrameRate 24 |
||||
#VideoBitRate 100 |
||||
#VideoSize 480x272 |
||||
#AVPresetVideo default |
||||
#AVPresetVideo baseline |
||||
#AVOptionVideo flags +global_header |
||||
# |
||||
#AudioCodec libfaac |
||||
#AudioBitRate 32 |
||||
#AudioChannels 2 |
||||
#AudioSampleRate 22050 |
||||
#AVOptionAudio flags +global_header |
||||
#</Stream> |
||||
|
||||
################################################################## |
||||
# SDP/multicast examples |
||||
# |
||||
# If you want to send your stream in multicast, you must set the |
||||
# multicast address with MulticastAddress. The port and the TTL can |
||||
# also be set. |
||||
# |
||||
# An SDP file is automatically generated by avserver by adding the |
||||
# 'sdp' extension to the stream name (here |
||||
# http://localhost:8090/test1-sdp.sdp). You should usually give this |
||||
# file to your player to play the stream. |
||||
# |
||||
# The 'NoLoop' option can be used to avoid looping when the stream is |
||||
# terminated. |
||||
|
||||
#<Stream test1-sdp.mpg> |
||||
#Format rtp |
||||
#File "/usr/local/httpd/htdocs/test1.mpg" |
||||
#MulticastAddress 224.124.0.1 |
||||
#MulticastPort 5000 |
||||
#MulticastTTL 16 |
||||
#NoLoop |
||||
#</Stream> |
||||
|
||||
|
||||
################################################################## |
||||
# Special streams |
||||
|
||||
# Server status |
||||
|
||||
<Stream stat.html> |
||||
Format status |
||||
|
||||
# Only allow local people to get the status |
||||
ACL allow localhost |
||||
ACL allow 192.168.0.0 192.168.255.255 |
||||
|
||||
#FaviconURL http://pond1.gladstonefamily.net:8080/favicon.ico |
||||
</Stream> |
||||
|
||||
|
||||
# Redirect index.html to the appropriate site |
||||
|
||||
<Redirect index.html> |
||||
URL http://www.libav.org/ |
||||
</Redirect> |
@ -1,276 +0,0 @@ |
||||
\input texinfo @c -*- texinfo -*- |
||||
|
||||
@settitle avserver Documentation |
||||
@titlepage |
||||
@center @titlefont{avserver Documentation} |
||||
@end titlepage |
||||
|
||||
@top |
||||
|
||||
@contents |
||||
|
||||
@chapter Synopsys |
||||
|
||||
The generic syntax is: |
||||
|
||||
@example |
||||
@c man begin SYNOPSIS |
||||
avserver [options] |
||||
@c man end |
||||
@end example |
||||
|
||||
@chapter Description |
||||
@c man begin DESCRIPTION |
||||
|
||||
WARNING: avserver is unmaintained, largely broken and in need of a |
||||
complete rewrite. It probably won't work for you. Use at your own |
||||
risk. |
||||
|
||||
avserver is a streaming server for both audio and video. It supports |
||||
several live feeds, streaming from files and time shifting on live feeds |
||||
(you can seek to positions in the past on each live feed, provided you |
||||
specify a big enough feed storage in avserver.conf). |
||||
|
||||
This documentation covers only the streaming aspects of avserver / |
||||
avconv. All questions about parameters for avconv, codec questions, |
||||
etc. are not covered here. Read @file{avconv.html} for more |
||||
information. |
||||
|
||||
@section How does it work? |
||||
|
||||
avserver receives prerecorded files or FFM streams from some avconv |
||||
instance as input, then streams them over RTP/RTSP/HTTP. |
||||
|
||||
An avserver instance will listen on some port as specified in the |
||||
configuration file. You can launch one or more instances of avconv and |
||||
send one or more FFM streams to the port where avserver is expecting |
||||
to receive them. Alternately, you can make avserver launch such avconv |
||||
instances at startup. |
||||
|
||||
Input streams are called feeds, and each one is specified by a <Feed> |
||||
section in the configuration file. |
||||
|
||||
For each feed you can have different output streams in various |
||||
formats, each one specified by a <Stream> section in the configuration |
||||
file. |
||||
|
||||
@section Status stream |
||||
|
||||
avserver supports an HTTP interface which exposes the current status |
||||
of the server. |
||||
|
||||
Simply point your browser to the address of the special status stream |
||||
specified in the configuration file. |
||||
|
||||
For example if you have: |
||||
@example |
||||
<Stream status.html> |
||||
Format status |
||||
|
||||
# Only allow local people to get the status |
||||
ACL allow localhost |
||||
ACL allow 192.168.0.0 192.168.255.255 |
||||
</Stream> |
||||
@end example |
||||
|
||||
then the server will post a page with the status information when |
||||
the special stream @file{status.html} is requested. |
||||
|
||||
@section What can this do? |
||||
|
||||
When properly configured and running, you can capture video and audio in real |
||||
time from a suitable capture card, and stream it out over the Internet to |
||||
either Windows Media Player or RealAudio player (with some restrictions). |
||||
|
||||
It can also stream from files, though that is currently broken. Very often, a |
||||
web server can be used to serve up the files just as well. |
||||
|
||||
It can stream prerecorded video from .ffm files, though it is somewhat tricky |
||||
to make it work correctly. |
||||
|
||||
@section What do I need? |
||||
|
||||
I use Linux on a 900 MHz Duron with a cheapo Bt848 based TV capture card. I'm |
||||
using stock Linux 2.4.17 with the stock drivers. [Actually that isn't true, |
||||
I needed some special drivers for my motherboard-based sound card.] |
||||
|
||||
I understand that FreeBSD systems work just fine as well. |
||||
|
||||
@section How do I make it work? |
||||
|
||||
First, build the kit. It *really* helps to have installed LAME first. Then when |
||||
you run the avserver ./configure, make sure that you have the |
||||
@code{--enable-libmp3lame} flag turned on. |
||||
|
||||
LAME is important as it allows for streaming audio to Windows Media Player. |
||||
Don't ask why the other audio types do not work. |
||||
|
||||
As a simple test, just run the following two command lines where INPUTFILE |
||||
is some file which you can decode with avconv: |
||||
|
||||
@example |
||||
./avserver -f doc/avserver.conf & |
||||
./avconv -i INPUTFILE http://localhost:8090/feed1.ffm |
||||
@end example |
||||
|
||||
At this point you should be able to go to your Windows machine and fire up |
||||
Windows Media Player (WMP). Go to Open URL and enter |
||||
|
||||
@example |
||||
http://<linuxbox>:8090/test.asf |
||||
@end example |
||||
|
||||
You should (after a short delay) see video and hear audio. |
||||
|
||||
WARNING: trying to stream test1.mpg doesn't work with WMP as it tries to |
||||
transfer the entire file before starting to play. |
||||
The same is true of AVI files. |
||||
|
||||
@section What happens next? |
||||
|
||||
You should edit the avserver.conf file to suit your needs (in terms of |
||||
frame rates etc). Then install avserver and avconv, write a script to start |
||||
them up, and off you go. |
||||
|
||||
@section Troubleshooting |
||||
|
||||
@subsection I don't hear any audio, but video is fine. |
||||
|
||||
Maybe you didn't install LAME, or got your ./configure statement wrong. Check |
||||
the avconv output to see if a line referring to MP3 is present. If not, then |
||||
your configuration was incorrect. If it is, then maybe your wiring is not |
||||
set up correctly. Maybe the sound card is not getting data from the right |
||||
input source. Maybe you have a really awful audio interface (like I do) |
||||
that only captures in stereo and also requires that one channel be flipped. |
||||
If you are one of these people, then export 'AUDIO_FLIP_LEFT=1' before |
||||
starting avconv. |
||||
|
||||
@subsection The audio and video lose sync after a while. |
||||
|
||||
Yes, they do. |
||||
|
||||
@subsection After a long while, the video update rate goes way down in WMP. |
||||
|
||||
Yes, it does. Who knows why? |
||||
|
||||
@subsection WMP 6.4 behaves differently to WMP 7. |
||||
|
||||
Yes, it does. Any thoughts on this would be gratefully received. These |
||||
differences extend to embedding WMP into a web page. [There are two |
||||
object IDs that you can use: The old one, which does not play well, and |
||||
the new one, which does (both tested on the same system). However, |
||||
I suspect that the new one is not available unless you have installed WMP 7]. |
||||
|
||||
@section What else can it do? |
||||
|
||||
You can replay video from .ffm files that was recorded earlier. |
||||
However, there are a number of caveats, including the fact that the |
||||
avserver parameters must match the original parameters used to record the |
||||
file. If they do not, then avserver deletes the file before recording into it. |
||||
(Now that I write this, it seems broken). |
||||
|
||||
You can fiddle with many of the codec choices and encoding parameters, and |
||||
there are a bunch more parameters that you cannot control. Post a message |
||||
to the mailing list if there are some 'must have' parameters. Look in |
||||
avserver.conf for a list of the currently available controls. |
||||
|
||||
It will automatically generate the ASX or RAM files that are often used |
||||
in browsers. These files are actually redirections to the underlying ASF |
||||
or RM file. The reason for this is that the browser often fetches the |
||||
entire file before starting up the external viewer. The redirection files |
||||
are very small and can be transferred quickly. [The stream itself is |
||||
often 'infinite' and thus the browser tries to download it and never |
||||
finishes.] |
||||
|
||||
@section Tips |
||||
|
||||
* When you connect to a live stream, most players (WMP, RA, etc) want to |
||||
buffer a certain number of seconds of material so that they can display the |
||||
signal continuously. However, avserver (by default) starts sending data |
||||
in realtime. This means that there is a pause of a few seconds while the |
||||
buffering is being done by the player. The good news is that this can be |
||||
cured by adding a '?buffer=5' to the end of the URL. This means that the |
||||
stream should start 5 seconds in the past -- and so the first 5 seconds |
||||
of the stream are sent as fast as the network will allow. It will then |
||||
slow down to real time. This noticeably improves the startup experience. |
||||
|
||||
You can also add a 'Preroll 15' statement into the avserver.conf that will |
||||
add the 15 second prebuffering on all requests that do not otherwise |
||||
specify a time. In addition, avserver will skip frames until a key_frame |
||||
is found. This further reduces the startup delay by not transferring data |
||||
that will be discarded. |
||||
|
||||
* You may want to adjust the MaxBandwidth in the avserver.conf to limit |
||||
the amount of bandwidth consumed by live streams. |
||||
|
||||
@section Why does the ?buffer / Preroll stop working after a time? |
||||
|
||||
It turns out that (on my machine at least) the number of frames successfully |
||||
grabbed is marginally less than the number that ought to be grabbed. This |
||||
means that the timestamp in the encoded data stream gets behind realtime. |
||||
This means that if you say 'Preroll 10', then when the stream gets 10 |
||||
or more seconds behind, there is no Preroll left. |
||||
|
||||
Fixing this requires a change in the internals of how timestamps are |
||||
handled. |
||||
|
||||
@section Does the @code{?date=} stuff work. |
||||
|
||||
Yes (subject to the limitation outlined above). Also note that whenever you |
||||
start avserver, it deletes the ffm file (if any parameters have changed), |
||||
thus wiping out what you had recorded before. |
||||
|
||||
The format of the @code{?date=xxxxxx} is fairly flexible. You should use one |
||||
of the following formats (the 'T' is literal): |
||||
|
||||
@example |
||||
* YYYY-MM-DDTHH:MM:SS (localtime) |
||||
* YYYY-MM-DDTHH:MM:SSZ (UTC) |
||||
@end example |
||||
|
||||
You can omit the YYYY-MM-DD, and then it refers to the current day. However |
||||
note that @samp{?date=16:00:00} refers to 16:00 on the current day -- this |
||||
may be in the future and so is unlikely to be useful. |
||||
|
||||
You use this by adding the ?date= to the end of the URL for the stream. |
||||
For example: @samp{http://localhost:8080/test.asf?date=2002-07-26T23:05:00}. |
||||
@c man end |
||||
|
||||
@chapter Options |
||||
@c man begin OPTIONS |
||||
|
||||
@include avtools-common-opts.texi |
||||
|
||||
@section Main options |
||||
|
||||
@table @option |
||||
@item -f @var{configfile} |
||||
Use @file{configfile} instead of @file{/etc/avserver.conf}. |
||||
@item -n |
||||
Enable no-launch mode. This option disables all the Launch directives |
||||
within the various <Stream> sections. Since avserver will not launch |
||||
any avconv instances, you will have to launch them manually. |
||||
@item -d |
||||
Enable debug mode. This option increases log verbosity, directs log |
||||
messages to stdout. |
||||
@end table |
||||
@c man end |
||||
|
||||
@ignore |
||||
|
||||
@setfilename avserver |
||||
@settitle avserver video server |
||||
|
||||
@c man begin SEEALSO |
||||
|
||||
avconv(1), avplay(1), avprobe(1), the @file{avserver.conf} |
||||
example and the Libav HTML documentation |
||||
@c man end |
||||
|
||||
@c man begin AUTHORS |
||||
The Libav developers |
||||
@c man end |
||||
|
||||
@end ignore |
||||
|
||||
@bye |
@ -1,59 +0,0 @@ |
||||
/*
|
||||
* FFM (avserver live feed) common header |
||||
* Copyright (c) 2001 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 |
||||
*/ |
||||
|
||||
#ifndef AVFORMAT_FFM_H |
||||
#define AVFORMAT_FFM_H |
||||
|
||||
#include <stdint.h> |
||||
#include "avformat.h" |
||||
#include "avio.h" |
||||
|
||||
/* The FFM file is made of blocks of fixed size */ |
||||
#define FFM_HEADER_SIZE 14 |
||||
#define FFM_PACKET_SIZE 4096 |
||||
#define PACKET_ID 0x666d |
||||
|
||||
/* each packet contains frames (which can span several packets */ |
||||
#define FRAME_HEADER_SIZE 16 |
||||
#define FLAG_KEY_FRAME 0x01 |
||||
#define FLAG_DTS 0x02 |
||||
|
||||
enum { |
||||
READ_HEADER, |
||||
READ_DATA, |
||||
}; |
||||
|
||||
typedef struct FFMContext { |
||||
/* only reading mode */ |
||||
int64_t write_index, file_size; |
||||
int read_state; |
||||
uint8_t header[FRAME_HEADER_SIZE+4]; |
||||
|
||||
/* read and write */ |
||||
int first_packet; /* true if first packet, needed to set the discontinuity tag */ |
||||
int packet_size; |
||||
int frame_offset; |
||||
int64_t dts; |
||||
uint8_t *packet_ptr, *packet_end; |
||||
uint8_t packet[FFM_PACKET_SIZE]; |
||||
} FFMContext; |
||||
|
||||
#endif /* AVFORMAT_FFM_H */ |
@ -1,483 +0,0 @@ |
||||
/*
|
||||
* FFM (avserver live feed) demuxer |
||||
* Copyright (c) 2001 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 <stdint.h> |
||||
|
||||
#include "libavutil/intreadwrite.h" |
||||
#include "libavutil/intfloat.h" |
||||
#include "avformat.h" |
||||
#include "internal.h" |
||||
#include "ffm.h" |
||||
|
||||
static int ffm_is_avail_data(AVFormatContext *s, int size) |
||||
{ |
||||
FFMContext *ffm = s->priv_data; |
||||
int64_t pos, avail_size; |
||||
int len; |
||||
|
||||
len = ffm->packet_end - ffm->packet_ptr; |
||||
if (size <= len) |
||||
return 1; |
||||
pos = avio_tell(s->pb); |
||||
if (!ffm->write_index) { |
||||
if (pos == ffm->file_size) |
||||
return AVERROR_EOF; |
||||
avail_size = ffm->file_size - pos; |
||||
} else { |
||||
if (pos == ffm->write_index) { |
||||
/* exactly at the end of stream */ |
||||
return AVERROR(EAGAIN); |
||||
} else if (pos < ffm->write_index) { |
||||
avail_size = ffm->write_index - pos; |
||||
} else { |
||||
avail_size = (ffm->file_size - pos) + (ffm->write_index - FFM_PACKET_SIZE); |
||||
} |
||||
} |
||||
avail_size = (avail_size / ffm->packet_size) * (ffm->packet_size - FFM_HEADER_SIZE) + len; |
||||
if (size <= avail_size) |
||||
return 1; |
||||
else |
||||
return AVERROR(EAGAIN); |
||||
} |
||||
|
||||
static int ffm_resync(AVFormatContext *s, int state) |
||||
{ |
||||
av_log(s, AV_LOG_ERROR, "resyncing\n"); |
||||
while (state != PACKET_ID) { |
||||
if (s->pb->eof_reached) { |
||||
av_log(s, AV_LOG_ERROR, "cannot find FFM syncword\n"); |
||||
return -1; |
||||
} |
||||
state = (state << 8) | avio_r8(s->pb); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/* first is true if we read the frame header */ |
||||
static int ffm_read_data(AVFormatContext *s, |
||||
uint8_t *buf, int size, int header) |
||||
{ |
||||
FFMContext *ffm = s->priv_data; |
||||
AVIOContext *pb = s->pb; |
||||
int len, fill_size, size1, frame_offset, id; |
||||
|
||||
size1 = size; |
||||
while (size > 0) { |
||||
redo: |
||||
len = ffm->packet_end - ffm->packet_ptr; |
||||
if (len < 0) |
||||
return -1; |
||||
if (len > size) |
||||
len = size; |
||||
if (len == 0) { |
||||
if (avio_tell(pb) == ffm->file_size) |
||||
avio_seek(pb, ffm->packet_size, SEEK_SET); |
||||
retry_read: |
||||
id = avio_rb16(pb); /* PACKET_ID */ |
||||
if (id != PACKET_ID) |
||||
if (ffm_resync(s, id) < 0) |
||||
return -1; |
||||
fill_size = avio_rb16(pb); |
||||
ffm->dts = avio_rb64(pb); |
||||
frame_offset = avio_rb16(pb); |
||||
avio_read(pb, ffm->packet, ffm->packet_size - FFM_HEADER_SIZE); |
||||
ffm->packet_end = ffm->packet + (ffm->packet_size - FFM_HEADER_SIZE - fill_size); |
||||
if (ffm->packet_end < ffm->packet || frame_offset < 0) |
||||
return -1; |
||||
/* if first packet or resynchronization packet, we must
|
||||
handle it specifically */ |
||||
if (ffm->first_packet || (frame_offset & 0x8000)) { |
||||
if (!frame_offset) { |
||||
/* This packet has no frame headers in it */ |
||||
if (avio_tell(pb) >= ffm->packet_size * 3) { |
||||
avio_seek(pb, -ffm->packet_size * 2, SEEK_CUR); |
||||
goto retry_read; |
||||
} |
||||
/* This is bad, we cannot find a valid frame header */ |
||||
return 0; |
||||
} |
||||
ffm->first_packet = 0; |
||||
if ((frame_offset & 0x7fff) < FFM_HEADER_SIZE) |
||||
return -1; |
||||
ffm->packet_ptr = ffm->packet + (frame_offset & 0x7fff) - FFM_HEADER_SIZE; |
||||
if (!header) |
||||
break; |
||||
} else { |
||||
ffm->packet_ptr = ffm->packet; |
||||
} |
||||
goto redo; |
||||
} |
||||
memcpy(buf, ffm->packet_ptr, len); |
||||
buf += len; |
||||
ffm->packet_ptr += len; |
||||
size -= len; |
||||
header = 0; |
||||
} |
||||
return size1 - size; |
||||
} |
||||
|
||||
/* ensure that acutal seeking happens between FFM_PACKET_SIZE
|
||||
and file_size - FFM_PACKET_SIZE */ |
||||
static int64_t ffm_seek1(AVFormatContext *s, int64_t pos1) |
||||
{ |
||||
FFMContext *ffm = s->priv_data; |
||||
AVIOContext *pb = s->pb; |
||||
int64_t pos; |
||||
|
||||
pos = FFMIN(pos1, ffm->file_size - FFM_PACKET_SIZE); |
||||
pos = FFMAX(pos, FFM_PACKET_SIZE); |
||||
av_dlog(s, "seek to %"PRIx64" -> %"PRIx64"\n", pos1, pos); |
||||
return avio_seek(pb, pos, SEEK_SET); |
||||
} |
||||
|
||||
static int64_t get_dts(AVFormatContext *s, int64_t pos) |
||||
{ |
||||
AVIOContext *pb = s->pb; |
||||
int64_t dts; |
||||
|
||||
ffm_seek1(s, pos); |
||||
avio_skip(pb, 4); |
||||
dts = avio_rb64(pb); |
||||
av_dlog(s, "dts=%0.6f\n", dts / 1000000.0); |
||||
return dts; |
||||
} |
||||
|
||||
static void adjust_write_index(AVFormatContext *s) |
||||
{ |
||||
FFMContext *ffm = s->priv_data; |
||||
AVIOContext *pb = s->pb; |
||||
int64_t pts; |
||||
//int64_t orig_write_index = ffm->write_index;
|
||||
int64_t pos_min, pos_max; |
||||
int64_t pts_start; |
||||
int64_t ptr = avio_tell(pb); |
||||
|
||||
|
||||
pos_min = 0; |
||||
pos_max = ffm->file_size - 2 * FFM_PACKET_SIZE; |
||||
|
||||
pts_start = get_dts(s, pos_min); |
||||
|
||||
pts = get_dts(s, pos_max); |
||||
|
||||
if (pts - 100000 > pts_start) |
||||
goto end; |
||||
|
||||
ffm->write_index = FFM_PACKET_SIZE; |
||||
|
||||
pts_start = get_dts(s, pos_min); |
||||
|
||||
pts = get_dts(s, pos_max); |
||||
|
||||
if (pts - 100000 <= pts_start) { |
||||
while (1) { |
||||
int64_t newpos; |
||||
int64_t newpts; |
||||
|
||||
newpos = ((pos_max + pos_min) / (2 * FFM_PACKET_SIZE)) * FFM_PACKET_SIZE; |
||||
|
||||
if (newpos == pos_min) |
||||
break; |
||||
|
||||
newpts = get_dts(s, newpos); |
||||
|
||||
if (newpts - 100000 <= pts) { |
||||
pos_max = newpos; |
||||
pts = newpts; |
||||
} else { |
||||
pos_min = newpos; |
||||
} |
||||
} |
||||
ffm->write_index += pos_max; |
||||
} |
||||
|
||||
end: |
||||
avio_seek(pb, ptr, SEEK_SET); |
||||
} |
||||
|
||||
|
||||
static int ffm_close(AVFormatContext *s) |
||||
{ |
||||
int i; |
||||
|
||||
for (i = 0; i < s->nb_streams; i++) |
||||
av_freep(&s->streams[i]->codec->rc_eq); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
|
||||
static int ffm_read_header(AVFormatContext *s) |
||||
{ |
||||
FFMContext *ffm = s->priv_data; |
||||
AVStream *st; |
||||
AVIOContext *pb = s->pb; |
||||
AVCodecContext *codec; |
||||
int i, nb_streams; |
||||
uint32_t tag; |
||||
|
||||
/* header */ |
||||
tag = avio_rl32(pb); |
||||
if (tag != MKTAG('F', 'F', 'M', '1')) |
||||
goto fail; |
||||
ffm->packet_size = avio_rb32(pb); |
||||
if (ffm->packet_size != FFM_PACKET_SIZE) |
||||
goto fail; |
||||
ffm->write_index = avio_rb64(pb); |
||||
/* get also filesize */ |
||||
if (pb->seekable) { |
||||
ffm->file_size = avio_size(pb); |
||||
if (ffm->write_index) |
||||
adjust_write_index(s); |
||||
} else { |
||||
ffm->file_size = (UINT64_C(1) << 63) - 1; |
||||
} |
||||
|
||||
nb_streams = avio_rb32(pb); |
||||
avio_rb32(pb); /* total bitrate */ |
||||
/* read each stream */ |
||||
for(i=0;i<nb_streams;i++) { |
||||
char rc_eq_buf[128]; |
||||
|
||||
st = avformat_new_stream(s, NULL); |
||||
if (!st) |
||||
goto fail; |
||||
|
||||
avpriv_set_pts_info(st, 64, 1, 1000000); |
||||
|
||||
codec = st->codec; |
||||
/* generic info */ |
||||
codec->codec_id = avio_rb32(pb); |
||||
codec->codec_type = avio_r8(pb); /* codec_type */ |
||||
codec->bit_rate = avio_rb32(pb); |
||||
codec->flags = avio_rb32(pb); |
||||
codec->flags2 = avio_rb32(pb); |
||||
codec->debug = avio_rb32(pb); |
||||
/* specific info */ |
||||
switch(codec->codec_type) { |
||||
case AVMEDIA_TYPE_VIDEO: |
||||
codec->time_base.num = avio_rb32(pb); |
||||
codec->time_base.den = avio_rb32(pb); |
||||
codec->width = avio_rb16(pb); |
||||
codec->height = avio_rb16(pb); |
||||
codec->gop_size = avio_rb16(pb); |
||||
codec->pix_fmt = avio_rb32(pb); |
||||
codec->qmin = avio_r8(pb); |
||||
codec->qmax = avio_r8(pb); |
||||
codec->max_qdiff = avio_r8(pb); |
||||
codec->qcompress = avio_rb16(pb) / 10000.0; |
||||
codec->qblur = avio_rb16(pb) / 10000.0; |
||||
codec->bit_rate_tolerance = avio_rb32(pb); |
||||
avio_get_str(pb, INT_MAX, rc_eq_buf, sizeof(rc_eq_buf)); |
||||
codec->rc_eq = av_strdup(rc_eq_buf); |
||||
codec->rc_max_rate = avio_rb32(pb); |
||||
codec->rc_min_rate = avio_rb32(pb); |
||||
codec->rc_buffer_size = avio_rb32(pb); |
||||
codec->i_quant_factor = av_int2double(avio_rb64(pb)); |
||||
codec->b_quant_factor = av_int2double(avio_rb64(pb)); |
||||
codec->i_quant_offset = av_int2double(avio_rb64(pb)); |
||||
codec->b_quant_offset = av_int2double(avio_rb64(pb)); |
||||
codec->dct_algo = avio_rb32(pb); |
||||
codec->strict_std_compliance = avio_rb32(pb); |
||||
codec->max_b_frames = avio_rb32(pb); |
||||
codec->mpeg_quant = avio_rb32(pb); |
||||
codec->intra_dc_precision = avio_rb32(pb); |
||||
codec->me_method = avio_rb32(pb); |
||||
codec->mb_decision = avio_rb32(pb); |
||||
codec->nsse_weight = avio_rb32(pb); |
||||
codec->frame_skip_cmp = avio_rb32(pb); |
||||
codec->rc_buffer_aggressivity = av_int2double(avio_rb64(pb)); |
||||
codec->codec_tag = avio_rb32(pb); |
||||
codec->thread_count = avio_r8(pb); |
||||
codec->coder_type = avio_rb32(pb); |
||||
codec->me_cmp = avio_rb32(pb); |
||||
codec->me_subpel_quality = avio_rb32(pb); |
||||
codec->me_range = avio_rb32(pb); |
||||
codec->keyint_min = avio_rb32(pb); |
||||
codec->scenechange_threshold = avio_rb32(pb); |
||||
codec->b_frame_strategy = avio_rb32(pb); |
||||
codec->qcompress = av_int2double(avio_rb64(pb)); |
||||
codec->qblur = av_int2double(avio_rb64(pb)); |
||||
codec->max_qdiff = avio_rb32(pb); |
||||
codec->refs = avio_rb32(pb); |
||||
break; |
||||
case AVMEDIA_TYPE_AUDIO: |
||||
codec->sample_rate = avio_rb32(pb); |
||||
codec->channels = avio_rl16(pb); |
||||
codec->frame_size = avio_rl16(pb); |
||||
break; |
||||
default: |
||||
goto fail; |
||||
} |
||||
if (codec->flags & CODEC_FLAG_GLOBAL_HEADER) { |
||||
codec->extradata_size = avio_rb32(pb); |
||||
codec->extradata = av_malloc(codec->extradata_size); |
||||
if (!codec->extradata) |
||||
return AVERROR(ENOMEM); |
||||
avio_read(pb, codec->extradata, codec->extradata_size); |
||||
} |
||||
} |
||||
|
||||
/* get until end of block reached */ |
||||
while ((avio_tell(pb) % ffm->packet_size) != 0) |
||||
avio_r8(pb); |
||||
|
||||
/* init packet demux */ |
||||
ffm->packet_ptr = ffm->packet; |
||||
ffm->packet_end = ffm->packet; |
||||
ffm->frame_offset = 0; |
||||
ffm->dts = 0; |
||||
ffm->read_state = READ_HEADER; |
||||
ffm->first_packet = 1; |
||||
return 0; |
||||
fail: |
||||
ffm_close(s); |
||||
return -1; |
||||
} |
||||
|
||||
/* return < 0 if eof */ |
||||
static int ffm_read_packet(AVFormatContext *s, AVPacket *pkt) |
||||
{ |
||||
int size; |
||||
FFMContext *ffm = s->priv_data; |
||||
int duration, ret; |
||||
|
||||
switch(ffm->read_state) { |
||||
case READ_HEADER: |
||||
if ((ret = ffm_is_avail_data(s, FRAME_HEADER_SIZE+4)) < 0) |
||||
return ret; |
||||
|
||||
av_dlog(s, "pos=%08"PRIx64" spos=%"PRIx64", write_index=%"PRIx64" size=%"PRIx64"\n", |
||||
avio_tell(s->pb), s->pb->pos, ffm->write_index, ffm->file_size); |
||||
if (ffm_read_data(s, ffm->header, FRAME_HEADER_SIZE, 1) != |
||||
FRAME_HEADER_SIZE) |
||||
return -1; |
||||
if (ffm->header[1] & FLAG_DTS) |
||||
if (ffm_read_data(s, ffm->header+16, 4, 1) != 4) |
||||
return -1; |
||||
ffm->read_state = READ_DATA; |
||||
/* fall thru */ |
||||
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); |
||||
|
||||
av_new_packet(pkt, size); |
||||
pkt->stream_index = ffm->header[0]; |
||||
if ((unsigned)pkt->stream_index >= s->nb_streams) { |
||||
av_log(s, AV_LOG_ERROR, "invalid stream index %d\n", pkt->stream_index); |
||||
av_free_packet(pkt); |
||||
ffm->read_state = READ_HEADER; |
||||
return -1; |
||||
} |
||||
pkt->pos = avio_tell(s->pb); |
||||
if (ffm->header[1] & FLAG_KEY_FRAME) |
||||
pkt->flags |= AV_PKT_FLAG_KEY; |
||||
|
||||
ffm->read_state = READ_HEADER; |
||||
if (ffm_read_data(s, pkt->data, size, 0) != size) { |
||||
/* bad case: desynchronized packet. we cancel all the packet loading */ |
||||
av_free_packet(pkt); |
||||
return -1; |
||||
} |
||||
pkt->pts = AV_RB64(ffm->header+8); |
||||
if (ffm->header[1] & FLAG_DTS) |
||||
pkt->dts = pkt->pts - AV_RB32(ffm->header+16); |
||||
else |
||||
pkt->dts = pkt->pts; |
||||
pkt->duration = duration; |
||||
break; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/* seek to a given time in the file. The file read pointer is
|
||||
positioned at or before pts. XXX: the following code is quite |
||||
approximative */ |
||||
static int ffm_seek(AVFormatContext *s, int stream_index, int64_t wanted_pts, int flags) |
||||
{ |
||||
FFMContext *ffm = s->priv_data; |
||||
int64_t pos_min, pos_max, pos; |
||||
int64_t pts_min, pts_max, pts; |
||||
double pos1; |
||||
|
||||
av_dlog(s, "wanted_pts=%0.6f\n", wanted_pts / 1000000.0); |
||||
/* find the position using linear interpolation (better than
|
||||
dichotomy in typical cases) */ |
||||
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); |
||||
/* 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') |
||||
return AVPROBE_SCORE_MAX + 1; |
||||
return 0; |
||||
} |
||||
|
||||
AVInputFormat ff_ffm_demuxer = { |
||||
.name = "ffm", |
||||
.long_name = NULL_IF_CONFIG_SMALL("FFM (AVserver live feed)"), |
||||
.priv_data_size = sizeof(FFMContext), |
||||
.read_probe = ffm_probe, |
||||
.read_header = ffm_read_header, |
||||
.read_packet = ffm_read_packet, |
||||
.read_close = ffm_close, |
||||
.read_seek = ffm_seek, |
||||
}; |
@ -1,249 +0,0 @@ |
||||
/*
|
||||
* FFM (avserver live feed) muxer |
||||
* Copyright (c) 2001 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 <assert.h> |
||||
|
||||
#include "libavutil/intreadwrite.h" |
||||
#include "libavutil/intfloat.h" |
||||
#include "avformat.h" |
||||
#include "internal.h" |
||||
#include "ffm.h" |
||||
|
||||
static void flush_packet(AVFormatContext *s) |
||||
{ |
||||
FFMContext *ffm = s->priv_data; |
||||
int fill_size, h; |
||||
AVIOContext *pb = s->pb; |
||||
|
||||
fill_size = ffm->packet_end - ffm->packet_ptr; |
||||
memset(ffm->packet_ptr, 0, fill_size); |
||||
|
||||
assert(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 int ffm_write_header(AVFormatContext *s) |
||||
{ |
||||
FFMContext *ffm = s->priv_data; |
||||
AVStream *st; |
||||
AVIOContext *pb = s->pb; |
||||
AVCodecContext *codec; |
||||
int bit_rate, i; |
||||
|
||||
ffm->packet_size = FFM_PACKET_SIZE; |
||||
|
||||
/* header */ |
||||
avio_wl32(pb, MKTAG('F', 'F', 'M', '1')); |
||||
avio_wb32(pb, ffm->packet_size); |
||||
avio_wb64(pb, 0); /* current write position */ |
||||
|
||||
avio_wb32(pb, s->nb_streams); |
||||
bit_rate = 0; |
||||
for(i=0;i<s->nb_streams;i++) { |
||||
st = s->streams[i]; |
||||
bit_rate += st->codec->bit_rate; |
||||
} |
||||
avio_wb32(pb, bit_rate); |
||||
|
||||
/* list of streams */ |
||||
for(i=0;i<s->nb_streams;i++) { |
||||
st = s->streams[i]; |
||||
avpriv_set_pts_info(st, 64, 1, 1000000); |
||||
|
||||
codec = st->codec; |
||||
/* generic info */ |
||||
avio_wb32(pb, codec->codec_id); |
||||
avio_w8(pb, codec->codec_type); |
||||
avio_wb32(pb, codec->bit_rate); |
||||
avio_wb32(pb, codec->flags); |
||||
avio_wb32(pb, codec->flags2); |
||||
avio_wb32(pb, codec->debug); |
||||
/* specific info */ |
||||
switch(codec->codec_type) { |
||||
case AVMEDIA_TYPE_VIDEO: |
||||
avio_wb32(pb, codec->time_base.num); |
||||
avio_wb32(pb, codec->time_base.den); |
||||
avio_wb16(pb, codec->width); |
||||
avio_wb16(pb, codec->height); |
||||
avio_wb16(pb, codec->gop_size); |
||||
avio_wb32(pb, codec->pix_fmt); |
||||
avio_w8(pb, codec->qmin); |
||||
avio_w8(pb, codec->qmax); |
||||
avio_w8(pb, codec->max_qdiff); |
||||
avio_wb16(pb, (int) (codec->qcompress * 10000.0)); |
||||
avio_wb16(pb, (int) (codec->qblur * 10000.0)); |
||||
avio_wb32(pb, codec->bit_rate_tolerance); |
||||
avio_put_str(pb, codec->rc_eq ? codec->rc_eq : "tex^qComp"); |
||||
avio_wb32(pb, codec->rc_max_rate); |
||||
avio_wb32(pb, codec->rc_min_rate); |
||||
avio_wb32(pb, codec->rc_buffer_size); |
||||
avio_wb64(pb, av_double2int(codec->i_quant_factor)); |
||||
avio_wb64(pb, av_double2int(codec->b_quant_factor)); |
||||
avio_wb64(pb, av_double2int(codec->i_quant_offset)); |
||||
avio_wb64(pb, av_double2int(codec->b_quant_offset)); |
||||
avio_wb32(pb, codec->dct_algo); |
||||
avio_wb32(pb, codec->strict_std_compliance); |
||||
avio_wb32(pb, codec->max_b_frames); |
||||
avio_wb32(pb, codec->mpeg_quant); |
||||
avio_wb32(pb, codec->intra_dc_precision); |
||||
avio_wb32(pb, codec->me_method); |
||||
avio_wb32(pb, codec->mb_decision); |
||||
avio_wb32(pb, codec->nsse_weight); |
||||
avio_wb32(pb, codec->frame_skip_cmp); |
||||
avio_wb64(pb, av_double2int(codec->rc_buffer_aggressivity)); |
||||
avio_wb32(pb, codec->codec_tag); |
||||
avio_w8(pb, codec->thread_count); |
||||
avio_wb32(pb, codec->coder_type); |
||||
avio_wb32(pb, codec->me_cmp); |
||||
avio_wb32(pb, codec->me_subpel_quality); |
||||
avio_wb32(pb, codec->me_range); |
||||
avio_wb32(pb, codec->keyint_min); |
||||
avio_wb32(pb, codec->scenechange_threshold); |
||||
avio_wb32(pb, codec->b_frame_strategy); |
||||
avio_wb64(pb, av_double2int(codec->qcompress)); |
||||
avio_wb64(pb, av_double2int(codec->qblur)); |
||||
avio_wb32(pb, codec->max_qdiff); |
||||
avio_wb32(pb, codec->refs); |
||||
break; |
||||
case AVMEDIA_TYPE_AUDIO: |
||||
avio_wb32(pb, codec->sample_rate); |
||||
avio_wl16(pb, codec->channels); |
||||
avio_wl16(pb, codec->frame_size); |
||||
break; |
||||
default: |
||||
return -1; |
||||
} |
||||
if (codec->flags & CODEC_FLAG_GLOBAL_HEADER) { |
||||
avio_wb32(pb, codec->extradata_size); |
||||
avio_write(pb, codec->extradata, codec->extradata_size); |
||||
} |
||||
} |
||||
|
||||
/* 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; |
||||
assert(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) |
||||
{ |
||||
int64_t dts; |
||||
uint8_t header[FRAME_HEADER_SIZE+4]; |
||||
int header_size = FRAME_HEADER_SIZE; |
||||
|
||||
dts = 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, 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 (AVserver live feed)"), |
||||
.mime_type = "", |
||||
.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, |
||||
}; |
@ -1,3 +0,0 @@ |
||||
f3f0c42283b75bc826f499f048085c27 *./tests/data/lavf/lavf.ffm |
||||
376832 ./tests/data/lavf/lavf.ffm |
||||
./tests/data/lavf/lavf.ffm CRC=0xdd24439e |
@ -1,53 +0,0 @@ |
||||
ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 8192 size: 24664 |
||||
ret: 0 st:-1 flags:0 ts:-1.000000 |
||||
ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 8192 size: 24664 |
||||
ret: 0 st:-1 flags:1 ts: 1.894167 |
||||
ret: 0 st: 1 flags:1 dts: 0.929501 pts: 0.929501 pos: 376832 size: 209 |
||||
ret: 0 st: 0 flags:0 ts: 0.788334 |
||||
ret: 0 st: 1 flags:1 dts: 0.772766 pts: 0.772766 pos: 315392 size: 209 |
||||
ret: 0 st: 0 flags:1 ts:-0.317499 |
||||
ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 8192 size: 24664 |
||||
ret: 0 st: 1 flags:0 ts: 2.576668 |
||||
ret: 0 st: 1 flags:1 dts: 0.929501 pts: 0.929501 pos: 376832 size: 209 |
||||
ret: 0 st: 1 flags:1 ts: 1.470835 |
||||
ret: 0 st: 1 flags:1 dts: 0.929501 pts: 0.929501 pos: 376832 size: 209 |
||||
ret: 0 st:-1 flags:0 ts: 0.365002 |
||||
ret: 0 st: 1 flags:1 dts: 0.328685 pts: 0.328685 pos: 155648 size: 209 |
||||
ret: 0 st:-1 flags:1 ts:-0.740831 |
||||
ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 8192 size: 24664 |
||||
ret: 0 st: 0 flags:0 ts: 2.153336 |
||||
ret: 0 st: 1 flags:1 dts: 0.929501 pts: 0.929501 pos: 376832 size: 209 |
||||
ret: 0 st: 0 flags:1 ts: 1.047503 |
||||
ret: 0 st: 1 flags:1 dts: 0.929501 pts: 0.929501 pos: 376832 size: 209 |
||||
ret: 0 st: 1 flags:0 ts:-0.058330 |
||||
ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 8192 size: 24664 |
||||
ret: 0 st: 1 flags:1 ts: 2.835837 |
||||
ret: 0 st: 1 flags:1 dts: 0.929501 pts: 0.929501 pos: 376832 size: 209 |
||||
ret: 0 st:-1 flags:0 ts: 1.730004 |
||||
ret: 0 st: 1 flags:1 dts: 0.929501 pts: 0.929501 pos: 376832 size: 209 |
||||
ret: 0 st:-1 flags:1 ts: 0.624171 |
||||
ret: 0 st: 1 flags:1 dts: 0.642154 pts: 0.642154 pos: 274432 size: 209 |
||||
ret: 0 st: 0 flags:0 ts:-0.481662 |
||||
ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 8192 size: 24664 |
||||
ret: 0 st: 0 flags:1 ts: 2.412505 |
||||
ret: 0 st: 1 flags:1 dts: 0.929501 pts: 0.929501 pos: 376832 size: 209 |
||||
ret: 0 st: 1 flags:0 ts: 1.306672 |
||||
ret: 0 st: 1 flags:1 dts: 0.929501 pts: 0.929501 pos: 376832 size: 209 |
||||
ret: 0 st: 1 flags:1 ts: 0.200839 |
||||
ret: 0 st: 1 flags:1 dts: 0.224195 pts: 0.224195 pos: 114688 size: 209 |
||||
ret: 0 st:-1 flags:0 ts:-0.904994 |
||||
ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 8192 size: 24664 |
||||
ret: 0 st:-1 flags:1 ts: 1.989173 |
||||
ret: 0 st: 1 flags:1 dts: 0.929501 pts: 0.929501 pos: 376832 size: 209 |
||||
ret: 0 st: 0 flags:0 ts: 0.883340 |
||||
ret: 0 st: 1 flags:1 dts: 0.877256 pts: 0.877256 pos: 339968 size: 209 |
||||
ret: 0 st: 0 flags:1 ts:-0.222493 |
||||
ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 8192 size: 24664 |
||||
ret: 0 st: 1 flags:0 ts: 2.671674 |
||||
ret: 0 st: 1 flags:1 dts: 0.929501 pts: 0.929501 pos: 376832 size: 209 |
||||
ret: 0 st: 1 flags:1 ts: 1.565841 |
||||
ret: 0 st: 1 flags:1 dts: 0.929501 pts: 0.929501 pos: 376832 size: 209 |
||||
ret: 0 st:-1 flags:0 ts: 0.460008 |
||||
ret: 0 st: 1 flags:1 dts: 0.459297 pts: 0.459297 pos: 204800 size: 209 |
||||
ret: 0 st:-1 flags:1 ts:-0.645825 |
||||
ret: 0 st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos: 8192 size: 24664 |
Loading…
Reference in new issue