mirror of
https://mirror.skon.top/https://github.com/FFmpeg/FFmpeg
synced 2026-04-20 21:00:41 +08:00
renamed libav to libavformat
Originally committed as revision 1276 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
6
libavformat/.cvsignore
Normal file
6
libavformat/.cvsignore
Normal file
@@ -0,0 +1,6 @@
|
||||
config.h
|
||||
config.mak
|
||||
*ffmpeg
|
||||
ffserver
|
||||
Makefile.*
|
||||
.depend
|
||||
72
libavformat/Makefile
Normal file
72
libavformat/Makefile
Normal file
@@ -0,0 +1,72 @@
|
||||
#
|
||||
# libavformat Makefile
|
||||
# (c) 2000, 2001, 2002 Fabrice Bellard
|
||||
#
|
||||
include ../config.mak
|
||||
|
||||
VPATH=$(SRC_PATH)/libav
|
||||
|
||||
CFLAGS= $(OPTFLAGS) -Wall -g -I.. -I$(SRC_PATH) -I$(SRC_PATH)/libavcodec -DHAVE_AV_CONFIG_H -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_GNU_SOURCE
|
||||
|
||||
OBJS= utils.o cutils.o allformats.o
|
||||
|
||||
# mux and demuxes
|
||||
OBJS+=mpeg.o mpegts.o ffm.o crc.o img.o raw.o rm.o asf.o \
|
||||
avienc.o avidec.o wav.o swf.o au.o gif.o mov.o jpeg.o dv.o framehook.o
|
||||
# file I/O
|
||||
OBJS+= avio.o aviobuf.o file.o
|
||||
|
||||
ifeq ($(BUILD_STRPTIME),yes)
|
||||
OBJS+= strptime.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_VIDEO4LINUX),yes)
|
||||
OBJS+= grab.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_AUDIO_OSS),yes)
|
||||
OBJS+= audio.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_AUDIO_BEOS),yes)
|
||||
OBJS+= beosaudio.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_NETWORK),yes)
|
||||
OBJS+= udp.o tcp.o http.o rtsp.o rtp.o rtpproto.o
|
||||
# BeOS network stuff
|
||||
ifeq ($(CONFIG_BEOS_NETSERVER),yes)
|
||||
OBJS+= barpainet.o
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_VORBIS),yes)
|
||||
OBJS+= ogg.o
|
||||
endif
|
||||
|
||||
LIB= libavformat.a
|
||||
|
||||
all: $(LIB)
|
||||
|
||||
$(LIB): $(OBJS)
|
||||
rm -f $@
|
||||
$(AR) rc $@ $(OBJS)
|
||||
$(RANLIB) $@
|
||||
|
||||
installlib: all
|
||||
install -m 644 $(LIB) $(prefix)/lib
|
||||
mkdir -p $(prefix)/include/ffmpeg
|
||||
install -m 644 $(SRC_PATH)/libav/avformat.h $(SRC_PATH)/libav/avio.h \
|
||||
$(SRC_PATH)/libav/rtp.h $(SRC_PATH)/libav/rtsp.h \
|
||||
$(SRC_PATH)/libav/rtspcodes.h \
|
||||
$(prefix)/include/ffmpeg
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
# BeOS: remove -Wall to get rid of all the "multibyte constant" warnings
|
||||
%.o: %.cpp
|
||||
g++ $(subst -Wall,,$(CFLAGS)) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *.o *~ *.a
|
||||
74
libavformat/allformats.c
Normal file
74
libavformat/allformats.c
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Register all the formats and protocols
|
||||
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
|
||||
/* If you do not call this function, then you can select exactly which
|
||||
formats you want to support */
|
||||
|
||||
/**
|
||||
* Initialize libavcodec and register all the codecs and formats.
|
||||
*/
|
||||
void av_register_all(void)
|
||||
{
|
||||
avcodec_init();
|
||||
avcodec_register_all();
|
||||
|
||||
mpegps_init();
|
||||
mpegts_init();
|
||||
crc_init();
|
||||
img_init();
|
||||
raw_init();
|
||||
rm_init();
|
||||
asf_init();
|
||||
avienc_init();
|
||||
avidec_init();
|
||||
wav_init();
|
||||
swf_init();
|
||||
au_init();
|
||||
gif_init();
|
||||
mov_init();
|
||||
jpeg_init();
|
||||
dv_init();
|
||||
|
||||
#ifdef CONFIG_VORBIS
|
||||
ogg_init();
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_WIN32
|
||||
ffm_init();
|
||||
#endif
|
||||
#ifdef CONFIG_VIDEO4LINUX
|
||||
video_grab_init();
|
||||
#endif
|
||||
#if defined(CONFIG_AUDIO_OSS) || defined(CONFIG_AUDIO_BEOS)
|
||||
audio_init();
|
||||
#endif
|
||||
|
||||
/* file protocols */
|
||||
register_protocol(&file_protocol);
|
||||
register_protocol(&pipe_protocol);
|
||||
#ifdef CONFIG_NETWORK
|
||||
rtsp_init();
|
||||
rtp_init();
|
||||
register_protocol(&udp_protocol);
|
||||
register_protocol(&rtp_protocol);
|
||||
register_protocol(&tcp_protocol);
|
||||
register_protocol(&http_protocol);
|
||||
#endif
|
||||
}
|
||||
1256
libavformat/asf.c
Normal file
1256
libavformat/asf.c
Normal file
File diff suppressed because it is too large
Load Diff
214
libavformat/au.c
Normal file
214
libavformat/au.c
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* AU encoder and decoder
|
||||
* Copyright (c) 2001 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* First version by Francois Revol revol@free.fr
|
||||
*
|
||||
* Reference documents:
|
||||
* http://www.opengroup.org/public/pubs/external/auformat.html
|
||||
* http://www.goice.co.jp/member/mo/formats/au.html
|
||||
*/
|
||||
|
||||
#include "avformat.h"
|
||||
#include "avi.h"
|
||||
|
||||
/* if we don't know the size in advance */
|
||||
#define AU_UNKOWN_SIZE ((UINT32)(~0))
|
||||
|
||||
/* The ffmpeg codecs we support, and the IDs they have in the file */
|
||||
static const CodecTag codec_au_tags[] = {
|
||||
{ CODEC_ID_PCM_MULAW, 1 },
|
||||
{ CODEC_ID_PCM_S16BE, 3 },
|
||||
{ CODEC_ID_PCM_ALAW, 27 },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
/* AUDIO_FILE header */
|
||||
static int put_au_header(ByteIOContext *pb, AVCodecContext *enc)
|
||||
{
|
||||
int tag;
|
||||
|
||||
tag = codec_get_tag(codec_au_tags, enc->codec_id);
|
||||
if (tag == 0)
|
||||
return -1;
|
||||
put_tag(pb, ".snd"); /* magic number */
|
||||
put_be32(pb, 24); /* header size */
|
||||
put_be32(pb, AU_UNKOWN_SIZE); /* data size */
|
||||
put_be32(pb, (UINT32)tag); /* codec ID */
|
||||
put_be32(pb, enc->sample_rate);
|
||||
put_be32(pb, (UINT32)enc->channels);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au_write_header(AVFormatContext *s)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
|
||||
s->priv_data = NULL;
|
||||
|
||||
/* format header */
|
||||
if (put_au_header(pb, &s->streams[0]->codec) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
put_flush_packet(pb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au_write_packet(AVFormatContext *s, int stream_index_ptr,
|
||||
UINT8 *buf, int size, int force_pts)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
put_buffer(pb, buf, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au_write_trailer(AVFormatContext *s)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
offset_t file_size;
|
||||
|
||||
if (!url_is_streamed(&s->pb)) {
|
||||
|
||||
/* update file size */
|
||||
file_size = url_ftell(pb);
|
||||
url_fseek(pb, 8, SEEK_SET);
|
||||
put_be32(pb, (UINT32)(file_size - 24));
|
||||
url_fseek(pb, file_size, SEEK_SET);
|
||||
|
||||
put_flush_packet(pb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au_probe(AVProbeData *p)
|
||||
{
|
||||
/* check file header */
|
||||
if (p->buf_size <= 24)
|
||||
return 0;
|
||||
if (p->buf[0] == '.' && p->buf[1] == 's' &&
|
||||
p->buf[2] == 'n' && p->buf[3] == 'd')
|
||||
return AVPROBE_SCORE_MAX;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* au input */
|
||||
static int au_read_header(AVFormatContext *s,
|
||||
AVFormatParameters *ap)
|
||||
{
|
||||
int size;
|
||||
unsigned int tag;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
unsigned int id, codec, channels, rate;
|
||||
AVStream *st;
|
||||
|
||||
/* check ".snd" header */
|
||||
tag = get_le32(pb);
|
||||
if (tag != MKTAG('.', 's', 'n', 'd'))
|
||||
return -1;
|
||||
size = get_be32(pb); /* header size */
|
||||
get_be32(pb); /* data size */
|
||||
|
||||
id = get_be32(pb);
|
||||
rate = get_be32(pb);
|
||||
channels = get_be32(pb);
|
||||
|
||||
codec = codec_get_id(codec_au_tags, id);
|
||||
|
||||
if (size >= 24) {
|
||||
/* skip unused data */
|
||||
url_fseek(pb, size - 24, SEEK_CUR);
|
||||
}
|
||||
|
||||
/* now we are ready: build format streams */
|
||||
st = av_malloc(sizeof(AVStream));
|
||||
if (!st)
|
||||
return -1;
|
||||
s->nb_streams = 1;
|
||||
s->streams[0] = st;
|
||||
|
||||
st->id = 0;
|
||||
|
||||
st->codec.codec_type = CODEC_TYPE_AUDIO;
|
||||
st->codec.codec_tag = id;
|
||||
st->codec.codec_id = codec;
|
||||
st->codec.channels = channels;
|
||||
st->codec.sample_rate = rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MAX_SIZE 4096
|
||||
|
||||
static int au_read_packet(AVFormatContext *s,
|
||||
AVPacket *pkt)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (url_feof(&s->pb))
|
||||
return -EIO;
|
||||
if (av_new_packet(pkt, MAX_SIZE))
|
||||
return -EIO;
|
||||
pkt->stream_index = 0;
|
||||
|
||||
ret = get_buffer(&s->pb, pkt->data, pkt->size);
|
||||
if (ret < 0)
|
||||
av_free_packet(pkt);
|
||||
/* note: we need to modify the packet size here to handle the last
|
||||
packet */
|
||||
pkt->size = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au_read_close(AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVInputFormat au_iformat = {
|
||||
"au",
|
||||
"SUN AU Format",
|
||||
0,
|
||||
au_probe,
|
||||
au_read_header,
|
||||
au_read_packet,
|
||||
au_read_close,
|
||||
};
|
||||
|
||||
static AVOutputFormat au_oformat = {
|
||||
"au",
|
||||
"SUN AU Format",
|
||||
"audio/basic",
|
||||
"au",
|
||||
0,
|
||||
CODEC_ID_PCM_S16BE,
|
||||
CODEC_ID_NONE,
|
||||
au_write_header,
|
||||
au_write_packet,
|
||||
au_write_trailer,
|
||||
};
|
||||
|
||||
int au_init(void)
|
||||
{
|
||||
av_register_input_format(&au_iformat);
|
||||
av_register_output_format(&au_oformat);
|
||||
return 0;
|
||||
}
|
||||
330
libavformat/audio.c
Normal file
330
libavformat/audio.c
Normal file
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Linux audio play and grab interface
|
||||
* Copyright (c) 2000, 2001 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/soundcard.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
const char *audio_device = "/dev/dsp";
|
||||
|
||||
#define AUDIO_BLOCK_SIZE 4096
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
int sample_rate;
|
||||
int channels;
|
||||
int frame_size; /* in bytes ! */
|
||||
int codec_id;
|
||||
int flip_left : 1;
|
||||
UINT8 buffer[AUDIO_BLOCK_SIZE];
|
||||
int buffer_ptr;
|
||||
} AudioData;
|
||||
|
||||
static int audio_open(AudioData *s, int is_output)
|
||||
{
|
||||
int audio_fd;
|
||||
int tmp, err;
|
||||
char *flip = getenv("AUDIO_FLIP_LEFT");
|
||||
|
||||
/* open linux audio device */
|
||||
if (is_output)
|
||||
audio_fd = open(audio_device, O_WRONLY);
|
||||
else
|
||||
audio_fd = open(audio_device, O_RDONLY);
|
||||
if (audio_fd < 0) {
|
||||
perror(audio_device);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (flip && *flip == '1') {
|
||||
s->flip_left = 1;
|
||||
}
|
||||
|
||||
/* non blocking mode */
|
||||
if (!is_output)
|
||||
fcntl(audio_fd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
s->frame_size = AUDIO_BLOCK_SIZE;
|
||||
#if 0
|
||||
tmp = (NB_FRAGMENTS << 16) | FRAGMENT_BITS;
|
||||
err = ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &tmp);
|
||||
if (err < 0) {
|
||||
perror("SNDCTL_DSP_SETFRAGMENT");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* select format : favour native format */
|
||||
err = ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &tmp);
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
if (tmp & AFMT_S16_BE) {
|
||||
tmp = AFMT_S16_BE;
|
||||
} else if (tmp & AFMT_S16_LE) {
|
||||
tmp = AFMT_S16_LE;
|
||||
} else {
|
||||
tmp = 0;
|
||||
}
|
||||
#else
|
||||
if (tmp & AFMT_S16_LE) {
|
||||
tmp = AFMT_S16_LE;
|
||||
} else if (tmp & AFMT_S16_BE) {
|
||||
tmp = AFMT_S16_BE;
|
||||
} else {
|
||||
tmp = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch(tmp) {
|
||||
case AFMT_S16_LE:
|
||||
s->codec_id = CODEC_ID_PCM_S16LE;
|
||||
break;
|
||||
case AFMT_S16_BE:
|
||||
s->codec_id = CODEC_ID_PCM_S16BE;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Soundcard does not support 16 bit sample format\n");
|
||||
close(audio_fd);
|
||||
return -EIO;
|
||||
}
|
||||
err=ioctl(audio_fd, SNDCTL_DSP_SETFMT, &tmp);
|
||||
if (err < 0) {
|
||||
perror("SNDCTL_DSP_SETFMT");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
tmp = (s->channels == 2);
|
||||
err = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp);
|
||||
if (err < 0) {
|
||||
perror("SNDCTL_DSP_STEREO");
|
||||
goto fail;
|
||||
}
|
||||
if (tmp)
|
||||
s->channels = 2;
|
||||
|
||||
tmp = s->sample_rate;
|
||||
err = ioctl(audio_fd, SNDCTL_DSP_SPEED, &tmp);
|
||||
if (err < 0) {
|
||||
perror("SNDCTL_DSP_SPEED");
|
||||
goto fail;
|
||||
}
|
||||
s->sample_rate = tmp; /* store real sample rate */
|
||||
s->fd = audio_fd;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
close(audio_fd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int audio_close(AudioData *s)
|
||||
{
|
||||
close(s->fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sound output support */
|
||||
static int audio_write_header(AVFormatContext *s1)
|
||||
{
|
||||
AudioData *s = s1->priv_data;
|
||||
AVStream *st;
|
||||
int ret;
|
||||
|
||||
st = s1->streams[0];
|
||||
s->sample_rate = st->codec.sample_rate;
|
||||
s->channels = st->codec.channels;
|
||||
ret = audio_open(s, 1);
|
||||
if (ret < 0) {
|
||||
return -EIO;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int audio_write_packet(AVFormatContext *s1, int stream_index,
|
||||
UINT8 *buf, int size, int force_pts)
|
||||
{
|
||||
AudioData *s = s1->priv_data;
|
||||
int len, ret;
|
||||
|
||||
while (size > 0) {
|
||||
len = AUDIO_BLOCK_SIZE - s->buffer_ptr;
|
||||
if (len > size)
|
||||
len = size;
|
||||
memcpy(s->buffer + s->buffer_ptr, buf, len);
|
||||
s->buffer_ptr += len;
|
||||
if (s->buffer_ptr >= AUDIO_BLOCK_SIZE) {
|
||||
for(;;) {
|
||||
ret = write(s->fd, s->buffer, AUDIO_BLOCK_SIZE);
|
||||
if (ret > 0)
|
||||
break;
|
||||
if (ret < 0 && (errno != EAGAIN && errno != EINTR))
|
||||
return -EIO;
|
||||
}
|
||||
s->buffer_ptr = 0;
|
||||
}
|
||||
buf += len;
|
||||
size -= len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audio_write_trailer(AVFormatContext *s1)
|
||||
{
|
||||
AudioData *s = s1->priv_data;
|
||||
|
||||
audio_close(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* grab support */
|
||||
|
||||
static int audio_read_header(AVFormatContext *s1, AVFormatParameters *ap)
|
||||
{
|
||||
AudioData *s = s1->priv_data;
|
||||
AVStream *st;
|
||||
int ret;
|
||||
|
||||
if (!ap || ap->sample_rate <= 0 || ap->channels <= 0)
|
||||
return -1;
|
||||
|
||||
st = av_new_stream(s1, 0);
|
||||
if (!st) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
s->sample_rate = ap->sample_rate;
|
||||
s->channels = ap->channels;
|
||||
|
||||
ret = audio_open(s, 0);
|
||||
if (ret < 0) {
|
||||
av_free(st);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* take real parameters */
|
||||
st->codec.codec_type = CODEC_TYPE_AUDIO;
|
||||
st->codec.codec_id = s->codec_id;
|
||||
st->codec.sample_rate = s->sample_rate;
|
||||
st->codec.channels = s->channels;
|
||||
|
||||
av_set_pts_info(s1, 48, 1, 1000000); /* 48 bits pts in us */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt)
|
||||
{
|
||||
AudioData *s = s1->priv_data;
|
||||
int ret, bdelay;
|
||||
int64_t cur_time;
|
||||
struct audio_buf_info abufi;
|
||||
|
||||
if (av_new_packet(pkt, s->frame_size) < 0)
|
||||
return -EIO;
|
||||
for(;;) {
|
||||
ret = read(s->fd, pkt->data, pkt->size);
|
||||
if (ret > 0)
|
||||
break;
|
||||
if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
|
||||
av_free_packet(pkt);
|
||||
pkt->size = 0;
|
||||
return 0;
|
||||
}
|
||||
if (!(ret == 0 || (ret == -1 && (errno == EAGAIN || errno == EINTR)))) {
|
||||
av_free_packet(pkt);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
pkt->size = ret;
|
||||
|
||||
/* compute pts of the start of the packet */
|
||||
cur_time = av_gettime();
|
||||
bdelay = ret;
|
||||
if (ioctl(s->fd, SNDCTL_DSP_GETISPACE, &abufi) == 0) {
|
||||
bdelay += abufi.bytes;
|
||||
}
|
||||
/* substract time represented by the number of bytes in the audio fifo */
|
||||
cur_time -= (bdelay * 1000000LL) / (s->sample_rate * s->channels);
|
||||
|
||||
/* convert to wanted units */
|
||||
pkt->pts = cur_time & ((1LL << 48) - 1);
|
||||
|
||||
if (s->flip_left && s->channels == 2) {
|
||||
int i;
|
||||
short *p = (short *) pkt->data;
|
||||
|
||||
for (i = 0; i < ret; i += 4) {
|
||||
*p = ~*p;
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audio_read_close(AVFormatContext *s1)
|
||||
{
|
||||
AudioData *s = s1->priv_data;
|
||||
|
||||
audio_close(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVInputFormat audio_in_format = {
|
||||
"audio_device",
|
||||
"audio grab and output",
|
||||
sizeof(AudioData),
|
||||
NULL,
|
||||
audio_read_header,
|
||||
audio_read_packet,
|
||||
audio_read_close,
|
||||
.flags = AVFMT_NOFILE,
|
||||
};
|
||||
|
||||
static AVOutputFormat audio_out_format = {
|
||||
"audio_device",
|
||||
"audio grab and output",
|
||||
"",
|
||||
"",
|
||||
sizeof(AudioData),
|
||||
/* XXX: we make the assumption that the soundcard accepts this format */
|
||||
/* XXX: find better solution with "preinit" method, needed also in
|
||||
other formats */
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
CODEC_ID_PCM_S16BE,
|
||||
#else
|
||||
CODEC_ID_PCM_S16LE,
|
||||
#endif
|
||||
CODEC_ID_NONE,
|
||||
audio_write_header,
|
||||
audio_write_packet,
|
||||
audio_write_trailer,
|
||||
.flags = AVFMT_NOFILE,
|
||||
};
|
||||
|
||||
int audio_init(void)
|
||||
{
|
||||
av_register_input_format(&audio_in_format);
|
||||
av_register_output_format(&audio_out_format);
|
||||
return 0;
|
||||
}
|
||||
351
libavformat/avformat.h
Normal file
351
libavformat/avformat.h
Normal file
@@ -0,0 +1,351 @@
|
||||
#ifndef AVFORMAT_H
|
||||
#define AVFORMAT_H
|
||||
|
||||
#define LIBAVFORMAT_VERSION_INT 0x000406
|
||||
#define LIBAVFORMAT_VERSION "0.4.6"
|
||||
#define LIBAVFORMAT_BUILD 4602
|
||||
|
||||
#include "avcodec.h"
|
||||
|
||||
#include "avio.h"
|
||||
|
||||
/* packet functions */
|
||||
|
||||
#define AV_NOPTS_VALUE 0
|
||||
|
||||
typedef struct AVPacket {
|
||||
INT64 pts; /* presentation time stamp in stream units (set av_set_pts_info) */
|
||||
UINT8 *data;
|
||||
int size;
|
||||
int stream_index;
|
||||
int flags;
|
||||
int duration;
|
||||
#define PKT_FLAG_KEY 0x0001
|
||||
} AVPacket;
|
||||
|
||||
int av_new_packet(AVPacket *pkt, int size);
|
||||
void av_free_packet(AVPacket *pkt);
|
||||
|
||||
/*************************************************/
|
||||
/* fractional numbers for exact pts handling */
|
||||
|
||||
/* the exact value of the fractional number is: 'val + num / den'. num
|
||||
is assumed to be such as 0 <= num < den */
|
||||
typedef struct AVFrac {
|
||||
INT64 val, num, den;
|
||||
} AVFrac;
|
||||
|
||||
void av_frac_init(AVFrac *f, INT64 val, INT64 num, INT64 den);
|
||||
void av_frac_add(AVFrac *f, INT64 incr);
|
||||
void av_frac_set(AVFrac *f, INT64 val);
|
||||
|
||||
/*************************************************/
|
||||
/* input/output formats */
|
||||
|
||||
struct AVFormatContext;
|
||||
|
||||
/* this structure contains the data a format has to probe a file */
|
||||
typedef struct AVProbeData {
|
||||
char *filename;
|
||||
unsigned char *buf;
|
||||
int buf_size;
|
||||
} AVProbeData;
|
||||
|
||||
#define AVPROBE_SCORE_MAX 100
|
||||
|
||||
typedef struct AVFormatParameters {
|
||||
int frame_rate;
|
||||
int sample_rate;
|
||||
int channels;
|
||||
int width;
|
||||
int height;
|
||||
enum PixelFormat pix_fmt;
|
||||
} AVFormatParameters;
|
||||
|
||||
#define AVFMT_NOFILE 0x0001 /* no file should be opened */
|
||||
#define AVFMT_NEEDNUMBER 0x0002 /* needs '%d' in filename */
|
||||
#define AVFMT_NOHEADER 0x0004 /* signal that no header is present
|
||||
(streams are added dynamically) */
|
||||
#define AVFMT_SHOW_IDS 0x0008 /* show format stream IDs numbers */
|
||||
#define AVFMT_RGB24 0x0010 /* force RGB24 output for ppm (hack
|
||||
- need better api) */
|
||||
#define AVFMT_RAWPICTURE 0x0020 /* format wants AVPicture structure for
|
||||
raw picture data */
|
||||
|
||||
typedef struct AVOutputFormat {
|
||||
const char *name;
|
||||
const char *long_name;
|
||||
const char *mime_type;
|
||||
const char *extensions; /* comma separated extensions */
|
||||
/* size of private data so that it can be allocated in the wrapper */
|
||||
int priv_data_size;
|
||||
/* output support */
|
||||
enum CodecID audio_codec; /* default audio codec */
|
||||
enum CodecID video_codec; /* default video codec */
|
||||
int (*write_header)(struct AVFormatContext *);
|
||||
/* XXX: change prototype for 64 bit pts */
|
||||
int (*write_packet)(struct AVFormatContext *,
|
||||
int stream_index,
|
||||
unsigned char *buf, int size, int force_pts);
|
||||
int (*write_trailer)(struct AVFormatContext *);
|
||||
/* can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER */
|
||||
int flags;
|
||||
/* private fields */
|
||||
struct AVOutputFormat *next;
|
||||
} AVOutputFormat;
|
||||
|
||||
typedef struct AVInputFormat {
|
||||
const char *name;
|
||||
const char *long_name;
|
||||
/* size of private data so that it can be allocated in the wrapper */
|
||||
int priv_data_size;
|
||||
/* tell if a given file has a chance of being parsing by this format */
|
||||
int (*read_probe)(AVProbeData *);
|
||||
/* read the format header and initialize the AVFormatContext
|
||||
structure. Return 0 if OK. 'ap' if non NULL contains
|
||||
additionnal paramters. Only used in raw format right
|
||||
now. 'av_new_stream' should be called to create new streams. */
|
||||
int (*read_header)(struct AVFormatContext *,
|
||||
AVFormatParameters *ap);
|
||||
/* read one packet and put it in 'pkt'. pts and flags are also
|
||||
set. 'av_new_stream' can be called only if the flag
|
||||
AVFMT_NOHEADER is used. */
|
||||
int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);
|
||||
/* close the stream. The AVFormatContext and AVStreams are not
|
||||
freed by this function */
|
||||
int (*read_close)(struct AVFormatContext *);
|
||||
/* seek at or before a given pts (given in microsecond). The pts
|
||||
origin is defined by the stream */
|
||||
int (*read_seek)(struct AVFormatContext *, INT64 pts);
|
||||
/* can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_NOHEADER */
|
||||
int flags;
|
||||
/* if extensions are defined, then no probe is done. You should
|
||||
usually not use extension format guessing because it is not
|
||||
reliable enough */
|
||||
const char *extensions;
|
||||
/* general purpose read only value that the format can use */
|
||||
int value;
|
||||
/* private fields */
|
||||
struct AVInputFormat *next;
|
||||
} AVInputFormat;
|
||||
|
||||
typedef struct AVStream {
|
||||
int index; /* stream index in AVFormatContext */
|
||||
int id; /* format specific stream id */
|
||||
AVCodecContext codec; /* codec context */
|
||||
int r_frame_rate; /* real frame rate of the stream */
|
||||
uint64_t time_length; /* real length of the stream in miliseconds */
|
||||
void *priv_data;
|
||||
/* internal data used in av_find_stream_info() */
|
||||
int codec_info_state;
|
||||
int codec_info_nb_repeat_frames;
|
||||
int codec_info_nb_real_frames;
|
||||
/* PTS generation when outputing stream */
|
||||
AVFrac pts;
|
||||
/* ffmpeg.c private use */
|
||||
int stream_copy; /* if TRUE, just copy stream */
|
||||
} AVStream;
|
||||
|
||||
#define MAX_STREAMS 20
|
||||
|
||||
/* format I/O context */
|
||||
typedef struct AVFormatContext {
|
||||
/* can only be iformat or oformat, not both at the same time */
|
||||
struct AVInputFormat *iformat;
|
||||
struct AVOutputFormat *oformat;
|
||||
void *priv_data;
|
||||
ByteIOContext pb;
|
||||
int nb_streams;
|
||||
AVStream *streams[MAX_STREAMS];
|
||||
char filename[1024]; /* input or output filename */
|
||||
/* stream info */
|
||||
char title[512];
|
||||
char author[512];
|
||||
char copyright[512];
|
||||
char comment[512];
|
||||
int flags; /* format specific flags */
|
||||
/* private data for pts handling (do not modify directly) */
|
||||
int pts_wrap_bits; /* number of bits in pts (used for wrapping control) */
|
||||
int pts_num, pts_den; /* value to convert to seconds */
|
||||
/* This buffer is only needed when packets were already buffered but
|
||||
not decoded, for example to get the codec parameters in mpeg
|
||||
streams */
|
||||
struct AVPacketList *packet_buffer;
|
||||
} AVFormatContext;
|
||||
|
||||
typedef struct AVPacketList {
|
||||
AVPacket pkt;
|
||||
struct AVPacketList *next;
|
||||
} AVPacketList;
|
||||
|
||||
extern AVInputFormat *first_iformat;
|
||||
extern AVOutputFormat *first_oformat;
|
||||
|
||||
/* XXX: use automatic init with either ELF sections or C file parser */
|
||||
/* modules */
|
||||
|
||||
/* mpeg.c */
|
||||
int mpegps_init(void);
|
||||
|
||||
/* mpegts.c */
|
||||
extern AVInputFormat mpegts_demux;
|
||||
int mpegts_init(void);
|
||||
|
||||
/* rm.c */
|
||||
int rm_init(void);
|
||||
|
||||
/* crc.c */
|
||||
int crc_init(void);
|
||||
|
||||
/* img.c */
|
||||
int img_init(void);
|
||||
|
||||
/* asf.c */
|
||||
int asf_init(void);
|
||||
|
||||
/* avienc.c */
|
||||
int avienc_init(void);
|
||||
|
||||
/* avidec.c */
|
||||
int avidec_init(void);
|
||||
|
||||
/* swf.c */
|
||||
int swf_init(void);
|
||||
|
||||
/* mov.c */
|
||||
int mov_init(void);
|
||||
|
||||
/* jpeg.c */
|
||||
int jpeg_init(void);
|
||||
|
||||
/* gif.c */
|
||||
int gif_init(void);
|
||||
|
||||
/* au.c */
|
||||
int au_init(void);
|
||||
|
||||
/* wav.c */
|
||||
int wav_init(void);
|
||||
|
||||
/* raw.c */
|
||||
int raw_init(void);
|
||||
|
||||
/* ogg.c */
|
||||
int ogg_init(void);
|
||||
|
||||
/* dv.c */
|
||||
int dv_init(void);
|
||||
|
||||
/* ffm.c */
|
||||
int ffm_init(void);
|
||||
|
||||
/* rtsp.c */
|
||||
extern AVInputFormat redir_demux;
|
||||
int redir_open(AVFormatContext **ic_ptr, ByteIOContext *f);
|
||||
|
||||
#include "rtp.h"
|
||||
|
||||
#include "rtsp.h"
|
||||
|
||||
/* utils.c */
|
||||
#define MKTAG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
|
||||
#define MKBETAG(a,b,c,d) (d | (c << 8) | (b << 16) | (a << 24))
|
||||
|
||||
void av_register_input_format(AVInputFormat *format);
|
||||
void av_register_output_format(AVOutputFormat *format);
|
||||
AVOutputFormat *guess_stream_format(const char *short_name,
|
||||
const char *filename, const char *mime_type);
|
||||
AVOutputFormat *guess_format(const char *short_name,
|
||||
const char *filename, const char *mime_type);
|
||||
|
||||
void av_hex_dump(UINT8 *buf, int size);
|
||||
|
||||
void av_register_all(void);
|
||||
|
||||
typedef struct FifoBuffer {
|
||||
UINT8 *buffer;
|
||||
UINT8 *rptr, *wptr, *end;
|
||||
} FifoBuffer;
|
||||
|
||||
int fifo_init(FifoBuffer *f, int size);
|
||||
void fifo_free(FifoBuffer *f);
|
||||
int fifo_size(FifoBuffer *f, UINT8 *rptr);
|
||||
int fifo_read(FifoBuffer *f, UINT8 *buf, int buf_size, UINT8 **rptr_ptr);
|
||||
void fifo_write(FifoBuffer *f, UINT8 *buf, int size, UINT8 **wptr_ptr);
|
||||
|
||||
/* media file input */
|
||||
AVInputFormat *av_find_input_format(const char *short_name);
|
||||
AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened);
|
||||
int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
|
||||
AVInputFormat *fmt,
|
||||
int buf_size,
|
||||
AVFormatParameters *ap);
|
||||
|
||||
#define AVERROR_UNKNOWN (-1) /* unknown error */
|
||||
#define AVERROR_IO (-2) /* i/o error */
|
||||
#define AVERROR_NUMEXPECTED (-3) /* number syntax expected in filename */
|
||||
#define AVERROR_INVALIDDATA (-4) /* invalid data found */
|
||||
#define AVERROR_NOMEM (-5) /* not enough memory */
|
||||
#define AVERROR_NOFMT (-6) /* unknown format */
|
||||
|
||||
int av_find_stream_info(AVFormatContext *ic);
|
||||
int av_read_packet(AVFormatContext *s, AVPacket *pkt);
|
||||
void av_close_input_file(AVFormatContext *s);
|
||||
AVStream *av_new_stream(AVFormatContext *s, int id);
|
||||
void av_set_pts_info(AVFormatContext *s, int pts_wrap_bits,
|
||||
int pts_num, int pts_den);
|
||||
|
||||
/* media file output */
|
||||
int av_write_header(AVFormatContext *s);
|
||||
int av_write_frame(AVFormatContext *s, int stream_index, const uint8_t *buf,
|
||||
int size);
|
||||
int av_write_trailer(AVFormatContext *s);
|
||||
|
||||
void dump_format(AVFormatContext *ic,
|
||||
int index,
|
||||
const char *url,
|
||||
int is_output);
|
||||
int parse_image_size(int *width_ptr, int *height_ptr, const char *str);
|
||||
INT64 parse_date(const char *datestr, int duration);
|
||||
|
||||
INT64 av_gettime(void);
|
||||
|
||||
/* ffm specific for ffserver */
|
||||
#define FFM_PACKET_SIZE 4096
|
||||
offset_t ffm_read_write_index(int fd);
|
||||
void ffm_write_write_index(int fd, offset_t pos);
|
||||
void ffm_set_write_index(AVFormatContext *s, offset_t pos, offset_t file_size);
|
||||
|
||||
int find_info_tag(char *arg, int arg_size, const char *tag1, const char *info);
|
||||
|
||||
int get_frame_filename(char *buf, int buf_size,
|
||||
const char *path, int number);
|
||||
int filename_number_test(const char *filename);
|
||||
|
||||
/* grab specific */
|
||||
int video_grab_init(void);
|
||||
int audio_init(void);
|
||||
|
||||
extern const char *v4l_device;
|
||||
extern const char *audio_device;
|
||||
|
||||
#ifdef HAVE_AV_CONFIG_H
|
||||
int strstart(const char *str, const char *val, const char **ptr);
|
||||
int stristart(const char *str, const char *val, const char **ptr);
|
||||
void pstrcpy(char *buf, int buf_size, const char *str);
|
||||
char *pstrcat(char *buf, int buf_size, const char *s);
|
||||
|
||||
struct in_addr;
|
||||
int resolve_host(struct in_addr *sin_addr, const char *hostname);
|
||||
|
||||
void url_split(char *proto, int proto_size,
|
||||
char *hostname, int hostname_size,
|
||||
int *port_ptr,
|
||||
char *path, int path_size,
|
||||
const char *url);
|
||||
|
||||
int match_ext(const char *filename, const char *extensions);
|
||||
|
||||
#endif /* HAVE_AV_CONFIG_H */
|
||||
|
||||
#endif /* AVFORMAT_H */
|
||||
28
libavformat/avi.h
Normal file
28
libavformat/avi.h
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
#define AVIF_HASINDEX 0x00000010 // Index at end of file?
|
||||
#define AVIF_MUSTUSEINDEX 0x00000020
|
||||
#define AVIF_ISINTERLEAVED 0x00000100
|
||||
#define AVIF_TRUSTCKTYPE 0x00000800 // Use CKType to find key frames?
|
||||
#define AVIF_WASCAPTUREFILE 0x00010000
|
||||
#define AVIF_COPYRIGHTED 0x00020000
|
||||
|
||||
offset_t start_tag(ByteIOContext *pb, const char *tag);
|
||||
void end_tag(ByteIOContext *pb, offset_t start);
|
||||
|
||||
typedef struct CodecTag {
|
||||
int id;
|
||||
unsigned int tag;
|
||||
unsigned int invalid_asf : 1;
|
||||
} CodecTag;
|
||||
|
||||
void put_bmp_header(ByteIOContext *pb, AVCodecContext *enc, const CodecTag *tags, int for_asf);
|
||||
int put_wav_header(ByteIOContext *pb, AVCodecContext *enc);
|
||||
int wav_codec_get_id(unsigned int tag, int bps);
|
||||
void get_wav_header(ByteIOContext *pb, AVCodecContext *codec,
|
||||
int has_extra_data);
|
||||
|
||||
extern const CodecTag codec_bmp_tags[];
|
||||
extern const CodecTag codec_wav_tags[];
|
||||
|
||||
unsigned int codec_get_tag(const CodecTag *tags, int id);
|
||||
int codec_get_id(const CodecTag *tags, unsigned int tag);
|
||||
256
libavformat/avidec.c
Normal file
256
libavformat/avidec.c
Normal file
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* AVI decoder.
|
||||
* Copyright (c) 2001 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
#include "avi.h"
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
typedef struct AVIIndex {
|
||||
unsigned char tag[4];
|
||||
unsigned int flags, pos, len;
|
||||
struct AVIIndex *next;
|
||||
} AVIIndex;
|
||||
|
||||
typedef struct {
|
||||
INT64 movi_end;
|
||||
offset_t movi_list;
|
||||
AVIIndex *first, *last;
|
||||
} AVIContext;
|
||||
|
||||
#ifdef DEBUG
|
||||
static void print_tag(const char *str, unsigned int tag, int size)
|
||||
{
|
||||
printf("%s: tag=%c%c%c%c size=0x%x\n",
|
||||
str, tag & 0xff,
|
||||
(tag >> 8) & 0xff,
|
||||
(tag >> 16) & 0xff,
|
||||
(tag >> 24) & 0xff,
|
||||
size);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
||||
{
|
||||
AVIContext *avi = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
UINT32 tag, tag1;
|
||||
int codec_type, stream_index, size, frame_period, bit_rate;
|
||||
int i;
|
||||
AVStream *st;
|
||||
|
||||
/* check RIFF header */
|
||||
tag = get_le32(pb);
|
||||
|
||||
if (tag != MKTAG('R', 'I', 'F', 'F'))
|
||||
return -1;
|
||||
get_le32(pb); /* file size */
|
||||
tag = get_le32(pb);
|
||||
if (tag != MKTAG('A', 'V', 'I', ' '))
|
||||
return -1;
|
||||
|
||||
/* first list tag */
|
||||
stream_index = -1;
|
||||
codec_type = -1;
|
||||
frame_period = 0;
|
||||
for(;;) {
|
||||
if (url_feof(pb))
|
||||
goto fail;
|
||||
tag = get_le32(pb);
|
||||
size = get_le32(pb);
|
||||
#ifdef DEBUG
|
||||
print_tag("tag", tag, size);
|
||||
#endif
|
||||
|
||||
switch(tag) {
|
||||
case MKTAG('L', 'I', 'S', 'T'):
|
||||
/* ignored, except when start of video packets */
|
||||
tag1 = get_le32(pb);
|
||||
#ifdef DEBUG
|
||||
print_tag("list", tag1, 0);
|
||||
#endif
|
||||
if (tag1 == MKTAG('m', 'o', 'v', 'i')) {
|
||||
avi->movi_end = url_ftell(pb) + size - 4;
|
||||
#ifdef DEBUG
|
||||
printf("movi end=%Lx\n", avi->movi_end);
|
||||
#endif
|
||||
goto end_of_header;
|
||||
}
|
||||
break;
|
||||
case MKTAG('a', 'v', 'i', 'h'):
|
||||
/* avi header */
|
||||
/* using frame_period is bad idea */
|
||||
frame_period = get_le32(pb);
|
||||
bit_rate = get_le32(pb) * 8;
|
||||
url_fskip(pb, 4 * 4);
|
||||
s->nb_streams = get_le32(pb);
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
AVStream *st = av_mallocz(sizeof(AVStream));
|
||||
if (!st)
|
||||
goto fail;
|
||||
s->streams[i] = st;
|
||||
}
|
||||
url_fskip(pb, size - 7 * 4);
|
||||
break;
|
||||
case MKTAG('s', 't', 'r', 'h'):
|
||||
/* stream header */
|
||||
stream_index++;
|
||||
tag1 = get_le32(pb);
|
||||
switch(tag1) {
|
||||
case MKTAG('v', 'i', 'd', 's'):
|
||||
codec_type = CODEC_TYPE_VIDEO;
|
||||
get_le32(pb); /* codec tag */
|
||||
get_le32(pb); /* flags */
|
||||
get_le16(pb); /* priority */
|
||||
get_le16(pb); /* language */
|
||||
get_le32(pb); /* XXX: initial frame ? */
|
||||
get_le32(pb); /* scale */
|
||||
get_le32(pb); /* rate */
|
||||
size -= 6 * 4;
|
||||
break;
|
||||
case MKTAG('a', 'u', 'd', 's'):
|
||||
codec_type = CODEC_TYPE_AUDIO;
|
||||
/* nothing really useful */
|
||||
}
|
||||
url_fskip(pb, size - 4);
|
||||
break;
|
||||
case MKTAG('s', 't', 'r', 'f'):
|
||||
/* stream header */
|
||||
if (stream_index >= s->nb_streams) {
|
||||
url_fskip(pb, size);
|
||||
} else {
|
||||
st = s->streams[stream_index];
|
||||
switch(codec_type) {
|
||||
case CODEC_TYPE_VIDEO:
|
||||
get_le32(pb); /* size */
|
||||
st->codec.width = get_le32(pb);
|
||||
st->codec.height = get_le32(pb);
|
||||
if (frame_period)
|
||||
st->codec.frame_rate = (INT64_C(1000000) * FRAME_RATE_BASE) / frame_period;
|
||||
else
|
||||
st->codec.frame_rate = 25 * FRAME_RATE_BASE;
|
||||
get_le16(pb); /* panes */
|
||||
get_le16(pb); /* depth */
|
||||
tag1 = get_le32(pb);
|
||||
#ifdef DEBUG
|
||||
print_tag("video", tag1, 0);
|
||||
#endif
|
||||
st->codec.codec_type = CODEC_TYPE_VIDEO;
|
||||
st->codec.codec_tag = tag1;
|
||||
st->codec.codec_id = codec_get_id(codec_bmp_tags, tag1);
|
||||
url_fskip(pb, size - 5 * 4);
|
||||
break;
|
||||
case CODEC_TYPE_AUDIO:
|
||||
get_wav_header(pb, &st->codec, (size >= 18));
|
||||
break;
|
||||
default:
|
||||
url_fskip(pb, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* skip tag */
|
||||
size += (size & 1);
|
||||
url_fskip(pb, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
end_of_header:
|
||||
/* check stream number */
|
||||
if (stream_index != s->nb_streams - 1) {
|
||||
fail:
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
av_freep(&s->streams[i]);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avi_read_packet(AVFormatContext *s, AVPacket *pkt)
|
||||
{
|
||||
AVIContext *avi = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
int n, d1, d2, size;
|
||||
|
||||
for(;;) {
|
||||
if (url_feof(pb) || url_ftell(pb) >= avi->movi_end)
|
||||
return -1;
|
||||
d1 = get_byte(pb) - '0';
|
||||
d2 = get_byte(pb) - '0';
|
||||
if (d1 < 0 || d1 > 9 || d2 < 0 || d2 > 9)
|
||||
continue;
|
||||
|
||||
n = d1 * 10 + d2;
|
||||
if (n < 0 || n >= s->nb_streams)
|
||||
continue;
|
||||
|
||||
d1 = get_byte(pb);
|
||||
d2 = get_byte(pb);
|
||||
if ((d1 == 'd' && d2 == 'c')
|
||||
|| (d1 == 'w' && d2 == 'b'))
|
||||
break;
|
||||
}
|
||||
size = get_le32(pb);
|
||||
av_new_packet(pkt, size);
|
||||
pkt->stream_index = n;
|
||||
|
||||
get_buffer(pb, pkt->data, pkt->size);
|
||||
|
||||
if (size & 1)
|
||||
get_byte(pb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avi_read_close(AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avi_probe(AVProbeData *p)
|
||||
{
|
||||
/* check file header */
|
||||
if (p->buf_size <= 32)
|
||||
return 0;
|
||||
if (p->buf[0] == 'R' && p->buf[1] == 'I' &&
|
||||
p->buf[2] == 'F' && p->buf[3] == 'F' &&
|
||||
p->buf[8] == 'A' && p->buf[9] == 'V' &&
|
||||
p->buf[10] == 'I' && p->buf[11] == ' ')
|
||||
return AVPROBE_SCORE_MAX;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVInputFormat avi_iformat = {
|
||||
"avi",
|
||||
"avi format",
|
||||
sizeof(AVIContext),
|
||||
avi_probe,
|
||||
avi_read_header,
|
||||
avi_read_packet,
|
||||
avi_read_close,
|
||||
};
|
||||
|
||||
int avidec_init(void)
|
||||
{
|
||||
av_register_input_format(&avi_iformat);
|
||||
return 0;
|
||||
}
|
||||
432
libavformat/avienc.c
Normal file
432
libavformat/avienc.c
Normal file
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* AVI encoder.
|
||||
* Copyright (c) 2000 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
#include "avi.h"
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* - fill all fields if non streamed (nb_frames for example)
|
||||
*/
|
||||
|
||||
typedef struct AVIIndex {
|
||||
unsigned char tag[4];
|
||||
unsigned int flags, pos, len;
|
||||
struct AVIIndex *next;
|
||||
} AVIIndex;
|
||||
|
||||
typedef struct {
|
||||
offset_t movi_list, frames_hdr_all, frames_hdr_strm[MAX_STREAMS];
|
||||
int audio_strm_length[MAX_STREAMS];
|
||||
AVIIndex *first, *last;
|
||||
} AVIContext;
|
||||
|
||||
offset_t start_tag(ByteIOContext *pb, const char *tag)
|
||||
{
|
||||
put_tag(pb, tag);
|
||||
put_le32(pb, 0);
|
||||
return url_ftell(pb);
|
||||
}
|
||||
|
||||
void end_tag(ByteIOContext *pb, offset_t start)
|
||||
{
|
||||
offset_t pos;
|
||||
|
||||
pos = url_ftell(pb);
|
||||
url_fseek(pb, start - 4, SEEK_SET);
|
||||
put_le32(pb, (UINT32)(pos - start));
|
||||
url_fseek(pb, pos, SEEK_SET);
|
||||
}
|
||||
|
||||
/* Note: when encoding, the first matching tag is used, so order is
|
||||
important if multiple tags possible for a given codec. */
|
||||
const CodecTag codec_bmp_tags[] = {
|
||||
{ CODEC_ID_H263, MKTAG('H', '2', '6', '3') },
|
||||
{ CODEC_ID_H263P, MKTAG('H', '2', '6', '3') },
|
||||
{ CODEC_ID_H263I, MKTAG('I', '2', '6', '3') }, /* intel h263 */
|
||||
{ CODEC_ID_MJPEG, MKTAG('M', 'J', 'P', 'G') },
|
||||
{ CODEC_ID_MPEG4, MKTAG('D', 'I', 'V', 'X'), .invalid_asf = 1 },
|
||||
{ CODEC_ID_MPEG4, MKTAG('d', 'i', 'v', 'x'), .invalid_asf = 1 },
|
||||
{ CODEC_ID_MPEG4, MKTAG('D', 'X', '5', '0'), .invalid_asf = 1 },
|
||||
{ CODEC_ID_MPEG4, MKTAG('X', 'V', 'I', 'D'), .invalid_asf = 1 },
|
||||
{ CODEC_ID_MPEG4, MKTAG('x', 'v', 'i', 'd'), .invalid_asf = 1 },
|
||||
{ CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 's'), .invalid_asf = 1 },
|
||||
{ CODEC_ID_MPEG4, MKTAG('M', 'P', '4', 'S') },
|
||||
{ CODEC_ID_MPEG4, MKTAG('M', '4', 'S', '2') },
|
||||
{ CODEC_ID_MPEG4, MKTAG('m', '4', 's', '2') },
|
||||
{ CODEC_ID_MPEG4, MKTAG(0x04, 0, 0, 0) }, /* some broken avi use this */
|
||||
{ CODEC_ID_MSMPEG4V3, MKTAG('D', 'I', 'V', '3'), .invalid_asf = 1 }, /* default signature when using MSMPEG4 */
|
||||
{ CODEC_ID_MSMPEG4V3, MKTAG('d', 'i', 'v', '3'), .invalid_asf = 1 },
|
||||
{ CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', '4', '3') },
|
||||
{ CODEC_ID_MSMPEG4V2, MKTAG('M', 'P', '4', '2') },
|
||||
{ CODEC_ID_MSMPEG4V1, MKTAG('M', 'P', 'G', '4') },
|
||||
{ CODEC_ID_WMV1, MKTAG('W', 'M', 'V', '1') },
|
||||
{ CODEC_ID_DVVIDEO, MKTAG('d', 'v', 's', 'l') },
|
||||
{ CODEC_ID_DVVIDEO, MKTAG('d', 'v', 's', 'd') },
|
||||
{ CODEC_ID_DVVIDEO, MKTAG('D', 'V', 'S', 'D') },
|
||||
{ CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', 'd') },
|
||||
{ CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'g', '1') },
|
||||
{ CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'g', '2') },
|
||||
{ CODEC_ID_MPEG1VIDEO, MKTAG('P', 'I', 'M', '1') },
|
||||
{ CODEC_ID_MJPEG, MKTAG('M', 'J', 'P', 'G') },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
unsigned int codec_get_tag(const CodecTag *tags, int id)
|
||||
{
|
||||
while (tags->id != 0) {
|
||||
if (tags->id == id)
|
||||
return tags->tag;
|
||||
tags++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int codec_get_asf_tag(const CodecTag *tags, int id)
|
||||
{
|
||||
while (tags->id != 0) {
|
||||
if (!tags->invalid_asf && tags->id == id)
|
||||
return tags->tag;
|
||||
tags++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int codec_get_id(const CodecTag *tags, unsigned int tag)
|
||||
{
|
||||
while (tags->id != 0) {
|
||||
if (tags->tag == tag)
|
||||
return tags->id;
|
||||
tags++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int codec_get_bmp_tag(int id)
|
||||
{
|
||||
return codec_get_tag(codec_bmp_tags, id);
|
||||
}
|
||||
|
||||
/* BITMAPINFOHEADER header */
|
||||
void put_bmp_header(ByteIOContext *pb, AVCodecContext *enc, const CodecTag *tags, int for_asf)
|
||||
{
|
||||
put_le32(pb, 40); /* size */
|
||||
put_le32(pb, enc->width);
|
||||
put_le32(pb, enc->height);
|
||||
put_le16(pb, 1); /* planes */
|
||||
put_le16(pb, 24); /* depth */
|
||||
/* compression type */
|
||||
put_le32(pb, for_asf ? codec_get_asf_tag(tags, enc->codec_id) : codec_get_tag(tags, enc->codec_id));
|
||||
put_le32(pb, enc->width * enc->height * 3);
|
||||
put_le32(pb, 0);
|
||||
put_le32(pb, 0);
|
||||
put_le32(pb, 0);
|
||||
put_le32(pb, 0);
|
||||
}
|
||||
|
||||
static void parse_specific_params(AVCodecContext *stream, int *au_byterate, int *au_ssize, int *au_scale)
|
||||
{
|
||||
switch(stream->codec_id) {
|
||||
case CODEC_ID_PCM_S16LE:
|
||||
*au_scale = *au_ssize = 2*stream->channels;
|
||||
*au_byterate = *au_ssize * stream->sample_rate;
|
||||
break;
|
||||
case CODEC_ID_PCM_U8:
|
||||
case CODEC_ID_PCM_ALAW:
|
||||
case CODEC_ID_PCM_MULAW:
|
||||
*au_scale = *au_ssize = stream->channels;
|
||||
*au_byterate = *au_ssize * stream->sample_rate;
|
||||
break;
|
||||
case CODEC_ID_MP2:
|
||||
*au_ssize = 1;
|
||||
*au_scale = 1;
|
||||
*au_byterate = stream->bit_rate / 8;
|
||||
case CODEC_ID_MP3LAME:
|
||||
*au_ssize = 1;
|
||||
*au_scale = 1;
|
||||
*au_byterate = stream->bit_rate / 8;
|
||||
default:
|
||||
*au_ssize = 1;
|
||||
*au_scale = 1;
|
||||
*au_byterate = stream->bit_rate / 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int avi_write_header(AVFormatContext *s)
|
||||
{
|
||||
AVIContext *avi = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
int bitrate, n, i, nb_frames, au_byterate, au_ssize, au_scale;
|
||||
AVCodecContext *stream, *video_enc;
|
||||
offset_t list1, list2, strh, strf;
|
||||
|
||||
put_tag(pb, "RIFF");
|
||||
put_le32(pb, 0); /* file length */
|
||||
put_tag(pb, "AVI ");
|
||||
|
||||
/* header list */
|
||||
list1 = start_tag(pb, "LIST");
|
||||
put_tag(pb, "hdrl");
|
||||
|
||||
/* avi header */
|
||||
put_tag(pb, "avih");
|
||||
put_le32(pb, 14 * 4);
|
||||
bitrate = 0;
|
||||
|
||||
video_enc = NULL;
|
||||
for(n=0;n<s->nb_streams;n++) {
|
||||
stream = &s->streams[n]->codec;
|
||||
bitrate += stream->bit_rate;
|
||||
if (stream->codec_type == CODEC_TYPE_VIDEO)
|
||||
video_enc = stream;
|
||||
}
|
||||
|
||||
if (!video_enc) {
|
||||
av_free(avi);
|
||||
return -1;
|
||||
}
|
||||
nb_frames = 0;
|
||||
|
||||
put_le32(pb, (UINT32)(INT64_C(1000000) * FRAME_RATE_BASE / video_enc->frame_rate));
|
||||
put_le32(pb, bitrate / 8); /* XXX: not quite exact */
|
||||
put_le32(pb, 0); /* padding */
|
||||
put_le32(pb, AVIF_TRUSTCKTYPE | AVIF_HASINDEX | AVIF_ISINTERLEAVED); /* flags */
|
||||
avi->frames_hdr_all = url_ftell(pb); /* remember this offset to fill later */
|
||||
put_le32(pb, nb_frames); /* nb frames, filled later */
|
||||
put_le32(pb, 0); /* initial frame */
|
||||
put_le32(pb, s->nb_streams); /* nb streams */
|
||||
put_le32(pb, 1024 * 1024); /* suggested buffer size */
|
||||
put_le32(pb, video_enc->width);
|
||||
put_le32(pb, video_enc->height);
|
||||
put_le32(pb, 0); /* reserved */
|
||||
put_le32(pb, 0); /* reserved */
|
||||
put_le32(pb, 0); /* reserved */
|
||||
put_le32(pb, 0); /* reserved */
|
||||
|
||||
/* stream list */
|
||||
for(i=0;i<n;i++) {
|
||||
list2 = start_tag(pb, "LIST");
|
||||
put_tag(pb, "strl");
|
||||
|
||||
stream = &s->streams[i]->codec;
|
||||
|
||||
/* stream generic header */
|
||||
strh = start_tag(pb, "strh");
|
||||
switch(stream->codec_type) {
|
||||
case CODEC_TYPE_VIDEO:
|
||||
put_tag(pb, "vids");
|
||||
put_le32(pb, codec_get_bmp_tag(stream->codec_id));
|
||||
put_le32(pb, 0); /* flags */
|
||||
put_le16(pb, 0); /* priority */
|
||||
put_le16(pb, 0); /* language */
|
||||
put_le32(pb, 0); /* initial frame */
|
||||
put_le32(pb, 1000); /* scale */
|
||||
put_le32(pb, (1000 * stream->frame_rate) / FRAME_RATE_BASE); /* rate */
|
||||
put_le32(pb, 0); /* start */
|
||||
avi->frames_hdr_strm[i] = url_ftell(pb); /* remember this offset to fill later */
|
||||
put_le32(pb, nb_frames); /* length, XXX: fill later */
|
||||
put_le32(pb, 1024 * 1024); /* suggested buffer size */
|
||||
put_le32(pb, -1); /* quality */
|
||||
put_le32(pb, stream->width * stream->height * 3); /* sample size */
|
||||
put_le16(pb, 0);
|
||||
put_le16(pb, 0);
|
||||
put_le16(pb, stream->width);
|
||||
put_le16(pb, stream->height);
|
||||
break;
|
||||
case CODEC_TYPE_AUDIO:
|
||||
put_tag(pb, "auds");
|
||||
put_le32(pb, 1); /* tag */
|
||||
put_le32(pb, 0); /* flags */
|
||||
put_le16(pb, 0); /* priority */
|
||||
put_le16(pb, 0); /* language */
|
||||
put_le32(pb, 0); /* initial frame */
|
||||
parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale);
|
||||
put_le32(pb, au_scale); /* scale */
|
||||
put_le32(pb, au_byterate); /* rate */
|
||||
put_le32(pb, 0); /* start */
|
||||
avi->frames_hdr_strm[i] = url_ftell(pb); /* remember this offset to fill later */
|
||||
put_le32(pb, 0); /* length, XXX: filled later */
|
||||
put_le32(pb, 12 * 1024); /* suggested buffer size */
|
||||
put_le32(pb, -1); /* quality */
|
||||
put_le32(pb, au_ssize); /* sample size */
|
||||
put_le32(pb, 0);
|
||||
put_le32(pb, 0);
|
||||
break;
|
||||
default:
|
||||
av_abort();
|
||||
}
|
||||
end_tag(pb, strh);
|
||||
|
||||
strf = start_tag(pb, "strf");
|
||||
switch(stream->codec_type) {
|
||||
case CODEC_TYPE_VIDEO:
|
||||
put_bmp_header(pb, stream, codec_bmp_tags, 0);
|
||||
break;
|
||||
case CODEC_TYPE_AUDIO:
|
||||
if (put_wav_header(pb, stream) < 0) {
|
||||
av_free(avi);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
av_abort();
|
||||
}
|
||||
end_tag(pb, strf);
|
||||
end_tag(pb, list2);
|
||||
}
|
||||
|
||||
end_tag(pb, list1);
|
||||
|
||||
avi->movi_list = start_tag(pb, "LIST");
|
||||
avi->first = NULL;
|
||||
avi->last = NULL;
|
||||
put_tag(pb, "movi");
|
||||
|
||||
put_flush_packet(pb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avi_write_packet(AVFormatContext *s, int stream_index,
|
||||
UINT8 *buf, int size, int force_pts)
|
||||
{
|
||||
AVIContext *avi = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
AVIIndex *idx;
|
||||
unsigned char tag[5];
|
||||
unsigned int flags;
|
||||
AVCodecContext *enc;
|
||||
|
||||
enc = &s->streams[stream_index]->codec;
|
||||
|
||||
tag[0] = '0';
|
||||
tag[1] = '0' + stream_index;
|
||||
if (enc->codec_type == CODEC_TYPE_VIDEO) {
|
||||
tag[2] = 'd';
|
||||
tag[3] = 'c';
|
||||
flags = enc->key_frame ? 0x10 : 0x00;
|
||||
} else {
|
||||
tag[2] = 'w';
|
||||
tag[3] = 'b';
|
||||
flags = 0x10;
|
||||
}
|
||||
if (enc->codec_type == CODEC_TYPE_AUDIO)
|
||||
avi->audio_strm_length[stream_index] += size;
|
||||
|
||||
if (!url_is_streamed(&s->pb)) {
|
||||
idx = av_malloc(sizeof(AVIIndex));
|
||||
memcpy(idx->tag, tag, 4);
|
||||
idx->flags = flags;
|
||||
idx->pos = url_ftell(pb) - avi->movi_list;
|
||||
idx->len = size;
|
||||
idx->next = NULL;
|
||||
if (!avi->last)
|
||||
avi->first = idx;
|
||||
else
|
||||
avi->last->next = idx;
|
||||
avi->last = idx;
|
||||
}
|
||||
|
||||
put_buffer(pb, tag, 4);
|
||||
put_le32(pb, size);
|
||||
put_buffer(pb, buf, size);
|
||||
if (size & 1)
|
||||
put_byte(pb, 0);
|
||||
|
||||
put_flush_packet(pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avi_write_trailer(AVFormatContext *s)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
AVIContext *avi = s->priv_data;
|
||||
offset_t file_size, idx_chunk;
|
||||
int n, nb_frames, au_byterate, au_ssize, au_scale;
|
||||
AVCodecContext *stream;
|
||||
AVIIndex *idx;
|
||||
|
||||
if (!url_is_streamed(&s->pb)) {
|
||||
end_tag(pb, avi->movi_list);
|
||||
|
||||
idx_chunk = start_tag(pb, "idx1");
|
||||
idx = avi->first;
|
||||
while (idx != NULL) {
|
||||
put_buffer(pb, idx->tag, 4);
|
||||
put_le32(pb, idx->flags);
|
||||
put_le32(pb, idx->pos);
|
||||
put_le32(pb, idx->len);
|
||||
idx = idx->next;
|
||||
}
|
||||
end_tag(pb, idx_chunk);
|
||||
|
||||
/* update file size */
|
||||
file_size = url_ftell(pb);
|
||||
url_fseek(pb, 4, SEEK_SET);
|
||||
put_le32(pb, (UINT32)(file_size - 8));
|
||||
|
||||
/* Fill in frame/sample counters */
|
||||
nb_frames = 0;
|
||||
for(n=0;n<s->nb_streams;n++) {
|
||||
if (avi->frames_hdr_strm[n] != 0) {
|
||||
stream = &s->streams[n]->codec;
|
||||
url_fseek(pb, avi->frames_hdr_strm[n], SEEK_SET);
|
||||
if (stream->codec_type == CODEC_TYPE_VIDEO) {
|
||||
put_le32(pb, stream->frame_number);
|
||||
if (nb_frames < stream->frame_number)
|
||||
nb_frames = stream->frame_number;
|
||||
} else {
|
||||
if (stream->codec_id == CODEC_ID_MP2 || stream->codec_id == CODEC_ID_MP3LAME) {
|
||||
put_le32(pb, stream->frame_number);
|
||||
nb_frames += stream->frame_number;
|
||||
} else {
|
||||
parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale);
|
||||
put_le32(pb, avi->audio_strm_length[n] / au_ssize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (avi->frames_hdr_all != 0) {
|
||||
url_fseek(pb, avi->frames_hdr_all, SEEK_SET);
|
||||
put_le32(pb, nb_frames);
|
||||
}
|
||||
url_fseek(pb, file_size, SEEK_SET);
|
||||
}
|
||||
put_flush_packet(pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVOutputFormat avi_oformat = {
|
||||
"avi",
|
||||
"avi format",
|
||||
"video/x-msvideo",
|
||||
"avi",
|
||||
sizeof(AVIContext),
|
||||
CODEC_ID_MP2,
|
||||
CODEC_ID_MSMPEG4V3,
|
||||
avi_write_header,
|
||||
avi_write_packet,
|
||||
avi_write_trailer,
|
||||
};
|
||||
|
||||
int avienc_init(void)
|
||||
{
|
||||
av_register_output_format(&avi_oformat);
|
||||
return 0;
|
||||
}
|
||||
156
libavformat/avio.c
Normal file
156
libavformat/avio.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Unbuffered io for ffmpeg system
|
||||
* Copyright (c) 2001 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
|
||||
URLProtocol *first_protocol = NULL;
|
||||
|
||||
int register_protocol(URLProtocol *protocol)
|
||||
{
|
||||
URLProtocol **p;
|
||||
p = &first_protocol;
|
||||
while (*p != NULL) p = &(*p)->next;
|
||||
*p = protocol;
|
||||
protocol->next = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int url_open(URLContext **puc, const char *filename, int flags)
|
||||
{
|
||||
URLContext *uc;
|
||||
URLProtocol *up;
|
||||
const char *p;
|
||||
char proto_str[128], *q;
|
||||
int err;
|
||||
|
||||
p = filename;
|
||||
q = proto_str;
|
||||
while (*p != '\0' && *p != ':') {
|
||||
if ((q - proto_str) < sizeof(proto_str) - 1)
|
||||
*q++ = *p;
|
||||
p++;
|
||||
}
|
||||
/* if the protocol has length 1, we consider it is a dos drive */
|
||||
if (*p == '\0' || (q - proto_str) <= 1) {
|
||||
strcpy(proto_str, "file");
|
||||
} else {
|
||||
*q = '\0';
|
||||
}
|
||||
|
||||
up = first_protocol;
|
||||
while (up != NULL) {
|
||||
if (!strcmp(proto_str, up->name))
|
||||
goto found;
|
||||
up = up->next;
|
||||
}
|
||||
err = -ENOENT;
|
||||
goto fail;
|
||||
found:
|
||||
uc = av_malloc(sizeof(URLContext));
|
||||
if (!uc) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
uc->prot = up;
|
||||
uc->flags = flags;
|
||||
uc->is_streamed = 0; /* default = not streamed */
|
||||
uc->max_packet_size = 0; /* default: stream file */
|
||||
err = up->url_open(uc, filename, flags);
|
||||
if (err < 0) {
|
||||
av_free(uc);
|
||||
*puc = NULL;
|
||||
return err;
|
||||
}
|
||||
*puc = uc;
|
||||
return 0;
|
||||
fail:
|
||||
*puc = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
int url_read(URLContext *h, unsigned char *buf, int size)
|
||||
{
|
||||
int ret;
|
||||
if (h->flags & URL_WRONLY)
|
||||
return -EIO;
|
||||
ret = h->prot->url_read(h, buf, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int url_write(URLContext *h, unsigned char *buf, int size)
|
||||
{
|
||||
int ret;
|
||||
if (!(h->flags & (URL_WRONLY | URL_RDWR)))
|
||||
return -EIO;
|
||||
/* avoid sending too big packets */
|
||||
if (h->max_packet_size && size > h->max_packet_size)
|
||||
return -EIO;
|
||||
ret = h->prot->url_write(h, buf, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
offset_t url_seek(URLContext *h, offset_t pos, int whence)
|
||||
{
|
||||
offset_t ret;
|
||||
|
||||
if (!h->prot->url_seek)
|
||||
return -EPIPE;
|
||||
ret = h->prot->url_seek(h, pos, whence);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int url_close(URLContext *h)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = h->prot->url_close(h);
|
||||
av_free(h);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int url_exist(const char *filename)
|
||||
{
|
||||
URLContext *h;
|
||||
if (url_open(&h, filename, URL_RDONLY) < 0)
|
||||
return 0;
|
||||
url_close(h);
|
||||
return 1;
|
||||
}
|
||||
|
||||
offset_t url_filesize(URLContext *h)
|
||||
{
|
||||
offset_t pos, size;
|
||||
|
||||
pos = url_seek(h, 0, SEEK_CUR);
|
||||
size = url_seek(h, 0, SEEK_END);
|
||||
url_seek(h, pos, SEEK_SET);
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the maximum packet size associated to packetized file
|
||||
* handle. If the file is not packetized (stream like http or file on
|
||||
* disk), then 0 is returned.
|
||||
*
|
||||
* @param h file handle
|
||||
* @return maximum packet size in bytes
|
||||
*/
|
||||
int url_get_max_packet_size(URLContext *h)
|
||||
{
|
||||
return h->max_packet_size;
|
||||
}
|
||||
153
libavformat/avio.h
Normal file
153
libavformat/avio.h
Normal file
@@ -0,0 +1,153 @@
|
||||
#ifndef AVIO_H
|
||||
#define AVIO_H
|
||||
|
||||
/* output byte stream handling */
|
||||
|
||||
typedef INT64 offset_t;
|
||||
|
||||
/* unbuffered I/O */
|
||||
|
||||
struct URLContext {
|
||||
struct URLProtocol *prot;
|
||||
int flags;
|
||||
int is_streamed; /* true if streamed (no seek possible), default = false */
|
||||
int max_packet_size; /* if non zero, the stream is packetized with this max packet size */
|
||||
void *priv_data;
|
||||
};
|
||||
|
||||
typedef struct URLContext URLContext;
|
||||
|
||||
typedef struct URLPollEntry {
|
||||
URLContext *handle;
|
||||
int events;
|
||||
int revents;
|
||||
} URLPollEntry;
|
||||
|
||||
#define URL_RDONLY 0
|
||||
#define URL_WRONLY 1
|
||||
#define URL_RDWR 2
|
||||
|
||||
int url_open(URLContext **h, const char *filename, int flags);
|
||||
int url_read(URLContext *h, unsigned char *buf, int size);
|
||||
int url_write(URLContext *h, unsigned char *buf, int size);
|
||||
offset_t url_seek(URLContext *h, offset_t pos, int whence);
|
||||
int url_close(URLContext *h);
|
||||
int url_exist(const char *filename);
|
||||
offset_t url_filesize(URLContext *h);
|
||||
int url_get_max_packet_size(URLContext *h);
|
||||
/* not implemented */
|
||||
int url_poll(URLPollEntry *poll_table, int n, int timeout);
|
||||
|
||||
typedef struct URLProtocol {
|
||||
const char *name;
|
||||
int (*url_open)(URLContext *h, const char *filename, int flags);
|
||||
int (*url_read)(URLContext *h, unsigned char *buf, int size);
|
||||
int (*url_write)(URLContext *h, unsigned char *buf, int size);
|
||||
offset_t (*url_seek)(URLContext *h, offset_t pos, int whence);
|
||||
int (*url_close)(URLContext *h);
|
||||
struct URLProtocol *next;
|
||||
} URLProtocol;
|
||||
|
||||
extern URLProtocol *first_protocol;
|
||||
|
||||
int register_protocol(URLProtocol *protocol);
|
||||
|
||||
typedef struct {
|
||||
unsigned char *buffer;
|
||||
int buffer_size;
|
||||
unsigned char *buf_ptr, *buf_end;
|
||||
void *opaque;
|
||||
int (*read_packet)(void *opaque, UINT8 *buf, int buf_size);
|
||||
void (*write_packet)(void *opaque, UINT8 *buf, int buf_size);
|
||||
int (*seek)(void *opaque, offset_t offset, int whence);
|
||||
offset_t pos; /* position in the file of the current buffer */
|
||||
int must_flush; /* true if the next seek should flush */
|
||||
int eof_reached; /* true if eof reached */
|
||||
int write_flag; /* true if open for writing */
|
||||
int is_streamed;
|
||||
int max_packet_size;
|
||||
} ByteIOContext;
|
||||
|
||||
int init_put_byte(ByteIOContext *s,
|
||||
unsigned char *buffer,
|
||||
int buffer_size,
|
||||
int write_flag,
|
||||
void *opaque,
|
||||
int (*read_packet)(void *opaque, UINT8 *buf, int buf_size),
|
||||
void (*write_packet)(void *opaque, UINT8 *buf, int buf_size),
|
||||
int (*seek)(void *opaque, offset_t offset, int whence));
|
||||
|
||||
void put_byte(ByteIOContext *s, int b);
|
||||
void put_buffer(ByteIOContext *s, const unsigned char *buf, int size);
|
||||
void put_le64(ByteIOContext *s, UINT64 val);
|
||||
void put_be64(ByteIOContext *s, UINT64 val);
|
||||
void put_le32(ByteIOContext *s, unsigned int val);
|
||||
void put_be32(ByteIOContext *s, unsigned int val);
|
||||
void put_le16(ByteIOContext *s, unsigned int val);
|
||||
void put_be16(ByteIOContext *s, unsigned int val);
|
||||
void put_tag(ByteIOContext *s, const char *tag);
|
||||
|
||||
void put_be64_double(ByteIOContext *s, double val);
|
||||
void put_strz(ByteIOContext *s, const char *buf);
|
||||
|
||||
offset_t url_fseek(ByteIOContext *s, offset_t offset, int whence);
|
||||
void url_fskip(ByteIOContext *s, offset_t offset);
|
||||
offset_t url_ftell(ByteIOContext *s);
|
||||
int url_feof(ByteIOContext *s);
|
||||
|
||||
#define URL_EOF (-1)
|
||||
int url_fgetc(ByteIOContext *s);
|
||||
int url_fprintf(ByteIOContext *s, const char *fmt, ...);
|
||||
char *url_fgets(ByteIOContext *s, char *buf, int buf_size);
|
||||
|
||||
void put_flush_packet(ByteIOContext *s);
|
||||
|
||||
int get_buffer(ByteIOContext *s, unsigned char *buf, int size);
|
||||
int get_byte(ByteIOContext *s);
|
||||
unsigned int get_le32(ByteIOContext *s);
|
||||
UINT64 get_le64(ByteIOContext *s);
|
||||
unsigned int get_le16(ByteIOContext *s);
|
||||
|
||||
double get_be64_double(ByteIOContext *s);
|
||||
char *get_strz(ByteIOContext *s, char *buf, int maxlen);
|
||||
unsigned int get_be16(ByteIOContext *s);
|
||||
unsigned int get_be32(ByteIOContext *s);
|
||||
UINT64 get_be64(ByteIOContext *s);
|
||||
|
||||
static inline int url_is_streamed(ByteIOContext *s)
|
||||
{
|
||||
return s->is_streamed;
|
||||
}
|
||||
|
||||
int url_fdopen(ByteIOContext *s, URLContext *h);
|
||||
int url_setbufsize(ByteIOContext *s, int buf_size);
|
||||
int url_fopen(ByteIOContext *s, const char *filename, int flags);
|
||||
int url_fclose(ByteIOContext *s);
|
||||
URLContext *url_fileno(ByteIOContext *s);
|
||||
int url_fget_max_packet_size(ByteIOContext *s);
|
||||
|
||||
int url_open_buf(ByteIOContext *s, UINT8 *buf, int buf_size, int flags);
|
||||
int url_close_buf(ByteIOContext *s);
|
||||
|
||||
int url_open_dyn_buf(ByteIOContext *s);
|
||||
int url_open_dyn_packet_buf(ByteIOContext *s, int max_packet_size);
|
||||
int url_close_dyn_buf(ByteIOContext *s, UINT8 **pbuffer);
|
||||
|
||||
/* file.c */
|
||||
extern URLProtocol file_protocol;
|
||||
extern URLProtocol pipe_protocol;
|
||||
|
||||
/* udp.c */
|
||||
extern URLProtocol udp_protocol;
|
||||
int udp_set_remote_url(URLContext *h, const char *uri);
|
||||
int udp_get_local_port(URLContext *h);
|
||||
int udp_get_file_handle(URLContext *h);
|
||||
|
||||
/* tcp.c */
|
||||
extern URLProtocol tcp_protocol;
|
||||
|
||||
/* http.c */
|
||||
extern URLProtocol http_protocol;
|
||||
|
||||
#endif
|
||||
|
||||
687
libavformat/aviobuf.c
Normal file
687
libavformat/aviobuf.c
Normal file
@@ -0,0 +1,687 @@
|
||||
/*
|
||||
* Buffered I/O for ffmpeg system
|
||||
* Copyright (c) 2000,2001 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#define IO_BUFFER_SIZE 32768
|
||||
|
||||
int init_put_byte(ByteIOContext *s,
|
||||
unsigned char *buffer,
|
||||
int buffer_size,
|
||||
int write_flag,
|
||||
void *opaque,
|
||||
int (*read_packet)(void *opaque, UINT8 *buf, int buf_size),
|
||||
void (*write_packet)(void *opaque, UINT8 *buf, int buf_size),
|
||||
int (*seek)(void *opaque, offset_t offset, int whence))
|
||||
{
|
||||
s->buffer = buffer;
|
||||
s->buffer_size = buffer_size;
|
||||
s->buf_ptr = buffer;
|
||||
s->write_flag = write_flag;
|
||||
if (!s->write_flag)
|
||||
s->buf_end = buffer;
|
||||
else
|
||||
s->buf_end = buffer + buffer_size;
|
||||
s->opaque = opaque;
|
||||
s->write_packet = write_packet;
|
||||
s->read_packet = read_packet;
|
||||
s->seek = seek;
|
||||
s->pos = 0;
|
||||
s->must_flush = 0;
|
||||
s->eof_reached = 0;
|
||||
s->is_streamed = 0;
|
||||
s->max_packet_size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void flush_buffer(ByteIOContext *s)
|
||||
{
|
||||
if (s->buf_ptr > s->buffer) {
|
||||
if (s->write_packet)
|
||||
s->write_packet(s->opaque, s->buffer, s->buf_ptr - s->buffer);
|
||||
s->pos += s->buf_ptr - s->buffer;
|
||||
}
|
||||
s->buf_ptr = s->buffer;
|
||||
}
|
||||
|
||||
void put_byte(ByteIOContext *s, int b)
|
||||
{
|
||||
*(s->buf_ptr)++ = b;
|
||||
if (s->buf_ptr >= s->buf_end)
|
||||
flush_buffer(s);
|
||||
}
|
||||
|
||||
void put_buffer(ByteIOContext *s, const unsigned char *buf, int size)
|
||||
{
|
||||
int len;
|
||||
|
||||
while (size > 0) {
|
||||
len = (s->buf_end - s->buf_ptr);
|
||||
if (len > size)
|
||||
len = size;
|
||||
memcpy(s->buf_ptr, buf, len);
|
||||
s->buf_ptr += len;
|
||||
|
||||
if (s->buf_ptr >= s->buf_end)
|
||||
flush_buffer(s);
|
||||
|
||||
buf += len;
|
||||
size -= len;
|
||||
}
|
||||
}
|
||||
|
||||
void put_flush_packet(ByteIOContext *s)
|
||||
{
|
||||
flush_buffer(s);
|
||||
s->must_flush = 0;
|
||||
}
|
||||
|
||||
offset_t url_fseek(ByteIOContext *s, offset_t offset, int whence)
|
||||
{
|
||||
offset_t offset1;
|
||||
|
||||
if (whence != SEEK_CUR && whence != SEEK_SET)
|
||||
return -EINVAL;
|
||||
|
||||
if (s->write_flag) {
|
||||
if (whence == SEEK_CUR) {
|
||||
offset1 = s->pos + (s->buf_ptr - s->buffer);
|
||||
if (offset == 0)
|
||||
return offset1;
|
||||
offset += offset1;
|
||||
}
|
||||
offset1 = offset - s->pos;
|
||||
if (!s->must_flush &&
|
||||
offset1 >= 0 && offset1 < (s->buf_end - s->buffer)) {
|
||||
/* can do the seek inside the buffer */
|
||||
s->buf_ptr = s->buffer + offset1;
|
||||
} else {
|
||||
if (!s->seek)
|
||||
return -EPIPE;
|
||||
flush_buffer(s);
|
||||
s->must_flush = 1;
|
||||
s->buf_ptr = s->buffer;
|
||||
s->seek(s->opaque, offset, SEEK_SET);
|
||||
s->pos = offset;
|
||||
}
|
||||
} else {
|
||||
if (whence == SEEK_CUR) {
|
||||
offset1 = s->pos - (s->buf_end - s->buffer) + (s->buf_ptr - s->buffer);
|
||||
if (offset == 0)
|
||||
return offset1;
|
||||
offset += offset1;
|
||||
}
|
||||
offset1 = offset - (s->pos - (s->buf_end - s->buffer));
|
||||
if (offset1 >= 0 && offset1 <= (s->buf_end - s->buffer)) {
|
||||
/* can do the seek inside the buffer */
|
||||
s->buf_ptr = s->buffer + offset1;
|
||||
} else {
|
||||
if (!s->seek)
|
||||
return -EPIPE;
|
||||
s->buf_ptr = s->buffer;
|
||||
s->buf_end = s->buffer;
|
||||
s->seek(s->opaque, offset, SEEK_SET);
|
||||
s->pos = offset;
|
||||
}
|
||||
s->eof_reached = 0;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
void url_fskip(ByteIOContext *s, offset_t offset)
|
||||
{
|
||||
url_fseek(s, offset, SEEK_CUR);
|
||||
}
|
||||
|
||||
offset_t url_ftell(ByteIOContext *s)
|
||||
{
|
||||
return url_fseek(s, 0, SEEK_CUR);
|
||||
}
|
||||
|
||||
int url_feof(ByteIOContext *s)
|
||||
{
|
||||
return s->eof_reached;
|
||||
}
|
||||
|
||||
void put_le32(ByteIOContext *s, unsigned int val)
|
||||
{
|
||||
put_byte(s, val);
|
||||
put_byte(s, val >> 8);
|
||||
put_byte(s, val >> 16);
|
||||
put_byte(s, val >> 24);
|
||||
}
|
||||
|
||||
void put_be32(ByteIOContext *s, unsigned int val)
|
||||
{
|
||||
put_byte(s, val >> 24);
|
||||
put_byte(s, val >> 16);
|
||||
put_byte(s, val >> 8);
|
||||
put_byte(s, val);
|
||||
}
|
||||
|
||||
/* IEEE format is assumed */
|
||||
void put_be64_double(ByteIOContext *s, double val)
|
||||
{
|
||||
union {
|
||||
double d;
|
||||
UINT64 ull;
|
||||
} u;
|
||||
u.d = val;
|
||||
put_be64(s, u.ull);
|
||||
}
|
||||
|
||||
void put_strz(ByteIOContext *s, const char *str)
|
||||
{
|
||||
if (str)
|
||||
put_buffer(s, (const unsigned char *) str, strlen(str) + 1);
|
||||
else
|
||||
put_byte(s, 0);
|
||||
}
|
||||
|
||||
void put_le64(ByteIOContext *s, UINT64 val)
|
||||
{
|
||||
put_le32(s, (UINT32)(val & 0xffffffff));
|
||||
put_le32(s, (UINT32)(val >> 32));
|
||||
}
|
||||
|
||||
void put_be64(ByteIOContext *s, UINT64 val)
|
||||
{
|
||||
put_be32(s, (UINT32)(val >> 32));
|
||||
put_be32(s, (UINT32)(val & 0xffffffff));
|
||||
}
|
||||
|
||||
void put_le16(ByteIOContext *s, unsigned int val)
|
||||
{
|
||||
put_byte(s, val);
|
||||
put_byte(s, val >> 8);
|
||||
}
|
||||
|
||||
void put_be16(ByteIOContext *s, unsigned int val)
|
||||
{
|
||||
put_byte(s, val >> 8);
|
||||
put_byte(s, val);
|
||||
}
|
||||
|
||||
void put_tag(ByteIOContext *s, const char *tag)
|
||||
{
|
||||
while (*tag) {
|
||||
put_byte(s, *tag++);
|
||||
}
|
||||
}
|
||||
|
||||
/* Input stream */
|
||||
|
||||
static void fill_buffer(ByteIOContext *s)
|
||||
{
|
||||
int len;
|
||||
|
||||
/* no need to do anything if EOF already reached */
|
||||
if (s->eof_reached)
|
||||
return;
|
||||
len = s->read_packet(s->opaque, s->buffer, s->buffer_size);
|
||||
if (len <= 0) {
|
||||
/* do not modify buffer if EOF reached so that a seek back can
|
||||
be done without rereading data */
|
||||
s->eof_reached = 1;
|
||||
} else {
|
||||
s->pos += len;
|
||||
s->buf_ptr = s->buffer;
|
||||
s->buf_end = s->buffer + len;
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: return 0 if EOF, so you cannot use it if EOF handling is
|
||||
necessary */
|
||||
/* XXX: put an inline version */
|
||||
int get_byte(ByteIOContext *s)
|
||||
{
|
||||
if (s->buf_ptr < s->buf_end) {
|
||||
return *s->buf_ptr++;
|
||||
} else {
|
||||
fill_buffer(s);
|
||||
if (s->buf_ptr < s->buf_end)
|
||||
return *s->buf_ptr++;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: return URL_EOF (-1) if EOF */
|
||||
int url_fgetc(ByteIOContext *s)
|
||||
{
|
||||
if (s->buf_ptr < s->buf_end) {
|
||||
return *s->buf_ptr++;
|
||||
} else {
|
||||
fill_buffer(s);
|
||||
if (s->buf_ptr < s->buf_end)
|
||||
return *s->buf_ptr++;
|
||||
else
|
||||
return URL_EOF;
|
||||
}
|
||||
}
|
||||
|
||||
int get_buffer(ByteIOContext *s, unsigned char *buf, int size)
|
||||
{
|
||||
int len, size1;
|
||||
|
||||
size1 = size;
|
||||
while (size > 0) {
|
||||
len = s->buf_end - s->buf_ptr;
|
||||
if (len > size)
|
||||
len = size;
|
||||
if (len == 0) {
|
||||
fill_buffer(s);
|
||||
len = s->buf_end - s->buf_ptr;
|
||||
if (len == 0)
|
||||
break;
|
||||
} else {
|
||||
memcpy(buf, s->buf_ptr, len);
|
||||
buf += len;
|
||||
s->buf_ptr += len;
|
||||
size -= len;
|
||||
}
|
||||
}
|
||||
return size1 - size;
|
||||
}
|
||||
|
||||
unsigned int get_le16(ByteIOContext *s)
|
||||
{
|
||||
unsigned int val;
|
||||
val = get_byte(s);
|
||||
val |= get_byte(s) << 8;
|
||||
return val;
|
||||
}
|
||||
|
||||
unsigned int get_le32(ByteIOContext *s)
|
||||
{
|
||||
unsigned int val;
|
||||
val = get_byte(s);
|
||||
val |= get_byte(s) << 8;
|
||||
val |= get_byte(s) << 16;
|
||||
val |= get_byte(s) << 24;
|
||||
return val;
|
||||
}
|
||||
|
||||
UINT64 get_le64(ByteIOContext *s)
|
||||
{
|
||||
UINT64 val;
|
||||
val = (UINT64)get_le32(s);
|
||||
val |= (UINT64)get_le32(s) << 32;
|
||||
return val;
|
||||
}
|
||||
|
||||
unsigned int get_be16(ByteIOContext *s)
|
||||
{
|
||||
unsigned int val;
|
||||
val = get_byte(s) << 8;
|
||||
val |= get_byte(s);
|
||||
return val;
|
||||
}
|
||||
|
||||
unsigned int get_be32(ByteIOContext *s)
|
||||
{
|
||||
unsigned int val;
|
||||
val = get_byte(s) << 24;
|
||||
val |= get_byte(s) << 16;
|
||||
val |= get_byte(s) << 8;
|
||||
val |= get_byte(s);
|
||||
return val;
|
||||
}
|
||||
|
||||
double get_be64_double(ByteIOContext *s)
|
||||
{
|
||||
union {
|
||||
double d;
|
||||
UINT64 ull;
|
||||
} u;
|
||||
|
||||
u.ull = get_be64(s);
|
||||
return u.d;
|
||||
}
|
||||
|
||||
char *get_strz(ByteIOContext *s, char *buf, int maxlen)
|
||||
{
|
||||
int i = 0;
|
||||
char c;
|
||||
|
||||
while ((c = get_byte(s))) {
|
||||
if (i < maxlen-1)
|
||||
buf[i++] = c;
|
||||
}
|
||||
|
||||
buf[i] = 0; /* Ensure null terminated, but may be truncated */
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
UINT64 get_be64(ByteIOContext *s)
|
||||
{
|
||||
UINT64 val;
|
||||
val = (UINT64)get_be32(s) << 32;
|
||||
val |= (UINT64)get_be32(s);
|
||||
return val;
|
||||
}
|
||||
|
||||
/* link with avio functions */
|
||||
|
||||
void url_write_packet(void *opaque, UINT8 *buf, int buf_size)
|
||||
{
|
||||
URLContext *h = opaque;
|
||||
url_write(h, buf, buf_size);
|
||||
}
|
||||
|
||||
int url_read_packet(void *opaque, UINT8 *buf, int buf_size)
|
||||
{
|
||||
URLContext *h = opaque;
|
||||
return url_read(h, buf, buf_size);
|
||||
}
|
||||
|
||||
int url_seek_packet(void *opaque, INT64 offset, int whence)
|
||||
{
|
||||
URLContext *h = opaque;
|
||||
url_seek(h, offset, whence);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int url_fdopen(ByteIOContext *s, URLContext *h)
|
||||
{
|
||||
UINT8 *buffer;
|
||||
int buffer_size, max_packet_size;
|
||||
|
||||
|
||||
max_packet_size = url_get_max_packet_size(h);
|
||||
if (max_packet_size) {
|
||||
buffer_size = max_packet_size; /* no need to bufferize more than one packet */
|
||||
} else {
|
||||
buffer_size = IO_BUFFER_SIZE;
|
||||
}
|
||||
buffer = av_malloc(buffer_size);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if (init_put_byte(s, buffer, buffer_size,
|
||||
(h->flags & URL_WRONLY) != 0, h,
|
||||
url_read_packet, url_write_packet, url_seek_packet) < 0) {
|
||||
av_free(buffer);
|
||||
return -EIO;
|
||||
}
|
||||
s->is_streamed = h->is_streamed;
|
||||
s->max_packet_size = max_packet_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: must be called before any I/O */
|
||||
int url_setbufsize(ByteIOContext *s, int buf_size)
|
||||
{
|
||||
UINT8 *buffer;
|
||||
buffer = av_malloc(buf_size);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
av_free(s->buffer);
|
||||
s->buffer = buffer;
|
||||
s->buffer_size = buf_size;
|
||||
s->buf_ptr = buffer;
|
||||
if (!s->write_flag)
|
||||
s->buf_end = buffer;
|
||||
else
|
||||
s->buf_end = buffer + buf_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* NOTE: when opened as read/write, the buffers are only used for
|
||||
reading */
|
||||
int url_fopen(ByteIOContext *s, const char *filename, int flags)
|
||||
{
|
||||
URLContext *h;
|
||||
int err;
|
||||
|
||||
err = url_open(&h, filename, flags);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = url_fdopen(s, h);
|
||||
if (err < 0) {
|
||||
url_close(h);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int url_fclose(ByteIOContext *s)
|
||||
{
|
||||
URLContext *h = s->opaque;
|
||||
|
||||
av_free(s->buffer);
|
||||
memset(s, 0, sizeof(ByteIOContext));
|
||||
return url_close(h);
|
||||
}
|
||||
|
||||
URLContext *url_fileno(ByteIOContext *s)
|
||||
{
|
||||
return s->opaque;
|
||||
}
|
||||
|
||||
/* XXX: currently size is limited */
|
||||
int url_fprintf(ByteIOContext *s, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[4096];
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
put_buffer(s, buf, strlen(buf));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* note: unlike fgets, the EOL character is not returned and a whole
|
||||
line is parsed. return NULL if first char read was EOF */
|
||||
char *url_fgets(ByteIOContext *s, char *buf, int buf_size)
|
||||
{
|
||||
int c;
|
||||
char *q;
|
||||
|
||||
c = url_fgetc(s);
|
||||
if (c == EOF)
|
||||
return NULL;
|
||||
q = buf;
|
||||
for(;;) {
|
||||
if (c == EOF || c == '\n')
|
||||
break;
|
||||
if ((q - buf) < buf_size - 1)
|
||||
*q++ = c;
|
||||
c = url_fgetc(s);
|
||||
}
|
||||
if (buf_size > 0)
|
||||
*q = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the maximum packet size associated to packetized buffered file
|
||||
* handle. If the file is not packetized (stream like http or file on
|
||||
* disk), then 0 is returned.
|
||||
*
|
||||
* @param h buffered file handle
|
||||
* @return maximum packet size in bytes
|
||||
*/
|
||||
int url_fget_max_packet_size(ByteIOContext *s)
|
||||
{
|
||||
return s->max_packet_size;
|
||||
}
|
||||
|
||||
/* buffer handling */
|
||||
int url_open_buf(ByteIOContext *s, UINT8 *buf, int buf_size, int flags)
|
||||
{
|
||||
return init_put_byte(s, buf, buf_size,
|
||||
(flags & URL_WRONLY) != 0, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/* return the written or read size */
|
||||
int url_close_buf(ByteIOContext *s)
|
||||
{
|
||||
put_flush_packet(s);
|
||||
return s->buf_ptr - s->buffer;
|
||||
}
|
||||
|
||||
/* output in a dynamic buffer */
|
||||
|
||||
typedef struct DynBuffer {
|
||||
int pos, size, allocated_size;
|
||||
UINT8 *buffer;
|
||||
int io_buffer_size;
|
||||
UINT8 io_buffer[1];
|
||||
} DynBuffer;
|
||||
|
||||
static void dyn_buf_write(void *opaque, UINT8 *buf, int buf_size)
|
||||
{
|
||||
DynBuffer *d = opaque;
|
||||
int new_size, new_allocated_size;
|
||||
UINT8 *new_buffer;
|
||||
|
||||
/* reallocate buffer if needed */
|
||||
new_size = d->pos + buf_size;
|
||||
new_allocated_size = d->allocated_size;
|
||||
while (new_size > new_allocated_size) {
|
||||
if (!new_allocated_size)
|
||||
new_allocated_size = new_size;
|
||||
else
|
||||
new_allocated_size = (new_allocated_size * 3) / 2 + 1;
|
||||
}
|
||||
|
||||
if (new_allocated_size > d->allocated_size) {
|
||||
new_buffer = av_malloc(new_allocated_size);
|
||||
if (!new_buffer)
|
||||
return;
|
||||
memcpy(new_buffer, d->buffer, d->size);
|
||||
av_free(d->buffer);
|
||||
d->buffer = new_buffer;
|
||||
d->allocated_size = new_allocated_size;
|
||||
}
|
||||
memcpy(d->buffer + d->pos, buf, buf_size);
|
||||
d->pos = new_size;
|
||||
if (d->pos > d->size)
|
||||
d->size = d->pos;
|
||||
}
|
||||
|
||||
static void dyn_packet_buf_write(void *opaque, UINT8 *buf, int buf_size)
|
||||
{
|
||||
unsigned char buf1[4];
|
||||
|
||||
/* packetized write: output the header */
|
||||
buf1[0] = (buf_size >> 24);
|
||||
buf1[1] = (buf_size >> 16);
|
||||
buf1[2] = (buf_size >> 8);
|
||||
buf1[3] = (buf_size);
|
||||
dyn_buf_write(opaque, buf1, 4);
|
||||
|
||||
/* then the data */
|
||||
dyn_buf_write(opaque, buf, buf_size);
|
||||
}
|
||||
|
||||
static int dyn_buf_seek(void *opaque, offset_t offset, int whence)
|
||||
{
|
||||
DynBuffer *d = opaque;
|
||||
|
||||
if (whence == SEEK_CUR)
|
||||
offset += d->pos;
|
||||
else if (whence == SEEK_END)
|
||||
offset += d->size;
|
||||
if (offset < 0 || offset > 0x7fffffffLL)
|
||||
return -1;
|
||||
d->pos = offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int url_open_dyn_buf_internal(ByteIOContext *s, int max_packet_size)
|
||||
{
|
||||
DynBuffer *d;
|
||||
int io_buffer_size, ret;
|
||||
|
||||
if (max_packet_size)
|
||||
io_buffer_size = max_packet_size;
|
||||
else
|
||||
io_buffer_size = 1024;
|
||||
|
||||
d = av_malloc(sizeof(DynBuffer) + io_buffer_size);
|
||||
if (!d)
|
||||
return -1;
|
||||
d->io_buffer_size = io_buffer_size;
|
||||
d->buffer = NULL;
|
||||
d->pos = 0;
|
||||
d->size = 0;
|
||||
d->allocated_size = 0;
|
||||
ret = init_put_byte(s, d->io_buffer, io_buffer_size,
|
||||
1, d, NULL,
|
||||
max_packet_size ? dyn_packet_buf_write : dyn_buf_write,
|
||||
max_packet_size ? NULL : dyn_buf_seek);
|
||||
if (ret == 0) {
|
||||
s->max_packet_size = max_packet_size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open a write only memory stream.
|
||||
*
|
||||
* @param s new IO context
|
||||
* @return zero if no error.
|
||||
*/
|
||||
int url_open_dyn_buf(ByteIOContext *s)
|
||||
{
|
||||
return url_open_dyn_buf_internal(s, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Open a write only packetized memory stream with a maximum packet
|
||||
* size of 'max_packet_size'. The stream is stored in a memory buffer
|
||||
* with a big endian 4 byte header giving the packet size in bytes.
|
||||
*
|
||||
* @param s new IO context
|
||||
* @param max_packet_size maximum packet size (must be > 0)
|
||||
* @return zero if no error.
|
||||
*/
|
||||
int url_open_dyn_packet_buf(ByteIOContext *s, int max_packet_size)
|
||||
{
|
||||
if (max_packet_size <= 0)
|
||||
return -1;
|
||||
return url_open_dyn_buf_internal(s, max_packet_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the written size and a pointer to the buffer. The buffer
|
||||
* must be freed with av_free().
|
||||
* @param s IO context
|
||||
* @param pointer to a byte buffer
|
||||
* @return the length of the byte buffer
|
||||
*/
|
||||
int url_close_dyn_buf(ByteIOContext *s, UINT8 **pbuffer)
|
||||
{
|
||||
DynBuffer *d = s->opaque;
|
||||
int size;
|
||||
|
||||
put_flush_packet(s);
|
||||
|
||||
*pbuffer = d->buffer;
|
||||
size = d->size;
|
||||
av_free(d);
|
||||
return size;
|
||||
}
|
||||
25
libavformat/barpainet.c
Normal file
25
libavformat/barpainet.c
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include "barpainet.h"
|
||||
|
||||
int inet_aton (const char * str, struct in_addr * add) {
|
||||
const char * pch = str;
|
||||
unsigned int add1 = 0, add2 = 0, add3 = 0, add4 = 0;
|
||||
|
||||
add1 = atoi(pch);
|
||||
pch = strpbrk(pch,".");
|
||||
if (pch == 0 || ++pch == 0) goto done;
|
||||
add2 = atoi(pch);
|
||||
pch = strpbrk(pch,".");
|
||||
if (pch == 0 || ++pch == 0) goto done;
|
||||
add3 = atoi(pch);
|
||||
pch = strpbrk(pch,".");
|
||||
if (pch == 0 || ++pch == 0) goto done;
|
||||
add4 = atoi(pch);
|
||||
|
||||
done:
|
||||
add->s_addr=(add4<<24)+(add3<<16)+(add2<<8)+add1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
23
libavformat/barpainet.h
Normal file
23
libavformat/barpainet.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef BARPA_INET_H
|
||||
#define BARPA_INET_H
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#ifdef CONFIG_BEOS_NETSERVER
|
||||
|
||||
# include <socket.h>
|
||||
int inet_aton (const char * str, struct in_addr * add);
|
||||
# define PF_INET AF_INET
|
||||
# define SO_SNDBUF 0x40000001
|
||||
|
||||
/* fake */
|
||||
struct ip_mreq {
|
||||
struct in_addr imr_multiaddr; /* IP multicast address of group */
|
||||
struct in_addr imr_interface; /* local IP address of interface */
|
||||
};
|
||||
|
||||
#else
|
||||
# include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#endif /* BARPA_INET_H */
|
||||
449
libavformat/beosaudio.cpp
Normal file
449
libavformat/beosaudio.cpp
Normal file
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* BeOS audio play interface
|
||||
* Copyright (c) 2000, 2001 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <Application.h>
|
||||
#include <SoundPlayer.h>
|
||||
|
||||
extern "C" {
|
||||
#include "avformat.h"
|
||||
}
|
||||
|
||||
/* enable performance checks */
|
||||
//#define PERF_CHECK
|
||||
|
||||
//const char *audio_device = "/dev/dsp";
|
||||
const char *audio_device = "beosaudio:";
|
||||
|
||||
/* Pipes are 4k in BeOS IIRC */
|
||||
#define AUDIO_BLOCK_SIZE 4096
|
||||
//#define AUDIO_BLOCK_SIZE 2048
|
||||
#define AUDIO_BLOCK_COUNT 8
|
||||
|
||||
#define AUDIO_BUFFER_SIZE (AUDIO_BLOCK_SIZE*AUDIO_BLOCK_COUNT)
|
||||
|
||||
/* pipes suck for realtime */
|
||||
#define USE_RING_BUFFER 1
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
int sample_rate;
|
||||
int channels;
|
||||
int frame_size; /* in bytes ! */
|
||||
CodecID codec_id;
|
||||
int flip_left : 1;
|
||||
UINT8 buffer[AUDIO_BUFFER_SIZE];
|
||||
int buffer_ptr;
|
||||
int pipefd; /* the other end of the pipe */
|
||||
/* ring buffer */
|
||||
sem_id input_sem;
|
||||
int input_index;
|
||||
sem_id output_sem;
|
||||
int output_index;
|
||||
int queued;
|
||||
BSoundPlayer *player;
|
||||
int has_quit; /* signal callbacks not to wait */
|
||||
volatile bigtime_t starve_time;
|
||||
} AudioData;
|
||||
|
||||
static thread_id main_thid;
|
||||
static thread_id bapp_thid;
|
||||
static int own_BApp_created = 0;
|
||||
static int refcount = 0;
|
||||
|
||||
/* create the BApplication and Run() it */
|
||||
static int32 bapp_thread(void *arg)
|
||||
{
|
||||
new BApplication("application/x-vnd.ffmpeg");
|
||||
own_BApp_created = 1;
|
||||
be_app->Run();
|
||||
/* kill the process group */
|
||||
// kill(0, SIGINT);
|
||||
// kill(main_thid, SIGHUP);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
/* create the BApplication only if needed */
|
||||
static void create_bapp_if_needed(void)
|
||||
{
|
||||
if (refcount++ == 0) {
|
||||
/* needed by libmedia */
|
||||
if (be_app == NULL) {
|
||||
bapp_thid = spawn_thread(bapp_thread, "ffmpeg BApplication", B_NORMAL_PRIORITY, NULL);
|
||||
resume_thread(bapp_thid);
|
||||
while (!own_BApp_created)
|
||||
snooze(50000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_bapp_if_needed(void)
|
||||
{
|
||||
if (--refcount == 0 && own_BApp_created) {
|
||||
be_app->Lock();
|
||||
be_app->Quit();
|
||||
be_app = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* called back by BSoundPlayer */
|
||||
static void audioplay_callback(void *cookie, void *buffer, size_t bufferSize, const media_raw_audio_format &format)
|
||||
{
|
||||
AudioData *s;
|
||||
size_t len, amount;
|
||||
unsigned char *buf = (unsigned char *)buffer;
|
||||
|
||||
s = (AudioData *)cookie;
|
||||
if (s->has_quit)
|
||||
return;
|
||||
while (bufferSize > 0) {
|
||||
#ifdef PERF_CHECK
|
||||
bigtime_t t;
|
||||
t = system_time();
|
||||
#endif
|
||||
#ifdef USE_RING_BUFFER
|
||||
len = MIN(AUDIO_BLOCK_SIZE, bufferSize);
|
||||
if (acquire_sem_etc(s->output_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK) {
|
||||
s->has_quit = 1;
|
||||
s->player->SetHasData(false);
|
||||
return;
|
||||
}
|
||||
amount = MIN(len, (AUDIO_BUFFER_SIZE - s->output_index));
|
||||
memcpy(buf, &s->buffer[s->output_index], amount);
|
||||
s->output_index += amount;
|
||||
if (s->output_index >= AUDIO_BUFFER_SIZE) {
|
||||
s->output_index %= AUDIO_BUFFER_SIZE;
|
||||
memcpy(buf + amount, &s->buffer[s->output_index], len - amount);
|
||||
s->output_index += len-amount;
|
||||
s->output_index %= AUDIO_BUFFER_SIZE;
|
||||
}
|
||||
release_sem_etc(s->input_sem, len, 0);
|
||||
#else
|
||||
len = read(s->pipefd, buf, bufferSize);
|
||||
#endif
|
||||
#ifdef PERF_CHECK
|
||||
t = system_time() - t;
|
||||
s->starve_time = MAX(s->starve_time, t);
|
||||
#endif
|
||||
#ifndef USE_RING_BUFFER
|
||||
if (len < B_OK) {
|
||||
puts("EPIPE");
|
||||
s->player->SetHasData(false);
|
||||
snooze(100000);
|
||||
return;
|
||||
}
|
||||
if (len == 0) {
|
||||
s->player->SetHasData(false);
|
||||
snooze(100000);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
buf += len;
|
||||
bufferSize -= len;
|
||||
}
|
||||
}
|
||||
|
||||
static int audio_open(AudioData *s, int is_output)
|
||||
{
|
||||
int p[2];
|
||||
int ret;
|
||||
media_raw_audio_format format;
|
||||
|
||||
if (!is_output)
|
||||
return -EIO; /* not for now */
|
||||
#ifdef USE_RING_BUFFER
|
||||
s->input_sem = create_sem(AUDIO_BUFFER_SIZE, "ffmpeg_ringbuffer_input");
|
||||
// s->input_sem = create_sem(AUDIO_BLOCK_SIZE, "ffmpeg_ringbuffer_input");
|
||||
if (s->input_sem < B_OK)
|
||||
return -EIO;
|
||||
s->output_sem = create_sem(0, "ffmpeg_ringbuffer_output");
|
||||
if (s->output_sem < B_OK) {
|
||||
delete_sem(s->input_sem);
|
||||
return -EIO;
|
||||
}
|
||||
s->input_index = 0;
|
||||
s->output_index = 0;
|
||||
s->queued = 0;
|
||||
#else
|
||||
ret = pipe(p);
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
s->fd = p[is_output?1:0];
|
||||
s->pipefd = p[is_output?0:1];
|
||||
if (s->fd < 0) {
|
||||
perror(is_output?"audio out":"audio in");
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
create_bapp_if_needed();
|
||||
/* non blocking mode */
|
||||
// fcntl(s->fd, F_SETFL, O_NONBLOCK);
|
||||
// fcntl(s->pipefd, F_SETFL, O_NONBLOCK);
|
||||
s->frame_size = AUDIO_BLOCK_SIZE;
|
||||
format = media_raw_audio_format::wildcard;
|
||||
format.format = media_raw_audio_format::B_AUDIO_SHORT;
|
||||
format.byte_order = B_HOST_IS_LENDIAN ? B_MEDIA_LITTLE_ENDIAN : B_MEDIA_BIG_ENDIAN;
|
||||
format.channel_count = s->channels;
|
||||
format.buffer_size = s->frame_size;
|
||||
format.frame_rate = s->sample_rate;
|
||||
s->player = new BSoundPlayer(&format, "ffmpeg output", audioplay_callback);
|
||||
if (s->player->InitCheck() != B_OK) {
|
||||
delete s->player;
|
||||
s->player = NULL;
|
||||
#ifdef USE_RING_BUFFER
|
||||
if (s->input_sem)
|
||||
delete_sem(s->input_sem);
|
||||
if (s->output_sem)
|
||||
delete_sem(s->output_sem);
|
||||
#else
|
||||
close(s->fd);
|
||||
close(s->pipefd);
|
||||
#endif
|
||||
return -EIO;
|
||||
}
|
||||
s->player->SetCookie(s);
|
||||
s->player->SetVolume(1.0);
|
||||
s->player->Start();
|
||||
s->player->SetHasData(true);
|
||||
/* bump up the priority (avoid realtime though) */
|
||||
set_thread_priority(find_thread(NULL), B_DISPLAY_PRIORITY+1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audio_close(AudioData *s)
|
||||
{
|
||||
#ifdef USE_RING_BUFFER
|
||||
if (s->input_sem)
|
||||
delete_sem(s->input_sem);
|
||||
if (s->output_sem)
|
||||
delete_sem(s->output_sem);
|
||||
#endif
|
||||
s->has_quit = 1;
|
||||
if (s->player) {
|
||||
s->player->Stop();
|
||||
}
|
||||
if (s->player)
|
||||
delete s->player;
|
||||
#ifndef USE_RING_BUFFER
|
||||
close(s->pipefd);
|
||||
close(s->fd);
|
||||
#endif
|
||||
destroy_bapp_if_needed();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sound output support */
|
||||
static int audio_write_header(AVFormatContext *s1)
|
||||
{
|
||||
AudioData *s = (AudioData *)s1->priv_data;
|
||||
AVStream *st;
|
||||
int ret;
|
||||
|
||||
st = s1->streams[0];
|
||||
s->sample_rate = st->codec.sample_rate;
|
||||
s->channels = st->codec.channels;
|
||||
ret = audio_open(s, 1);
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audio_write_packet(AVFormatContext *s1, int stream_index,
|
||||
UINT8 *buf, int size, int force_pts)
|
||||
{
|
||||
AudioData *s = (AudioData *)s1->priv_data;
|
||||
int len, ret;
|
||||
#ifdef PERF_CHECK
|
||||
bigtime_t t = s->starve_time;
|
||||
s->starve_time = 0;
|
||||
printf("starve_time: %lld \n", t);
|
||||
#endif
|
||||
#ifdef USE_RING_BUFFER
|
||||
while (size > 0) {
|
||||
int amount;
|
||||
len = MIN(size, AUDIO_BLOCK_SIZE);
|
||||
if (acquire_sem_etc(s->input_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK)
|
||||
return -EIO;
|
||||
amount = MIN(len, (AUDIO_BUFFER_SIZE - s->input_index));
|
||||
memcpy(&s->buffer[s->input_index], buf, amount);
|
||||
s->input_index += amount;
|
||||
if (s->input_index >= AUDIO_BUFFER_SIZE) {
|
||||
s->input_index %= AUDIO_BUFFER_SIZE;
|
||||
memcpy(&s->buffer[s->input_index], buf + amount, len - amount);
|
||||
s->input_index += len - amount;
|
||||
}
|
||||
release_sem_etc(s->output_sem, len, 0);
|
||||
buf += len;
|
||||
size -= len;
|
||||
}
|
||||
#else
|
||||
while (size > 0) {
|
||||
len = AUDIO_BLOCK_SIZE - s->buffer_ptr;
|
||||
if (len > size)
|
||||
len = size;
|
||||
memcpy(s->buffer + s->buffer_ptr, buf, len);
|
||||
s->buffer_ptr += len;
|
||||
if (s->buffer_ptr >= AUDIO_BLOCK_SIZE) {
|
||||
for(;;) {
|
||||
//snooze(1000);
|
||||
ret = write(s->fd, s->buffer, AUDIO_BLOCK_SIZE);
|
||||
if (ret != 0)
|
||||
break;
|
||||
if (ret < 0 && (errno != EAGAIN && errno != EINTR))
|
||||
return -EIO;
|
||||
}
|
||||
s->buffer_ptr = 0;
|
||||
}
|
||||
buf += len;
|
||||
size -= len;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audio_write_trailer(AVFormatContext *s1)
|
||||
{
|
||||
AudioData *s = (AudioData *)s1->priv_data;
|
||||
|
||||
audio_close(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* grab support */
|
||||
|
||||
static int audio_read_header(AVFormatContext *s1, AVFormatParameters *ap)
|
||||
{
|
||||
AudioData *s = (AudioData *)s1->priv_data;
|
||||
AVStream *st;
|
||||
int ret;
|
||||
|
||||
if (!ap || ap->sample_rate <= 0 || ap->channels <= 0)
|
||||
return -1;
|
||||
|
||||
st = av_new_stream(s1, 0);
|
||||
if (!st) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
s->sample_rate = ap->sample_rate;
|
||||
s->channels = ap->channels;
|
||||
|
||||
ret = audio_open(s, 0);
|
||||
if (ret < 0) {
|
||||
av_free(st);
|
||||
return -EIO;
|
||||
} else {
|
||||
/* take real parameters */
|
||||
st->codec.codec_type = CODEC_TYPE_AUDIO;
|
||||
st->codec.codec_id = s->codec_id;
|
||||
st->codec.sample_rate = s->sample_rate;
|
||||
st->codec.channels = s->channels;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt)
|
||||
{
|
||||
AudioData *s = (AudioData *)s1->priv_data;
|
||||
int ret;
|
||||
|
||||
if (av_new_packet(pkt, s->frame_size) < 0)
|
||||
return -EIO;
|
||||
for(;;) {
|
||||
ret = read(s->fd, pkt->data, pkt->size);
|
||||
if (ret > 0)
|
||||
break;
|
||||
if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
|
||||
av_free_packet(pkt);
|
||||
pkt->size = 0;
|
||||
return 0;
|
||||
}
|
||||
if (!(ret == 0 || (ret == -1 && (errno == EAGAIN || errno == EINTR)))) {
|
||||
av_free_packet(pkt);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
pkt->size = ret;
|
||||
if (s->flip_left && s->channels == 2) {
|
||||
int i;
|
||||
short *p = (short *) pkt->data;
|
||||
|
||||
for (i = 0; i < ret; i += 4) {
|
||||
*p = ~*p;
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audio_read_close(AVFormatContext *s1)
|
||||
{
|
||||
AudioData *s = (AudioData *)s1->priv_data;
|
||||
|
||||
audio_close(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AVInputFormat audio_in_format = {
|
||||
"audio_device",
|
||||
"audio grab and output",
|
||||
sizeof(AudioData),
|
||||
NULL,
|
||||
audio_read_header,
|
||||
audio_read_packet,
|
||||
audio_read_close,
|
||||
NULL,
|
||||
AVFMT_NOFILE,
|
||||
};
|
||||
|
||||
AVOutputFormat audio_out_format = {
|
||||
"audio_device",
|
||||
"audio grab and output",
|
||||
"",
|
||||
"",
|
||||
sizeof(AudioData),
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
CODEC_ID_PCM_S16BE,
|
||||
#else
|
||||
CODEC_ID_PCM_S16LE,
|
||||
#endif
|
||||
CODEC_ID_NONE,
|
||||
audio_write_header,
|
||||
audio_write_packet,
|
||||
audio_write_trailer,
|
||||
AVFMT_NOFILE,
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
int audio_init(void)
|
||||
{
|
||||
main_thid = find_thread(NULL);
|
||||
av_register_input_format(&audio_in_format);
|
||||
av_register_output_format(&audio_out_format);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // "C"
|
||||
|
||||
111
libavformat/crc.c
Normal file
111
libavformat/crc.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* CRC decoder (for codec/format testing)
|
||||
* Copyright (c) 2002 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
|
||||
/* adler32.c -- compute the Adler-32 checksum of a data stream
|
||||
* Copyright (C) 1995 Mark Adler
|
||||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||||
*/
|
||||
|
||||
#define BASE 65521L /* largest prime smaller than 65536 */
|
||||
#define NMAX 5552
|
||||
/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
|
||||
|
||||
#define DO1(buf) {s1 += *buf++; s2 += s1;}
|
||||
#define DO2(buf) DO1(buf); DO1(buf);
|
||||
#define DO4(buf) DO2(buf); DO2(buf);
|
||||
#define DO8(buf) DO4(buf); DO4(buf);
|
||||
#define DO16(buf) DO8(buf); DO8(buf);
|
||||
|
||||
static UINT32 adler32(UINT32 adler, UINT8 *buf, unsigned int len)
|
||||
{
|
||||
unsigned long s1 = adler & 0xffff;
|
||||
unsigned long s2 = (adler >> 16) & 0xffff;
|
||||
int k;
|
||||
|
||||
if (buf == NULL) return 1L;
|
||||
|
||||
while (len > 0) {
|
||||
k = len < NMAX ? len : NMAX;
|
||||
len -= k;
|
||||
while (k >= 16) {
|
||||
DO16(buf);
|
||||
k -= 16;
|
||||
}
|
||||
if (k != 0) do {
|
||||
DO1(buf);
|
||||
} while (--k);
|
||||
s1 %= BASE;
|
||||
s2 %= BASE;
|
||||
}
|
||||
return (s2 << 16) | s1;
|
||||
}
|
||||
|
||||
typedef struct CRCState {
|
||||
UINT32 crcval;
|
||||
} CRCState;
|
||||
|
||||
static int crc_write_header(struct AVFormatContext *s)
|
||||
{
|
||||
CRCState *crc = s->priv_data;
|
||||
|
||||
/* init CRC */
|
||||
crc->crcval = adler32(0, NULL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int crc_write_packet(struct AVFormatContext *s,
|
||||
int stream_index,
|
||||
unsigned char *buf, int size, int force_pts)
|
||||
{
|
||||
CRCState *crc = s->priv_data;
|
||||
crc->crcval = adler32(crc->crcval, buf, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int crc_write_trailer(struct AVFormatContext *s)
|
||||
{
|
||||
CRCState *crc = s->priv_data;
|
||||
char buf[64];
|
||||
|
||||
snprintf(buf, sizeof(buf), "CRC=%08x\n", crc->crcval);
|
||||
put_buffer(&s->pb, buf, strlen(buf));
|
||||
put_flush_packet(&s->pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVOutputFormat crc_format = {
|
||||
"crc",
|
||||
"crc testing format",
|
||||
NULL,
|
||||
"",
|
||||
sizeof(CRCState),
|
||||
CODEC_ID_PCM_S16LE,
|
||||
CODEC_ID_RAWVIDEO,
|
||||
crc_write_header,
|
||||
crc_write_packet,
|
||||
crc_write_trailer,
|
||||
};
|
||||
|
||||
int crc_init(void)
|
||||
{
|
||||
av_register_output_format(&crc_format);
|
||||
return 0;
|
||||
}
|
||||
110
libavformat/cutils.c
Normal file
110
libavformat/cutils.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Various simple utilities for ffmpeg system
|
||||
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
#include <ctype.h>
|
||||
|
||||
#if !defined(CONFIG_NOCUTILS)
|
||||
/**
|
||||
* Return TRUE if val is a prefix of str. If it returns TRUE, ptr is
|
||||
* set to the next character in 'str' after the prefix.
|
||||
*
|
||||
* @param str input string
|
||||
* @param val prefix to test
|
||||
* @param ptr updated after the prefix in str in there is a match
|
||||
* @return TRUE if there is a match
|
||||
*/
|
||||
int strstart(const char *str, const char *val, const char **ptr)
|
||||
{
|
||||
const char *p, *q;
|
||||
p = str;
|
||||
q = val;
|
||||
while (*q != '\0') {
|
||||
if (*p != *q)
|
||||
return 0;
|
||||
p++;
|
||||
q++;
|
||||
}
|
||||
if (ptr)
|
||||
*ptr = p;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return TRUE if val is a prefix of str (case independent). If it
|
||||
* returns TRUE, ptr is set to the next character in 'str' after the
|
||||
* prefix.
|
||||
*
|
||||
* @param str input string
|
||||
* @param val prefix to test
|
||||
* @param ptr updated after the prefix in str in there is a match
|
||||
* @return TRUE if there is a match */
|
||||
int stristart(const char *str, const char *val, const char **ptr)
|
||||
{
|
||||
const char *p, *q;
|
||||
p = str;
|
||||
q = val;
|
||||
while (*q != '\0') {
|
||||
if (toupper(*(unsigned char *)p) != toupper(*(unsigned char *)q))
|
||||
return 0;
|
||||
p++;
|
||||
q++;
|
||||
}
|
||||
if (ptr)
|
||||
*ptr = p;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the string str to buf. If str length is bigger than buf_size -
|
||||
* 1 then it is clamped to buf_size - 1.
|
||||
* NOTE: this function does what strncpy should have done to be
|
||||
* useful. NEVER use strncpy.
|
||||
*
|
||||
* @param buf destination buffer
|
||||
* @param buf_size size of destination buffer
|
||||
* @param str source string
|
||||
*/
|
||||
void pstrcpy(char *buf, int buf_size, const char *str)
|
||||
{
|
||||
int c;
|
||||
char *q = buf;
|
||||
|
||||
if (buf_size <= 0)
|
||||
return;
|
||||
|
||||
for(;;) {
|
||||
c = *str++;
|
||||
if (c == 0 || q >= buf + buf_size - 1)
|
||||
break;
|
||||
*q++ = c;
|
||||
}
|
||||
*q = '\0';
|
||||
}
|
||||
|
||||
/* strcat and truncate. */
|
||||
char *pstrcat(char *buf, int buf_size, const char *s)
|
||||
{
|
||||
int len;
|
||||
len = strlen(buf);
|
||||
if (len < buf_size)
|
||||
pstrcpy(buf + len, buf_size - len, s);
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif
|
||||
134
libavformat/dv.c
Normal file
134
libavformat/dv.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Raw DV format
|
||||
* Copyright (c) 2002 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
|
||||
#define NTSC_FRAME_SIZE 120000
|
||||
#define PAL_FRAME_SIZE 144000
|
||||
|
||||
typedef struct DVDemuxContext {
|
||||
int is_audio;
|
||||
} DVDemuxContext;
|
||||
|
||||
/* raw input */
|
||||
static int dv_read_header(AVFormatContext *s,
|
||||
AVFormatParameters *ap)
|
||||
{
|
||||
AVStream *vst, *ast;
|
||||
|
||||
vst = av_new_stream(s, 0);
|
||||
if (!vst)
|
||||
return AVERROR_NOMEM;
|
||||
vst->codec.codec_type = CODEC_TYPE_VIDEO;
|
||||
vst->codec.codec_id = CODEC_ID_DVVIDEO;
|
||||
|
||||
#if 0
|
||||
ast = av_new_stream(s, 1);
|
||||
if (!ast)
|
||||
return AVERROR_NOMEM;
|
||||
|
||||
ast->codec.codec_type = CODEC_TYPE_AUDIO;
|
||||
ast->codec.codec_id = CODEC_ID_DVAUDIO;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: build fake audio stream when DV audio decoder will be finished */
|
||||
static int dv_read_packet(AVFormatContext *s, AVPacket *pkt)
|
||||
{
|
||||
int ret, size, dsf;
|
||||
uint8_t buf[4];
|
||||
|
||||
ret = get_buffer(&s->pb, buf, 4);
|
||||
if (ret <= 0)
|
||||
return -EIO;
|
||||
dsf = buf[3] & 0x80;
|
||||
if (!dsf)
|
||||
size = NTSC_FRAME_SIZE;
|
||||
else
|
||||
size = PAL_FRAME_SIZE;
|
||||
|
||||
if (av_new_packet(pkt, size) < 0)
|
||||
return -EIO;
|
||||
|
||||
pkt->stream_index = 0;
|
||||
memcpy(pkt->data, buf, 4);
|
||||
ret = get_buffer(&s->pb, pkt->data + 4, size - 4);
|
||||
if (ret <= 0) {
|
||||
av_free_packet(pkt);
|
||||
return -EIO;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dv_read_close(AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVInputFormat dv_iformat = {
|
||||
"dv",
|
||||
"DV video format",
|
||||
sizeof(DVDemuxContext),
|
||||
NULL,
|
||||
dv_read_header,
|
||||
dv_read_packet,
|
||||
dv_read_close,
|
||||
.extensions = "dv",
|
||||
};
|
||||
|
||||
#if 0
|
||||
int dv_write_header(struct AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dv_write_packet(struct AVFormatContext *s,
|
||||
int stream_index,
|
||||
unsigned char *buf, int size, int force_pts)
|
||||
{
|
||||
put_buffer(&s->pb, buf, size);
|
||||
put_flush_packet(&s->pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dv_write_trailer(struct AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
AVOutputFormat dv_oformat = {
|
||||
"dv",
|
||||
"DV video format",
|
||||
NULL,
|
||||
"dv",
|
||||
0,
|
||||
CODEC_ID_DVVIDEO,
|
||||
CODEC_ID_DVAUDIO,
|
||||
dv_write_header,
|
||||
dv_write_packet,
|
||||
dv_write_trailer,
|
||||
};
|
||||
#endif
|
||||
|
||||
int dv_init(void)
|
||||
{
|
||||
av_register_input_format(&dv_iformat);
|
||||
// av_register_output_format(&dv_oformat);
|
||||
return 0;
|
||||
}
|
||||
684
libavformat/ffm.c
Normal file
684
libavformat/ffm.c
Normal file
@@ -0,0 +1,684 @@
|
||||
/*
|
||||
* FFM (ffserver live feed) encoder and decoder
|
||||
* Copyright (c) 2001 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
#include <unistd.h>
|
||||
|
||||
/* The FFM file is made of blocks of fixed size */
|
||||
#define FFM_HEADER_SIZE 14
|
||||
#define PACKET_ID 0x666d
|
||||
|
||||
/* each packet contains frames (which can span several packets */
|
||||
#define FRAME_HEADER_SIZE 8
|
||||
#define FLAG_KEY_FRAME 0x01
|
||||
|
||||
typedef struct FFMStream {
|
||||
INT64 pts;
|
||||
} FFMStream;
|
||||
|
||||
enum {
|
||||
READ_HEADER,
|
||||
READ_DATA,
|
||||
};
|
||||
|
||||
typedef struct FFMContext {
|
||||
/* only reading mode */
|
||||
offset_t write_index, file_size;
|
||||
int read_state;
|
||||
UINT8 header[FRAME_HEADER_SIZE];
|
||||
|
||||
/* read and write */
|
||||
int first_packet; /* true if first packet, needed to set the discontinuity tag */
|
||||
int packet_size;
|
||||
int frame_offset;
|
||||
INT64 pts;
|
||||
UINT8 *packet_ptr, *packet_end;
|
||||
UINT8 packet[FFM_PACKET_SIZE];
|
||||
} FFMContext;
|
||||
|
||||
/* disable pts hack for testing */
|
||||
int ffm_nopts = 0;
|
||||
|
||||
static void flush_packet(AVFormatContext *s)
|
||||
{
|
||||
FFMContext *ffm = s->priv_data;
|
||||
int fill_size, h;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
|
||||
fill_size = ffm->packet_end - ffm->packet_ptr;
|
||||
memset(ffm->packet_ptr, 0, fill_size);
|
||||
|
||||
/* put header */
|
||||
put_be16(pb, PACKET_ID);
|
||||
put_be16(pb, fill_size);
|
||||
put_be64(pb, ffm->pts);
|
||||
h = ffm->frame_offset;
|
||||
if (ffm->first_packet)
|
||||
h |= 0x8000;
|
||||
put_be16(pb, h);
|
||||
put_buffer(pb, ffm->packet, ffm->packet_end - ffm->packet);
|
||||
|
||||
/* prepare next packet */
|
||||
ffm->frame_offset = 0; /* no key frame */
|
||||
ffm->pts = 0; /* no pts */
|
||||
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,
|
||||
UINT8 *buf, int size,
|
||||
INT64 pts, int first)
|
||||
{
|
||||
FFMContext *ffm = s->priv_data;
|
||||
int len;
|
||||
|
||||
if (first && ffm->frame_offset == 0)
|
||||
ffm->frame_offset = ffm->packet_ptr - ffm->packet + FFM_HEADER_SIZE;
|
||||
if (first && ffm->pts == 0)
|
||||
ffm->pts = pts;
|
||||
|
||||
/* 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) {
|
||||
/* special case : no pts in packet : we leave the current one */
|
||||
if (ffm->pts == 0)
|
||||
ffm->pts = pts;
|
||||
|
||||
flush_packet(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int ffm_write_header(AVFormatContext *s)
|
||||
{
|
||||
FFMContext *ffm = s->priv_data;
|
||||
AVStream *st;
|
||||
FFMStream *fst;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
AVCodecContext *codec;
|
||||
int bit_rate, i;
|
||||
|
||||
ffm->packet_size = FFM_PACKET_SIZE;
|
||||
|
||||
/* header */
|
||||
put_tag(pb, "FFM1");
|
||||
put_be32(pb, ffm->packet_size);
|
||||
/* XXX: store write position in other file ? */
|
||||
put_be64(pb, ffm->packet_size); /* current write position */
|
||||
|
||||
put_be32(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;
|
||||
}
|
||||
put_be32(pb, bit_rate);
|
||||
|
||||
/* list of streams */
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
st = s->streams[i];
|
||||
fst = av_mallocz(sizeof(FFMStream));
|
||||
if (!fst)
|
||||
goto fail;
|
||||
st->priv_data = fst;
|
||||
|
||||
codec = &st->codec;
|
||||
/* generic info */
|
||||
put_be32(pb, codec->codec_id);
|
||||
put_byte(pb, codec->codec_type);
|
||||
put_be32(pb, codec->bit_rate);
|
||||
put_be32(pb, codec->quality);
|
||||
put_be32(pb, codec->flags);
|
||||
/* specific info */
|
||||
switch(codec->codec_type) {
|
||||
case CODEC_TYPE_VIDEO:
|
||||
put_be32(pb, (codec->frame_rate * 1000) / FRAME_RATE_BASE);
|
||||
put_be16(pb, codec->width);
|
||||
put_be16(pb, codec->height);
|
||||
put_be16(pb, codec->gop_size);
|
||||
put_byte(pb, codec->qmin);
|
||||
put_byte(pb, codec->qmax);
|
||||
put_byte(pb, codec->max_qdiff);
|
||||
put_be16(pb, (int) (codec->qcompress * 10000.0));
|
||||
put_be16(pb, (int) (codec->qblur * 10000.0));
|
||||
put_be32(pb, codec->bit_rate_tolerance);
|
||||
put_strz(pb, codec->rc_eq);
|
||||
put_be32(pb, codec->rc_max_rate);
|
||||
put_be32(pb, codec->rc_min_rate);
|
||||
put_be32(pb, codec->rc_buffer_size);
|
||||
put_be64_double(pb, codec->i_quant_factor);
|
||||
put_be64_double(pb, codec->b_quant_factor);
|
||||
put_be64_double(pb, codec->i_quant_offset);
|
||||
put_be64_double(pb, codec->b_quant_offset);
|
||||
put_be32(pb, codec->dct_algo);
|
||||
break;
|
||||
case CODEC_TYPE_AUDIO:
|
||||
put_be32(pb, codec->sample_rate);
|
||||
put_le16(pb, codec->channels);
|
||||
put_le16(pb, codec->frame_size);
|
||||
break;
|
||||
default:
|
||||
av_abort();
|
||||
}
|
||||
/* hack to have real time */
|
||||
if (ffm_nopts)
|
||||
fst->pts = 0;
|
||||
else
|
||||
fst->pts = av_gettime();
|
||||
}
|
||||
|
||||
/* flush until end of block reached */
|
||||
while ((url_ftell(pb) % ffm->packet_size) != 0)
|
||||
put_byte(pb, 0);
|
||||
|
||||
put_flush_packet(pb);
|
||||
|
||||
/* init packet mux */
|
||||
ffm->packet_ptr = ffm->packet;
|
||||
ffm->packet_end = ffm->packet + ffm->packet_size - FFM_HEADER_SIZE;
|
||||
ffm->frame_offset = 0;
|
||||
ffm->pts = 0;
|
||||
ffm->first_packet = 1;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
st = s->streams[i];
|
||||
av_freep(&st->priv_data);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ffm_write_packet(AVFormatContext *s, int stream_index,
|
||||
UINT8 *buf, int size, int force_pts)
|
||||
{
|
||||
AVStream *st = s->streams[stream_index];
|
||||
FFMStream *fst = st->priv_data;
|
||||
INT64 pts;
|
||||
UINT8 header[FRAME_HEADER_SIZE];
|
||||
int duration;
|
||||
|
||||
if (st->codec.codec_type == CODEC_TYPE_AUDIO) {
|
||||
duration = ((float)st->codec.frame_size / st->codec.sample_rate * 1000000.0);
|
||||
} else {
|
||||
duration = (1000000.0 * FRAME_RATE_BASE / (float)st->codec.frame_rate);
|
||||
}
|
||||
|
||||
pts = fst->pts;
|
||||
/* packet size & key_frame */
|
||||
header[0] = stream_index;
|
||||
header[1] = 0;
|
||||
if (st->codec.key_frame)
|
||||
header[1] |= FLAG_KEY_FRAME;
|
||||
header[2] = (size >> 16) & 0xff;
|
||||
header[3] = (size >> 8) & 0xff;
|
||||
header[4] = size & 0xff;
|
||||
header[5] = (duration >> 16) & 0xff;
|
||||
header[6] = (duration >> 8) & 0xff;
|
||||
header[7] = duration & 0xff;
|
||||
ffm_write_data(s, header, FRAME_HEADER_SIZE, pts, 1);
|
||||
ffm_write_data(s, buf, size, pts, 0);
|
||||
|
||||
fst->pts += duration;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ffm_write_trailer(AVFormatContext *s)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
FFMContext *ffm = s->priv_data;
|
||||
int i;
|
||||
|
||||
/* flush packets */
|
||||
if (ffm->packet_ptr > ffm->packet)
|
||||
flush_packet(s);
|
||||
|
||||
put_flush_packet(pb);
|
||||
|
||||
if (!url_is_streamed(pb)) {
|
||||
INT64 size;
|
||||
/* update the write offset */
|
||||
size = url_ftell(pb);
|
||||
url_fseek(pb, 8, SEEK_SET);
|
||||
put_be64(pb, size);
|
||||
put_flush_packet(pb);
|
||||
}
|
||||
|
||||
for(i=0;i<s->nb_streams;i++)
|
||||
av_freep(&s->streams[i]->priv_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ffm demux */
|
||||
|
||||
static int ffm_is_avail_data(AVFormatContext *s, int size)
|
||||
{
|
||||
FFMContext *ffm = s->priv_data;
|
||||
offset_t pos, avail_size;
|
||||
int len;
|
||||
|
||||
len = ffm->packet_end - ffm->packet_ptr;
|
||||
if (!ffm_nopts) {
|
||||
/* XXX: I don't understand this test, so I disabled it for testing */
|
||||
if (size <= len)
|
||||
return 1;
|
||||
}
|
||||
pos = url_ftell(&s->pb);
|
||||
if (pos == ffm->write_index) {
|
||||
/* exactly at the end of stream */
|
||||
return 0;
|
||||
} 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 0;
|
||||
}
|
||||
|
||||
/* first is true if we read the frame header */
|
||||
static int ffm_read_data(AVFormatContext *s,
|
||||
UINT8 *buf, int size, int first)
|
||||
{
|
||||
FFMContext *ffm = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
int len, fill_size, size1, frame_offset;
|
||||
|
||||
size1 = size;
|
||||
while (size > 0) {
|
||||
redo:
|
||||
len = ffm->packet_end - ffm->packet_ptr;
|
||||
if (len > size)
|
||||
len = size;
|
||||
if (len == 0) {
|
||||
if (url_ftell(pb) == ffm->file_size)
|
||||
url_fseek(pb, ffm->packet_size, SEEK_SET);
|
||||
retry_read:
|
||||
get_be16(pb); /* PACKET_ID */
|
||||
fill_size = get_be16(pb);
|
||||
ffm->pts = get_be64(pb);
|
||||
frame_offset = get_be16(pb);
|
||||
get_buffer(pb, ffm->packet, ffm->packet_size - FFM_HEADER_SIZE);
|
||||
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 (url_ftell(pb) >= ffm->packet_size * 3) {
|
||||
url_fseek(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 & 0x7ffff) < FFM_HEADER_SIZE)
|
||||
av_abort();
|
||||
ffm->packet_ptr = ffm->packet + (frame_offset & 0x7fff) - FFM_HEADER_SIZE;
|
||||
if (!first)
|
||||
break;
|
||||
} else {
|
||||
ffm->packet_ptr = ffm->packet;
|
||||
}
|
||||
goto redo;
|
||||
}
|
||||
memcpy(buf, ffm->packet_ptr, len);
|
||||
buf += len;
|
||||
ffm->packet_ptr += len;
|
||||
size -= len;
|
||||
first = 0;
|
||||
}
|
||||
return size1 - size;
|
||||
}
|
||||
|
||||
|
||||
static int ffm_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
||||
{
|
||||
FFMContext *ffm = s->priv_data;
|
||||
AVStream *st;
|
||||
FFMStream *fst;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
AVCodecContext *codec;
|
||||
int i;
|
||||
UINT32 tag;
|
||||
|
||||
/* header */
|
||||
tag = get_le32(pb);
|
||||
if (tag != MKTAG('F', 'F', 'M', '1'))
|
||||
goto fail;
|
||||
ffm->packet_size = get_be32(pb);
|
||||
if (ffm->packet_size != FFM_PACKET_SIZE)
|
||||
goto fail;
|
||||
ffm->write_index = get_be64(pb);
|
||||
/* get also filesize */
|
||||
if (!url_is_streamed(pb)) {
|
||||
ffm->file_size = url_filesize(url_fileno(pb));
|
||||
} else {
|
||||
ffm->file_size = (UINT64_C(1) << 63) - 1;
|
||||
}
|
||||
|
||||
s->nb_streams = get_be32(pb);
|
||||
get_be32(pb); /* total bitrate */
|
||||
/* read each stream */
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
char rc_eq_buf[128];
|
||||
|
||||
st = av_mallocz(sizeof(AVStream));
|
||||
if (!st)
|
||||
goto fail;
|
||||
s->streams[i] = st;
|
||||
fst = av_mallocz(sizeof(FFMStream));
|
||||
if (!fst)
|
||||
goto fail;
|
||||
st->priv_data = fst;
|
||||
|
||||
codec = &st->codec;
|
||||
/* generic info */
|
||||
st->codec.codec_id = get_be32(pb);
|
||||
st->codec.codec_type = get_byte(pb); /* codec_type */
|
||||
codec->bit_rate = get_be32(pb);
|
||||
codec->quality = get_be32(pb);
|
||||
codec->flags = get_be32(pb);
|
||||
/* specific info */
|
||||
switch(codec->codec_type) {
|
||||
case CODEC_TYPE_VIDEO:
|
||||
codec->frame_rate = ((INT64)get_be32(pb) * FRAME_RATE_BASE) / 1000;
|
||||
codec->width = get_be16(pb);
|
||||
codec->height = get_be16(pb);
|
||||
codec->gop_size = get_be16(pb);
|
||||
codec->qmin = get_byte(pb);
|
||||
codec->qmax = get_byte(pb);
|
||||
codec->max_qdiff = get_byte(pb);
|
||||
codec->qcompress = get_be16(pb) / 10000.0;
|
||||
codec->qblur = get_be16(pb) / 10000.0;
|
||||
codec->bit_rate_tolerance = get_be32(pb);
|
||||
codec->rc_eq = strdup(get_strz(pb, rc_eq_buf, sizeof(rc_eq_buf)));
|
||||
codec->rc_max_rate = get_be32(pb);
|
||||
codec->rc_min_rate = get_be32(pb);
|
||||
codec->rc_buffer_size = get_be32(pb);
|
||||
codec->i_quant_factor = get_be64_double(pb);
|
||||
codec->b_quant_factor = get_be64_double(pb);
|
||||
codec->i_quant_offset = get_be64_double(pb);
|
||||
codec->b_quant_offset = get_be64_double(pb);
|
||||
codec->dct_algo = get_be32(pb);
|
||||
break;
|
||||
case CODEC_TYPE_AUDIO:
|
||||
codec->sample_rate = get_be32(pb);
|
||||
codec->channels = get_le16(pb);
|
||||
codec->frame_size = get_le16(pb);
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* get until end of block reached */
|
||||
while ((url_ftell(pb) % ffm->packet_size) != 0)
|
||||
get_byte(pb);
|
||||
|
||||
/* init packet demux */
|
||||
ffm->packet_ptr = ffm->packet;
|
||||
ffm->packet_end = ffm->packet;
|
||||
ffm->frame_offset = 0;
|
||||
ffm->pts = 0;
|
||||
ffm->read_state = READ_HEADER;
|
||||
ffm->first_packet = 1;
|
||||
return 0;
|
||||
fail:
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
st = s->streams[i];
|
||||
if (st) {
|
||||
av_freep(&st->priv_data);
|
||||
av_free(st);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* return < 0 if eof */
|
||||
static int ffm_read_packet(AVFormatContext *s, AVPacket *pkt)
|
||||
{
|
||||
int size;
|
||||
FFMContext *ffm = s->priv_data;
|
||||
int duration;
|
||||
|
||||
switch(ffm->read_state) {
|
||||
case READ_HEADER:
|
||||
if (!ffm_is_avail_data(s, FRAME_HEADER_SIZE)) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
#if 0
|
||||
printf("pos=%08Lx spos=%Lx, write_index=%Lx size=%Lx\n",
|
||||
url_ftell(&s->pb), s->pb.pos, ffm->write_index, ffm->file_size);
|
||||
#endif
|
||||
if (ffm_read_data(s, ffm->header, FRAME_HEADER_SIZE, 1) !=
|
||||
FRAME_HEADER_SIZE)
|
||||
return -EAGAIN;
|
||||
#if 0
|
||||
{
|
||||
int i;
|
||||
for(i=0;i<FRAME_HEADER_SIZE;i++)
|
||||
printf("%02x ", ffm->header[i]);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
ffm->read_state = READ_DATA;
|
||||
/* fall thru */
|
||||
case READ_DATA:
|
||||
size = (ffm->header[2] << 16) | (ffm->header[3] << 8) | ffm->header[4];
|
||||
if (!ffm_is_avail_data(s, size)) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
duration = (ffm->header[5] << 16) | (ffm->header[6] << 8) | ffm->header[7];
|
||||
|
||||
av_new_packet(pkt, size);
|
||||
pkt->stream_index = ffm->header[0];
|
||||
if (ffm->header[1] & FLAG_KEY_FRAME)
|
||||
pkt->flags |= 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 -EAGAIN;
|
||||
}
|
||||
pkt->pts = ffm->pts;
|
||||
pkt->duration = duration;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//#define DEBUG_SEEK
|
||||
|
||||
/* pos is between 0 and file_size - FFM_PACKET_SIZE. It is translated
|
||||
by the write position inside this function */
|
||||
static void ffm_seek1(AVFormatContext *s, offset_t pos1)
|
||||
{
|
||||
FFMContext *ffm = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
offset_t pos;
|
||||
|
||||
pos = pos1 + ffm->write_index;
|
||||
if (pos >= ffm->file_size)
|
||||
pos -= (ffm->file_size - FFM_PACKET_SIZE);
|
||||
#ifdef DEBUG_SEEK
|
||||
printf("seek to %Lx -> %Lx\n", pos1, pos);
|
||||
#endif
|
||||
url_fseek(pb, pos, SEEK_SET);
|
||||
}
|
||||
|
||||
static INT64 get_pts(AVFormatContext *s, offset_t pos)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
INT64 pts;
|
||||
|
||||
ffm_seek1(s, pos);
|
||||
url_fskip(pb, 4);
|
||||
pts = get_be64(pb);
|
||||
#ifdef DEBUG_SEEK
|
||||
printf("pts=%0.6f\n", pts / 1000000.0);
|
||||
#endif
|
||||
return pts;
|
||||
}
|
||||
|
||||
/* seek to a given time in the file. The file read pointer is
|
||||
positionned at or before pts. XXX: the following code is quite
|
||||
approximative */
|
||||
static int ffm_seek(AVFormatContext *s, INT64 wanted_pts)
|
||||
{
|
||||
FFMContext *ffm = s->priv_data;
|
||||
offset_t pos_min, pos_max, pos;
|
||||
INT64 pts_min, pts_max, pts;
|
||||
double pos1;
|
||||
|
||||
#ifdef DEBUG_SEEK
|
||||
printf("wanted_pts=%0.6f\n", wanted_pts / 1000000.0);
|
||||
#endif
|
||||
/* find the position using linear interpolation (better than
|
||||
dichotomy in typical cases) */
|
||||
pos_min = 0;
|
||||
pos_max = ffm->file_size - 2 * FFM_PACKET_SIZE;
|
||||
while (pos_min <= pos_max) {
|
||||
pts_min = get_pts(s, pos_min);
|
||||
pts_max = get_pts(s, pos_max);
|
||||
/* linear interpolation */
|
||||
pos1 = (double)(pos_max - pos_min) * (double)(wanted_pts - pts_min) /
|
||||
(double)(pts_max - pts_min);
|
||||
pos = (((INT64)pos1) / FFM_PACKET_SIZE) * FFM_PACKET_SIZE;
|
||||
if (pos <= pos_min)
|
||||
pos = pos_min;
|
||||
else if (pos >= pos_max)
|
||||
pos = pos_max;
|
||||
pts = get_pts(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 = pos_min;
|
||||
if (pos > 0)
|
||||
pos -= FFM_PACKET_SIZE;
|
||||
found:
|
||||
ffm_seek1(s, pos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
offset_t ffm_read_write_index(int fd)
|
||||
{
|
||||
UINT8 buf[8];
|
||||
offset_t pos;
|
||||
int i;
|
||||
|
||||
lseek(fd, 8, SEEK_SET);
|
||||
read(fd, buf, 8);
|
||||
pos = 0;
|
||||
for(i=0;i<8;i++)
|
||||
pos |= buf[i] << (56 - i * 8);
|
||||
return pos;
|
||||
}
|
||||
|
||||
void ffm_write_write_index(int fd, offset_t pos)
|
||||
{
|
||||
UINT8 buf[8];
|
||||
int i;
|
||||
|
||||
for(i=0;i<8;i++)
|
||||
buf[i] = (pos >> (56 - i * 8)) & 0xff;
|
||||
lseek(fd, 8, SEEK_SET);
|
||||
write(fd, buf, 8);
|
||||
}
|
||||
|
||||
void ffm_set_write_index(AVFormatContext *s, offset_t pos, offset_t file_size)
|
||||
{
|
||||
FFMContext *ffm = s->priv_data;
|
||||
ffm->write_index = pos;
|
||||
ffm->file_size = file_size;
|
||||
}
|
||||
|
||||
static int ffm_read_close(AVFormatContext *s)
|
||||
{
|
||||
AVStream *st;
|
||||
int i;
|
||||
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
st = s->streams[i];
|
||||
av_freep(&st->priv_data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ffm_probe(AVProbeData *p)
|
||||
{
|
||||
if (p->buf_size >= 4 &&
|
||||
p->buf[0] == 'F' && p->buf[1] == 'F' && p->buf[2] == 'M' &&
|
||||
p->buf[3] == '1')
|
||||
return AVPROBE_SCORE_MAX + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVInputFormat ffm_iformat = {
|
||||
"ffm",
|
||||
"ffm format",
|
||||
sizeof(FFMContext),
|
||||
ffm_probe,
|
||||
ffm_read_header,
|
||||
ffm_read_packet,
|
||||
ffm_read_close,
|
||||
ffm_seek,
|
||||
};
|
||||
|
||||
static AVOutputFormat ffm_oformat = {
|
||||
"ffm",
|
||||
"ffm format",
|
||||
"",
|
||||
"ffm",
|
||||
sizeof(FFMContext),
|
||||
/* not really used */
|
||||
CODEC_ID_MP2,
|
||||
CODEC_ID_MPEG1VIDEO,
|
||||
ffm_write_header,
|
||||
ffm_write_packet,
|
||||
ffm_write_trailer,
|
||||
};
|
||||
|
||||
int ffm_init(void)
|
||||
{
|
||||
av_register_input_format(&ffm_iformat);
|
||||
av_register_output_format(&ffm_oformat);
|
||||
return 0;
|
||||
}
|
||||
130
libavformat/file.c
Normal file
130
libavformat/file.c
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Buffered file io for ffmpeg system
|
||||
* Copyright (c) 2001 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
#include <fcntl.h>
|
||||
#ifndef CONFIG_WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#define open(fname,oflag,pmode) _open(fname,oflag,pmode)
|
||||
#endif /* CONFIG_WIN32 */
|
||||
|
||||
|
||||
/* standard file protocol */
|
||||
|
||||
static int file_open(URLContext *h, const char *filename, int flags)
|
||||
{
|
||||
int access;
|
||||
int fd;
|
||||
|
||||
if (flags & URL_WRONLY) {
|
||||
access = O_CREAT | O_TRUNC | O_WRONLY;
|
||||
} else {
|
||||
access = O_RDONLY;
|
||||
}
|
||||
#ifdef CONFIG_WIN32
|
||||
access |= O_BINARY;
|
||||
#endif
|
||||
fd = open(filename, access, 0666);
|
||||
if (fd < 0)
|
||||
return -ENOENT;
|
||||
h->priv_data = (void *)fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int file_read(URLContext *h, unsigned char *buf, int size)
|
||||
{
|
||||
int fd = (int)h->priv_data;
|
||||
return read(fd, buf, size);
|
||||
}
|
||||
|
||||
static int file_write(URLContext *h, unsigned char *buf, int size)
|
||||
{
|
||||
int fd = (int)h->priv_data;
|
||||
return write(fd, buf, size);
|
||||
}
|
||||
|
||||
/* XXX: use llseek */
|
||||
static offset_t file_seek(URLContext *h, offset_t pos, int whence)
|
||||
{
|
||||
int fd = (int)h->priv_data;
|
||||
#ifdef CONFIG_WIN32
|
||||
return _lseeki64(fd, pos, whence);
|
||||
#else
|
||||
return lseek(fd, pos, whence);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int file_close(URLContext *h)
|
||||
{
|
||||
int fd = (int)h->priv_data;
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
URLProtocol file_protocol = {
|
||||
"file",
|
||||
file_open,
|
||||
file_read,
|
||||
file_write,
|
||||
file_seek,
|
||||
file_close,
|
||||
};
|
||||
|
||||
/* pipe protocol */
|
||||
|
||||
static int pipe_open(URLContext *h, const char *filename, int flags)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (flags & URL_WRONLY) {
|
||||
fd = 1;
|
||||
} else {
|
||||
fd = 0;
|
||||
}
|
||||
h->priv_data = (void *)fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pipe_read(URLContext *h, unsigned char *buf, int size)
|
||||
{
|
||||
int fd = (int)h->priv_data;
|
||||
return read(fd, buf, size);
|
||||
}
|
||||
|
||||
static int pipe_write(URLContext *h, unsigned char *buf, int size)
|
||||
{
|
||||
int fd = (int)h->priv_data;
|
||||
return write(fd, buf, size);
|
||||
}
|
||||
|
||||
static int pipe_close(URLContext *h)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
URLProtocol pipe_protocol = {
|
||||
"pipe",
|
||||
pipe_open,
|
||||
pipe_read,
|
||||
pipe_write,
|
||||
NULL,
|
||||
pipe_close,
|
||||
};
|
||||
102
libavformat/framehook.c
Normal file
102
libavformat/framehook.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Video processing hooks
|
||||
* Copyright (c) 2000, 2001 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include "config.h"
|
||||
#include "framehook.h"
|
||||
#include "avformat.h"
|
||||
|
||||
#ifdef HAVE_VHOOK
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct _FrameHookEntry {
|
||||
struct _FrameHookEntry *next;
|
||||
FrameHookConfigureFn Configure;
|
||||
FrameHookProcessFn Process;
|
||||
void *ctx;
|
||||
} FrameHookEntry;
|
||||
|
||||
static FrameHookEntry *first_hook;
|
||||
|
||||
/* Returns 0 on OK */
|
||||
int frame_hook_add(int argc, char *argv[])
|
||||
{
|
||||
#ifdef HAVE_VHOOK
|
||||
void *loaded;
|
||||
FrameHookEntry *fhe, **fhep;
|
||||
|
||||
if (argc < 1) {
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
loaded = dlopen(argv[0], RTLD_NOW);
|
||||
if (!loaded) {
|
||||
fprintf(stderr, "%s\n", dlerror());
|
||||
return -1;
|
||||
}
|
||||
|
||||
fhe = av_mallocz(sizeof(*fhe));
|
||||
if (!fhe) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
fhe->Configure = dlsym(loaded, "Configure");
|
||||
fhe->Process = dlsym(loaded, "Process");
|
||||
|
||||
if (!fhe->Process) {
|
||||
fprintf(stderr, "Failed to find Process entrypoint in %s\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!fhe->Configure && argc > 1) {
|
||||
fprintf(stderr, "Failed to find Configure entrypoint in %s\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc > 1 || fhe->Configure) {
|
||||
if (fhe->Configure(&fhe->ctx, argc, argv)) {
|
||||
fprintf(stderr, "Failed to Configure %s\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (fhep = &first_hook; *fhep; fhep = &((*fhep)->next)) {
|
||||
}
|
||||
|
||||
*fhep = fhe;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
fprintf(stderr, "Video hooking not compiled into this version\n");
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void frame_hook_process(AVPicture *pict, enum PixelFormat pix_fmt, int width, int height)
|
||||
{
|
||||
if (first_hook) {
|
||||
FrameHookEntry *fhe;
|
||||
INT64 pts = av_gettime();
|
||||
|
||||
for (fhe = first_hook; fhe; fhe = fhe->next) {
|
||||
fhe->Process(fhe->ctx, pict, pix_fmt, width, height, pts);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
libavformat/framehook.h
Normal file
19
libavformat/framehook.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef _FRAMEHOOK_H
|
||||
#define _FRAMEHOOK_H
|
||||
|
||||
/*
|
||||
* Prototypes for interface to .so that implement a video processing hook
|
||||
*/
|
||||
|
||||
#include "avcodec.h"
|
||||
|
||||
/* Function must be called 'Configure' */
|
||||
typedef int (*FrameHookConfigureFn)(void **ctxp, int argc, char *argv[]);
|
||||
|
||||
/* Function must be called 'Process' */
|
||||
typedef void (*FrameHookProcessFn)(void *ctx, struct AVPicture *pict, enum PixelFormat pix_fmt, int width, int height, INT64 pts);
|
||||
|
||||
extern int frame_hook_add(int argc, char *argv[]);
|
||||
extern void frame_hook_process(struct AVPicture *pict, enum PixelFormat pix_fmt, int width, int height);
|
||||
|
||||
#endif
|
||||
375
libavformat/gif.c
Normal file
375
libavformat/gif.c
Normal file
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Animated GIF encoder
|
||||
* Copyright (c) 2000 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* First version by Francois Revol revol@free.fr
|
||||
*
|
||||
* 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"
|
||||
|
||||
/* bitstream minipacket size */
|
||||
#define GIF_CHUNKS 100
|
||||
|
||||
/* slows down the decoding (and some browsers doesn't like it) */
|
||||
/* #define GIF_ADD_APP_HEADER */
|
||||
|
||||
typedef struct {
|
||||
unsigned char r;
|
||||
unsigned char g;
|
||||
unsigned char b;
|
||||
} rgb_triplet;
|
||||
|
||||
/* we use the standard 216 color palette */
|
||||
|
||||
/* this script was used to create the palette:
|
||||
* for r in 00 33 66 99 cc ff; do for g in 00 33 66 99 cc ff; do echo -n " "; for b in 00 33 66 99 cc ff; do
|
||||
* echo -n "{ 0x$r, 0x$g, 0x$b }, "; done; echo ""; done; done
|
||||
*/
|
||||
|
||||
static const rgb_triplet gif_clut[216] = {
|
||||
{ 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x33 }, { 0x00, 0x00, 0x66 }, { 0x00, 0x00, 0x99 }, { 0x00, 0x00, 0xcc }, { 0x00, 0x00, 0xff },
|
||||
{ 0x00, 0x33, 0x00 }, { 0x00, 0x33, 0x33 }, { 0x00, 0x33, 0x66 }, { 0x00, 0x33, 0x99 }, { 0x00, 0x33, 0xcc }, { 0x00, 0x33, 0xff },
|
||||
{ 0x00, 0x66, 0x00 }, { 0x00, 0x66, 0x33 }, { 0x00, 0x66, 0x66 }, { 0x00, 0x66, 0x99 }, { 0x00, 0x66, 0xcc }, { 0x00, 0x66, 0xff },
|
||||
{ 0x00, 0x99, 0x00 }, { 0x00, 0x99, 0x33 }, { 0x00, 0x99, 0x66 }, { 0x00, 0x99, 0x99 }, { 0x00, 0x99, 0xcc }, { 0x00, 0x99, 0xff },
|
||||
{ 0x00, 0xcc, 0x00 }, { 0x00, 0xcc, 0x33 }, { 0x00, 0xcc, 0x66 }, { 0x00, 0xcc, 0x99 }, { 0x00, 0xcc, 0xcc }, { 0x00, 0xcc, 0xff },
|
||||
{ 0x00, 0xff, 0x00 }, { 0x00, 0xff, 0x33 }, { 0x00, 0xff, 0x66 }, { 0x00, 0xff, 0x99 }, { 0x00, 0xff, 0xcc }, { 0x00, 0xff, 0xff },
|
||||
{ 0x33, 0x00, 0x00 }, { 0x33, 0x00, 0x33 }, { 0x33, 0x00, 0x66 }, { 0x33, 0x00, 0x99 }, { 0x33, 0x00, 0xcc }, { 0x33, 0x00, 0xff },
|
||||
{ 0x33, 0x33, 0x00 }, { 0x33, 0x33, 0x33 }, { 0x33, 0x33, 0x66 }, { 0x33, 0x33, 0x99 }, { 0x33, 0x33, 0xcc }, { 0x33, 0x33, 0xff },
|
||||
{ 0x33, 0x66, 0x00 }, { 0x33, 0x66, 0x33 }, { 0x33, 0x66, 0x66 }, { 0x33, 0x66, 0x99 }, { 0x33, 0x66, 0xcc }, { 0x33, 0x66, 0xff },
|
||||
{ 0x33, 0x99, 0x00 }, { 0x33, 0x99, 0x33 }, { 0x33, 0x99, 0x66 }, { 0x33, 0x99, 0x99 }, { 0x33, 0x99, 0xcc }, { 0x33, 0x99, 0xff },
|
||||
{ 0x33, 0xcc, 0x00 }, { 0x33, 0xcc, 0x33 }, { 0x33, 0xcc, 0x66 }, { 0x33, 0xcc, 0x99 }, { 0x33, 0xcc, 0xcc }, { 0x33, 0xcc, 0xff },
|
||||
{ 0x33, 0xff, 0x00 }, { 0x33, 0xff, 0x33 }, { 0x33, 0xff, 0x66 }, { 0x33, 0xff, 0x99 }, { 0x33, 0xff, 0xcc }, { 0x33, 0xff, 0xff },
|
||||
{ 0x66, 0x00, 0x00 }, { 0x66, 0x00, 0x33 }, { 0x66, 0x00, 0x66 }, { 0x66, 0x00, 0x99 }, { 0x66, 0x00, 0xcc }, { 0x66, 0x00, 0xff },
|
||||
{ 0x66, 0x33, 0x00 }, { 0x66, 0x33, 0x33 }, { 0x66, 0x33, 0x66 }, { 0x66, 0x33, 0x99 }, { 0x66, 0x33, 0xcc }, { 0x66, 0x33, 0xff },
|
||||
{ 0x66, 0x66, 0x00 }, { 0x66, 0x66, 0x33 }, { 0x66, 0x66, 0x66 }, { 0x66, 0x66, 0x99 }, { 0x66, 0x66, 0xcc }, { 0x66, 0x66, 0xff },
|
||||
{ 0x66, 0x99, 0x00 }, { 0x66, 0x99, 0x33 }, { 0x66, 0x99, 0x66 }, { 0x66, 0x99, 0x99 }, { 0x66, 0x99, 0xcc }, { 0x66, 0x99, 0xff },
|
||||
{ 0x66, 0xcc, 0x00 }, { 0x66, 0xcc, 0x33 }, { 0x66, 0xcc, 0x66 }, { 0x66, 0xcc, 0x99 }, { 0x66, 0xcc, 0xcc }, { 0x66, 0xcc, 0xff },
|
||||
{ 0x66, 0xff, 0x00 }, { 0x66, 0xff, 0x33 }, { 0x66, 0xff, 0x66 }, { 0x66, 0xff, 0x99 }, { 0x66, 0xff, 0xcc }, { 0x66, 0xff, 0xff },
|
||||
{ 0x99, 0x00, 0x00 }, { 0x99, 0x00, 0x33 }, { 0x99, 0x00, 0x66 }, { 0x99, 0x00, 0x99 }, { 0x99, 0x00, 0xcc }, { 0x99, 0x00, 0xff },
|
||||
{ 0x99, 0x33, 0x00 }, { 0x99, 0x33, 0x33 }, { 0x99, 0x33, 0x66 }, { 0x99, 0x33, 0x99 }, { 0x99, 0x33, 0xcc }, { 0x99, 0x33, 0xff },
|
||||
{ 0x99, 0x66, 0x00 }, { 0x99, 0x66, 0x33 }, { 0x99, 0x66, 0x66 }, { 0x99, 0x66, 0x99 }, { 0x99, 0x66, 0xcc }, { 0x99, 0x66, 0xff },
|
||||
{ 0x99, 0x99, 0x00 }, { 0x99, 0x99, 0x33 }, { 0x99, 0x99, 0x66 }, { 0x99, 0x99, 0x99 }, { 0x99, 0x99, 0xcc }, { 0x99, 0x99, 0xff },
|
||||
{ 0x99, 0xcc, 0x00 }, { 0x99, 0xcc, 0x33 }, { 0x99, 0xcc, 0x66 }, { 0x99, 0xcc, 0x99 }, { 0x99, 0xcc, 0xcc }, { 0x99, 0xcc, 0xff },
|
||||
{ 0x99, 0xff, 0x00 }, { 0x99, 0xff, 0x33 }, { 0x99, 0xff, 0x66 }, { 0x99, 0xff, 0x99 }, { 0x99, 0xff, 0xcc }, { 0x99, 0xff, 0xff },
|
||||
{ 0xcc, 0x00, 0x00 }, { 0xcc, 0x00, 0x33 }, { 0xcc, 0x00, 0x66 }, { 0xcc, 0x00, 0x99 }, { 0xcc, 0x00, 0xcc }, { 0xcc, 0x00, 0xff },
|
||||
{ 0xcc, 0x33, 0x00 }, { 0xcc, 0x33, 0x33 }, { 0xcc, 0x33, 0x66 }, { 0xcc, 0x33, 0x99 }, { 0xcc, 0x33, 0xcc }, { 0xcc, 0x33, 0xff },
|
||||
{ 0xcc, 0x66, 0x00 }, { 0xcc, 0x66, 0x33 }, { 0xcc, 0x66, 0x66 }, { 0xcc, 0x66, 0x99 }, { 0xcc, 0x66, 0xcc }, { 0xcc, 0x66, 0xff },
|
||||
{ 0xcc, 0x99, 0x00 }, { 0xcc, 0x99, 0x33 }, { 0xcc, 0x99, 0x66 }, { 0xcc, 0x99, 0x99 }, { 0xcc, 0x99, 0xcc }, { 0xcc, 0x99, 0xff },
|
||||
{ 0xcc, 0xcc, 0x00 }, { 0xcc, 0xcc, 0x33 }, { 0xcc, 0xcc, 0x66 }, { 0xcc, 0xcc, 0x99 }, { 0xcc, 0xcc, 0xcc }, { 0xcc, 0xcc, 0xff },
|
||||
{ 0xcc, 0xff, 0x00 }, { 0xcc, 0xff, 0x33 }, { 0xcc, 0xff, 0x66 }, { 0xcc, 0xff, 0x99 }, { 0xcc, 0xff, 0xcc }, { 0xcc, 0xff, 0xff },
|
||||
{ 0xff, 0x00, 0x00 }, { 0xff, 0x00, 0x33 }, { 0xff, 0x00, 0x66 }, { 0xff, 0x00, 0x99 }, { 0xff, 0x00, 0xcc }, { 0xff, 0x00, 0xff },
|
||||
{ 0xff, 0x33, 0x00 }, { 0xff, 0x33, 0x33 }, { 0xff, 0x33, 0x66 }, { 0xff, 0x33, 0x99 }, { 0xff, 0x33, 0xcc }, { 0xff, 0x33, 0xff },
|
||||
{ 0xff, 0x66, 0x00 }, { 0xff, 0x66, 0x33 }, { 0xff, 0x66, 0x66 }, { 0xff, 0x66, 0x99 }, { 0xff, 0x66, 0xcc }, { 0xff, 0x66, 0xff },
|
||||
{ 0xff, 0x99, 0x00 }, { 0xff, 0x99, 0x33 }, { 0xff, 0x99, 0x66 }, { 0xff, 0x99, 0x99 }, { 0xff, 0x99, 0xcc }, { 0xff, 0x99, 0xff },
|
||||
{ 0xff, 0xcc, 0x00 }, { 0xff, 0xcc, 0x33 }, { 0xff, 0xcc, 0x66 }, { 0xff, 0xcc, 0x99 }, { 0xff, 0xcc, 0xcc }, { 0xff, 0xcc, 0xff },
|
||||
{ 0xff, 0xff, 0x00 }, { 0xff, 0xff, 0x33 }, { 0xff, 0xff, 0x66 }, { 0xff, 0xff, 0x99 }, { 0xff, 0xff, 0xcc }, { 0xff, 0xff, 0xff },
|
||||
};
|
||||
|
||||
/* The GIF format uses reversed order for bitstreams... */
|
||||
/* at least they don't use PDP_ENDIAN :) */
|
||||
/* so we 'extend' PutBitContext. hmmm, OOP :) */
|
||||
/* seems this thing changed slightly since I wrote it... */
|
||||
|
||||
#ifdef ALT_BITSTREAM_WRITER
|
||||
# error no ALT_BITSTREAM_WRITER support for now
|
||||
#endif
|
||||
|
||||
static void gif_put_bits_rev(PutBitContext *s, int n, unsigned int value)
|
||||
{
|
||||
unsigned int bit_buf;
|
||||
int bit_cnt;
|
||||
|
||||
#ifdef STATS
|
||||
st_out_bit_counts[st_current_index] += n;
|
||||
#endif
|
||||
// printf("put_bits=%d %x\n", n, value);
|
||||
assert(n == 32 || value < (1U << n));
|
||||
|
||||
bit_buf = s->bit_buf;
|
||||
bit_cnt = 32 - s->bit_left; /* XXX:lazyness... was = s->bit_cnt; */
|
||||
|
||||
// printf("n=%d value=%x cnt=%d buf=%x\n", n, value, bit_cnt, bit_buf);
|
||||
/* XXX: optimize */
|
||||
if (n < (32-bit_cnt)) {
|
||||
bit_buf |= value << (bit_cnt);
|
||||
bit_cnt+=n;
|
||||
} else {
|
||||
bit_buf |= value << (bit_cnt);
|
||||
|
||||
*s->buf_ptr = bit_buf & 0xff;
|
||||
s->buf_ptr[1] = (bit_buf >> 8) & 0xff;
|
||||
s->buf_ptr[2] = (bit_buf >> 16) & 0xff;
|
||||
s->buf_ptr[3] = (bit_buf >> 24) & 0xff;
|
||||
|
||||
//printf("bitbuf = %08x\n", bit_buf);
|
||||
s->buf_ptr+=4;
|
||||
if (s->buf_ptr >= s->buf_end)
|
||||
puts("bit buffer overflow !!"); // should never happen ! who got rid of the callback ???
|
||||
// flush_buffer_rev(s);
|
||||
bit_cnt=bit_cnt + n - 32;
|
||||
if (bit_cnt == 0) {
|
||||
bit_buf = 0;
|
||||
} else {
|
||||
bit_buf = value >> (n - bit_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
s->bit_buf = bit_buf;
|
||||
s->bit_left = 32 - bit_cnt;
|
||||
}
|
||||
|
||||
/* pad the end of the output stream with zeros */
|
||||
static void gif_flush_put_bits_rev(PutBitContext *s)
|
||||
{
|
||||
while (s->bit_left < 32) {
|
||||
/* XXX: should test end of buffer */
|
||||
*s->buf_ptr++=s->bit_buf & 0xff;
|
||||
s->bit_buf>>=8;
|
||||
s->bit_left+=8;
|
||||
}
|
||||
// flush_buffer_rev(s);
|
||||
s->bit_left=32;
|
||||
s->bit_buf=0;
|
||||
}
|
||||
|
||||
/* !RevPutBitContext */
|
||||
|
||||
typedef struct {
|
||||
UINT8 buffer[100]; /* data chunks */
|
||||
INT64 time, file_time;
|
||||
} GIFContext;
|
||||
|
||||
static int gif_write_header(AVFormatContext *s)
|
||||
{
|
||||
GIFContext *gif = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
AVCodecContext *enc, *video_enc;
|
||||
int i, width, height, rate;
|
||||
|
||||
/* XXX: do we reject audio streams or just ignore them ?
|
||||
if(s->nb_streams > 1)
|
||||
return -1;
|
||||
*/
|
||||
gif->time = 0;
|
||||
gif->file_time = 0;
|
||||
|
||||
video_enc = NULL;
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
enc = &s->streams[i]->codec;
|
||||
if (enc->codec_type != CODEC_TYPE_AUDIO)
|
||||
video_enc = enc;
|
||||
}
|
||||
|
||||
if (!video_enc) {
|
||||
av_free(gif);
|
||||
return -1;
|
||||
} else {
|
||||
width = video_enc->width;
|
||||
height = video_enc->height;
|
||||
rate = video_enc->frame_rate;
|
||||
}
|
||||
|
||||
/* XXX: is it allowed ? seems to work so far... */
|
||||
video_enc->pix_fmt = PIX_FMT_RGB24;
|
||||
|
||||
/* GIF header */
|
||||
|
||||
put_tag(pb, "GIF");
|
||||
put_tag(pb, "89a");
|
||||
put_le16(pb, width);
|
||||
put_le16(pb, height);
|
||||
|
||||
put_byte(pb, 0xf7); /* flags: global clut, 256 entries */
|
||||
put_byte(pb, 0x1f); /* background color index */
|
||||
put_byte(pb, 0); /* aspect ratio */
|
||||
|
||||
/* the global palette */
|
||||
|
||||
put_buffer(pb, (unsigned char *)gif_clut, 216*3);
|
||||
for(i=0;i<((256-216)*3);i++)
|
||||
put_byte(pb, 0);
|
||||
|
||||
/* application extension header */
|
||||
/* XXX: not really sure what to put in here... */
|
||||
#ifdef GIF_ADD_APP_HEADER
|
||||
put_byte(pb, 0x21);
|
||||
put_byte(pb, 0xff);
|
||||
put_byte(pb, 0x0b);
|
||||
put_tag(pb, "NETSCAPE2.0");
|
||||
put_byte(pb, 0x03);
|
||||
put_byte(pb, 0x01);
|
||||
put_byte(pb, 0x00);
|
||||
put_byte(pb, 0x00);
|
||||
#endif
|
||||
|
||||
put_flush_packet(&s->pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this is maybe slow, but allows for extensions */
|
||||
static inline unsigned char gif_clut_index(rgb_triplet *clut, UINT8 r, UINT8 g, UINT8 b)
|
||||
{
|
||||
return ((((r)/47)%6)*6*6+(((g)/47)%6)*6+(((b)/47)%6));
|
||||
}
|
||||
|
||||
/* chunk writer callback */
|
||||
/* !!! XXX:deprecated
|
||||
static void gif_put_chunk(void *pbctx, UINT8 *buffer, int count)
|
||||
{
|
||||
ByteIOContext *pb = (ByteIOContext *)pbctx;
|
||||
put_byte(pb, (UINT8)count);
|
||||
put_buffer(pb, buffer, count);
|
||||
}
|
||||
*/
|
||||
|
||||
static int gif_write_video(AVFormatContext *s,
|
||||
AVCodecContext *enc, UINT8 *buf, int size)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
GIFContext *gif = s->priv_data;
|
||||
int i, left, jiffies;
|
||||
INT64 delay;
|
||||
PutBitContext p;
|
||||
UINT8 buffer[200]; /* 100 * 9 / 8 = 113 */
|
||||
|
||||
|
||||
/* graphic control extension block */
|
||||
put_byte(pb, 0x21);
|
||||
put_byte(pb, 0xf9);
|
||||
put_byte(pb, 0x04); /* block size */
|
||||
put_byte(pb, 0x04); /* flags */
|
||||
|
||||
/* 1 jiffy is 1/70 s */
|
||||
/* the delay_time field indicates the number of jiffies - 1 */
|
||||
delay = gif->file_time - gif->time;
|
||||
|
||||
/* 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*FRAME_RATE_BASE/enc->frame_rate) - 1;
|
||||
|
||||
put_le16(pb, jiffies);
|
||||
|
||||
put_byte(pb, 0x1f); /* transparent color index */
|
||||
put_byte(pb, 0x00);
|
||||
|
||||
/* image block */
|
||||
|
||||
put_byte(pb, 0x2c);
|
||||
put_le16(pb, 0);
|
||||
put_le16(pb, 0);
|
||||
put_le16(pb, enc->width);
|
||||
put_le16(pb, enc->height);
|
||||
put_byte(pb, 0x00); /* flags */
|
||||
/* no local clut */
|
||||
|
||||
put_byte(pb, 0x08);
|
||||
|
||||
left=size/3;
|
||||
|
||||
init_put_bits(&p, buffer, 130, NULL, NULL);
|
||||
|
||||
/*
|
||||
* 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 !)
|
||||
*/
|
||||
|
||||
while(left>0) {
|
||||
|
||||
gif_put_bits_rev(&p, 9, 0x0100); /* clear code */
|
||||
|
||||
for(i=0;i<GIF_CHUNKS;i++) {
|
||||
gif_put_bits_rev(&p, 9, gif_clut_index(NULL, *buf, buf[1], buf[2]));
|
||||
buf+=3;
|
||||
}
|
||||
|
||||
if(left<=GIF_CHUNKS) {
|
||||
gif_put_bits_rev(&p, 9, 0x101); /* end of stream */
|
||||
gif_flush_put_bits_rev(&p);
|
||||
}
|
||||
if(pbBufPtr(&p) - p.buf > 0) {
|
||||
put_byte(pb, pbBufPtr(&p) - p.buf); /* byte count of the packet */
|
||||
put_buffer(pb, p.buf, pbBufPtr(&p) - p.buf); /* the actual buffer */
|
||||
p.data_out_size += pbBufPtr(&p) - p.buf;
|
||||
p.buf_ptr = p.buf; /* dequeue the bytes off the bitstream */
|
||||
}
|
||||
if(left<=GIF_CHUNKS) {
|
||||
put_byte(pb, 0x00); /* end of image block */
|
||||
}
|
||||
|
||||
left-=GIF_CHUNKS;
|
||||
}
|
||||
|
||||
put_flush_packet(&s->pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gif_write_packet(AVFormatContext *s, int stream_index,
|
||||
UINT8 *buf, int size, int force_pts)
|
||||
{
|
||||
AVCodecContext *codec = &s->streams[stream_index]->codec;
|
||||
if (codec->codec_type == CODEC_TYPE_AUDIO)
|
||||
return 0; /* just ignore audio */
|
||||
else
|
||||
return gif_write_video(s, codec, buf, size);
|
||||
}
|
||||
|
||||
static int gif_write_trailer(AVFormatContext *s)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
|
||||
put_byte(pb, 0x3b);
|
||||
put_flush_packet(&s->pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVOutputFormat gif_oformat = {
|
||||
"gif",
|
||||
"GIF Animation",
|
||||
"image/gif",
|
||||
"gif",
|
||||
sizeof(GIFContext),
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_RAWVIDEO,
|
||||
gif_write_header,
|
||||
gif_write_packet,
|
||||
gif_write_trailer,
|
||||
};
|
||||
|
||||
int gif_init(void)
|
||||
{
|
||||
av_register_output_format(&gif_oformat);
|
||||
return 0;
|
||||
}
|
||||
829
libavformat/grab.c
Normal file
829
libavformat/grab.c
Normal file
@@ -0,0 +1,829 @@
|
||||
/*
|
||||
* Linux video grab interface
|
||||
* Copyright (c) 2000,2001 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
#include <linux/videodev.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
int frame_format; /* see VIDEO_PALETTE_xxx */
|
||||
int use_mmap;
|
||||
int width, height;
|
||||
int frame_rate;
|
||||
INT64 time_frame;
|
||||
int frame_size;
|
||||
struct video_capability video_cap;
|
||||
struct video_audio audio_saved;
|
||||
UINT8 *video_buf;
|
||||
struct video_mbuf gb_buffers;
|
||||
struct video_mmap gb_buf;
|
||||
int gb_frame;
|
||||
|
||||
/* ATI All In Wonder specific stuff */
|
||||
/* XXX: remove and merge in libavcodec/imgconvert.c */
|
||||
int aiw_enabled;
|
||||
int deint;
|
||||
int halfw;
|
||||
UINT8 *src_mem;
|
||||
UINT8 *lum_m4_mem;
|
||||
} VideoData;
|
||||
|
||||
static int aiw_init(VideoData *s);
|
||||
static int aiw_read_picture(VideoData *s, uint8_t *data);
|
||||
static int aiw_close(VideoData *s);
|
||||
|
||||
const char *v4l_device = "/dev/video";
|
||||
|
||||
static int grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
|
||||
{
|
||||
VideoData *s = s1->priv_data;
|
||||
AVStream *st;
|
||||
int width, height;
|
||||
int video_fd, frame_size;
|
||||
int ret, frame_rate;
|
||||
int desired_palette;
|
||||
struct video_audio audio;
|
||||
|
||||
if (!ap || ap->width <= 0 || ap->height <= 0 || ap->frame_rate <= 0)
|
||||
return -1;
|
||||
|
||||
width = ap->width;
|
||||
height = ap->height;
|
||||
frame_rate = ap->frame_rate;
|
||||
|
||||
st = av_new_stream(s1, 0);
|
||||
if (!st)
|
||||
return -ENOMEM;
|
||||
|
||||
s->width = width;
|
||||
s->height = height;
|
||||
s->frame_rate = frame_rate;
|
||||
|
||||
video_fd = open(v4l_device, O_RDWR);
|
||||
if (video_fd < 0) {
|
||||
perror(v4l_device);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ioctl(video_fd,VIDIOCGCAP, &s->video_cap) < 0) {
|
||||
perror("VIDIOCGCAP");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(s->video_cap.type & VID_TYPE_CAPTURE)) {
|
||||
fprintf(stderr, "Fatal: grab device does not handle capture\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
desired_palette = -1;
|
||||
if (st->codec.pix_fmt == PIX_FMT_YUV420P) {
|
||||
desired_palette = VIDEO_PALETTE_YUV420P;
|
||||
} else if (st->codec.pix_fmt == PIX_FMT_YUV422) {
|
||||
desired_palette = VIDEO_PALETTE_YUV422;
|
||||
} else if (st->codec.pix_fmt == PIX_FMT_BGR24) {
|
||||
desired_palette = VIDEO_PALETTE_RGB24;
|
||||
}
|
||||
|
||||
/* unmute audio */
|
||||
audio.audio = 0;
|
||||
ioctl(video_fd, VIDIOCGAUDIO, &audio);
|
||||
memcpy(&s->audio_saved, &audio, sizeof(audio));
|
||||
audio.flags &= ~VIDEO_AUDIO_MUTE;
|
||||
ioctl(video_fd, VIDIOCSAUDIO, &audio);
|
||||
|
||||
ret = ioctl(video_fd,VIDIOCGMBUF,&s->gb_buffers);
|
||||
if (ret < 0) {
|
||||
/* try to use read based access */
|
||||
struct video_window win;
|
||||
struct video_picture pict;
|
||||
int val;
|
||||
|
||||
win.x = 0;
|
||||
win.y = 0;
|
||||
win.width = width;
|
||||
win.height = height;
|
||||
win.chromakey = -1;
|
||||
win.flags = 0;
|
||||
|
||||
ioctl(video_fd, VIDIOCSWIN, &win);
|
||||
|
||||
ioctl(video_fd, VIDIOCGPICT, &pict);
|
||||
#if 0
|
||||
printf("v4l: colour=%d hue=%d brightness=%d constrast=%d whiteness=%d\n",
|
||||
pict.colour,
|
||||
pict.hue,
|
||||
pict.brightness,
|
||||
pict.contrast,
|
||||
pict.whiteness);
|
||||
#endif
|
||||
/* try to choose a suitable video format */
|
||||
pict.palette = desired_palette;
|
||||
if (desired_palette == -1 || (ret = ioctl(video_fd, VIDIOCSPICT, &pict)) < 0) {
|
||||
pict.palette=VIDEO_PALETTE_YUV420P;
|
||||
ret = ioctl(video_fd, VIDIOCSPICT, &pict);
|
||||
if (ret < 0) {
|
||||
pict.palette=VIDEO_PALETTE_YUV422;
|
||||
ret = ioctl(video_fd, VIDIOCSPICT, &pict);
|
||||
if (ret < 0) {
|
||||
pict.palette=VIDEO_PALETTE_RGB24;
|
||||
ret = ioctl(video_fd, VIDIOCSPICT, &pict);
|
||||
if (ret < 0)
|
||||
goto fail1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s->frame_format = pict.palette;
|
||||
|
||||
val = 1;
|
||||
ioctl(video_fd, VIDIOCCAPTURE, &val);
|
||||
|
||||
s->time_frame = av_gettime();
|
||||
s->use_mmap = 0;
|
||||
|
||||
/* ATI All In Wonder automatic activation */
|
||||
if (!strcmp(s->video_cap.name, "Km")) {
|
||||
if (aiw_init(s) < 0)
|
||||
goto fail;
|
||||
s->aiw_enabled = 1;
|
||||
/* force 420P format because convertion from YUV422 to YUV420P
|
||||
is done in this driver (ugly) */
|
||||
s->frame_format = VIDEO_PALETTE_YUV420P;
|
||||
}
|
||||
} else {
|
||||
s->video_buf = mmap(0,s->gb_buffers.size,PROT_READ|PROT_WRITE,MAP_SHARED,video_fd,0);
|
||||
if ((unsigned char*)-1 == s->video_buf) {
|
||||
perror("mmap");
|
||||
goto fail;
|
||||
}
|
||||
s->gb_frame = 0;
|
||||
s->time_frame = av_gettime();
|
||||
|
||||
/* start to grab the first frame */
|
||||
s->gb_buf.frame = s->gb_frame % s->gb_buffers.frames;
|
||||
s->gb_buf.height = height;
|
||||
s->gb_buf.width = width;
|
||||
s->gb_buf.format = desired_palette;
|
||||
|
||||
if (desired_palette == -1 || (ret = ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf)) < 0) {
|
||||
s->gb_buf.format = VIDEO_PALETTE_YUV420P;
|
||||
|
||||
ret = ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf);
|
||||
if (ret < 0 && errno != EAGAIN) {
|
||||
/* try YUV422 */
|
||||
s->gb_buf.format = VIDEO_PALETTE_YUV422;
|
||||
|
||||
ret = ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf);
|
||||
if (ret < 0 && errno != EAGAIN) {
|
||||
/* try RGB24 */
|
||||
s->gb_buf.format = VIDEO_PALETTE_RGB24;
|
||||
ret = ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret < 0) {
|
||||
if (errno != EAGAIN) {
|
||||
fail1:
|
||||
fprintf(stderr, "Fatal: grab device does not support suitable format\n");
|
||||
} else {
|
||||
fprintf(stderr,"Fatal: grab device does not receive any video signal\n");
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
s->frame_format = s->gb_buf.format;
|
||||
s->use_mmap = 1;
|
||||
}
|
||||
|
||||
switch(s->frame_format) {
|
||||
case VIDEO_PALETTE_YUV420P:
|
||||
frame_size = (width * height * 3) / 2;
|
||||
st->codec.pix_fmt = PIX_FMT_YUV420P;
|
||||
break;
|
||||
case VIDEO_PALETTE_YUV422:
|
||||
frame_size = width * height * 2;
|
||||
st->codec.pix_fmt = PIX_FMT_YUV422;
|
||||
break;
|
||||
case VIDEO_PALETTE_RGB24:
|
||||
frame_size = width * height * 3;
|
||||
st->codec.pix_fmt = PIX_FMT_BGR24; /* NOTE: v4l uses BGR24, not RGB24 ! */
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
s->fd = video_fd;
|
||||
s->frame_size = frame_size;
|
||||
|
||||
st->codec.codec_type = CODEC_TYPE_VIDEO;
|
||||
st->codec.codec_id = CODEC_ID_RAWVIDEO;
|
||||
st->codec.width = width;
|
||||
st->codec.height = height;
|
||||
st->codec.frame_rate = frame_rate;
|
||||
|
||||
av_set_pts_info(s1, 48, 1, 1000000); /* 48 bits pts in us */
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
if (video_fd >= 0)
|
||||
close(video_fd);
|
||||
av_free(st);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int v4l_mm_read_picture(VideoData *s, UINT8 *buf)
|
||||
{
|
||||
UINT8 *ptr;
|
||||
|
||||
/* Setup to capture the next frame */
|
||||
s->gb_buf.frame = (s->gb_frame + 1) % s->gb_buffers.frames;
|
||||
if (ioctl(s->fd, VIDIOCMCAPTURE, &s->gb_buf) < 0) {
|
||||
if (errno == EAGAIN)
|
||||
fprintf(stderr,"Cannot Sync\n");
|
||||
else
|
||||
perror("VIDIOCMCAPTURE");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
while (ioctl(s->fd, VIDIOCSYNC, &s->gb_frame) < 0 &&
|
||||
(errno == EAGAIN || errno == EINTR));
|
||||
|
||||
ptr = s->video_buf + s->gb_buffers.offsets[s->gb_frame];
|
||||
memcpy(buf, ptr, s->frame_size);
|
||||
|
||||
/* This is now the grabbing frame */
|
||||
s->gb_frame = s->gb_buf.frame;
|
||||
|
||||
return s->frame_size;
|
||||
}
|
||||
|
||||
static int grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
|
||||
{
|
||||
VideoData *s = s1->priv_data;
|
||||
INT64 curtime, delay;
|
||||
struct timespec ts;
|
||||
INT64 per_frame = (INT64_C(1000000) * FRAME_RATE_BASE) / s->frame_rate;
|
||||
|
||||
/* Calculate the time of the next frame */
|
||||
s->time_frame += per_frame;
|
||||
|
||||
/* wait based on the frame rate */
|
||||
for(;;) {
|
||||
curtime = av_gettime();
|
||||
delay = s->time_frame - curtime;
|
||||
if (delay <= 0) {
|
||||
if (delay < -per_frame) {
|
||||
/* printf("grabbing is %d frames late (dropping)\n", (int) -(delay / 16666)); */
|
||||
s->time_frame += per_frame;
|
||||
}
|
||||
break;
|
||||
}
|
||||
ts.tv_sec = delay / 1000000;
|
||||
ts.tv_nsec = (delay % 1000000) * 1000;
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
if (av_new_packet(pkt, s->frame_size) < 0)
|
||||
return -EIO;
|
||||
|
||||
pkt->pts = curtime & ((1LL << 48) - 1);
|
||||
|
||||
/* read one frame */
|
||||
if (s->aiw_enabled) {
|
||||
return aiw_read_picture(s, pkt->data);
|
||||
} else if (s->use_mmap) {
|
||||
return v4l_mm_read_picture(s, pkt->data);
|
||||
} else {
|
||||
if (read(s->fd, pkt->data, pkt->size) != pkt->size)
|
||||
return -EIO;
|
||||
return s->frame_size;
|
||||
}
|
||||
}
|
||||
|
||||
static int grab_read_close(AVFormatContext *s1)
|
||||
{
|
||||
VideoData *s = s1->priv_data;
|
||||
|
||||
if (s->aiw_enabled)
|
||||
aiw_close(s);
|
||||
|
||||
if (s->use_mmap)
|
||||
munmap(s->video_buf, s->gb_buffers.size);
|
||||
|
||||
/* mute audio. we must force it because the BTTV driver does not
|
||||
return its state correctly */
|
||||
s->audio_saved.flags |= VIDEO_AUDIO_MUTE;
|
||||
ioctl(s->fd, VIDIOCSAUDIO, &s->audio_saved);
|
||||
|
||||
close(s->fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVInputFormat video_grab_device_format = {
|
||||
"video_grab_device",
|
||||
"video grab",
|
||||
sizeof(VideoData),
|
||||
NULL,
|
||||
grab_read_header,
|
||||
grab_read_packet,
|
||||
grab_read_close,
|
||||
.flags = AVFMT_NOFILE,
|
||||
};
|
||||
|
||||
/* All in Wonder specific stuff */
|
||||
/* XXX: remove and merge in libavcodec/imgconvert.c */
|
||||
|
||||
static int aiw_init(VideoData *s)
|
||||
{
|
||||
int width, height;
|
||||
|
||||
width = s->width;
|
||||
height = s->height;
|
||||
|
||||
if ((width == s->video_cap.maxwidth && height == s->video_cap.maxheight) ||
|
||||
(width == s->video_cap.maxwidth && height == s->video_cap.maxheight*2) ||
|
||||
(width == s->video_cap.maxwidth/2 && height == s->video_cap.maxheight)) {
|
||||
|
||||
s->deint=0;
|
||||
s->halfw=0;
|
||||
if (height == s->video_cap.maxheight*2) s->deint=1;
|
||||
if (width == s->video_cap.maxwidth/2) s->halfw=1;
|
||||
} else {
|
||||
fprintf(stderr,"\nIncorrect Grab Size Supplied - Supported Sizes Are:\n");
|
||||
fprintf(stderr," %dx%d %dx%d %dx%d\n\n",
|
||||
s->video_cap.maxwidth,s->video_cap.maxheight,
|
||||
s->video_cap.maxwidth,s->video_cap.maxheight*2,
|
||||
s->video_cap.maxwidth/2,s->video_cap.maxheight);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (s->halfw == 0) {
|
||||
s->src_mem = av_malloc(s->width*2);
|
||||
} else {
|
||||
s->src_mem = av_malloc(s->width*4);
|
||||
}
|
||||
if (!s->src_mem) goto fail;
|
||||
|
||||
s->lum_m4_mem = av_malloc(s->width);
|
||||
if (!s->lum_m4_mem)
|
||||
goto fail;
|
||||
return 0;
|
||||
fail:
|
||||
av_freep(&s->src_mem);
|
||||
av_freep(&s->lum_m4_mem);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MMX
|
||||
#include "../libavcodec/i386/mmx.h"
|
||||
|
||||
#define LINE_WITH_UV \
|
||||
movq_m2r(ptr[0],mm0); \
|
||||
movq_m2r(ptr[8],mm1); \
|
||||
movq_r2r(mm0, mm4); \
|
||||
punpcklbw_r2r(mm1,mm0); \
|
||||
punpckhbw_r2r(mm1,mm4); \
|
||||
movq_r2r(mm0,mm5); \
|
||||
punpcklbw_r2r(mm4,mm0); \
|
||||
punpckhbw_r2r(mm4,mm5); \
|
||||
movq_r2r(mm0,mm1); \
|
||||
punpcklbw_r2r(mm5,mm1); \
|
||||
movq_r2m(mm1,lum[0]); \
|
||||
movq_m2r(ptr[16],mm2); \
|
||||
movq_m2r(ptr[24],mm1); \
|
||||
movq_r2r(mm2,mm4); \
|
||||
punpcklbw_r2r(mm1,mm2); \
|
||||
punpckhbw_r2r(mm1,mm4); \
|
||||
movq_r2r(mm2,mm3); \
|
||||
punpcklbw_r2r(mm4,mm2); \
|
||||
punpckhbw_r2r(mm4,mm3); \
|
||||
movq_r2r(mm2,mm1); \
|
||||
punpcklbw_r2r(mm3,mm1); \
|
||||
movq_r2m(mm1,lum[8]); \
|
||||
punpckhdq_r2r(mm2,mm0); \
|
||||
punpckhdq_r2r(mm3,mm5); \
|
||||
movq_r2m(mm0,cb[0]); \
|
||||
movq_r2m(mm5,cr[0]);
|
||||
|
||||
#define LINE_NO_UV \
|
||||
movq_m2r(ptr[0],mm0);\
|
||||
movq_m2r(ptr[8],mm1);\
|
||||
movq_r2r(mm0, mm4);\
|
||||
punpcklbw_r2r(mm1,mm0); \
|
||||
punpckhbw_r2r(mm1,mm4);\
|
||||
movq_r2r(mm0,mm5);\
|
||||
punpcklbw_r2r(mm4,mm0);\
|
||||
punpckhbw_r2r(mm4,mm5);\
|
||||
movq_r2r(mm0,mm1);\
|
||||
punpcklbw_r2r(mm5,mm1);\
|
||||
movq_r2m(mm1,lum[0]);\
|
||||
movq_m2r(ptr[16],mm2);\
|
||||
movq_m2r(ptr[24],mm1);\
|
||||
movq_r2r(mm2,mm4);\
|
||||
punpcklbw_r2r(mm1,mm2);\
|
||||
punpckhbw_r2r(mm1,mm4);\
|
||||
movq_r2r(mm2,mm3);\
|
||||
punpcklbw_r2r(mm4,mm2);\
|
||||
punpckhbw_r2r(mm4,mm3);\
|
||||
movq_r2r(mm2,mm1);\
|
||||
punpcklbw_r2r(mm3,mm1);\
|
||||
movq_r2m(mm1,lum[8]);
|
||||
|
||||
#define LINE_WITHUV_AVG \
|
||||
movq_m2r(ptr[0], mm0);\
|
||||
movq_m2r(ptr[8], mm1);\
|
||||
movq_r2r(mm0, mm4);\
|
||||
punpcklbw_r2r(mm1,mm0);\
|
||||
punpckhbw_r2r(mm1,mm4);\
|
||||
movq_r2r(mm0,mm5);\
|
||||
punpcklbw_r2r(mm4,mm0);\
|
||||
punpckhbw_r2r(mm4,mm5);\
|
||||
movq_r2r(mm0,mm1);\
|
||||
movq_r2r(mm5,mm2);\
|
||||
punpcklbw_r2r(mm7,mm1);\
|
||||
punpcklbw_r2r(mm7,mm2);\
|
||||
paddw_r2r(mm6,mm1);\
|
||||
paddw_r2r(mm2,mm1);\
|
||||
psraw_i2r(1,mm1);\
|
||||
packuswb_r2r(mm7,mm1);\
|
||||
movd_r2m(mm1,lum[0]);\
|
||||
movq_m2r(ptr[16],mm2);\
|
||||
movq_m2r(ptr[24],mm1);\
|
||||
movq_r2r(mm2,mm4);\
|
||||
punpcklbw_r2r(mm1,mm2);\
|
||||
punpckhbw_r2r(mm1,mm4);\
|
||||
movq_r2r(mm2,mm3);\
|
||||
punpcklbw_r2r(mm4,mm2);\
|
||||
punpckhbw_r2r(mm4,mm3);\
|
||||
movq_r2r(mm2,mm1);\
|
||||
movq_r2r(mm3,mm4);\
|
||||
punpcklbw_r2r(mm7,mm1);\
|
||||
punpcklbw_r2r(mm7,mm4);\
|
||||
paddw_r2r(mm6,mm1);\
|
||||
paddw_r2r(mm4,mm1);\
|
||||
psraw_i2r(1,mm1);\
|
||||
packuswb_r2r(mm7,mm1);\
|
||||
movd_r2m(mm1,lum[4]);\
|
||||
punpckhbw_r2r(mm7,mm0);\
|
||||
punpckhbw_r2r(mm7,mm2);\
|
||||
paddw_r2r(mm6,mm0);\
|
||||
paddw_r2r(mm2,mm0);\
|
||||
psraw_i2r(1,mm0);\
|
||||
packuswb_r2r(mm7,mm0);\
|
||||
punpckhbw_r2r(mm7,mm5);\
|
||||
punpckhbw_r2r(mm7,mm3);\
|
||||
paddw_r2r(mm6,mm5);\
|
||||
paddw_r2r(mm3,mm5);\
|
||||
psraw_i2r(1,mm5);\
|
||||
packuswb_r2r(mm7,mm5);\
|
||||
movd_r2m(mm0,cb[0]);\
|
||||
movd_r2m(mm5,cr[0]);
|
||||
|
||||
#define LINE_NOUV_AVG \
|
||||
movq_m2r(ptr[0],mm0);\
|
||||
movq_m2r(ptr[8],mm1);\
|
||||
pand_r2r(mm5,mm0);\
|
||||
pand_r2r(mm5,mm1);\
|
||||
pmaddwd_r2r(mm6,mm0);\
|
||||
pmaddwd_r2r(mm6,mm1);\
|
||||
packssdw_r2r(mm1,mm0);\
|
||||
paddw_r2r(mm6,mm0);\
|
||||
psraw_i2r(1,mm0);\
|
||||
movq_m2r(ptr[16],mm2);\
|
||||
movq_m2r(ptr[24],mm3);\
|
||||
pand_r2r(mm5,mm2);\
|
||||
pand_r2r(mm5,mm3);\
|
||||
pmaddwd_r2r(mm6,mm2);\
|
||||
pmaddwd_r2r(mm6,mm3);\
|
||||
packssdw_r2r(mm3,mm2);\
|
||||
paddw_r2r(mm6,mm2);\
|
||||
psraw_i2r(1,mm2);\
|
||||
packuswb_r2r(mm2,mm0);\
|
||||
movq_r2m(mm0,lum[0]);
|
||||
|
||||
#define DEINT_LINE_LUM(ptroff) \
|
||||
movd_m2r(lum_m4[(ptroff)],mm0);\
|
||||
movd_m2r(lum_m3[(ptroff)],mm1);\
|
||||
movd_m2r(lum_m2[(ptroff)],mm2);\
|
||||
movd_m2r(lum_m1[(ptroff)],mm3);\
|
||||
movd_m2r(lum[(ptroff)],mm4);\
|
||||
punpcklbw_r2r(mm7,mm0);\
|
||||
movd_r2m(mm2,lum_m4[(ptroff)]);\
|
||||
punpcklbw_r2r(mm7,mm1);\
|
||||
punpcklbw_r2r(mm7,mm2);\
|
||||
punpcklbw_r2r(mm7,mm3);\
|
||||
punpcklbw_r2r(mm7,mm4);\
|
||||
psllw_i2r(2,mm1);\
|
||||
psllw_i2r(1,mm2);\
|
||||
paddw_r2r(mm6,mm1);\
|
||||
psllw_i2r(2,mm3);\
|
||||
paddw_r2r(mm2,mm1);\
|
||||
paddw_r2r(mm4,mm0);\
|
||||
paddw_r2r(mm3,mm1);\
|
||||
psubusw_r2r(mm0,mm1);\
|
||||
psrlw_i2r(3,mm1);\
|
||||
packuswb_r2r(mm7,mm1);\
|
||||
movd_r2m(mm1,lum_m2[(ptroff)]);
|
||||
|
||||
#else
|
||||
#include "../libavcodec/dsputil.h"
|
||||
|
||||
#define LINE_WITH_UV \
|
||||
lum[0]=ptr[0];lum[1]=ptr[2];lum[2]=ptr[4];lum[3]=ptr[6];\
|
||||
cb[0]=ptr[1];cb[1]=ptr[5];\
|
||||
cr[0]=ptr[3];cr[1]=ptr[7];\
|
||||
lum[4]=ptr[8];lum[5]=ptr[10];lum[6]=ptr[12];lum[7]=ptr[14];\
|
||||
cb[2]=ptr[9];cb[3]=ptr[13];\
|
||||
cr[2]=ptr[11];cr[3]=ptr[15];\
|
||||
lum[8]=ptr[16];lum[9]=ptr[18];lum[10]=ptr[20];lum[11]=ptr[22];\
|
||||
cb[4]=ptr[17];cb[5]=ptr[21];\
|
||||
cr[4]=ptr[19];cr[5]=ptr[23];\
|
||||
lum[12]=ptr[24];lum[13]=ptr[26];lum[14]=ptr[28];lum[15]=ptr[30];\
|
||||
cb[6]=ptr[25];cb[7]=ptr[29];\
|
||||
cr[6]=ptr[27];cr[7]=ptr[31];
|
||||
|
||||
#define LINE_NO_UV \
|
||||
lum[0]=ptr[0];lum[1]=ptr[2];lum[2]=ptr[4];lum[3]=ptr[6];\
|
||||
lum[4]=ptr[8];lum[5]=ptr[10];lum[6]=ptr[12];lum[7]=ptr[14];\
|
||||
lum[8]=ptr[16];lum[9]=ptr[18];lum[10]=ptr[20];lum[11]=ptr[22];\
|
||||
lum[12]=ptr[24];lum[13]=ptr[26];lum[14]=ptr[28];lum[15]=ptr[30];
|
||||
|
||||
#define LINE_WITHUV_AVG \
|
||||
sum=(ptr[0]+ptr[2]+1) >> 1;lum[0]=sum; \
|
||||
sum=(ptr[4]+ptr[6]+1) >> 1;lum[1]=sum; \
|
||||
sum=(ptr[1]+ptr[5]+1) >> 1;cb[0]=sum; \
|
||||
sum=(ptr[3]+ptr[7]+1) >> 1;cr[0]=sum; \
|
||||
sum=(ptr[8]+ptr[10]+1) >> 1;lum[2]=sum; \
|
||||
sum=(ptr[12]+ptr[14]+1) >> 1;lum[3]=sum; \
|
||||
sum=(ptr[9]+ptr[13]+1) >> 1;cb[1]=sum; \
|
||||
sum=(ptr[11]+ptr[15]+1) >> 1;cr[1]=sum; \
|
||||
sum=(ptr[16]+ptr[18]+1) >> 1;lum[4]=sum; \
|
||||
sum=(ptr[20]+ptr[22]+1) >> 1;lum[5]=sum; \
|
||||
sum=(ptr[17]+ptr[21]+1) >> 1;cb[2]=sum; \
|
||||
sum=(ptr[19]+ptr[23]+1) >> 1;cr[2]=sum; \
|
||||
sum=(ptr[24]+ptr[26]+1) >> 1;lum[6]=sum; \
|
||||
sum=(ptr[28]+ptr[30]+1) >> 1;lum[7]=sum; \
|
||||
sum=(ptr[25]+ptr[29]+1) >> 1;cb[3]=sum; \
|
||||
sum=(ptr[27]+ptr[31]+1) >> 1;cr[3]=sum;
|
||||
|
||||
#define LINE_NOUV_AVG \
|
||||
sum=(ptr[0]+ptr[2]+1) >> 1;lum[0]=sum; \
|
||||
sum=(ptr[4]+ptr[6]+1) >> 1;lum[1]=sum; \
|
||||
sum=(ptr[8]+ptr[10]+1) >> 1;lum[2]=sum; \
|
||||
sum=(ptr[12]+ptr[14]+1) >> 1;lum[3]=sum; \
|
||||
sum=(ptr[16]+ptr[18]+1) >> 1;lum[4]=sum; \
|
||||
sum=(ptr[20]+ptr[22]+1) >> 1;lum[5]=sum; \
|
||||
sum=(ptr[24]+ptr[26]+1) >> 1;lum[6]=sum; \
|
||||
sum=(ptr[28]+ptr[30]+1) >> 1;lum[7]=sum;
|
||||
|
||||
#define DEINT_LINE_LUM(ptroff) \
|
||||
sum=(-lum_m4[(ptroff)]+(lum_m3[(ptroff)]<<2)+(lum_m2[(ptroff)]<<1)+(lum_m1[(ptroff)]<<2)-lum[(ptroff)]); \
|
||||
lum_m4[(ptroff)]=lum_m2[(ptroff)];\
|
||||
lum_m2[(ptroff)]=cm[(sum+4)>>3];\
|
||||
sum=(-lum_m4[(ptroff)+1]+(lum_m3[(ptroff)+1]<<2)+(lum_m2[(ptroff)+1]<<1)+(lum_m1[(ptroff)+1]<<2)-lum[(ptroff)+1]); \
|
||||
lum_m4[(ptroff)+1]=lum_m2[(ptroff)+1];\
|
||||
lum_m2[(ptroff)+1]=cm[(sum+4)>>3];\
|
||||
sum=(-lum_m4[(ptroff)+2]+(lum_m3[(ptroff)+2]<<2)+(lum_m2[(ptroff)+2]<<1)+(lum_m1[(ptroff)+2]<<2)-lum[(ptroff)+2]); \
|
||||
lum_m4[(ptroff)+2]=lum_m2[(ptroff)+2];\
|
||||
lum_m2[(ptroff)+2]=cm[(sum+4)>>3];\
|
||||
sum=(-lum_m4[(ptroff)+3]+(lum_m3[(ptroff)+3]<<2)+(lum_m2[(ptroff)+3]<<1)+(lum_m1[(ptroff)+3]<<2)-lum[(ptroff)+3]); \
|
||||
lum_m4[(ptroff)+3]=lum_m2[(ptroff)+3];\
|
||||
lum_m2[(ptroff)+3]=cm[(sum+4)>>3];
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* Read two fields separately. */
|
||||
static int aiw_read_picture(VideoData *s, uint8_t *data)
|
||||
{
|
||||
UINT8 *ptr, *lum, *cb, *cr;
|
||||
int h;
|
||||
#ifndef HAVE_MMX
|
||||
int sum;
|
||||
#endif
|
||||
UINT8* src = s->src_mem;
|
||||
UINT8 *ptrend = &src[s->width*2];
|
||||
lum=data;
|
||||
cb=&lum[s->width*s->height];
|
||||
cr=&cb[(s->width*s->height)/4];
|
||||
if (s->deint == 0 && s->halfw == 0) {
|
||||
while (read(s->fd,src,s->width*2) < 0) {
|
||||
usleep(100);
|
||||
}
|
||||
for (h = 0; h < s->height-2; h+=2) {
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) {
|
||||
LINE_WITH_UV
|
||||
}
|
||||
read(s->fd,src,s->width*2);
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16) {
|
||||
LINE_NO_UV
|
||||
}
|
||||
read(s->fd,src,s->width*2);
|
||||
}
|
||||
/*
|
||||
* Do last two lines
|
||||
*/
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) {
|
||||
LINE_WITH_UV
|
||||
}
|
||||
read(s->fd,src,s->width*2);
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16) {
|
||||
LINE_NO_UV
|
||||
}
|
||||
/* drop second field */
|
||||
while (read(s->fd,src,s->width*2) < 0) {
|
||||
usleep(100);
|
||||
}
|
||||
for (h = 0; h < s->height - 1; h++) {
|
||||
read(s->fd,src,s->width*2);
|
||||
}
|
||||
} else if (s->halfw == 1) {
|
||||
#ifdef HAVE_MMX
|
||||
mmx_t rounder;
|
||||
mmx_t masker;
|
||||
rounder.uw[0]=1;
|
||||
rounder.uw[1]=1;
|
||||
rounder.uw[2]=1;
|
||||
rounder.uw[3]=1;
|
||||
masker.ub[0]=0xff;
|
||||
masker.ub[1]=0;
|
||||
masker.ub[2]=0xff;
|
||||
masker.ub[3]=0;
|
||||
masker.ub[4]=0xff;
|
||||
masker.ub[5]=0;
|
||||
masker.ub[6]=0xff;
|
||||
masker.ub[7]=0;
|
||||
pxor_r2r(mm7,mm7);
|
||||
movq_m2r(rounder,mm6);
|
||||
#endif
|
||||
while (read(s->fd,src,s->width*4) < 0) {
|
||||
usleep(100);
|
||||
}
|
||||
ptrend = &src[s->width*4];
|
||||
for (h = 0; h < s->height-2; h+=2) {
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=8, cb+=4, cr+=4) {
|
||||
LINE_WITHUV_AVG
|
||||
}
|
||||
read(s->fd,src,s->width*4);
|
||||
#ifdef HAVE_MMX
|
||||
movq_m2r(masker,mm5);
|
||||
#endif
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=8) {
|
||||
LINE_NOUV_AVG
|
||||
}
|
||||
read(s->fd,src,s->width*4);
|
||||
}
|
||||
/*
|
||||
* Do last two lines
|
||||
*/
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=8, cb+=4, cr+=4) {
|
||||
LINE_WITHUV_AVG
|
||||
}
|
||||
read(s->fd,src,s->width*4);
|
||||
#ifdef HAVE_MMX
|
||||
movq_m2r(masker,mm5);
|
||||
#endif
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=8) {
|
||||
LINE_NOUV_AVG
|
||||
}
|
||||
/* drop second field */
|
||||
while (read(s->fd,src,s->width*4) < 0) {
|
||||
usleep(100);
|
||||
}
|
||||
for (h = 0; h < s->height - 1; h++) {
|
||||
read(s->fd,src,s->width*4);
|
||||
}
|
||||
} else {
|
||||
UINT8 *lum_m1, *lum_m2, *lum_m3, *lum_m4;
|
||||
#ifdef HAVE_MMX
|
||||
mmx_t rounder;
|
||||
rounder.uw[0]=4;
|
||||
rounder.uw[1]=4;
|
||||
rounder.uw[2]=4;
|
||||
rounder.uw[3]=4;
|
||||
movq_m2r(rounder,mm6);
|
||||
pxor_r2r(mm7,mm7);
|
||||
#else
|
||||
UINT8 *cm = cropTbl + MAX_NEG_CROP;
|
||||
#endif
|
||||
|
||||
/* read two fields and deinterlace them */
|
||||
while (read(s->fd,src,s->width*2) < 0) {
|
||||
usleep(100);
|
||||
}
|
||||
for (h = 0; h < (s->height/2)-2; h+=2) {
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) {
|
||||
LINE_WITH_UV
|
||||
}
|
||||
read(s->fd,src,s->width*2);
|
||||
/* skip a luminance line - will be filled in later */
|
||||
lum += s->width;
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) {
|
||||
LINE_WITH_UV
|
||||
}
|
||||
/* skip a luminance line - will be filled in later */
|
||||
lum += s->width;
|
||||
read(s->fd,src,s->width*2);
|
||||
}
|
||||
/*
|
||||
* Do last two lines
|
||||
*/
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) {
|
||||
LINE_WITH_UV
|
||||
}
|
||||
/* skip a luminance line - will be filled in later */
|
||||
lum += s->width;
|
||||
read(s->fd,src,s->width*2);
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) {
|
||||
LINE_WITH_UV
|
||||
}
|
||||
/*
|
||||
*
|
||||
* SECOND FIELD
|
||||
*
|
||||
*/
|
||||
lum=&data[s->width];
|
||||
while (read(s->fd,src,s->width*2) < 0) {
|
||||
usleep(10);
|
||||
}
|
||||
/* First (and last) two lines not interlaced */
|
||||
for (h = 0; h < 2; h++) {
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16) {
|
||||
LINE_NO_UV
|
||||
}
|
||||
read(s->fd,src,s->width*2);
|
||||
/* skip a luminance line */
|
||||
lum += s->width;
|
||||
}
|
||||
lum_m1=&lum[-s->width];
|
||||
lum_m2=&lum_m1[-s->width];
|
||||
lum_m3=&lum_m2[-s->width];
|
||||
memmove(s->lum_m4_mem,&lum_m3[-s->width],s->width);
|
||||
for (; h < (s->height/2)-1; h++) {
|
||||
lum_m4=s->lum_m4_mem;
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16,lum_m1+=16,lum_m2+=16,lum_m3+=16,lum_m4+=16) {
|
||||
LINE_NO_UV
|
||||
|
||||
DEINT_LINE_LUM(0)
|
||||
DEINT_LINE_LUM(4)
|
||||
DEINT_LINE_LUM(8)
|
||||
DEINT_LINE_LUM(12)
|
||||
}
|
||||
read(s->fd,src,s->width*2);
|
||||
/* skip a luminance line */
|
||||
lum += s->width;
|
||||
lum_m1 += s->width;
|
||||
lum_m2 += s->width;
|
||||
lum_m3 += s->width;
|
||||
// lum_m4 += s->width;
|
||||
}
|
||||
/*
|
||||
* Do last line
|
||||
*/
|
||||
lum_m4=s->lum_m4_mem;
|
||||
for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, lum_m1+=16, lum_m2+=16, lum_m3+=16, lum_m4+=16) {
|
||||
LINE_NO_UV
|
||||
|
||||
DEINT_LINE_LUM(0)
|
||||
DEINT_LINE_LUM(4)
|
||||
DEINT_LINE_LUM(8)
|
||||
DEINT_LINE_LUM(12)
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_MMX
|
||||
emms();
|
||||
#endif
|
||||
return s->frame_size;
|
||||
}
|
||||
|
||||
static int aiw_close(VideoData *s)
|
||||
{
|
||||
av_freep(&s->lum_m4_mem);
|
||||
av_freep(&s->src_mem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int video_grab_init(void)
|
||||
{
|
||||
av_register_input_format(&video_grab_device_format);
|
||||
return 0;
|
||||
}
|
||||
290
libavformat/http.c
Normal file
290
libavformat/http.c
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* HTTP protocol for ffmpeg client
|
||||
* Copyright (c) 2000, 2001 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#ifndef __BEOS__
|
||||
# include <arpa/inet.h>
|
||||
#else
|
||||
# include "barpainet.h"
|
||||
#endif
|
||||
#include <netdb.h>
|
||||
|
||||
|
||||
/* XXX: POST protocol is not completly implemented because ffmpeg use
|
||||
only a subset of it */
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
/* used for protocol handling */
|
||||
#define BUFFER_SIZE 1024
|
||||
#define URL_SIZE 4096
|
||||
|
||||
typedef struct {
|
||||
URLContext *hd;
|
||||
unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
|
||||
int line_count;
|
||||
int http_code;
|
||||
char location[URL_SIZE];
|
||||
} HTTPContext;
|
||||
|
||||
static int http_connect(URLContext *h, const char *path, const char *hoststr);
|
||||
static int http_write(URLContext *h, UINT8 *buf, int size);
|
||||
|
||||
|
||||
/* return non zero if error */
|
||||
static int http_open(URLContext *h, const char *uri, int flags)
|
||||
{
|
||||
const char *path, *proxy_path;
|
||||
char hostname[1024], hoststr[1024];
|
||||
char path1[1024];
|
||||
char buf[1024];
|
||||
int port, use_proxy, err;
|
||||
HTTPContext *s;
|
||||
URLContext *hd = NULL;
|
||||
|
||||
h->is_streamed = 1;
|
||||
|
||||
s = av_malloc(sizeof(HTTPContext));
|
||||
if (!s) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
h->priv_data = s;
|
||||
|
||||
proxy_path = getenv("http_proxy");
|
||||
use_proxy = (proxy_path != NULL) && !getenv("no_proxy") &&
|
||||
strstart(proxy_path, "http://", NULL);
|
||||
|
||||
/* fill the dest addr */
|
||||
redo:
|
||||
/* needed in any case to build the host string */
|
||||
url_split(NULL, 0, hostname, sizeof(hostname), &port,
|
||||
path1, sizeof(path1), uri);
|
||||
if (port > 0) {
|
||||
snprintf(hoststr, sizeof(hoststr), "%s:%d", hostname, port);
|
||||
} else {
|
||||
pstrcpy(hoststr, sizeof(hoststr), hostname);
|
||||
}
|
||||
|
||||
if (use_proxy) {
|
||||
url_split(NULL, 0, hostname, sizeof(hostname), &port,
|
||||
NULL, 0, proxy_path);
|
||||
path = uri;
|
||||
} else {
|
||||
if (path1[0] == '\0')
|
||||
path = "/";
|
||||
else
|
||||
path = path1;
|
||||
}
|
||||
if (port < 0)
|
||||
port = 80;
|
||||
|
||||
snprintf(buf, sizeof(buf), "tcp://%s:%d", hostname, port);
|
||||
err = url_open(&hd, buf, URL_RDWR);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
s->hd = hd;
|
||||
if (http_connect(h, path, hoststr) < 0)
|
||||
goto fail;
|
||||
if (s->http_code == 303 && s->location[0] != '\0') {
|
||||
/* url moved, get next */
|
||||
uri = s->location;
|
||||
url_close(hd);
|
||||
goto redo;
|
||||
}
|
||||
return 0;
|
||||
fail:
|
||||
if (hd)
|
||||
url_close(hd);
|
||||
av_free(s);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int http_getc(HTTPContext *s)
|
||||
{
|
||||
int len;
|
||||
if (s->buf_ptr >= s->buf_end) {
|
||||
len = url_read(s->hd, s->buffer, BUFFER_SIZE);
|
||||
if (len < 0) {
|
||||
return -EIO;
|
||||
} else if (len == 0) {
|
||||
return -1;
|
||||
} else {
|
||||
s->buf_ptr = s->buffer;
|
||||
s->buf_end = s->buffer + len;
|
||||
}
|
||||
}
|
||||
return *s->buf_ptr++;
|
||||
}
|
||||
|
||||
static int process_line(HTTPContext *s, char *line, int line_count)
|
||||
{
|
||||
char *tag, *p;
|
||||
|
||||
/* end of header */
|
||||
if (line[0] == '\0')
|
||||
return 0;
|
||||
|
||||
p = line;
|
||||
if (line_count == 0) {
|
||||
while (!isspace(*p) && *p != '\0')
|
||||
p++;
|
||||
while (isspace(*p))
|
||||
p++;
|
||||
s->http_code = strtol(p, NULL, 10);
|
||||
#ifdef DEBUG
|
||||
printf("http_code=%d\n", s->http_code);
|
||||
#endif
|
||||
} else {
|
||||
while (*p != '\0' && *p != ':')
|
||||
p++;
|
||||
if (*p != ':')
|
||||
return 1;
|
||||
|
||||
*p = '\0';
|
||||
tag = line;
|
||||
p++;
|
||||
while (isspace(*p))
|
||||
p++;
|
||||
if (!strcmp(tag, "Location")) {
|
||||
strcpy(s->location, p);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int http_connect(URLContext *h, const char *path, const char *hoststr)
|
||||
{
|
||||
HTTPContext *s = h->priv_data;
|
||||
int post, err, ch;
|
||||
char line[1024], *q;
|
||||
|
||||
|
||||
/* send http header */
|
||||
post = h->flags & URL_WRONLY;
|
||||
|
||||
snprintf(s->buffer, sizeof(s->buffer),
|
||||
"%s %s HTTP/1.0\n"
|
||||
"User-Agent: FFmpeg %s\n"
|
||||
"Accept: */*\n"
|
||||
"Host: %s\n"
|
||||
"\n",
|
||||
post ? "POST" : "GET",
|
||||
path,
|
||||
FFMPEG_VERSION,
|
||||
hoststr);
|
||||
|
||||
if (http_write(h, s->buffer, strlen(s->buffer)) < 0)
|
||||
return -EIO;
|
||||
|
||||
/* init input buffer */
|
||||
s->buf_ptr = s->buffer;
|
||||
s->buf_end = s->buffer;
|
||||
s->line_count = 0;
|
||||
s->location[0] = '\0';
|
||||
if (post) {
|
||||
sleep(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* wait for header */
|
||||
q = line;
|
||||
for(;;) {
|
||||
ch = http_getc(s);
|
||||
if (ch < 0)
|
||||
return -EIO;
|
||||
if (ch == '\n') {
|
||||
/* process line */
|
||||
if (q > line && q[-1] == '\r')
|
||||
q--;
|
||||
*q = '\0';
|
||||
#ifdef DEBUG
|
||||
printf("header='%s'\n", line);
|
||||
#endif
|
||||
err = process_line(s, line, s->line_count);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (err == 0)
|
||||
return 0;
|
||||
s->line_count++;
|
||||
q = line;
|
||||
} else {
|
||||
if ((q - line) < sizeof(line) - 1)
|
||||
*q++ = ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int http_read(URLContext *h, UINT8 *buf, int size)
|
||||
{
|
||||
HTTPContext *s = h->priv_data;
|
||||
int size1, len;
|
||||
|
||||
size1 = size;
|
||||
while (size > 0) {
|
||||
/* read bytes from input buffer first */
|
||||
len = s->buf_end - s->buf_ptr;
|
||||
if (len > 0) {
|
||||
if (len > size)
|
||||
len = size;
|
||||
memcpy(buf, s->buf_ptr, len);
|
||||
s->buf_ptr += len;
|
||||
} else {
|
||||
len = url_read (s->hd, buf, size);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
} else if (len == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
size -= len;
|
||||
buf += len;
|
||||
}
|
||||
return size1 - size;
|
||||
}
|
||||
|
||||
/* used only when posting data */
|
||||
static int http_write(URLContext *h, UINT8 *buf, int size)
|
||||
{
|
||||
HTTPContext *s = h->priv_data;
|
||||
return url_write(s->hd, buf, size);
|
||||
}
|
||||
|
||||
static int http_close(URLContext *h)
|
||||
{
|
||||
HTTPContext *s = h->priv_data;
|
||||
url_close(s->hd);
|
||||
av_free(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
URLProtocol http_protocol = {
|
||||
"http",
|
||||
http_open,
|
||||
http_read,
|
||||
http_write,
|
||||
NULL, /* seek */
|
||||
http_close,
|
||||
};
|
||||
|
||||
945
libavformat/img.c
Normal file
945
libavformat/img.c
Normal file
@@ -0,0 +1,945 @@
|
||||
/*
|
||||
* Image format
|
||||
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
|
||||
extern AVInputFormat pgm_iformat;
|
||||
extern AVOutputFormat pgm_oformat;
|
||||
extern AVInputFormat pgmyuv_iformat;
|
||||
extern AVOutputFormat pgmyuv_oformat;
|
||||
extern AVInputFormat ppm_iformat;
|
||||
extern AVOutputFormat ppm_oformat;
|
||||
extern AVInputFormat imgyuv_iformat;
|
||||
extern AVOutputFormat imgyuv_oformat;
|
||||
extern AVInputFormat pgmpipe_iformat;
|
||||
extern AVOutputFormat pgmpipe_oformat;
|
||||
extern AVInputFormat pgmyuvpipe_iformat;
|
||||
extern AVOutputFormat pgmyuvpipe_oformat;
|
||||
extern AVInputFormat ppmpipe_iformat;
|
||||
extern AVOutputFormat ppmpipe_oformat;
|
||||
extern AVOutputFormat yuv4mpegpipe_oformat;
|
||||
|
||||
#define IMGFMT_YUV 1
|
||||
#define IMGFMT_PGMYUV 2
|
||||
#define IMGFMT_PGM 3
|
||||
#define IMGFMT_PPM 4
|
||||
#define IMGFMT_YUV4MPEG 5
|
||||
|
||||
#define Y4M_MAGIC "YUV4MPEG2"
|
||||
#define Y4M_FRAME_MAGIC "FRAME"
|
||||
#define Y4M_LINE_MAX 256
|
||||
|
||||
typedef struct {
|
||||
int width;
|
||||
int height;
|
||||
int img_number;
|
||||
int img_size;
|
||||
int img_fmt;
|
||||
int is_pipe;
|
||||
int header_written;
|
||||
char path[1024];
|
||||
} VideoData;
|
||||
|
||||
static inline int pnm_space(int c)
|
||||
{
|
||||
return (c==' ' || c=='\n' || c=='\r' || c=='\t');
|
||||
}
|
||||
|
||||
static void pnm_get(ByteIOContext *f, char *str, int buf_size)
|
||||
{
|
||||
char *s;
|
||||
int c;
|
||||
|
||||
do {
|
||||
c=get_byte(f);
|
||||
if (c=='#') {
|
||||
do {
|
||||
c=get_byte(f);
|
||||
} while (c!='\n');
|
||||
c=get_byte(f);
|
||||
}
|
||||
} while (pnm_space(c));
|
||||
|
||||
s=str;
|
||||
do {
|
||||
if (url_feof(f))
|
||||
break;
|
||||
if ((s - str) < buf_size - 1)
|
||||
*s++=c;
|
||||
c=get_byte(f);
|
||||
} while (!pnm_space(c));
|
||||
*s = '\0';
|
||||
}
|
||||
|
||||
static int pgm_read(VideoData *s, ByteIOContext *f, UINT8 *buf, int size, int is_yuv)
|
||||
{
|
||||
int width, height, i;
|
||||
char buf1[32];
|
||||
UINT8 *picture[3];
|
||||
|
||||
width = s->width;
|
||||
height = s->height;
|
||||
|
||||
pnm_get(f, buf1, sizeof(buf1));
|
||||
if (strcmp(buf1, "P5")) {
|
||||
return -EIO;
|
||||
}
|
||||
pnm_get(f, buf1, sizeof(buf1));
|
||||
pnm_get(f, buf1, sizeof(buf1));
|
||||
pnm_get(f, buf1, sizeof(buf1));
|
||||
|
||||
picture[0] = buf;
|
||||
picture[1] = buf + width * height;
|
||||
picture[2] = buf + width * height + (width * height / 4);
|
||||
get_buffer(f, picture[0], width * height);
|
||||
|
||||
height>>=1;
|
||||
width>>=1;
|
||||
if (is_yuv) {
|
||||
for(i=0;i<height;i++) {
|
||||
get_buffer(f, picture[1] + i * width, width);
|
||||
get_buffer(f, picture[2] + i * width, width);
|
||||
}
|
||||
} else {
|
||||
for(i=0;i<height;i++) {
|
||||
memset(picture[1] + i * width, 128, width);
|
||||
memset(picture[2] + i * width, 128, width);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ppm_read(VideoData *s, ByteIOContext *f, UINT8 *buf, int size)
|
||||
{
|
||||
int width, height;
|
||||
char buf1[32];
|
||||
UINT8 *picture[3];
|
||||
|
||||
width = s->width;
|
||||
height = s->height;
|
||||
|
||||
pnm_get(f, buf1, sizeof(buf1));
|
||||
if (strcmp(buf1, "P6")) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
pnm_get(f, buf1, sizeof(buf1));
|
||||
pnm_get(f, buf1, sizeof(buf1));
|
||||
pnm_get(f, buf1, sizeof(buf1));
|
||||
|
||||
picture[0] = buf;
|
||||
get_buffer(f, picture[0], width * height*3);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int yuv_read(VideoData *s, const char *filename, UINT8 *buf, int size1)
|
||||
{
|
||||
ByteIOContext pb1, *pb = &pb1;
|
||||
char fname[1024], *p;
|
||||
int size;
|
||||
|
||||
size = s->width * s->height;
|
||||
|
||||
strcpy(fname, filename);
|
||||
p = strrchr(fname, '.');
|
||||
if (!p || p[1] != 'Y')
|
||||
return -EIO;
|
||||
|
||||
if (url_fopen(pb, fname, URL_RDONLY) < 0)
|
||||
return -EIO;
|
||||
|
||||
get_buffer(pb, buf, size);
|
||||
url_fclose(pb);
|
||||
|
||||
p[1] = 'U';
|
||||
if (url_fopen(pb, fname, URL_RDONLY) < 0)
|
||||
return -EIO;
|
||||
|
||||
get_buffer(pb, buf + size, size / 4);
|
||||
url_fclose(pb);
|
||||
|
||||
p[1] = 'V';
|
||||
if (url_fopen(pb, fname, URL_RDONLY) < 0)
|
||||
return -EIO;
|
||||
|
||||
get_buffer(pb, buf + size + (size / 4), size / 4);
|
||||
url_fclose(pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_read_packet(AVFormatContext *s1, AVPacket *pkt)
|
||||
{
|
||||
VideoData *s = s1->priv_data;
|
||||
char filename[1024];
|
||||
int ret;
|
||||
ByteIOContext f1, *f;
|
||||
|
||||
/*
|
||||
This if-statement destroys pipes - I do not see why it is necessary
|
||||
if (get_frame_filename(filename, sizeof(filename),
|
||||
s->path, s->img_number) < 0)
|
||||
return -EIO;
|
||||
*/
|
||||
get_frame_filename(filename, sizeof(filename),
|
||||
s->path, s->img_number);
|
||||
if (!s->is_pipe) {
|
||||
f = &f1;
|
||||
if (url_fopen(f, filename, URL_RDONLY) < 0)
|
||||
return -EIO;
|
||||
} else {
|
||||
f = &s1->pb;
|
||||
if (url_feof(f))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
av_new_packet(pkt, s->img_size);
|
||||
pkt->stream_index = 0;
|
||||
|
||||
switch(s->img_fmt) {
|
||||
case IMGFMT_PGMYUV:
|
||||
ret = pgm_read(s, f, pkt->data, pkt->size, 1);
|
||||
break;
|
||||
case IMGFMT_PGM:
|
||||
ret = pgm_read(s, f, pkt->data, pkt->size, 0);
|
||||
break;
|
||||
case IMGFMT_YUV:
|
||||
ret = yuv_read(s, filename, pkt->data, pkt->size);
|
||||
break;
|
||||
case IMGFMT_PPM:
|
||||
ret = ppm_read(s, f, pkt->data, pkt->size);
|
||||
break;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!s->is_pipe) {
|
||||
url_fclose(f);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
av_free_packet(pkt);
|
||||
return -EIO; /* signal EOF */
|
||||
} else {
|
||||
pkt->pts = ((INT64)s->img_number * s1->pts_den * FRAME_RATE_BASE) / (s1->streams[0]->codec.frame_rate * s1->pts_num);
|
||||
s->img_number++;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int sizes[][2] = {
|
||||
{ 640, 480 },
|
||||
{ 720, 480 },
|
||||
{ 720, 576 },
|
||||
{ 352, 288 },
|
||||
{ 352, 240 },
|
||||
{ 160, 128 },
|
||||
{ 512, 384 },
|
||||
{ 640, 352 },
|
||||
{ 640, 240 },
|
||||
};
|
||||
|
||||
static int infer_size(int *width_ptr, int *height_ptr, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0;i<sizeof(sizes)/sizeof(sizes[0]);i++) {
|
||||
if ((sizes[i][0] * sizes[i][1]) == size) {
|
||||
*width_ptr = sizes[i][0];
|
||||
*height_ptr = sizes[i][1];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int img_read_header(AVFormatContext *s1, AVFormatParameters *ap)
|
||||
{
|
||||
VideoData *s = s1->priv_data;
|
||||
int i, h;
|
||||
char buf[1024];
|
||||
char buf1[32];
|
||||
ByteIOContext pb1, *f = &pb1;
|
||||
AVStream *st;
|
||||
|
||||
st = av_new_stream(s1, 0);
|
||||
if (!st) {
|
||||
av_free(s);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
strcpy(s->path, s1->filename);
|
||||
s->img_number = 0;
|
||||
|
||||
/* find format */
|
||||
if (s1->iformat->flags & AVFMT_NOFILE)
|
||||
s->is_pipe = 0;
|
||||
else
|
||||
s->is_pipe = 1;
|
||||
|
||||
if (s1->iformat == &pgmyuvpipe_iformat ||
|
||||
s1->iformat == &pgmyuv_iformat)
|
||||
s->img_fmt = IMGFMT_PGMYUV;
|
||||
else if (s1->iformat == &pgmpipe_iformat ||
|
||||
s1->iformat == &pgm_iformat)
|
||||
s->img_fmt = IMGFMT_PGM;
|
||||
else if (s1->iformat == &imgyuv_iformat)
|
||||
s->img_fmt = IMGFMT_YUV;
|
||||
else if (s1->iformat == &ppmpipe_iformat ||
|
||||
s1->iformat == &ppm_iformat)
|
||||
s->img_fmt = IMGFMT_PPM;
|
||||
else
|
||||
goto fail;
|
||||
|
||||
if (!s->is_pipe) {
|
||||
/* try to find the first image */
|
||||
for(i=0;i<5;i++) {
|
||||
if (get_frame_filename(buf, sizeof(buf), s->path, s->img_number) < 0)
|
||||
goto fail;
|
||||
if (url_fopen(f, buf, URL_RDONLY) >= 0)
|
||||
break;
|
||||
s->img_number++;
|
||||
}
|
||||
if (i == 5)
|
||||
goto fail;
|
||||
} else {
|
||||
f = &s1->pb;
|
||||
}
|
||||
|
||||
/* find the image size */
|
||||
/* XXX: use generic file format guessing, as mpeg */
|
||||
switch(s->img_fmt) {
|
||||
case IMGFMT_PGM:
|
||||
case IMGFMT_PGMYUV:
|
||||
case IMGFMT_PPM:
|
||||
pnm_get(f, buf1, sizeof(buf1));
|
||||
pnm_get(f, buf1, sizeof(buf1));
|
||||
s->width = atoi(buf1);
|
||||
pnm_get(f, buf1, sizeof(buf1));
|
||||
h = atoi(buf1);
|
||||
if (s->img_fmt == IMGFMT_PGMYUV)
|
||||
h = (h * 2) / 3;
|
||||
s->height = h;
|
||||
if (s->width <= 0 ||
|
||||
s->height <= 0 ||
|
||||
(s->width % 2) != 0 ||
|
||||
(s->height % 2) != 0) {
|
||||
goto fail1;
|
||||
}
|
||||
break;
|
||||
case IMGFMT_YUV:
|
||||
/* infer size by using the file size. */
|
||||
{
|
||||
int img_size;
|
||||
URLContext *h;
|
||||
|
||||
/* XXX: hack hack */
|
||||
h = url_fileno(f);
|
||||
img_size = url_seek(h, 0, SEEK_END);
|
||||
if (infer_size(&s->width, &s->height, img_size) < 0) {
|
||||
goto fail1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (!s->is_pipe) {
|
||||
url_fclose(f);
|
||||
} else {
|
||||
url_fseek(f, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
|
||||
st->codec.codec_type = CODEC_TYPE_VIDEO;
|
||||
st->codec.codec_id = CODEC_ID_RAWVIDEO;
|
||||
st->codec.width = s->width;
|
||||
st->codec.height = s->height;
|
||||
if (s->img_fmt == IMGFMT_PPM) {
|
||||
st->codec.pix_fmt = PIX_FMT_RGB24;
|
||||
s->img_size = (s->width * s->height * 3);
|
||||
} else {
|
||||
st->codec.pix_fmt = PIX_FMT_YUV420P;
|
||||
s->img_size = (s->width * s->height * 3) / 2;
|
||||
}
|
||||
if (!ap || !ap->frame_rate)
|
||||
st->codec.frame_rate = 25 * FRAME_RATE_BASE;
|
||||
else
|
||||
st->codec.frame_rate = ap->frame_rate;
|
||||
|
||||
return 0;
|
||||
fail1:
|
||||
if (!s->is_pipe)
|
||||
url_fclose(f);
|
||||
fail:
|
||||
av_free(s);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int img_read_close(AVFormatContext *s1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************/
|
||||
/* image output */
|
||||
|
||||
static int pgm_save(AVPicture *picture, int width, int height, ByteIOContext *pb, int is_yuv)
|
||||
{
|
||||
int i, h;
|
||||
char buf[100];
|
||||
UINT8 *ptr, *ptr1, *ptr2;
|
||||
|
||||
h = height;
|
||||
if (is_yuv)
|
||||
h = (height * 3) / 2;
|
||||
snprintf(buf, sizeof(buf),
|
||||
"P5\n%d %d\n%d\n",
|
||||
width, h, 255);
|
||||
put_buffer(pb, buf, strlen(buf));
|
||||
|
||||
ptr = picture->data[0];
|
||||
for(i=0;i<height;i++) {
|
||||
put_buffer(pb, ptr, width);
|
||||
ptr += picture->linesize[0];
|
||||
}
|
||||
|
||||
if (is_yuv) {
|
||||
height >>= 1;
|
||||
width >>= 1;
|
||||
ptr1 = picture->data[1];
|
||||
ptr2 = picture->data[2];
|
||||
for(i=0;i<height;i++) {
|
||||
put_buffer(pb, ptr1, width);
|
||||
put_buffer(pb, ptr2, width);
|
||||
ptr1 += picture->linesize[1];
|
||||
ptr2 += picture->linesize[2];
|
||||
}
|
||||
}
|
||||
put_flush_packet(pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ppm_save(AVPicture *picture, int width, int height, ByteIOContext *pb)
|
||||
{
|
||||
int i;
|
||||
char buf[100];
|
||||
UINT8 *ptr;
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"P6\n%d %d\n%d\n",
|
||||
width, height, 255);
|
||||
put_buffer(pb, buf, strlen(buf));
|
||||
|
||||
ptr = picture->data[0];
|
||||
for(i=0;i<height;i++) {
|
||||
put_buffer(pb, ptr, width * 3);
|
||||
ptr += picture->linesize[0];
|
||||
}
|
||||
|
||||
put_flush_packet(pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int yuv_save(AVPicture *picture, int width, int height, const char *filename)
|
||||
{
|
||||
ByteIOContext pb1, *pb = &pb1;
|
||||
char fname[1024], *p;
|
||||
int i, j;
|
||||
UINT8 *ptr;
|
||||
static char *ext = "YUV";
|
||||
|
||||
strcpy(fname, filename);
|
||||
p = strrchr(fname, '.');
|
||||
if (!p || p[1] != 'Y')
|
||||
return -EIO;
|
||||
|
||||
for(i=0;i<3;i++) {
|
||||
if (i == 1) {
|
||||
width >>= 1;
|
||||
height >>= 1;
|
||||
}
|
||||
|
||||
p[1] = ext[i];
|
||||
if (url_fopen(pb, fname, URL_WRONLY) < 0)
|
||||
return -EIO;
|
||||
|
||||
ptr = picture->data[i];
|
||||
for(j=0;j<height;j++) {
|
||||
put_buffer(pb, ptr, width);
|
||||
ptr += picture->linesize[i];
|
||||
}
|
||||
put_flush_packet(pb);
|
||||
url_fclose(pb);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int yuv4mpeg_save(AVPicture *picture, int width, int height, ByteIOContext *pb, int need_stream_header,
|
||||
int is_yuv, int raten, int rated, int aspectn, int aspectd)
|
||||
{
|
||||
int i, n, m;
|
||||
char buf[Y4M_LINE_MAX+1], buf1[20];
|
||||
UINT8 *ptr, *ptr1, *ptr2;
|
||||
|
||||
/* construct stream header, if this is the first frame */
|
||||
if(need_stream_header) {
|
||||
n = snprintf(buf, sizeof(buf), "%s W%d H%d F%d:%d I%s A%d:%d\n",
|
||||
Y4M_MAGIC,
|
||||
width,
|
||||
height,
|
||||
raten, rated,
|
||||
"p", /* ffmpeg seems to only output progressive video */
|
||||
aspectn, aspectd);
|
||||
if (n < 0) {
|
||||
fprintf(stderr, "Error. YUV4MPEG stream header write failed.\n");
|
||||
} else {
|
||||
fprintf(stderr, "YUV4MPEG stream header written. FPS is %d\n", raten);
|
||||
put_buffer(pb, buf, strlen(buf));
|
||||
}
|
||||
}
|
||||
|
||||
/* construct frame header */
|
||||
m = snprintf(buf1, sizeof(buf1), "%s \n", Y4M_FRAME_MAGIC);
|
||||
if (m < 0) {
|
||||
fprintf(stderr, "Error. YUV4MPEG frame header write failed.\n");
|
||||
} else {
|
||||
/* fprintf(stderr, "YUV4MPEG frame header written.\n"); */
|
||||
put_buffer(pb, buf1, strlen(buf1));
|
||||
}
|
||||
|
||||
ptr = picture->data[0];
|
||||
for(i=0;i<height;i++) {
|
||||
put_buffer(pb, ptr, width);
|
||||
ptr += picture->linesize[0];
|
||||
}
|
||||
|
||||
if (is_yuv) {
|
||||
height >>= 1;
|
||||
width >>= 1;
|
||||
ptr1 = picture->data[1];
|
||||
ptr2 = picture->data[2];
|
||||
for(i=0;i<height;i++) { /* Cb */
|
||||
put_buffer(pb, ptr1, width);
|
||||
ptr1 += picture->linesize[1];
|
||||
}
|
||||
for(i=0;i<height;i++) { /* Cr */
|
||||
put_buffer(pb, ptr2, width);
|
||||
ptr2 += picture->linesize[2];
|
||||
}
|
||||
}
|
||||
put_flush_packet(pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_write_header(AVFormatContext *s)
|
||||
{
|
||||
VideoData *img = s->priv_data;
|
||||
|
||||
img->img_number = 1;
|
||||
strcpy(img->path, s->filename);
|
||||
|
||||
/* find format */
|
||||
if (s->oformat->flags & AVFMT_NOFILE)
|
||||
img->is_pipe = 0;
|
||||
else
|
||||
img->is_pipe = 1;
|
||||
|
||||
if (s->oformat == &pgmyuvpipe_oformat ||
|
||||
s->oformat == &pgmyuv_oformat) {
|
||||
img->img_fmt = IMGFMT_PGMYUV;
|
||||
} else if (s->oformat == &pgmpipe_oformat ||
|
||||
s->oformat == &pgm_oformat) {
|
||||
img->img_fmt = IMGFMT_PGM;
|
||||
} else if (s->oformat == &imgyuv_oformat) {
|
||||
img->img_fmt = IMGFMT_YUV;
|
||||
} else if (s->oformat == &ppmpipe_oformat ||
|
||||
s->oformat == &ppm_oformat) {
|
||||
img->img_fmt = IMGFMT_PPM;
|
||||
} else if (s->oformat == &yuv4mpegpipe_oformat) {
|
||||
img->img_fmt = IMGFMT_YUV4MPEG;
|
||||
img->header_written = 0;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
fail:
|
||||
av_free(img);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int img_write_packet(AVFormatContext *s, int stream_index,
|
||||
UINT8 *buf, int size, int force_pts)
|
||||
{
|
||||
VideoData *img = s->priv_data;
|
||||
AVStream *st = s->streams[stream_index];
|
||||
ByteIOContext pb1, *pb;
|
||||
AVPicture picture;
|
||||
int width, height, need_stream_header, ret, size1, raten, rated, aspectn, aspectd, fps, fps1;
|
||||
char filename[1024];
|
||||
|
||||
width = st->codec.width;
|
||||
height = st->codec.height;
|
||||
|
||||
if (img->img_number == 1) {
|
||||
need_stream_header = 1;
|
||||
} else {
|
||||
need_stream_header = 0;
|
||||
}
|
||||
|
||||
fps = st->codec.frame_rate;
|
||||
fps1 = (((float)fps / FRAME_RATE_BASE) * 1000);
|
||||
|
||||
/* Sorry about this messy code, but mpeg2enc is very picky about
|
||||
* the framerates it accepts. */
|
||||
switch(fps1) {
|
||||
case 23976:
|
||||
raten = 24000; /* turn the framerate into a ratio */
|
||||
rated = 1001;
|
||||
break;
|
||||
case 29970:
|
||||
raten = 30000;
|
||||
rated = 1001;
|
||||
break;
|
||||
case 25000:
|
||||
raten = 25;
|
||||
rated = 1;
|
||||
break;
|
||||
case 30000:
|
||||
raten = 30;
|
||||
rated = 1;
|
||||
break;
|
||||
case 24000:
|
||||
raten = 24;
|
||||
rated = 1;
|
||||
break;
|
||||
case 50000:
|
||||
raten = 50;
|
||||
rated = 1;
|
||||
break;
|
||||
case 59940:
|
||||
raten = 60000;
|
||||
rated = 1001;
|
||||
break;
|
||||
case 60000:
|
||||
raten = 60;
|
||||
rated = 1;
|
||||
break;
|
||||
default:
|
||||
raten = fps1; /* this setting should work, but often doesn't */
|
||||
rated = 1000;
|
||||
break;
|
||||
}
|
||||
|
||||
aspectn = 1;
|
||||
aspectd = 1; /* ffmpeg always uses a 1:1 aspect ratio */
|
||||
|
||||
switch(st->codec.pix_fmt) {
|
||||
case PIX_FMT_YUV420P:
|
||||
size1 = (width * height * 3) / 2;
|
||||
if (size != size1)
|
||||
return -EIO;
|
||||
|
||||
picture.data[0] = buf;
|
||||
picture.data[1] = picture.data[0] + width * height;
|
||||
picture.data[2] = picture.data[1] + (width * height) / 4;
|
||||
picture.linesize[0] = width;
|
||||
picture.linesize[1] = width >> 1;
|
||||
picture.linesize[2] = width >> 1;
|
||||
break;
|
||||
case PIX_FMT_RGB24:
|
||||
size1 = (width * height * 3);
|
||||
if (size != size1)
|
||||
return -EIO;
|
||||
picture.data[0] = buf;
|
||||
picture.linesize[0] = width * 3;
|
||||
break;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
This if-statement destroys pipes - I do not see why it is necessary
|
||||
if (get_frame_filename(filename, sizeof(filename),
|
||||
img->path, img->img_number) < 0)
|
||||
return -EIO;
|
||||
*/
|
||||
get_frame_filename(filename, sizeof(filename),
|
||||
img->path, img->img_number);
|
||||
if (!img->is_pipe) {
|
||||
pb = &pb1;
|
||||
if (url_fopen(pb, filename, URL_WRONLY) < 0)
|
||||
return -EIO;
|
||||
} else {
|
||||
pb = &s->pb;
|
||||
}
|
||||
switch(img->img_fmt) {
|
||||
case IMGFMT_PGMYUV:
|
||||
ret = pgm_save(&picture, width, height, pb, 1);
|
||||
break;
|
||||
case IMGFMT_PGM:
|
||||
ret = pgm_save(&picture, width, height, pb, 0);
|
||||
break;
|
||||
case IMGFMT_YUV:
|
||||
ret = yuv_save(&picture, width, height, filename);
|
||||
break;
|
||||
case IMGFMT_PPM:
|
||||
ret = ppm_save(&picture, width, height, pb);
|
||||
break;
|
||||
case IMGFMT_YUV4MPEG:
|
||||
ret = yuv4mpeg_save(&picture, width, height, pb,
|
||||
need_stream_header, 1, raten, rated, aspectn, aspectd);
|
||||
break;
|
||||
}
|
||||
if (!img->is_pipe) {
|
||||
url_fclose(pb);
|
||||
}
|
||||
|
||||
img->img_number++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_write_trailer(AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVInputFormat pgm_iformat = {
|
||||
"pgm",
|
||||
"pgm image format",
|
||||
sizeof(VideoData),
|
||||
NULL,
|
||||
img_read_header,
|
||||
img_read_packet,
|
||||
img_read_close,
|
||||
NULL,
|
||||
AVFMT_NOFILE | AVFMT_NEEDNUMBER,
|
||||
.extensions = "pgm",
|
||||
};
|
||||
|
||||
static AVOutputFormat pgm_oformat = {
|
||||
"pgm",
|
||||
"pgm image format",
|
||||
"",
|
||||
"pgm",
|
||||
sizeof(VideoData),
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_RAWVIDEO,
|
||||
img_write_header,
|
||||
img_write_packet,
|
||||
img_write_trailer,
|
||||
AVFMT_NOFILE | AVFMT_NEEDNUMBER,
|
||||
};
|
||||
|
||||
static AVInputFormat pgmyuv_iformat = {
|
||||
"pgmyuv",
|
||||
"pgm with YUV content image format",
|
||||
sizeof(VideoData),
|
||||
NULL, /* no probe */
|
||||
img_read_header,
|
||||
img_read_packet,
|
||||
img_read_close,
|
||||
NULL,
|
||||
AVFMT_NOFILE | AVFMT_NEEDNUMBER,
|
||||
};
|
||||
|
||||
static AVOutputFormat pgmyuv_oformat = {
|
||||
"pgmyuv",
|
||||
"pgm with YUV content image format",
|
||||
"",
|
||||
"pgm",
|
||||
sizeof(VideoData),
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_RAWVIDEO,
|
||||
img_write_header,
|
||||
img_write_packet,
|
||||
img_write_trailer,
|
||||
AVFMT_NOFILE | AVFMT_NEEDNUMBER,
|
||||
};
|
||||
|
||||
static AVInputFormat ppm_iformat = {
|
||||
"ppm",
|
||||
"ppm image format",
|
||||
sizeof(VideoData),
|
||||
NULL,
|
||||
img_read_header,
|
||||
img_read_packet,
|
||||
img_read_close,
|
||||
NULL,
|
||||
AVFMT_NOFILE | AVFMT_NEEDNUMBER | AVFMT_RGB24,
|
||||
.extensions = "ppm",
|
||||
};
|
||||
|
||||
static AVOutputFormat ppm_oformat = {
|
||||
"ppm",
|
||||
"ppm image format",
|
||||
"",
|
||||
"ppm",
|
||||
sizeof(VideoData),
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_RAWVIDEO,
|
||||
img_write_header,
|
||||
img_write_packet,
|
||||
img_write_trailer,
|
||||
AVFMT_NOFILE | AVFMT_NEEDNUMBER | AVFMT_RGB24,
|
||||
};
|
||||
|
||||
static AVInputFormat imgyuv_iformat = {
|
||||
".Y.U.V",
|
||||
".Y.U.V format",
|
||||
sizeof(VideoData),
|
||||
NULL,
|
||||
img_read_header,
|
||||
img_read_packet,
|
||||
img_read_close,
|
||||
NULL,
|
||||
AVFMT_NOFILE | AVFMT_NEEDNUMBER,
|
||||
.extensions = "Y",
|
||||
};
|
||||
|
||||
static AVOutputFormat imgyuv_oformat = {
|
||||
".Y.U.V",
|
||||
".Y.U.V format",
|
||||
"",
|
||||
"Y",
|
||||
sizeof(VideoData),
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_RAWVIDEO,
|
||||
img_write_header,
|
||||
img_write_packet,
|
||||
img_write_trailer,
|
||||
AVFMT_NOFILE | AVFMT_NEEDNUMBER,
|
||||
};
|
||||
|
||||
static AVInputFormat pgmpipe_iformat = {
|
||||
"pgmpipe",
|
||||
"PGM pipe format",
|
||||
sizeof(VideoData),
|
||||
NULL, /* no probe */
|
||||
img_read_header,
|
||||
img_read_packet,
|
||||
img_read_close,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static AVOutputFormat pgmpipe_oformat = {
|
||||
"pgmpipe",
|
||||
"PGM pipe format",
|
||||
"",
|
||||
"pgm",
|
||||
sizeof(VideoData),
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_RAWVIDEO,
|
||||
img_write_header,
|
||||
img_write_packet,
|
||||
img_write_trailer,
|
||||
};
|
||||
|
||||
static AVInputFormat pgmyuvpipe_iformat = {
|
||||
"pgmyuvpipe",
|
||||
"PGM YUV pipe format",
|
||||
sizeof(VideoData),
|
||||
NULL, /* no probe */
|
||||
img_read_header,
|
||||
img_read_packet,
|
||||
img_read_close,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static AVOutputFormat pgmyuvpipe_oformat = {
|
||||
"pgmyuvpipe",
|
||||
"PGM YUV pipe format",
|
||||
"",
|
||||
"pgm",
|
||||
sizeof(VideoData),
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_RAWVIDEO,
|
||||
img_write_header,
|
||||
img_write_packet,
|
||||
img_write_trailer,
|
||||
};
|
||||
|
||||
static AVInputFormat ppmpipe_iformat = {
|
||||
"ppmpipe",
|
||||
"PPM pipe format",
|
||||
sizeof(VideoData),
|
||||
NULL, /* no probe */
|
||||
img_read_header,
|
||||
img_read_packet,
|
||||
img_read_close,
|
||||
NULL,
|
||||
.flags = AVFMT_RGB24,
|
||||
};
|
||||
|
||||
static AVOutputFormat ppmpipe_oformat = {
|
||||
"ppmpipe",
|
||||
"PPM pipe format",
|
||||
"",
|
||||
"ppm",
|
||||
sizeof(VideoData),
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_RAWVIDEO,
|
||||
img_write_header,
|
||||
img_write_packet,
|
||||
img_write_trailer,
|
||||
.flags = AVFMT_RGB24,
|
||||
};
|
||||
|
||||
|
||||
static AVOutputFormat yuv4mpegpipe_oformat = {
|
||||
"yuv4mpegpipe",
|
||||
"YUV4MPEG pipe format",
|
||||
"",
|
||||
"yuv4mpeg",
|
||||
sizeof(VideoData),
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_RAWVIDEO,
|
||||
img_write_header,
|
||||
img_write_packet,
|
||||
img_write_trailer,
|
||||
};
|
||||
|
||||
|
||||
int img_init(void)
|
||||
{
|
||||
av_register_input_format(&pgm_iformat);
|
||||
av_register_output_format(&pgm_oformat);
|
||||
|
||||
av_register_input_format(&pgmyuv_iformat);
|
||||
av_register_output_format(&pgmyuv_oformat);
|
||||
|
||||
av_register_input_format(&ppm_iformat);
|
||||
av_register_output_format(&ppm_oformat);
|
||||
|
||||
av_register_input_format(&imgyuv_iformat);
|
||||
av_register_output_format(&imgyuv_oformat);
|
||||
|
||||
av_register_input_format(&pgmpipe_iformat);
|
||||
av_register_output_format(&pgmpipe_oformat);
|
||||
|
||||
av_register_input_format(&pgmyuvpipe_iformat);
|
||||
av_register_output_format(&pgmyuvpipe_oformat);
|
||||
|
||||
av_register_input_format(&ppmpipe_iformat);
|
||||
av_register_output_format(&ppmpipe_oformat);
|
||||
|
||||
av_register_output_format(&yuv4mpegpipe_oformat);
|
||||
|
||||
return 0;
|
||||
}
|
||||
266
libavformat/jpeg.c
Normal file
266
libavformat/jpeg.c
Normal file
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* JPEG based formats
|
||||
* Copyright (c) 2000, 2001 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
|
||||
/* Multipart JPEG */
|
||||
|
||||
#define BOUNDARY_TAG "ffserver"
|
||||
|
||||
static int mpjpeg_write_header(AVFormatContext *s)
|
||||
{
|
||||
UINT8 buf1[256];
|
||||
|
||||
snprintf(buf1, sizeof(buf1), "--%s\n", BOUNDARY_TAG);
|
||||
put_buffer(&s->pb, buf1, strlen(buf1));
|
||||
put_flush_packet(&s->pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpjpeg_write_packet(AVFormatContext *s, int stream_index,
|
||||
UINT8 *buf, int size, int force_pts)
|
||||
{
|
||||
UINT8 buf1[256];
|
||||
|
||||
snprintf(buf1, sizeof(buf1), "Content-type: image/jpeg\n\n");
|
||||
put_buffer(&s->pb, buf1, strlen(buf1));
|
||||
put_buffer(&s->pb, buf, size);
|
||||
|
||||
snprintf(buf1, sizeof(buf1), "\n--%s\n", BOUNDARY_TAG);
|
||||
put_buffer(&s->pb, buf1, strlen(buf1));
|
||||
put_flush_packet(&s->pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpjpeg_write_trailer(AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVOutputFormat mpjpeg_format = {
|
||||
"mpjpeg",
|
||||
"Mime multipart JPEG format",
|
||||
"multipart/x-mixed-replace;boundary=" BOUNDARY_TAG,
|
||||
"mjpg",
|
||||
0,
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_MJPEG,
|
||||
mpjpeg_write_header,
|
||||
mpjpeg_write_packet,
|
||||
mpjpeg_write_trailer,
|
||||
};
|
||||
|
||||
|
||||
/*************************************/
|
||||
/* single frame JPEG */
|
||||
|
||||
static int single_jpeg_write_header(AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int single_jpeg_write_packet(AVFormatContext *s, int stream_index,
|
||||
UINT8 *buf, int size, int force_pts)
|
||||
{
|
||||
put_buffer(&s->pb, buf, size);
|
||||
put_flush_packet(&s->pb);
|
||||
return 1; /* no more data can be sent */
|
||||
}
|
||||
|
||||
static int single_jpeg_write_trailer(AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVOutputFormat single_jpeg_format = {
|
||||
"singlejpeg",
|
||||
"single JPEG image",
|
||||
"image/jpeg",
|
||||
NULL, /* note: no extension to favorize jpeg multiple images match */
|
||||
0,
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_MJPEG,
|
||||
single_jpeg_write_header,
|
||||
single_jpeg_write_packet,
|
||||
single_jpeg_write_trailer,
|
||||
};
|
||||
|
||||
/*************************************/
|
||||
/* multiple jpeg images */
|
||||
|
||||
typedef struct JpegContext {
|
||||
char path[1024];
|
||||
int img_number;
|
||||
} JpegContext;
|
||||
|
||||
static int jpeg_write_header(AVFormatContext *s1)
|
||||
{
|
||||
JpegContext *s;
|
||||
|
||||
s = av_mallocz(sizeof(JpegContext));
|
||||
if (!s)
|
||||
return -1;
|
||||
s1->priv_data = s;
|
||||
pstrcpy(s->path, sizeof(s->path), s1->filename);
|
||||
s->img_number = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jpeg_write_packet(AVFormatContext *s1, int stream_index,
|
||||
UINT8 *buf, int size, int force_pts)
|
||||
{
|
||||
JpegContext *s = s1->priv_data;
|
||||
char filename[1024];
|
||||
ByteIOContext f1, *pb = &f1;
|
||||
|
||||
if (get_frame_filename(filename, sizeof(filename),
|
||||
s->path, s->img_number) < 0)
|
||||
return -EIO;
|
||||
if (url_fopen(pb, filename, URL_WRONLY) < 0)
|
||||
return -EIO;
|
||||
|
||||
put_buffer(pb, buf, size);
|
||||
put_flush_packet(pb);
|
||||
|
||||
url_fclose(pb);
|
||||
s->img_number++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jpeg_write_trailer(AVFormatContext *s1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***/
|
||||
|
||||
static int jpeg_read_header(AVFormatContext *s1, AVFormatParameters *ap)
|
||||
{
|
||||
JpegContext *s;
|
||||
int i;
|
||||
char buf[1024];
|
||||
ByteIOContext pb1, *f = &pb1;
|
||||
AVStream *st;
|
||||
|
||||
s = av_mallocz(sizeof(JpegContext));
|
||||
if (!s)
|
||||
return -1;
|
||||
s1->priv_data = s;
|
||||
pstrcpy(s->path, sizeof(s->path), s1->filename);
|
||||
|
||||
s1->nb_streams = 1;
|
||||
st = av_mallocz(sizeof(AVStream));
|
||||
if (!st) {
|
||||
av_free(s);
|
||||
return -ENOMEM;
|
||||
}
|
||||
s1->streams[0] = st;
|
||||
s->img_number = 0;
|
||||
|
||||
/* try to find the first image */
|
||||
for(i=0;i<5;i++) {
|
||||
if (get_frame_filename(buf, sizeof(buf), s->path, s->img_number) < 0)
|
||||
goto fail;
|
||||
if (url_fopen(f, buf, URL_RDONLY) >= 0)
|
||||
break;
|
||||
s->img_number++;
|
||||
}
|
||||
if (i == 5)
|
||||
goto fail;
|
||||
url_fclose(f);
|
||||
st->codec.codec_type = CODEC_TYPE_VIDEO;
|
||||
st->codec.codec_id = CODEC_ID_MJPEG;
|
||||
|
||||
if (!ap || !ap->frame_rate)
|
||||
st->codec.frame_rate = 25 * FRAME_RATE_BASE;
|
||||
else
|
||||
st->codec.frame_rate = ap->frame_rate;
|
||||
return 0;
|
||||
fail:
|
||||
av_free(s);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int jpeg_read_packet(AVFormatContext *s1, AVPacket *pkt)
|
||||
{
|
||||
JpegContext *s = s1->priv_data;
|
||||
char filename[1024];
|
||||
int size;
|
||||
ByteIOContext f1, *f = &f1;
|
||||
|
||||
if (get_frame_filename(filename, sizeof(filename),
|
||||
s->path, s->img_number) < 0)
|
||||
return -EIO;
|
||||
|
||||
f = &f1;
|
||||
if (url_fopen(f, filename, URL_RDONLY) < 0)
|
||||
return -EIO;
|
||||
|
||||
size = url_seek(url_fileno(f), 0, SEEK_END);
|
||||
url_seek(url_fileno(f), 0, SEEK_SET);
|
||||
|
||||
av_new_packet(pkt, size);
|
||||
pkt->stream_index = 0;
|
||||
get_buffer(f, pkt->data, size);
|
||||
|
||||
url_fclose(f);
|
||||
s->img_number++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jpeg_read_close(AVFormatContext *s1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVInputFormat jpeg_iformat = {
|
||||
"jpeg",
|
||||
"JPEG image",
|
||||
sizeof(JpegContext),
|
||||
NULL,
|
||||
jpeg_read_header,
|
||||
jpeg_read_packet,
|
||||
jpeg_read_close,
|
||||
NULL,
|
||||
.flags = AVFMT_NOFILE | AVFMT_NEEDNUMBER,
|
||||
.extensions = "jpg,jpeg",
|
||||
};
|
||||
|
||||
static AVOutputFormat jpeg_oformat = {
|
||||
"jpeg",
|
||||
"JPEG image",
|
||||
"image/jpeg",
|
||||
"jpg,jpeg",
|
||||
sizeof(JpegContext),
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_MJPEG,
|
||||
jpeg_write_header,
|
||||
jpeg_write_packet,
|
||||
jpeg_write_trailer,
|
||||
.flags = AVFMT_NOFILE | AVFMT_NEEDNUMBER,
|
||||
};
|
||||
|
||||
int jpeg_init(void)
|
||||
{
|
||||
av_register_output_format(&mpjpeg_format);
|
||||
av_register_output_format(&single_jpeg_format);
|
||||
av_register_input_format(&jpeg_iformat);
|
||||
av_register_output_format(&jpeg_oformat);
|
||||
return 0;
|
||||
}
|
||||
1347
libavformat/mov.c
Normal file
1347
libavformat/mov.c
Normal file
File diff suppressed because it is too large
Load Diff
685
libavformat/mpeg.c
Normal file
685
libavformat/mpeg.c
Normal file
@@ -0,0 +1,685 @@
|
||||
/*
|
||||
* MPEG1/2 mux/demux
|
||||
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
|
||||
#define MAX_PAYLOAD_SIZE 4096
|
||||
#define NB_STREAMS 2
|
||||
|
||||
typedef struct {
|
||||
UINT8 buffer[MAX_PAYLOAD_SIZE];
|
||||
int buffer_ptr;
|
||||
UINT8 id;
|
||||
int max_buffer_size; /* in bytes */
|
||||
int packet_number;
|
||||
INT64 start_pts;
|
||||
} StreamInfo;
|
||||
|
||||
typedef struct {
|
||||
int packet_size; /* required packet size */
|
||||
int packet_data_max_size; /* maximum data size inside a packet */
|
||||
int packet_number;
|
||||
int pack_header_freq; /* frequency (in packets^-1) at which we send pack headers */
|
||||
int system_header_freq;
|
||||
int mux_rate; /* bitrate in units of 50 bytes/s */
|
||||
/* stream info */
|
||||
int audio_bound;
|
||||
int video_bound;
|
||||
int is_mpeg2;
|
||||
int is_vcd;
|
||||
} MpegMuxContext;
|
||||
|
||||
#define PACK_START_CODE ((unsigned int)0x000001ba)
|
||||
#define SYSTEM_HEADER_START_CODE ((unsigned int)0x000001bb)
|
||||
#define SEQUENCE_END_CODE ((unsigned int)0x000001b7)
|
||||
#define PACKET_START_CODE_MASK ((unsigned int)0xffffff00)
|
||||
#define PACKET_START_CODE_PREFIX ((unsigned int)0x00000100)
|
||||
#define ISO_11172_END_CODE ((unsigned int)0x000001b9)
|
||||
|
||||
/* mpeg2 */
|
||||
#define PROGRAM_STREAM_MAP 0x1bc
|
||||
#define PRIVATE_STREAM_1 0x1bd
|
||||
#define PADDING_STREAM 0x1be
|
||||
#define PRIVATE_STREAM_2 0x1bf
|
||||
|
||||
|
||||
#define AUDIO_ID 0xc0
|
||||
#define VIDEO_ID 0xe0
|
||||
|
||||
extern AVOutputFormat mpeg1system_mux;
|
||||
extern AVOutputFormat mpeg1vcd_mux;
|
||||
extern AVOutputFormat mpeg2vob_mux;
|
||||
|
||||
static int put_pack_header(AVFormatContext *ctx,
|
||||
UINT8 *buf, INT64 timestamp)
|
||||
{
|
||||
MpegMuxContext *s = ctx->priv_data;
|
||||
PutBitContext pb;
|
||||
|
||||
init_put_bits(&pb, buf, 128, NULL, NULL);
|
||||
|
||||
put_bits(&pb, 32, PACK_START_CODE);
|
||||
if (s->is_mpeg2) {
|
||||
put_bits(&pb, 2, 0x2);
|
||||
} else {
|
||||
put_bits(&pb, 4, 0x2);
|
||||
}
|
||||
put_bits(&pb, 3, (UINT32)((timestamp >> 30) & 0x07));
|
||||
put_bits(&pb, 1, 1);
|
||||
put_bits(&pb, 15, (UINT32)((timestamp >> 15) & 0x7fff));
|
||||
put_bits(&pb, 1, 1);
|
||||
put_bits(&pb, 15, (UINT32)((timestamp) & 0x7fff));
|
||||
put_bits(&pb, 1, 1);
|
||||
if (s->is_mpeg2) {
|
||||
/* clock extension */
|
||||
put_bits(&pb, 9, 0);
|
||||
put_bits(&pb, 1, 1);
|
||||
}
|
||||
put_bits(&pb, 1, 1);
|
||||
put_bits(&pb, 22, s->mux_rate);
|
||||
put_bits(&pb, 1, 1);
|
||||
if (s->is_mpeg2) {
|
||||
put_bits(&pb, 5, 0x1f); /* reserved */
|
||||
put_bits(&pb, 3, 0); /* stuffing length */
|
||||
}
|
||||
flush_put_bits(&pb);
|
||||
return pbBufPtr(&pb) - pb.buf;
|
||||
}
|
||||
|
||||
static int put_system_header(AVFormatContext *ctx, UINT8 *buf)
|
||||
{
|
||||
MpegMuxContext *s = ctx->priv_data;
|
||||
int size, rate_bound, i, private_stream_coded, id;
|
||||
PutBitContext pb;
|
||||
|
||||
init_put_bits(&pb, buf, 128, NULL, NULL);
|
||||
|
||||
put_bits(&pb, 32, SYSTEM_HEADER_START_CODE);
|
||||
put_bits(&pb, 16, 0);
|
||||
put_bits(&pb, 1, 1);
|
||||
|
||||
rate_bound = s->mux_rate; /* maximum bit rate of the multiplexed stream */
|
||||
put_bits(&pb, 22, rate_bound);
|
||||
put_bits(&pb, 1, 1); /* marker */
|
||||
put_bits(&pb, 6, s->audio_bound);
|
||||
|
||||
put_bits(&pb, 1, 1); /* variable bitrate */
|
||||
put_bits(&pb, 1, 1); /* non constrainted bit stream */
|
||||
|
||||
put_bits(&pb, 1, 0); /* audio locked */
|
||||
put_bits(&pb, 1, 0); /* video locked */
|
||||
put_bits(&pb, 1, 1); /* marker */
|
||||
|
||||
put_bits(&pb, 5, s->video_bound);
|
||||
put_bits(&pb, 8, 0xff); /* reserved byte */
|
||||
|
||||
/* audio stream info */
|
||||
private_stream_coded = 0;
|
||||
for(i=0;i<ctx->nb_streams;i++) {
|
||||
StreamInfo *stream = ctx->streams[i]->priv_data;
|
||||
id = stream->id;
|
||||
if (id < 0xc0) {
|
||||
/* special case for private streams (AC3 use that) */
|
||||
if (private_stream_coded)
|
||||
continue;
|
||||
private_stream_coded = 1;
|
||||
id = 0xbd;
|
||||
}
|
||||
put_bits(&pb, 8, id); /* stream ID */
|
||||
put_bits(&pb, 2, 3);
|
||||
if (id < 0xe0) {
|
||||
/* audio */
|
||||
put_bits(&pb, 1, 0);
|
||||
put_bits(&pb, 13, stream->max_buffer_size / 128);
|
||||
} else {
|
||||
/* video */
|
||||
put_bits(&pb, 1, 1);
|
||||
put_bits(&pb, 13, stream->max_buffer_size / 1024);
|
||||
}
|
||||
}
|
||||
flush_put_bits(&pb);
|
||||
size = pbBufPtr(&pb) - pb.buf;
|
||||
/* patch packet size */
|
||||
buf[4] = (size - 6) >> 8;
|
||||
buf[5] = (size - 6) & 0xff;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int mpeg_mux_init(AVFormatContext *ctx)
|
||||
{
|
||||
MpegMuxContext *s = ctx->priv_data;
|
||||
int bitrate, i, mpa_id, mpv_id, ac3_id;
|
||||
AVStream *st;
|
||||
StreamInfo *stream;
|
||||
|
||||
s->packet_number = 0;
|
||||
s->is_vcd = (ctx->oformat == &mpeg1vcd_mux);
|
||||
s->is_mpeg2 = (ctx->oformat == &mpeg2vob_mux);
|
||||
|
||||
if (s->is_vcd)
|
||||
s->packet_size = 2324; /* VCD packet size */
|
||||
else
|
||||
s->packet_size = 2048;
|
||||
|
||||
/* startcode(4) + length(2) + flags(1) */
|
||||
s->packet_data_max_size = s->packet_size - 7;
|
||||
s->audio_bound = 0;
|
||||
s->video_bound = 0;
|
||||
mpa_id = AUDIO_ID;
|
||||
ac3_id = 0x80;
|
||||
mpv_id = VIDEO_ID;
|
||||
for(i=0;i<ctx->nb_streams;i++) {
|
||||
st = ctx->streams[i];
|
||||
stream = av_mallocz(sizeof(StreamInfo));
|
||||
if (!stream)
|
||||
goto fail;
|
||||
st->priv_data = stream;
|
||||
|
||||
switch(st->codec.codec_type) {
|
||||
case CODEC_TYPE_AUDIO:
|
||||
if (st->codec.codec_id == CODEC_ID_AC3)
|
||||
stream->id = ac3_id++;
|
||||
else
|
||||
stream->id = mpa_id++;
|
||||
stream->max_buffer_size = 4 * 1024;
|
||||
s->audio_bound++;
|
||||
break;
|
||||
case CODEC_TYPE_VIDEO:
|
||||
stream->id = mpv_id++;
|
||||
stream->max_buffer_size = 46 * 1024;
|
||||
s->video_bound++;
|
||||
break;
|
||||
default:
|
||||
av_abort();
|
||||
}
|
||||
}
|
||||
|
||||
/* we increase slightly the bitrate to take into account the
|
||||
headers. XXX: compute it exactly */
|
||||
bitrate = 2000;
|
||||
for(i=0;i<ctx->nb_streams;i++) {
|
||||
st = ctx->streams[i];
|
||||
bitrate += st->codec.bit_rate;
|
||||
}
|
||||
s->mux_rate = (bitrate + (8 * 50) - 1) / (8 * 50);
|
||||
|
||||
if (s->is_vcd || s->is_mpeg2)
|
||||
/* every packet */
|
||||
s->pack_header_freq = 1;
|
||||
else
|
||||
/* every 2 seconds */
|
||||
s->pack_header_freq = 2 * bitrate / s->packet_size / 8;
|
||||
|
||||
if (s->is_mpeg2)
|
||||
/* every 200 packets. Need to look at the spec. */
|
||||
s->system_header_freq = s->pack_header_freq * 40;
|
||||
else if (s->is_vcd)
|
||||
/* every 40 packets, this is my invention */
|
||||
s->system_header_freq = s->pack_header_freq * 40;
|
||||
else
|
||||
s->system_header_freq = s->pack_header_freq * 5;
|
||||
|
||||
for(i=0;i<ctx->nb_streams;i++) {
|
||||
stream = ctx->streams[i]->priv_data;
|
||||
stream->buffer_ptr = 0;
|
||||
stream->packet_number = 0;
|
||||
stream->start_pts = -1;
|
||||
}
|
||||
return 0;
|
||||
fail:
|
||||
for(i=0;i<ctx->nb_streams;i++) {
|
||||
av_free(ctx->streams[i]->priv_data);
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* flush the packet on stream stream_index */
|
||||
static void flush_packet(AVFormatContext *ctx, int stream_index, int last_pkt)
|
||||
{
|
||||
MpegMuxContext *s = ctx->priv_data;
|
||||
StreamInfo *stream = ctx->streams[stream_index]->priv_data;
|
||||
UINT8 *buf_ptr;
|
||||
int size, payload_size, startcode, id, len, stuffing_size, i, header_len;
|
||||
INT64 timestamp;
|
||||
UINT8 buffer[128];
|
||||
int last = last_pkt ? 4 : 0;
|
||||
|
||||
id = stream->id;
|
||||
timestamp = stream->start_pts;
|
||||
|
||||
#if 0
|
||||
printf("packet ID=%2x PTS=%0.3f\n",
|
||||
id, timestamp / 90000.0);
|
||||
#endif
|
||||
|
||||
buf_ptr = buffer;
|
||||
if (((s->packet_number % s->pack_header_freq) == 0)) {
|
||||
/* output pack and systems header if needed */
|
||||
size = put_pack_header(ctx, buf_ptr, timestamp);
|
||||
buf_ptr += size;
|
||||
if ((s->packet_number % s->system_header_freq) == 0) {
|
||||
size = put_system_header(ctx, buf_ptr);
|
||||
buf_ptr += size;
|
||||
}
|
||||
}
|
||||
size = buf_ptr - buffer;
|
||||
put_buffer(&ctx->pb, buffer, size);
|
||||
|
||||
/* packet header */
|
||||
if (s->is_mpeg2) {
|
||||
header_len = 8;
|
||||
} else {
|
||||
header_len = 5;
|
||||
}
|
||||
payload_size = s->packet_size - (size + 6 + header_len + last);
|
||||
if (id < 0xc0) {
|
||||
startcode = PRIVATE_STREAM_1;
|
||||
payload_size -= 4;
|
||||
} else {
|
||||
startcode = 0x100 + id;
|
||||
}
|
||||
stuffing_size = payload_size - stream->buffer_ptr;
|
||||
if (stuffing_size < 0)
|
||||
stuffing_size = 0;
|
||||
|
||||
put_be32(&ctx->pb, startcode);
|
||||
|
||||
put_be16(&ctx->pb, payload_size + header_len);
|
||||
/* stuffing */
|
||||
for(i=0;i<stuffing_size;i++)
|
||||
put_byte(&ctx->pb, 0xff);
|
||||
|
||||
if (s->is_mpeg2) {
|
||||
put_byte(&ctx->pb, 0x80); /* mpeg2 id */
|
||||
put_byte(&ctx->pb, 0x80); /* flags */
|
||||
put_byte(&ctx->pb, 0x05); /* header len (only pts is included) */
|
||||
}
|
||||
put_byte(&ctx->pb,
|
||||
(0x02 << 4) |
|
||||
(((timestamp >> 30) & 0x07) << 1) |
|
||||
1);
|
||||
put_be16(&ctx->pb, (UINT16)((((timestamp >> 15) & 0x7fff) << 1) | 1));
|
||||
put_be16(&ctx->pb, (UINT16)((((timestamp) & 0x7fff) << 1) | 1));
|
||||
|
||||
if (startcode == PRIVATE_STREAM_1) {
|
||||
put_byte(&ctx->pb, id);
|
||||
if (id >= 0x80 && id <= 0xbf) {
|
||||
/* XXX: need to check AC3 spec */
|
||||
put_byte(&ctx->pb, 1);
|
||||
put_byte(&ctx->pb, 0);
|
||||
put_byte(&ctx->pb, 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (last_pkt) {
|
||||
put_be32(&ctx->pb, ISO_11172_END_CODE);
|
||||
}
|
||||
/* output data */
|
||||
put_buffer(&ctx->pb, stream->buffer, payload_size - stuffing_size);
|
||||
put_flush_packet(&ctx->pb);
|
||||
|
||||
/* preserve remaining data */
|
||||
len = stream->buffer_ptr - payload_size;
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
memmove(stream->buffer, stream->buffer + stream->buffer_ptr - len, len);
|
||||
stream->buffer_ptr = len;
|
||||
|
||||
s->packet_number++;
|
||||
stream->packet_number++;
|
||||
stream->start_pts = -1;
|
||||
}
|
||||
|
||||
static int mpeg_mux_write_packet(AVFormatContext *ctx, int stream_index,
|
||||
UINT8 *buf, int size, int pts)
|
||||
{
|
||||
MpegMuxContext *s = ctx->priv_data;
|
||||
AVStream *st = ctx->streams[stream_index];
|
||||
StreamInfo *stream = st->priv_data;
|
||||
int len;
|
||||
|
||||
while (size > 0) {
|
||||
/* set pts */
|
||||
if (stream->start_pts == -1) {
|
||||
stream->start_pts = pts;
|
||||
}
|
||||
len = s->packet_data_max_size - stream->buffer_ptr;
|
||||
if (len > size)
|
||||
len = size;
|
||||
memcpy(stream->buffer + stream->buffer_ptr, buf, len);
|
||||
stream->buffer_ptr += len;
|
||||
buf += len;
|
||||
size -= len;
|
||||
while (stream->buffer_ptr >= s->packet_data_max_size) {
|
||||
/* output the packet */
|
||||
if (stream->start_pts == -1)
|
||||
stream->start_pts = pts;
|
||||
flush_packet(ctx, stream_index, 0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpeg_mux_end(AVFormatContext *ctx)
|
||||
{
|
||||
StreamInfo *stream;
|
||||
int i;
|
||||
|
||||
/* flush each packet */
|
||||
for(i=0;i<ctx->nb_streams;i++) {
|
||||
stream = ctx->streams[i]->priv_data;
|
||||
if (stream->buffer_ptr > 0) {
|
||||
if (i == (ctx->nb_streams - 1))
|
||||
flush_packet(ctx, i, 1);
|
||||
else
|
||||
flush_packet(ctx, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* write the end header */
|
||||
//put_be32(&ctx->pb, ISO_11172_END_CODE);
|
||||
//put_flush_packet(&ctx->pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
/* demux code */
|
||||
|
||||
#define MAX_SYNC_SIZE 100000
|
||||
|
||||
static int mpegps_probe(AVProbeData *p)
|
||||
{
|
||||
int code, c, i;
|
||||
code = 0xff;
|
||||
|
||||
/* we search the first start code. If it is a packet start code,
|
||||
then we decide it is mpeg ps. We do not send highest value to
|
||||
give a chance to mpegts */
|
||||
for(i=0;i<p->buf_size;i++) {
|
||||
c = p->buf[i];
|
||||
code = (code << 8) | c;
|
||||
if ((code & 0xffffff00) == 0x100) {
|
||||
if (code == PACK_START_CODE ||
|
||||
code == SYSTEM_HEADER_START_CODE ||
|
||||
(code >= 0x1e0 && code <= 0x1ef) ||
|
||||
(code >= 0x1c0 && code <= 0x1df) ||
|
||||
code == PRIVATE_STREAM_2 ||
|
||||
code == PROGRAM_STREAM_MAP ||
|
||||
code == PRIVATE_STREAM_1 ||
|
||||
code == PADDING_STREAM)
|
||||
return AVPROBE_SCORE_MAX - 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
typedef struct MpegDemuxContext {
|
||||
int header_state;
|
||||
} MpegDemuxContext;
|
||||
|
||||
static int find_start_code(ByteIOContext *pb, int *size_ptr,
|
||||
UINT32 *header_state)
|
||||
{
|
||||
unsigned int state, v;
|
||||
int val, n;
|
||||
|
||||
state = *header_state;
|
||||
n = *size_ptr;
|
||||
while (n > 0) {
|
||||
if (url_feof(pb))
|
||||
break;
|
||||
v = get_byte(pb);
|
||||
n--;
|
||||
if (state == 0x000001) {
|
||||
state = ((state << 8) | v) & 0xffffff;
|
||||
val = state;
|
||||
goto found;
|
||||
}
|
||||
state = ((state << 8) | v) & 0xffffff;
|
||||
}
|
||||
val = -1;
|
||||
found:
|
||||
*header_state = state;
|
||||
*size_ptr = n;
|
||||
return val;
|
||||
}
|
||||
|
||||
static int mpegps_read_header(AVFormatContext *s,
|
||||
AVFormatParameters *ap)
|
||||
{
|
||||
MpegDemuxContext *m = s->priv_data;
|
||||
m->header_state = 0xff;
|
||||
/* no need to do more */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static INT64 get_pts(ByteIOContext *pb, int c)
|
||||
{
|
||||
INT64 pts;
|
||||
int val;
|
||||
|
||||
if (c < 0)
|
||||
c = get_byte(pb);
|
||||
pts = (INT64)((c >> 1) & 0x07) << 30;
|
||||
val = get_be16(pb);
|
||||
pts |= (INT64)(val >> 1) << 15;
|
||||
val = get_be16(pb);
|
||||
pts |= (INT64)(val >> 1);
|
||||
return pts;
|
||||
}
|
||||
|
||||
static int mpegps_read_packet(AVFormatContext *s,
|
||||
AVPacket *pkt)
|
||||
{
|
||||
MpegDemuxContext *m = s->priv_data;
|
||||
AVStream *st;
|
||||
int len, size, startcode, i, c, flags, header_len, type, codec_id;
|
||||
INT64 pts, dts;
|
||||
|
||||
/* next start code (should be immediately after) */
|
||||
redo:
|
||||
m->header_state = 0xff;
|
||||
size = MAX_SYNC_SIZE;
|
||||
startcode = find_start_code(&s->pb, &size, &m->header_state);
|
||||
//printf("startcode=%x pos=0x%Lx\n", startcode, url_ftell(&s->pb));
|
||||
if (startcode < 0)
|
||||
return -EIO;
|
||||
if (startcode == PACK_START_CODE)
|
||||
goto redo;
|
||||
if (startcode == SYSTEM_HEADER_START_CODE)
|
||||
goto redo;
|
||||
if (startcode == PADDING_STREAM ||
|
||||
startcode == PRIVATE_STREAM_2) {
|
||||
/* skip them */
|
||||
len = get_be16(&s->pb);
|
||||
url_fskip(&s->pb, len);
|
||||
goto redo;
|
||||
}
|
||||
/* find matching stream */
|
||||
if (!((startcode >= 0x1c0 && startcode <= 0x1df) ||
|
||||
(startcode >= 0x1e0 && startcode <= 0x1ef) ||
|
||||
(startcode == 0x1bd)))
|
||||
goto redo;
|
||||
|
||||
len = get_be16(&s->pb);
|
||||
pts = AV_NOPTS_VALUE;
|
||||
dts = AV_NOPTS_VALUE;
|
||||
/* stuffing */
|
||||
for(;;) {
|
||||
c = get_byte(&s->pb);
|
||||
len--;
|
||||
/* XXX: for mpeg1, should test only bit 7 */
|
||||
if (c != 0xff)
|
||||
break;
|
||||
}
|
||||
if ((c & 0xc0) == 0x40) {
|
||||
/* buffer scale & size */
|
||||
get_byte(&s->pb);
|
||||
c = get_byte(&s->pb);
|
||||
len -= 2;
|
||||
}
|
||||
if ((c & 0xf0) == 0x20) {
|
||||
pts = get_pts(&s->pb, c);
|
||||
len -= 4;
|
||||
} else if ((c & 0xf0) == 0x30) {
|
||||
pts = get_pts(&s->pb, c);
|
||||
dts = get_pts(&s->pb, -1);
|
||||
len -= 9;
|
||||
} else if ((c & 0xc0) == 0x80) {
|
||||
/* mpeg 2 PES */
|
||||
if ((c & 0x30) != 0) {
|
||||
fprintf(stderr, "Encrypted multiplex not handled\n");
|
||||
return -EIO;
|
||||
}
|
||||
flags = get_byte(&s->pb);
|
||||
header_len = get_byte(&s->pb);
|
||||
len -= 2;
|
||||
if (header_len > len)
|
||||
goto redo;
|
||||
if ((flags & 0xc0) == 0x80) {
|
||||
pts = get_pts(&s->pb, -1);
|
||||
header_len -= 5;
|
||||
len -= 5;
|
||||
} if ((flags & 0xc0) == 0xc0) {
|
||||
pts = get_pts(&s->pb, -1);
|
||||
dts = get_pts(&s->pb, -1);
|
||||
header_len -= 10;
|
||||
len -= 10;
|
||||
}
|
||||
len -= header_len;
|
||||
while (header_len > 0) {
|
||||
get_byte(&s->pb);
|
||||
header_len--;
|
||||
}
|
||||
}
|
||||
if (startcode == 0x1bd) {
|
||||
startcode = get_byte(&s->pb);
|
||||
len--;
|
||||
if (startcode >= 0x80 && startcode <= 0xbf) {
|
||||
/* audio: skip header */
|
||||
get_byte(&s->pb);
|
||||
get_byte(&s->pb);
|
||||
get_byte(&s->pb);
|
||||
len -= 3;
|
||||
}
|
||||
}
|
||||
|
||||
/* now find stream */
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
st = s->streams[i];
|
||||
if (st->id == startcode)
|
||||
goto found;
|
||||
}
|
||||
if (startcode >= 0x1e0 && startcode <= 0x1ef) {
|
||||
type = CODEC_TYPE_VIDEO;
|
||||
codec_id = CODEC_ID_MPEG1VIDEO;
|
||||
} else if (startcode >= 0x1c0 && startcode <= 0x1df) {
|
||||
type = CODEC_TYPE_AUDIO;
|
||||
codec_id = CODEC_ID_MP2;
|
||||
} else if (startcode >= 0x80 && startcode <= 0x9f) {
|
||||
type = CODEC_TYPE_AUDIO;
|
||||
codec_id = CODEC_ID_AC3;
|
||||
} else {
|
||||
skip:
|
||||
/* skip packet */
|
||||
url_fskip(&s->pb, len);
|
||||
goto redo;
|
||||
}
|
||||
/* no stream found: add a new stream */
|
||||
st = av_new_stream(s, startcode);
|
||||
if (!st)
|
||||
goto skip;
|
||||
st->codec.codec_type = type;
|
||||
st->codec.codec_id = codec_id;
|
||||
found:
|
||||
av_new_packet(pkt, len);
|
||||
//printf("\nRead Packet ID: %x PTS: %f Size: %d", startcode,
|
||||
// (float)pts/90000, len);
|
||||
get_buffer(&s->pb, pkt->data, pkt->size);
|
||||
pkt->pts = pts;
|
||||
pkt->stream_index = st->index;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpegps_read_close(AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVOutputFormat mpeg1system_mux = {
|
||||
"mpeg",
|
||||
"MPEG1 System format",
|
||||
"video/x-mpeg",
|
||||
"mpg,mpeg",
|
||||
sizeof(MpegMuxContext),
|
||||
CODEC_ID_MP2,
|
||||
CODEC_ID_MPEG1VIDEO,
|
||||
mpeg_mux_init,
|
||||
mpeg_mux_write_packet,
|
||||
mpeg_mux_end,
|
||||
};
|
||||
|
||||
static AVOutputFormat mpeg1vcd_mux = {
|
||||
"vcd",
|
||||
"MPEG1 System format (VCD)",
|
||||
"video/x-mpeg",
|
||||
NULL,
|
||||
sizeof(MpegMuxContext),
|
||||
CODEC_ID_MP2,
|
||||
CODEC_ID_MPEG1VIDEO,
|
||||
mpeg_mux_init,
|
||||
mpeg_mux_write_packet,
|
||||
mpeg_mux_end,
|
||||
};
|
||||
|
||||
static AVOutputFormat mpeg2vob_mux = {
|
||||
"vob",
|
||||
"MPEG2 PS format (VOB)",
|
||||
"video/x-mpeg",
|
||||
"vob",
|
||||
sizeof(MpegMuxContext),
|
||||
CODEC_ID_MP2,
|
||||
CODEC_ID_MPEG1VIDEO,
|
||||
mpeg_mux_init,
|
||||
mpeg_mux_write_packet,
|
||||
mpeg_mux_end,
|
||||
};
|
||||
|
||||
static AVInputFormat mpegps_demux = {
|
||||
"mpeg",
|
||||
"MPEG PS format",
|
||||
sizeof(MpegDemuxContext),
|
||||
mpegps_probe,
|
||||
mpegps_read_header,
|
||||
mpegps_read_packet,
|
||||
mpegps_read_close,
|
||||
.flags = AVFMT_NOHEADER,
|
||||
};
|
||||
|
||||
int mpegps_init(void)
|
||||
{
|
||||
av_register_output_format(&mpeg1system_mux);
|
||||
av_register_output_format(&mpeg1vcd_mux);
|
||||
av_register_output_format(&mpeg2vob_mux);
|
||||
av_register_input_format(&mpegps_demux);
|
||||
return 0;
|
||||
}
|
||||
316
libavformat/mpegts.c
Normal file
316
libavformat/mpegts.c
Normal file
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* MPEG2 transport stream (aka DVB) demux
|
||||
* Copyright (c) 2002 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
|
||||
#define TS_FEC_PACKET_SIZE 204
|
||||
#define TS_PACKET_SIZE 188
|
||||
#define NB_PID_MAX 8192
|
||||
|
||||
enum MpegTSState {
|
||||
MPEGTS_HEADER = 0,
|
||||
MPEGTS_PESHEADER_FILL,
|
||||
MPEGTS_PESHEADER_FLAGS,
|
||||
MPEGTS_PESHEADER_SIZE,
|
||||
MPEGTS_PESHEADER_READ,
|
||||
MPEGTS_PAYLOAD,
|
||||
MPEGTS_SKIP,
|
||||
};
|
||||
|
||||
/* enough for PES header + length */
|
||||
#define MAX_HEADER_SIZE 6
|
||||
|
||||
typedef struct MpegTSStream {
|
||||
int pid;
|
||||
enum MpegTSState state;
|
||||
int last_cc; /* last cc code (-1 if first packet) */
|
||||
/* used to get the format */
|
||||
int header_size;
|
||||
int payload_size;
|
||||
int pes_header_size;
|
||||
AVStream *st;
|
||||
unsigned char header[MAX_HEADER_SIZE];
|
||||
} MpegTSStream;
|
||||
|
||||
typedef struct MpegTSContext {
|
||||
int raw_packet_size; /* raw packet size, including FEC if present */
|
||||
MpegTSStream *pids[NB_PID_MAX];
|
||||
} MpegTSContext;
|
||||
|
||||
/* autodetect fec presence. Must have at least 1024 bytes */
|
||||
static int get_packet_size(const unsigned char *buf, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (size < (TS_FEC_PACKET_SIZE * 5 + 1))
|
||||
return -1;
|
||||
for(i=0;i<5;i++) {
|
||||
if (buf[i * TS_PACKET_SIZE] != 0x47)
|
||||
goto try_fec;
|
||||
}
|
||||
return TS_PACKET_SIZE;
|
||||
try_fec:
|
||||
for(i=0;i<5;i++) {
|
||||
if (buf[i * TS_FEC_PACKET_SIZE] != 0x47)
|
||||
return -1;
|
||||
}
|
||||
return TS_FEC_PACKET_SIZE;
|
||||
}
|
||||
|
||||
static int mpegts_probe(AVProbeData *p)
|
||||
{
|
||||
int size;
|
||||
size = get_packet_size(p->buf, p->buf_size);
|
||||
if (size < 0)
|
||||
return 0;
|
||||
return AVPROBE_SCORE_MAX;
|
||||
}
|
||||
|
||||
static int mpegts_read_header(AVFormatContext *s,
|
||||
AVFormatParameters *ap)
|
||||
{
|
||||
MpegTSContext *ts = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
unsigned char buf[1024];
|
||||
int len;
|
||||
INT64 pos;
|
||||
|
||||
/* read the first 1024 bytes to get packet size */
|
||||
pos = url_ftell(pb);
|
||||
len = get_buffer(pb, buf, sizeof(buf));
|
||||
if (len != sizeof(buf))
|
||||
goto fail;
|
||||
ts->raw_packet_size = get_packet_size(buf, sizeof(buf));
|
||||
if (ts->raw_packet_size <= 0)
|
||||
goto fail;
|
||||
/* go again to the start */
|
||||
url_fseek(pb, pos, SEEK_SET);
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* return non zero if a packet could be constructed */
|
||||
static int mpegts_push_data(AVFormatContext *s, MpegTSStream *tss,
|
||||
AVPacket *pkt,
|
||||
const unsigned char *buf, int buf_size, int is_start)
|
||||
{
|
||||
AVStream *st;
|
||||
const unsigned char *p;
|
||||
int len, code, codec_type, codec_id;
|
||||
|
||||
if (is_start) {
|
||||
tss->state = MPEGTS_HEADER;
|
||||
tss->header_size = 0;
|
||||
}
|
||||
p = buf;
|
||||
while (buf_size > 0) {
|
||||
len = buf_size;
|
||||
switch(tss->state) {
|
||||
case MPEGTS_HEADER:
|
||||
if (len > MAX_HEADER_SIZE - tss->header_size)
|
||||
len = MAX_HEADER_SIZE - tss->header_size;
|
||||
memcpy(tss->header, p, len);
|
||||
tss->header_size += len;
|
||||
p += len;
|
||||
buf_size -= len;
|
||||
if (tss->header_size == MAX_HEADER_SIZE) {
|
||||
/* we got all the PES or section header. We can now
|
||||
decide */
|
||||
#if 0
|
||||
av_hex_dump(tss->header, tss->header_size);
|
||||
#endif
|
||||
if (tss->header[0] == 0x00 && tss->header[1] == 0x00 &&
|
||||
tss->header[2] == 0x01) {
|
||||
/* it must be an mpeg2 PES stream */
|
||||
/* XXX: add AC3 support */
|
||||
code = tss->header[3] | 0x100;
|
||||
if (!((code >= 0x1c0 && code <= 0x1df) ||
|
||||
(code >= 0x1e0 && code <= 0x1ef)))
|
||||
goto skip;
|
||||
if (!tss->st) {
|
||||
/* allocate stream */
|
||||
if (code >= 0x1c0 && code <= 0x1df) {
|
||||
codec_type = CODEC_TYPE_AUDIO;
|
||||
codec_id = CODEC_ID_MP2;
|
||||
} else {
|
||||
codec_type = CODEC_TYPE_VIDEO;
|
||||
codec_id = CODEC_ID_MPEG1VIDEO;
|
||||
}
|
||||
st = av_new_stream(s, tss->pid);
|
||||
if (st) {
|
||||
st->priv_data = tss;
|
||||
st->codec.codec_type = codec_type;
|
||||
st->codec.codec_id = codec_id;
|
||||
tss->st = st;
|
||||
}
|
||||
}
|
||||
tss->state = MPEGTS_PESHEADER_FILL;
|
||||
tss->payload_size = (tss->header[4] << 8) | tss->header[5];
|
||||
if (tss->payload_size == 0)
|
||||
tss->payload_size = 65536;
|
||||
} else {
|
||||
/* otherwise, it should be a table */
|
||||
/* skip packet */
|
||||
skip:
|
||||
tss->state = MPEGTS_SKIP;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
/**********************************************/
|
||||
/* PES packing parsing */
|
||||
case MPEGTS_PESHEADER_FILL:
|
||||
/* skip filling */
|
||||
code = *p++;
|
||||
buf_size--;
|
||||
tss->payload_size--;
|
||||
if (code != 0xff) {
|
||||
if ((code & 0xc0) != 0x80)
|
||||
goto skip;
|
||||
tss->state = MPEGTS_PESHEADER_FLAGS;
|
||||
}
|
||||
break;
|
||||
case MPEGTS_PESHEADER_FLAGS:
|
||||
code = *p++;
|
||||
buf_size--;
|
||||
tss->payload_size--;
|
||||
tss->state = MPEGTS_PESHEADER_SIZE;
|
||||
break;
|
||||
case MPEGTS_PESHEADER_SIZE:
|
||||
tss->pes_header_size = *p++;
|
||||
buf_size--;
|
||||
tss->payload_size--;
|
||||
tss->state = MPEGTS_PESHEADER_READ;
|
||||
break;
|
||||
case MPEGTS_PESHEADER_READ:
|
||||
/* currently we do nothing except skipping */
|
||||
if (len > tss->pes_header_size)
|
||||
len = tss->pes_header_size;
|
||||
p += len;
|
||||
buf_size -= len;
|
||||
tss->pes_header_size -= len;
|
||||
tss->payload_size -= len;
|
||||
if (tss->pes_header_size == 0)
|
||||
tss->state = MPEGTS_PAYLOAD;
|
||||
break;
|
||||
case MPEGTS_PAYLOAD:
|
||||
if (len > tss->payload_size)
|
||||
len = tss->payload_size;
|
||||
if (len > 0) {
|
||||
if (tss->st && av_new_packet(pkt, buf_size) == 0) {
|
||||
memcpy(pkt->data, p, buf_size);
|
||||
pkt->stream_index = tss->st->index;
|
||||
return 1;
|
||||
}
|
||||
tss->payload_size -= len;
|
||||
}
|
||||
buf_size = 0;
|
||||
break;
|
||||
case MPEGTS_SKIP:
|
||||
buf_size = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpegts_read_packet(AVFormatContext *s,
|
||||
AVPacket *pkt)
|
||||
{
|
||||
MpegTSContext *ts = s->priv_data;
|
||||
MpegTSStream *tss;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
unsigned char packet[TS_FEC_PACKET_SIZE];
|
||||
int len, pid, cc, cc_ok, afc;
|
||||
const unsigned char *p;
|
||||
|
||||
for(;;) {
|
||||
len = get_buffer(pb, packet, ts->raw_packet_size);
|
||||
if (len != ts->raw_packet_size)
|
||||
return AVERROR_IO;
|
||||
/* check paquet sync byte */
|
||||
/* XXX: accept to resync ? */
|
||||
if (packet[0] != 0x47)
|
||||
return AVERROR_INVALIDDATA;
|
||||
|
||||
pid = ((packet[1] & 0x1f) << 8) | packet[2];
|
||||
tss = ts->pids[pid];
|
||||
if (tss == NULL) {
|
||||
/* if no pid found, then add a pid context */
|
||||
tss = av_mallocz(sizeof(MpegTSStream));
|
||||
if (!tss)
|
||||
continue;
|
||||
ts->pids[pid] = tss;
|
||||
tss->pid = pid;
|
||||
tss->last_cc = -1;
|
||||
// printf("new pid=0x%x\n", pid);
|
||||
}
|
||||
|
||||
/* continuity check (currently not used) */
|
||||
cc = (packet[3] & 0xf);
|
||||
cc_ok = (tss->last_cc < 0) || ((((tss->last_cc + 1) & 0x0f) == cc));
|
||||
tss->last_cc = cc;
|
||||
|
||||
/* skip adaptation field */
|
||||
afc = (packet[3] >> 4) & 3;
|
||||
p = packet + 4;
|
||||
if (afc == 0) /* reserved value */
|
||||
continue;
|
||||
if (afc == 2) /* adaptation field only */
|
||||
continue;
|
||||
if (afc == 3) {
|
||||
/* skip adapation field */
|
||||
p += p[0] + 1;
|
||||
}
|
||||
/* if past the end of packet, ignore */
|
||||
if (p >= packet + TS_PACKET_SIZE)
|
||||
continue;
|
||||
|
||||
if (mpegts_push_data(s, tss, pkt, p, TS_PACKET_SIZE - (p - packet),
|
||||
packet[1] & 0x40))
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpegts_read_close(AVFormatContext *s)
|
||||
{
|
||||
MpegTSContext *ts = s->priv_data;
|
||||
int i;
|
||||
for(i=0;i<NB_PID_MAX;i++)
|
||||
av_free(ts->pids[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AVInputFormat mpegts_demux = {
|
||||
"mpegts",
|
||||
"MPEG2 transport stream format",
|
||||
sizeof(MpegTSContext),
|
||||
mpegts_probe,
|
||||
mpegts_read_header,
|
||||
mpegts_read_packet,
|
||||
mpegts_read_close,
|
||||
.flags = AVFMT_NOHEADER | AVFMT_SHOW_IDS,
|
||||
};
|
||||
|
||||
int mpegts_init(void)
|
||||
{
|
||||
av_register_input_format(&mpegts_demux);
|
||||
return 0;
|
||||
}
|
||||
269
libavformat/ogg.c
Normal file
269
libavformat/ogg.c
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Ogg bitstream support
|
||||
* Mark Hills <mark@pogo.org.uk>
|
||||
*
|
||||
* Uses libogg, but requires libvorbisenc to construct correct headers
|
||||
* when containing Vorbis stream -- currently the only format supported
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <ogg/ogg.h>
|
||||
#include <vorbis/vorbisenc.h>
|
||||
|
||||
#include "avformat.h"
|
||||
#include "oggvorbis.h"
|
||||
|
||||
#define DECODER_BUFFER_SIZE 4096
|
||||
|
||||
|
||||
typedef struct OggContext {
|
||||
/* output */
|
||||
ogg_stream_state os ;
|
||||
int header_handled ;
|
||||
ogg_int64_t base_packet_no ;
|
||||
ogg_int64_t base_granule_pos ;
|
||||
|
||||
/* input */
|
||||
ogg_sync_state oy ;
|
||||
} OggContext ;
|
||||
|
||||
|
||||
static int ogg_write_header(AVFormatContext *avfcontext) {
|
||||
OggContext *context ;
|
||||
AVCodecContext *avccontext ;
|
||||
vorbis_info vi ;
|
||||
vorbis_dsp_state vd ;
|
||||
vorbis_comment vc ;
|
||||
vorbis_block vb ;
|
||||
ogg_packet header, header_comm, header_code ;
|
||||
int n ;
|
||||
|
||||
if(!(context = malloc(sizeof(OggContext))))
|
||||
return -1 ;
|
||||
avfcontext->priv_data = context ;
|
||||
|
||||
srand(time(NULL));
|
||||
ogg_stream_init(&context->os, rand());
|
||||
|
||||
for(n = 0 ; n < avfcontext->nb_streams ; n++) {
|
||||
avccontext = &avfcontext->streams[n]->codec ;
|
||||
|
||||
/* begin vorbis specific code */
|
||||
|
||||
vorbis_info_init(&vi) ;
|
||||
|
||||
/* code copied from libavcodec/oggvorbis.c */
|
||||
|
||||
if(oggvorbis_init_encoder(&vi, avccontext) < 0) {
|
||||
fprintf(stderr, "ogg_write_header: init_encoder failed") ;
|
||||
return -1 ;
|
||||
}
|
||||
|
||||
vorbis_analysis_init(&vd, &vi) ;
|
||||
vorbis_block_init(&vd, &vb) ;
|
||||
|
||||
vorbis_comment_init(&vc) ;
|
||||
vorbis_comment_add_tag(&vc, "encoder", "ffmpeg") ;
|
||||
if(*avfcontext->title)
|
||||
vorbis_comment_add_tag(&vc, "title", avfcontext->title) ;
|
||||
|
||||
vorbis_analysis_headerout(&vd, &vc, &header,
|
||||
&header_comm, &header_code) ;
|
||||
ogg_stream_packetin(&context->os, &header) ;
|
||||
ogg_stream_packetin(&context->os, &header_comm) ;
|
||||
ogg_stream_packetin(&context->os, &header_code) ;
|
||||
|
||||
vorbis_comment_clear(&vc) ;
|
||||
|
||||
/* end of vorbis specific code */
|
||||
|
||||
context->header_handled = 0 ;
|
||||
context->base_packet_no = 0 ;
|
||||
}
|
||||
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
|
||||
static int ogg_write_packet(AVFormatContext *avfcontext,
|
||||
int stream_index,
|
||||
unsigned char *buf, int size, int force_pts)
|
||||
{
|
||||
OggContext *context = avfcontext->priv_data ;
|
||||
ogg_packet *op ;
|
||||
ogg_page og ;
|
||||
int l = 0 ;
|
||||
|
||||
/* flush header packets so audio starts on a new page */
|
||||
|
||||
if(!context->header_handled) {
|
||||
while(ogg_stream_flush(&context->os, &og)) {
|
||||
put_buffer(&avfcontext->pb, og.header, og.header_len) ;
|
||||
put_buffer(&avfcontext->pb, og.body, og.body_len) ;
|
||||
put_flush_packet(&avfcontext->pb);
|
||||
}
|
||||
context->header_handled = 1 ;
|
||||
}
|
||||
|
||||
while(l < size) {
|
||||
op = (ogg_packet*)(buf + l) ;
|
||||
op->packet = buf + l + sizeof(ogg_packet) ; /* fix data pointer */
|
||||
|
||||
if(!context->base_packet_no) { /* this is the first packet */
|
||||
context->base_packet_no = op->packetno ;
|
||||
context->base_granule_pos = op->granulepos ;
|
||||
}
|
||||
|
||||
/* correct the fields in the packet -- essential for streaming */
|
||||
|
||||
op->packetno -= context->base_packet_no ;
|
||||
op->granulepos -= context->base_granule_pos ;
|
||||
|
||||
ogg_stream_packetin(&context->os, op) ;
|
||||
l += sizeof(ogg_packet) + op->bytes ;
|
||||
|
||||
while(ogg_stream_pageout(&context->os, &og)) {
|
||||
put_buffer(&avfcontext->pb, og.header, og.header_len) ;
|
||||
put_buffer(&avfcontext->pb, og.body, og.body_len) ;
|
||||
put_flush_packet(&avfcontext->pb);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ogg_write_trailer(AVFormatContext *avfcontext) {
|
||||
OggContext *context = avfcontext->priv_data ;
|
||||
ogg_page og ;
|
||||
|
||||
while(ogg_stream_flush(&context->os, &og)) {
|
||||
put_buffer(&avfcontext->pb, og.header, og.header_len) ;
|
||||
put_buffer(&avfcontext->pb, og.body, og.body_len) ;
|
||||
put_flush_packet(&avfcontext->pb);
|
||||
}
|
||||
|
||||
ogg_stream_clear(&context->os) ;
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
|
||||
static AVOutputFormat ogg_oformat = {
|
||||
"ogg",
|
||||
"Ogg Vorbis",
|
||||
"audio/x-vorbis",
|
||||
"ogg",
|
||||
sizeof(OggContext),
|
||||
CODEC_ID_VORBIS,
|
||||
0,
|
||||
ogg_write_header,
|
||||
ogg_write_packet,
|
||||
ogg_write_trailer,
|
||||
} ;
|
||||
|
||||
|
||||
static int next_packet(AVFormatContext *avfcontext, ogg_packet *op) {
|
||||
OggContext *context = avfcontext->priv_data ;
|
||||
ogg_page og ;
|
||||
char *buf ;
|
||||
|
||||
while(ogg_stream_packetout(&context->os, op) != 1) {
|
||||
|
||||
/* while no pages are available, read in more data to the sync */
|
||||
while(ogg_sync_pageout(&context->oy, &og) != 1) {
|
||||
buf = ogg_sync_buffer(&context->oy, DECODER_BUFFER_SIZE) ;
|
||||
if(get_buffer(&avfcontext->pb, buf, DECODER_BUFFER_SIZE) <= 0)
|
||||
return 1 ;
|
||||
ogg_sync_wrote(&context->oy, DECODER_BUFFER_SIZE) ;
|
||||
}
|
||||
|
||||
/* got a page. Feed it into the stream and get the packet */
|
||||
if(ogg_stream_pagein(&context->os, &og) != 0)
|
||||
return 1 ;
|
||||
}
|
||||
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
|
||||
static int ogg_read_header(AVFormatContext *avfcontext, AVFormatParameters *ap)
|
||||
{
|
||||
OggContext *context ;
|
||||
char *buf ;
|
||||
ogg_page og ;
|
||||
AVStream *ast ;
|
||||
|
||||
if(!(context = malloc(sizeof(OggContext)))) {
|
||||
perror("malloc") ;
|
||||
return -1 ;
|
||||
}
|
||||
avfcontext->priv_data = context ;
|
||||
|
||||
ogg_sync_init(&context->oy) ;
|
||||
buf = ogg_sync_buffer(&context->oy, DECODER_BUFFER_SIZE) ;
|
||||
|
||||
if(get_buffer(&avfcontext->pb, buf, DECODER_BUFFER_SIZE) <= 0)
|
||||
return -EIO ;
|
||||
|
||||
ogg_sync_wrote(&context->oy, DECODER_BUFFER_SIZE) ;
|
||||
ogg_sync_pageout(&context->oy, &og) ;
|
||||
ogg_stream_init(&context->os, ogg_page_serialno(&og)) ;
|
||||
ogg_stream_pagein(&context->os, &og) ;
|
||||
|
||||
/* currently only one vorbis stream supported */
|
||||
|
||||
ast = av_new_stream(avfcontext, 0) ;
|
||||
if(!ast)
|
||||
return AVERROR_NOMEM ;
|
||||
|
||||
ast->codec.codec_type = CODEC_TYPE_AUDIO ;
|
||||
ast->codec.codec_id = CODEC_ID_VORBIS ;
|
||||
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
|
||||
static int ogg_read_packet(AVFormatContext *avfcontext, AVPacket *pkt) {
|
||||
ogg_packet op ;
|
||||
|
||||
if(next_packet(avfcontext, &op))
|
||||
return -EIO ;
|
||||
if(av_new_packet(pkt, sizeof(ogg_packet) + op.bytes) < 0)
|
||||
return -EIO ;
|
||||
pkt->stream_index = 0 ;
|
||||
memcpy(pkt->data, &op, sizeof(ogg_packet)) ;
|
||||
memcpy(pkt->data + sizeof(ogg_packet), op.packet, op.bytes) ;
|
||||
|
||||
return sizeof(ogg_packet) + op.bytes ;
|
||||
}
|
||||
|
||||
|
||||
static int ogg_read_close(AVFormatContext *avfcontext) {
|
||||
OggContext *context = avfcontext->priv_data ;
|
||||
|
||||
ogg_stream_clear(&context->os) ;
|
||||
ogg_sync_clear(&context->oy) ;
|
||||
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
|
||||
static AVInputFormat ogg_iformat = {
|
||||
"ogg",
|
||||
"Ogg Vorbis",
|
||||
sizeof(OggContext),
|
||||
NULL,
|
||||
ogg_read_header,
|
||||
ogg_read_packet,
|
||||
ogg_read_close,
|
||||
.extensions = "ogg",
|
||||
} ;
|
||||
|
||||
|
||||
int ogg_init(void) {
|
||||
av_register_output_format(&ogg_oformat) ;
|
||||
av_register_input_format(&ogg_iformat);
|
||||
return 0 ;
|
||||
}
|
||||
509
libavformat/raw.c
Normal file
509
libavformat/raw.c
Normal file
@@ -0,0 +1,509 @@
|
||||
/*
|
||||
* RAW encoder and decoder
|
||||
* Copyright (c) 2001 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
|
||||
/* simple formats */
|
||||
int raw_write_header(struct AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int raw_write_packet(struct AVFormatContext *s,
|
||||
int stream_index,
|
||||
unsigned char *buf, int size, int force_pts)
|
||||
{
|
||||
put_buffer(&s->pb, buf, size);
|
||||
put_flush_packet(&s->pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int raw_write_trailer(struct AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* raw input */
|
||||
static int raw_read_header(AVFormatContext *s,
|
||||
AVFormatParameters *ap)
|
||||
{
|
||||
AVStream *st;
|
||||
int id;
|
||||
|
||||
st = av_new_stream(s, 0);
|
||||
if (!st)
|
||||
return AVERROR_NOMEM;
|
||||
if (ap) {
|
||||
id = s->iformat->value;
|
||||
if (id == CODEC_ID_RAWVIDEO) {
|
||||
st->codec.codec_type = CODEC_TYPE_VIDEO;
|
||||
} else {
|
||||
st->codec.codec_type = CODEC_TYPE_AUDIO;
|
||||
}
|
||||
st->codec.codec_id = id;
|
||||
|
||||
switch(st->codec.codec_type) {
|
||||
case CODEC_TYPE_AUDIO:
|
||||
st->codec.sample_rate = ap->sample_rate;
|
||||
st->codec.channels = ap->channels;
|
||||
break;
|
||||
case CODEC_TYPE_VIDEO:
|
||||
st->codec.frame_rate = ap->frame_rate;
|
||||
st->codec.width = ap->width;
|
||||
st->codec.height = ap->height;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RAW_PACKET_SIZE 1024
|
||||
|
||||
int raw_read_packet(AVFormatContext *s,
|
||||
AVPacket *pkt)
|
||||
{
|
||||
int ret, size;
|
||||
AVStream *st = s->streams[0];
|
||||
|
||||
size= RAW_PACKET_SIZE;
|
||||
|
||||
if (av_new_packet(pkt, size) < 0)
|
||||
return -EIO;
|
||||
|
||||
pkt->stream_index = 0;
|
||||
ret = get_buffer(&s->pb, pkt->data, size);
|
||||
if (ret <= 0) {
|
||||
av_free_packet(pkt);
|
||||
return -EIO;
|
||||
}
|
||||
/* note: we need to modify the packet size here to handle the last
|
||||
packet */
|
||||
pkt->size = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int raw_read_close(AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* mp3 read */
|
||||
static int mp3_read_header(AVFormatContext *s,
|
||||
AVFormatParameters *ap)
|
||||
{
|
||||
AVStream *st;
|
||||
|
||||
st = av_new_stream(s, 0);
|
||||
if (!st)
|
||||
return AVERROR_NOMEM;
|
||||
|
||||
st->codec.codec_type = CODEC_TYPE_AUDIO;
|
||||
st->codec.codec_id = CODEC_ID_MP2;
|
||||
/* the parameters will be extracted from the compressed bitstream */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* mpeg1/h263 input */
|
||||
static int video_read_header(AVFormatContext *s,
|
||||
AVFormatParameters *ap)
|
||||
{
|
||||
AVStream *st;
|
||||
|
||||
st = av_new_stream(s, 0);
|
||||
if (!st)
|
||||
return AVERROR_NOMEM;
|
||||
|
||||
st->codec.codec_type = CODEC_TYPE_VIDEO;
|
||||
st->codec.codec_id = s->iformat->value;
|
||||
/* for mjpeg, specify frame rate */
|
||||
/* for mpeg4 specify it too (most mpeg4 streams dont have the fixed_vop_rate set ...)*/
|
||||
if (st->codec.codec_id == CODEC_ID_MJPEG || st->codec.codec_id == CODEC_ID_MPEG4) {
|
||||
if (ap) {
|
||||
st->codec.frame_rate = ap->frame_rate;
|
||||
} else {
|
||||
st->codec.frame_rate = 25 * FRAME_RATE_BASE;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SEQ_START_CODE 0x000001b3
|
||||
#define GOP_START_CODE 0x000001b8
|
||||
#define PICTURE_START_CODE 0x00000100
|
||||
|
||||
/* XXX: improve that by looking at several start codes */
|
||||
static int mpegvideo_probe(AVProbeData *p)
|
||||
{
|
||||
int code, c, i;
|
||||
code = 0xff;
|
||||
|
||||
/* we search the first start code. If it is a sequence, gop or
|
||||
picture start code then we decide it is an mpeg video
|
||||
stream. We do not send highest value to give a chance to mpegts */
|
||||
for(i=0;i<p->buf_size;i++) {
|
||||
c = p->buf[i];
|
||||
code = (code << 8) | c;
|
||||
if ((code & 0xffffff00) == 0x100) {
|
||||
if (code == SEQ_START_CODE ||
|
||||
code == GOP_START_CODE ||
|
||||
code == PICTURE_START_CODE)
|
||||
return 50 - 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
AVInputFormat mp3_iformat = {
|
||||
"mp3",
|
||||
"MPEG audio",
|
||||
0,
|
||||
NULL,
|
||||
mp3_read_header,
|
||||
raw_read_packet,
|
||||
raw_read_close,
|
||||
.extensions = "mp2,mp3", /* XXX: use probe */
|
||||
};
|
||||
|
||||
AVOutputFormat mp2_oformat = {
|
||||
"mp2",
|
||||
"MPEG audio layer 2",
|
||||
"audio/x-mpeg",
|
||||
"mp2,mp3",
|
||||
0,
|
||||
CODEC_ID_MP2,
|
||||
0,
|
||||
raw_write_header,
|
||||
raw_write_packet,
|
||||
raw_write_trailer,
|
||||
};
|
||||
|
||||
|
||||
AVInputFormat ac3_iformat = {
|
||||
"ac3",
|
||||
"raw ac3",
|
||||
0,
|
||||
NULL,
|
||||
raw_read_header,
|
||||
raw_read_packet,
|
||||
raw_read_close,
|
||||
.extensions = "ac3",
|
||||
.value = CODEC_ID_AC3,
|
||||
};
|
||||
|
||||
AVOutputFormat ac3_oformat = {
|
||||
"ac3",
|
||||
"raw ac3",
|
||||
"audio/x-ac3",
|
||||
"ac3",
|
||||
0,
|
||||
CODEC_ID_AC3,
|
||||
0,
|
||||
raw_write_header,
|
||||
raw_write_packet,
|
||||
raw_write_trailer,
|
||||
};
|
||||
|
||||
AVOutputFormat h263_oformat = {
|
||||
"h263",
|
||||
"raw h263",
|
||||
"video/x-h263",
|
||||
"h263",
|
||||
0,
|
||||
0,
|
||||
CODEC_ID_H263,
|
||||
raw_write_header,
|
||||
raw_write_packet,
|
||||
raw_write_trailer,
|
||||
};
|
||||
|
||||
AVInputFormat m4v_iformat = {
|
||||
"m4v",
|
||||
"raw MPEG4 video format",
|
||||
0,
|
||||
NULL /*mpegvideo_probe*/,
|
||||
video_read_header,
|
||||
raw_read_packet,
|
||||
raw_read_close,
|
||||
.extensions = "m4v", //FIXME remove after writing mpeg4_probe
|
||||
.value = CODEC_ID_MPEG4,
|
||||
};
|
||||
|
||||
AVOutputFormat m4v_oformat = {
|
||||
"m4v",
|
||||
"raw MPEG4 video format",
|
||||
NULL,
|
||||
"m4v",
|
||||
0,
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_MPEG4,
|
||||
raw_write_header,
|
||||
raw_write_packet,
|
||||
raw_write_trailer,
|
||||
};
|
||||
|
||||
AVInputFormat mpegvideo_iformat = {
|
||||
"mpegvideo",
|
||||
"MPEG video",
|
||||
0,
|
||||
mpegvideo_probe,
|
||||
video_read_header,
|
||||
raw_read_packet,
|
||||
raw_read_close,
|
||||
.value = CODEC_ID_MPEG1VIDEO,
|
||||
};
|
||||
|
||||
AVOutputFormat mpeg1video_oformat = {
|
||||
"mpeg1video",
|
||||
"MPEG video",
|
||||
"video/x-mpeg",
|
||||
"mpg,mpeg",
|
||||
0,
|
||||
0,
|
||||
CODEC_ID_MPEG1VIDEO,
|
||||
raw_write_header,
|
||||
raw_write_packet,
|
||||
raw_write_trailer,
|
||||
};
|
||||
|
||||
AVInputFormat mjpeg_iformat = {
|
||||
"mjpeg",
|
||||
"MJPEG video",
|
||||
0,
|
||||
NULL,
|
||||
video_read_header,
|
||||
raw_read_packet,
|
||||
raw_read_close,
|
||||
.extensions = "mjpg,mjpeg",
|
||||
.value = CODEC_ID_MJPEG,
|
||||
};
|
||||
|
||||
AVOutputFormat mjpeg_oformat = {
|
||||
"mjpeg",
|
||||
"MJPEG video",
|
||||
"video/x-mjpeg",
|
||||
"mjpg,mjpeg",
|
||||
0,
|
||||
0,
|
||||
CODEC_ID_MJPEG,
|
||||
raw_write_header,
|
||||
raw_write_packet,
|
||||
raw_write_trailer,
|
||||
};
|
||||
|
||||
/* pcm formats */
|
||||
|
||||
#define PCMDEF(name, long_name, ext, codec) \
|
||||
AVInputFormat pcm_ ## name ## _iformat = {\
|
||||
#name,\
|
||||
long_name,\
|
||||
0,\
|
||||
NULL,\
|
||||
raw_read_header,\
|
||||
raw_read_packet,\
|
||||
raw_read_close,\
|
||||
.extensions = ext,\
|
||||
.value = codec,\
|
||||
};\
|
||||
\
|
||||
AVOutputFormat pcm_ ## name ## _oformat = {\
|
||||
#name,\
|
||||
long_name,\
|
||||
NULL,\
|
||||
ext,\
|
||||
0,\
|
||||
codec,\
|
||||
0,\
|
||||
raw_write_header,\
|
||||
raw_write_packet,\
|
||||
raw_write_trailer,\
|
||||
};
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define BE_DEF(s) s
|
||||
#define LE_DEF(s) NULL
|
||||
#else
|
||||
#define BE_DEF(s) NULL
|
||||
#define LE_DEF(s) s
|
||||
#endif
|
||||
|
||||
|
||||
PCMDEF(s16le, "pcm signed 16 bit little endian format",
|
||||
LE_DEF("sw"), CODEC_ID_PCM_S16LE)
|
||||
|
||||
PCMDEF(s16be, "pcm signed 16 bit big endian format",
|
||||
BE_DEF("sw"), CODEC_ID_PCM_S16BE)
|
||||
|
||||
PCMDEF(u16le, "pcm unsigned 16 bit little endian format",
|
||||
LE_DEF("uw"), CODEC_ID_PCM_U16LE)
|
||||
|
||||
PCMDEF(u16be, "pcm unsigned 16 bit big endian format",
|
||||
BE_DEF("uw"), CODEC_ID_PCM_U16BE)
|
||||
|
||||
PCMDEF(s8, "pcm signed 8 bit format",
|
||||
"sb", CODEC_ID_PCM_S8)
|
||||
|
||||
PCMDEF(u8, "pcm unsigned 8 bit format",
|
||||
"ub", CODEC_ID_PCM_U8)
|
||||
|
||||
PCMDEF(mulaw, "pcm mu law format",
|
||||
"ul", CODEC_ID_PCM_MULAW)
|
||||
|
||||
PCMDEF(alaw, "pcm A law format",
|
||||
"al", CODEC_ID_PCM_ALAW)
|
||||
|
||||
int rawvideo_read_packet(AVFormatContext *s,
|
||||
AVPacket *pkt)
|
||||
{
|
||||
int packet_size, ret, width, height;
|
||||
AVStream *st = s->streams[0];
|
||||
|
||||
width = st->codec.width;
|
||||
height = st->codec.height;
|
||||
|
||||
switch(st->codec.pix_fmt) {
|
||||
case PIX_FMT_YUV420P:
|
||||
packet_size = (width * height * 3) / 2;
|
||||
break;
|
||||
case PIX_FMT_YUV422:
|
||||
packet_size = (width * height * 2);
|
||||
break;
|
||||
case PIX_FMT_BGR24:
|
||||
case PIX_FMT_RGB24:
|
||||
packet_size = (width * height * 3);
|
||||
break;
|
||||
default:
|
||||
av_abort();
|
||||
break;
|
||||
}
|
||||
|
||||
if (av_new_packet(pkt, packet_size) < 0)
|
||||
return -EIO;
|
||||
|
||||
pkt->stream_index = 0;
|
||||
#if 0
|
||||
/* bypass buffered I/O */
|
||||
ret = url_read(url_fileno(&s->pb), pkt->data, pkt->size);
|
||||
#else
|
||||
ret = get_buffer(&s->pb, pkt->data, pkt->size);
|
||||
#endif
|
||||
if (ret != pkt->size) {
|
||||
av_free_packet(pkt);
|
||||
return -EIO;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
AVInputFormat rawvideo_iformat = {
|
||||
"rawvideo",
|
||||
"raw video format",
|
||||
0,
|
||||
NULL,
|
||||
raw_read_header,
|
||||
rawvideo_read_packet,
|
||||
raw_read_close,
|
||||
.extensions = "yuv",
|
||||
.value = CODEC_ID_RAWVIDEO,
|
||||
};
|
||||
|
||||
AVOutputFormat rawvideo_oformat = {
|
||||
"rawvideo",
|
||||
"raw video format",
|
||||
NULL,
|
||||
"yuv",
|
||||
0,
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_RAWVIDEO,
|
||||
raw_write_header,
|
||||
raw_write_packet,
|
||||
raw_write_trailer,
|
||||
};
|
||||
|
||||
static int null_write_packet(struct AVFormatContext *s,
|
||||
int stream_index,
|
||||
unsigned char *buf, int size, int force_pts)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
AVOutputFormat null_oformat = {
|
||||
"null",
|
||||
"null video format",
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
CODEC_ID_PCM_S16BE,
|
||||
#else
|
||||
CODEC_ID_PCM_S16LE,
|
||||
#endif
|
||||
CODEC_ID_RAWVIDEO,
|
||||
raw_write_header,
|
||||
null_write_packet,
|
||||
raw_write_trailer,
|
||||
.flags = AVFMT_NOFILE | AVFMT_RAWPICTURE,
|
||||
};
|
||||
|
||||
int raw_init(void)
|
||||
{
|
||||
av_register_input_format(&mp3_iformat);
|
||||
av_register_output_format(&mp2_oformat);
|
||||
|
||||
av_register_input_format(&ac3_iformat);
|
||||
av_register_output_format(&ac3_oformat);
|
||||
|
||||
av_register_output_format(&h263_oformat);
|
||||
|
||||
av_register_input_format(&m4v_iformat);
|
||||
av_register_output_format(&m4v_oformat);
|
||||
|
||||
av_register_input_format(&mpegvideo_iformat);
|
||||
av_register_output_format(&mpeg1video_oformat);
|
||||
|
||||
av_register_input_format(&mjpeg_iformat);
|
||||
av_register_output_format(&mjpeg_oformat);
|
||||
|
||||
av_register_input_format(&pcm_s16le_iformat);
|
||||
av_register_output_format(&pcm_s16le_oformat);
|
||||
av_register_input_format(&pcm_s16be_iformat);
|
||||
av_register_output_format(&pcm_s16be_oformat);
|
||||
av_register_input_format(&pcm_u16le_iformat);
|
||||
av_register_output_format(&pcm_u16le_oformat);
|
||||
av_register_input_format(&pcm_u16be_iformat);
|
||||
av_register_output_format(&pcm_u16be_oformat);
|
||||
av_register_input_format(&pcm_s8_iformat);
|
||||
av_register_output_format(&pcm_s8_oformat);
|
||||
av_register_input_format(&pcm_u8_iformat);
|
||||
av_register_output_format(&pcm_u8_oformat);
|
||||
av_register_input_format(&pcm_mulaw_iformat);
|
||||
av_register_output_format(&pcm_mulaw_oformat);
|
||||
av_register_input_format(&pcm_alaw_iformat);
|
||||
av_register_output_format(&pcm_alaw_oformat);
|
||||
|
||||
av_register_input_format(&rawvideo_iformat);
|
||||
av_register_output_format(&rawvideo_oformat);
|
||||
|
||||
av_register_output_format(&null_oformat);
|
||||
return 0;
|
||||
}
|
||||
773
libavformat/rm.c
Normal file
773
libavformat/rm.c
Normal file
@@ -0,0 +1,773 @@
|
||||
/*
|
||||
* "Real" compatible mux and demux.
|
||||
* Copyright (c) 2000, 2001 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
|
||||
/* in ms */
|
||||
#define BUFFER_DURATION 0
|
||||
|
||||
typedef struct {
|
||||
int nb_packets;
|
||||
int packet_total_size;
|
||||
int packet_max_size;
|
||||
/* codec related output */
|
||||
int bit_rate;
|
||||
float frame_rate;
|
||||
int nb_frames; /* current frame number */
|
||||
int total_frames; /* total number of frames */
|
||||
int num;
|
||||
AVCodecContext *enc;
|
||||
} StreamInfo;
|
||||
|
||||
typedef struct {
|
||||
StreamInfo streams[2];
|
||||
StreamInfo *audio_stream, *video_stream;
|
||||
int data_pos; /* position of the data after the header */
|
||||
int nb_packets;
|
||||
} RMContext;
|
||||
|
||||
static void put_str(ByteIOContext *s, const char *tag)
|
||||
{
|
||||
put_be16(s,strlen(tag));
|
||||
while (*tag) {
|
||||
put_byte(s, *tag++);
|
||||
}
|
||||
}
|
||||
|
||||
static void put_str8(ByteIOContext *s, const char *tag)
|
||||
{
|
||||
put_byte(s, strlen(tag));
|
||||
while (*tag) {
|
||||
put_byte(s, *tag++);
|
||||
}
|
||||
}
|
||||
|
||||
static void rv10_write_header(AVFormatContext *ctx,
|
||||
int data_size, int index_pos)
|
||||
{
|
||||
RMContext *rm = ctx->priv_data;
|
||||
ByteIOContext *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;
|
||||
|
||||
start_ptr = s->buf_ptr;
|
||||
|
||||
put_tag(s, ".RMF");
|
||||
put_be32(s,18); /* header size */
|
||||
put_be16(s,0);
|
||||
put_be32(s,0);
|
||||
put_be32(s,4 + ctx->nb_streams); /* num headers */
|
||||
|
||||
put_tag(s,"PROP");
|
||||
put_be32(s, 50);
|
||||
put_be16(s, 0);
|
||||
packet_max_size = 0;
|
||||
packet_total_size = 0;
|
||||
nb_packets = 0;
|
||||
bit_rate = 0;
|
||||
duration = 0;
|
||||
for(i=0;i<ctx->nb_streams;i++) {
|
||||
StreamInfo *stream = &rm->streams[i];
|
||||
bit_rate += stream->bit_rate;
|
||||
if (stream->packet_max_size > packet_max_size)
|
||||
packet_max_size = stream->packet_max_size;
|
||||
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);
|
||||
if (v > duration)
|
||||
duration = v;
|
||||
}
|
||||
put_be32(s, bit_rate); /* max bit rate */
|
||||
put_be32(s, bit_rate); /* avg bit rate */
|
||||
put_be32(s, packet_max_size); /* max packet size */
|
||||
if (nb_packets > 0)
|
||||
packet_avg_size = packet_total_size / nb_packets;
|
||||
else
|
||||
packet_avg_size = 0;
|
||||
put_be32(s, packet_avg_size); /* avg packet size */
|
||||
put_be32(s, nb_packets); /* num packets */
|
||||
put_be32(s, duration); /* duration */
|
||||
put_be32(s, BUFFER_DURATION); /* preroll */
|
||||
put_be32(s, index_pos); /* index offset */
|
||||
/* computation of data the data offset */
|
||||
data_offset_ptr = s->buf_ptr;
|
||||
put_be32(s, 0); /* data offset : will be patched after */
|
||||
put_be16(s, ctx->nb_streams); /* num streams */
|
||||
flags = 1 | 2; /* save allowed & perfect play */
|
||||
if (url_is_streamed(s))
|
||||
flags |= 4; /* live broadcast */
|
||||
put_be16(s, flags);
|
||||
|
||||
/* comments */
|
||||
|
||||
put_tag(s,"CONT");
|
||||
size = strlen(ctx->title) + strlen(ctx->author) + strlen(ctx->copyright) +
|
||||
strlen(ctx->comment) + 4 * 2 + 10;
|
||||
put_be32(s,size);
|
||||
put_be16(s,0);
|
||||
put_str(s, ctx->title);
|
||||
put_str(s, ctx->author);
|
||||
put_str(s, ctx->copyright);
|
||||
put_str(s, ctx->comment);
|
||||
|
||||
for(i=0;i<ctx->nb_streams;i++) {
|
||||
int codec_data_size;
|
||||
|
||||
stream = &rm->streams[i];
|
||||
|
||||
if (stream->enc->codec_type == CODEC_TYPE_AUDIO) {
|
||||
desc = "The Audio Stream";
|
||||
mimetype = "audio/x-pn-realaudio";
|
||||
codec_data_size = 73;
|
||||
} else {
|
||||
desc = "The Video Stream";
|
||||
mimetype = "video/x-pn-realvideo";
|
||||
codec_data_size = 34;
|
||||
}
|
||||
|
||||
put_tag(s,"MDPR");
|
||||
size = 10 + 9 * 4 + strlen(desc) + strlen(mimetype) + codec_data_size;
|
||||
put_be32(s, size);
|
||||
put_be16(s, 0);
|
||||
|
||||
put_be16(s, i); /* stream number */
|
||||
put_be32(s, stream->bit_rate); /* max bit rate */
|
||||
put_be32(s, stream->bit_rate); /* avg bit rate */
|
||||
put_be32(s, stream->packet_max_size); /* max packet size */
|
||||
if (stream->nb_packets > 0)
|
||||
packet_avg_size = stream->packet_total_size /
|
||||
stream->nb_packets;
|
||||
else
|
||||
packet_avg_size = 0;
|
||||
put_be32(s, packet_avg_size); /* avg packet size */
|
||||
put_be32(s, 0); /* start time */
|
||||
put_be32(s, BUFFER_DURATION); /* preroll */
|
||||
/* duration */
|
||||
if (url_is_streamed(s) || !stream->total_frames)
|
||||
put_be32(s, (int)(3600 * 1000));
|
||||
else
|
||||
put_be32(s, (int)(stream->total_frames * 1000 / stream->frame_rate));
|
||||
put_str8(s, desc);
|
||||
put_str8(s, mimetype);
|
||||
put_be32(s, codec_data_size);
|
||||
|
||||
if (stream->enc->codec_type == CODEC_TYPE_AUDIO) {
|
||||
int coded_frame_size, fscode, sample_rate;
|
||||
sample_rate = stream->enc->sample_rate;
|
||||
coded_frame_size = (stream->enc->bit_rate *
|
||||
stream->enc->frame_size) / (8 * sample_rate);
|
||||
/* audio codec info */
|
||||
put_tag(s, ".ra");
|
||||
put_byte(s, 0xfd);
|
||||
put_be32(s, 0x00040000); /* version */
|
||||
put_tag(s, ".ra4");
|
||||
put_be32(s, 0x01b53530); /* stream length */
|
||||
put_be16(s, 4); /* unknown */
|
||||
put_be32(s, 0x39); /* header size */
|
||||
|
||||
switch(sample_rate) {
|
||||
case 48000:
|
||||
case 24000:
|
||||
case 12000:
|
||||
fscode = 1;
|
||||
break;
|
||||
default:
|
||||
case 44100:
|
||||
case 22050:
|
||||
case 11025:
|
||||
fscode = 2;
|
||||
break;
|
||||
case 32000:
|
||||
case 16000:
|
||||
case 8000:
|
||||
fscode = 3;
|
||||
}
|
||||
put_be16(s, fscode); /* codec additional info, for AC3, seems
|
||||
to be a frequency code */
|
||||
/* special hack to compensate rounding errors... */
|
||||
if (coded_frame_size == 557)
|
||||
coded_frame_size--;
|
||||
put_be32(s, coded_frame_size); /* frame length */
|
||||
put_be32(s, 0x51540); /* unknown */
|
||||
put_be32(s, 0x249f0); /* unknown */
|
||||
put_be32(s, 0x249f0); /* unknown */
|
||||
put_be16(s, 0x01);
|
||||
/* frame length : seems to be very important */
|
||||
put_be16(s, coded_frame_size);
|
||||
put_be32(s, 0); /* unknown */
|
||||
put_be16(s, stream->enc->sample_rate); /* sample rate */
|
||||
put_be32(s, 0x10); /* unknown */
|
||||
put_be16(s, stream->enc->channels);
|
||||
put_str8(s, "Int0"); /* codec name */
|
||||
put_str8(s, "dnet"); /* codec name */
|
||||
put_be16(s, 0); /* title length */
|
||||
put_be16(s, 0); /* author length */
|
||||
put_be16(s, 0); /* copyright length */
|
||||
put_byte(s, 0); /* end of header */
|
||||
} else {
|
||||
/* video codec info */
|
||||
put_be32(s,34); /* size */
|
||||
put_tag(s,"VIDORV10");
|
||||
put_be16(s, stream->enc->width);
|
||||
put_be16(s, stream->enc->height);
|
||||
put_be16(s, (int) stream->frame_rate); /* frames per seconds ? */
|
||||
put_be32(s,0); /* unknown meaning */
|
||||
put_be16(s, (int) stream->frame_rate); /* unknown meaning */
|
||||
put_be32(s,0); /* unknown meaning */
|
||||
put_be16(s, 8); /* unknown meaning */
|
||||
/* Seems to be the codec version: only use basic H263. The next
|
||||
versions seems to add a diffential DC coding as in
|
||||
MPEG... nothing new under the sun */
|
||||
put_be32(s,0x10000000);
|
||||
//put_be32(s,0x10003000);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* data stream */
|
||||
put_tag(s,"DATA");
|
||||
put_be32(s,data_size + 10 + 8);
|
||||
put_be16(s,0);
|
||||
|
||||
put_be32(s, nb_packets); /* number of packets */
|
||||
put_be32(s,0); /* next data header */
|
||||
}
|
||||
|
||||
static void write_packet_header(AVFormatContext *ctx, StreamInfo *stream,
|
||||
int length, int key_frame)
|
||||
{
|
||||
int timestamp;
|
||||
ByteIOContext *s = &ctx->pb;
|
||||
|
||||
stream->nb_packets++;
|
||||
stream->packet_total_size += length;
|
||||
if (length > stream->packet_max_size)
|
||||
stream->packet_max_size = length;
|
||||
|
||||
put_be16(s,0); /* version */
|
||||
put_be16(s,length + 12);
|
||||
put_be16(s, stream->num); /* stream number */
|
||||
timestamp = (1000 * (float)stream->nb_frames) / stream->frame_rate;
|
||||
put_be32(s, timestamp); /* timestamp */
|
||||
put_byte(s, 0); /* reserved */
|
||||
put_byte(s, key_frame ? 2 : 0); /* flags */
|
||||
}
|
||||
|
||||
static int rm_write_header(AVFormatContext *s)
|
||||
{
|
||||
RMContext *rm = s->priv_data;
|
||||
StreamInfo *stream;
|
||||
int n;
|
||||
AVCodecContext *codec;
|
||||
|
||||
for(n=0;n<s->nb_streams;n++) {
|
||||
s->streams[n]->id = n;
|
||||
codec = &s->streams[n]->codec;
|
||||
stream = &rm->streams[n];
|
||||
memset(stream, 0, sizeof(StreamInfo));
|
||||
stream->num = n;
|
||||
stream->bit_rate = codec->bit_rate;
|
||||
stream->enc = codec;
|
||||
|
||||
switch(codec->codec_type) {
|
||||
case CODEC_TYPE_AUDIO:
|
||||
rm->audio_stream = stream;
|
||||
stream->frame_rate = (float)codec->sample_rate / (float)codec->frame_size;
|
||||
/* XXX: dummy values */
|
||||
stream->packet_max_size = 1024;
|
||||
stream->nb_packets = 0;
|
||||
stream->total_frames = stream->nb_packets;
|
||||
break;
|
||||
case CODEC_TYPE_VIDEO:
|
||||
rm->video_stream = stream;
|
||||
stream->frame_rate = (float)codec->frame_rate / (float)FRAME_RATE_BASE;
|
||||
/* XXX: dummy values */
|
||||
stream->packet_max_size = 4096;
|
||||
stream->nb_packets = 0;
|
||||
stream->total_frames = stream->nb_packets;
|
||||
break;
|
||||
default:
|
||||
av_abort();
|
||||
}
|
||||
}
|
||||
|
||||
rv10_write_header(s, 0, 0);
|
||||
put_flush_packet(&s->pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm_write_audio(AVFormatContext *s, UINT8 *buf, int size)
|
||||
{
|
||||
UINT8 *buf1;
|
||||
RMContext *rm = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
StreamInfo *stream = rm->audio_stream;
|
||||
int i;
|
||||
|
||||
/* XXX: suppress this malloc */
|
||||
buf1= (UINT8*) av_malloc( size * sizeof(UINT8) );
|
||||
|
||||
write_packet_header(s, stream, size, stream->enc->key_frame);
|
||||
|
||||
/* for AC3, the words seems to be reversed */
|
||||
for(i=0;i<size;i+=2) {
|
||||
buf1[i] = buf[i+1];
|
||||
buf1[i+1] = buf[i];
|
||||
}
|
||||
put_buffer(pb, buf1, size);
|
||||
put_flush_packet(pb);
|
||||
stream->nb_frames++;
|
||||
av_free(buf1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm_write_video(AVFormatContext *s, UINT8 *buf, int size)
|
||||
{
|
||||
RMContext *rm = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
StreamInfo *stream = rm->video_stream;
|
||||
int key_frame = stream->enc->key_frame;
|
||||
|
||||
/* XXX: this is incorrect: should be a parameter */
|
||||
|
||||
/* Well, I spent some time finding the meaning of these bits. I am
|
||||
not sure I understood everything, but it works !! */
|
||||
#if 1
|
||||
write_packet_header(s, stream, size + 7, key_frame);
|
||||
/* bit 7: '1' if final packet of a frame converted in several packets */
|
||||
put_byte(pb, 0x81);
|
||||
/* bit 7: '1' if I frame. bits 6..0 : sequence number in current
|
||||
frame starting from 1 */
|
||||
if (key_frame) {
|
||||
put_byte(pb, 0x81);
|
||||
} else {
|
||||
put_byte(pb, 0x01);
|
||||
}
|
||||
put_be16(pb, 0x4000 | (size)); /* total frame size */
|
||||
put_be16(pb, 0x4000 | (size)); /* offset from the start or the end */
|
||||
#else
|
||||
/* full frame */
|
||||
write_packet_header(s, size + 6);
|
||||
put_byte(pb, 0xc0);
|
||||
put_be16(pb, 0x4000 | size); /* total frame size */
|
||||
put_be16(pb, 0x4000 + packet_number * 126); /* position in stream */
|
||||
#endif
|
||||
put_byte(pb, stream->nb_frames & 0xff);
|
||||
|
||||
put_buffer(pb, buf, size);
|
||||
put_flush_packet(pb);
|
||||
|
||||
stream->nb_frames++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm_write_packet(AVFormatContext *s, int stream_index,
|
||||
UINT8 *buf, int size, int force_pts)
|
||||
{
|
||||
if (s->streams[stream_index]->codec.codec_type ==
|
||||
CODEC_TYPE_AUDIO)
|
||||
return rm_write_audio(s, buf, size);
|
||||
else
|
||||
return rm_write_video(s, buf, size);
|
||||
}
|
||||
|
||||
static int rm_write_trailer(AVFormatContext *s)
|
||||
{
|
||||
RMContext *rm = s->priv_data;
|
||||
int data_size, index_pos, i;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
|
||||
if (!url_is_streamed(&s->pb)) {
|
||||
/* end of file: finish to write header */
|
||||
index_pos = url_fseek(pb, 0, SEEK_CUR);
|
||||
data_size = index_pos - rm->data_pos;
|
||||
|
||||
/* index */
|
||||
put_tag(pb, "INDX");
|
||||
put_be32(pb, 10 + 10 * s->nb_streams);
|
||||
put_be16(pb, 0);
|
||||
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
put_be32(pb, 0); /* zero indices */
|
||||
put_be16(pb, i); /* stream number */
|
||||
put_be32(pb, 0); /* next index */
|
||||
}
|
||||
/* undocumented end header */
|
||||
put_be32(pb, 0);
|
||||
put_be32(pb, 0);
|
||||
|
||||
url_fseek(pb, 0, SEEK_SET);
|
||||
for(i=0;i<s->nb_streams;i++)
|
||||
rm->streams[i].total_frames = rm->streams[i].nb_frames;
|
||||
rv10_write_header(s, data_size, index_pos);
|
||||
} else {
|
||||
/* undocumented end header */
|
||||
put_be32(pb, 0);
|
||||
put_be32(pb, 0);
|
||||
}
|
||||
put_flush_packet(pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***************************************************/
|
||||
|
||||
static void get_str(ByteIOContext *pb, char *buf, int buf_size)
|
||||
{
|
||||
int len, i;
|
||||
char *q;
|
||||
|
||||
len = get_be16(pb);
|
||||
q = buf;
|
||||
for(i=0;i<len;i++) {
|
||||
if (i < buf_size - 1)
|
||||
*q++ = get_byte(pb);
|
||||
}
|
||||
*q = '\0';
|
||||
}
|
||||
|
||||
static void get_str8(ByteIOContext *pb, char *buf, int buf_size)
|
||||
{
|
||||
int len, i;
|
||||
char *q;
|
||||
|
||||
len = get_byte(pb);
|
||||
q = buf;
|
||||
for(i=0;i<len;i++) {
|
||||
if (i < buf_size - 1)
|
||||
*q++ = get_byte(pb);
|
||||
}
|
||||
*q = '\0';
|
||||
}
|
||||
|
||||
static int rm_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
||||
{
|
||||
RMContext *rm = s->priv_data;
|
||||
AVStream *st;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
unsigned int tag, v;
|
||||
int tag_size, size, codec_data_size, i;
|
||||
INT64 codec_pos;
|
||||
unsigned int h263_hack_version;
|
||||
char buf[128];
|
||||
int flags = 0;
|
||||
|
||||
if (get_le32(pb) != MKTAG('.', 'R', 'M', 'F'))
|
||||
return -EIO;
|
||||
|
||||
get_be32(pb); /* header size */
|
||||
get_be16(pb);
|
||||
get_be32(pb);
|
||||
get_be32(pb); /* number of headers */
|
||||
|
||||
for(;;) {
|
||||
if (url_feof(pb))
|
||||
goto fail;
|
||||
tag = get_le32(pb);
|
||||
tag_size = get_be32(pb);
|
||||
get_be16(pb);
|
||||
#if 0
|
||||
printf("tag=%c%c%c%c (%08x) size=%d\n",
|
||||
(tag) & 0xff,
|
||||
(tag >> 8) & 0xff,
|
||||
(tag >> 16) & 0xff,
|
||||
(tag >> 24) & 0xff,
|
||||
tag,
|
||||
tag_size);
|
||||
#endif
|
||||
if (tag_size < 10)
|
||||
goto fail;
|
||||
switch(tag) {
|
||||
case MKTAG('P', 'R', 'O', 'P'):
|
||||
/* file header */
|
||||
get_be32(pb); /* max bit rate */
|
||||
get_be32(pb); /* avg bit rate */
|
||||
get_be32(pb); /* max packet size */
|
||||
get_be32(pb); /* avg packet size */
|
||||
get_be32(pb); /* nb packets */
|
||||
get_be32(pb); /* duration */
|
||||
get_be32(pb); /* preroll */
|
||||
get_be32(pb); /* index offset */
|
||||
get_be32(pb); /* data offset */
|
||||
get_be16(pb); /* nb streams */
|
||||
flags = get_be16(pb); /* flags */
|
||||
break;
|
||||
case MKTAG('C', 'O', 'N', 'T'):
|
||||
get_str(pb, s->title, sizeof(s->title));
|
||||
get_str(pb, s->author, sizeof(s->author));
|
||||
get_str(pb, s->copyright, sizeof(s->copyright));
|
||||
get_str(pb, s->comment, sizeof(s->comment));
|
||||
break;
|
||||
case MKTAG('M', 'D', 'P', 'R'):
|
||||
st = av_mallocz(sizeof(AVStream));
|
||||
if (!st)
|
||||
goto fail;
|
||||
s->streams[s->nb_streams++] = st;
|
||||
st->id = get_be16(pb);
|
||||
get_be32(pb); /* max bit rate */
|
||||
st->codec.bit_rate = get_be32(pb); /* bit rate */
|
||||
get_be32(pb); /* max packet size */
|
||||
get_be32(pb); /* avg packet size */
|
||||
get_be32(pb); /* start time */
|
||||
get_be32(pb); /* preroll */
|
||||
get_be32(pb); /* duration */
|
||||
get_str8(pb, buf, sizeof(buf)); /* desc */
|
||||
get_str8(pb, buf, sizeof(buf)); /* mimetype */
|
||||
codec_data_size = get_be32(pb);
|
||||
codec_pos = url_ftell(pb);
|
||||
|
||||
v = get_be32(pb);
|
||||
if (v == MKTAG(0xfd, 'a', 'r', '.')) {
|
||||
/* ra type header */
|
||||
get_be32(pb); /* version */
|
||||
get_be32(pb); /* .ra4 */
|
||||
get_be32(pb);
|
||||
get_be16(pb);
|
||||
get_be32(pb); /* header size */
|
||||
get_be16(pb); /* add codec info */
|
||||
get_be32(pb); /* coded frame size */
|
||||
get_be32(pb); /* ??? */
|
||||
get_be32(pb); /* ??? */
|
||||
get_be32(pb); /* ??? */
|
||||
get_be16(pb); /* 1 */
|
||||
get_be16(pb); /* coded frame size */
|
||||
get_be32(pb);
|
||||
st->codec.sample_rate = get_be16(pb);
|
||||
get_be32(pb);
|
||||
st->codec.channels = get_be16(pb);
|
||||
get_str8(pb, buf, sizeof(buf)); /* desc */
|
||||
get_str8(pb, buf, sizeof(buf)); /* desc */
|
||||
st->codec.codec_type = CODEC_TYPE_AUDIO;
|
||||
if (!strcmp(buf, "dnet")) {
|
||||
st->codec.codec_id = CODEC_ID_AC3;
|
||||
} else {
|
||||
st->codec.codec_id = CODEC_ID_NONE;
|
||||
pstrcpy(st->codec.codec_name, sizeof(st->codec.codec_name),
|
||||
buf);
|
||||
}
|
||||
} else {
|
||||
if (get_le32(pb) != MKTAG('V', 'I', 'D', 'O')) {
|
||||
fail1:
|
||||
fprintf(stderr, "Unsupported video codec\n");
|
||||
goto fail;
|
||||
}
|
||||
st->codec.codec_tag = get_le32(pb);
|
||||
if (st->codec.codec_tag != MKTAG('R', 'V', '1', '0'))
|
||||
goto fail1;
|
||||
st->codec.width = get_be16(pb);
|
||||
st->codec.height = get_be16(pb);
|
||||
st->codec.frame_rate = get_be16(pb) * FRAME_RATE_BASE;
|
||||
st->codec.codec_type = CODEC_TYPE_VIDEO;
|
||||
get_be32(pb);
|
||||
get_be16(pb);
|
||||
get_be32(pb);
|
||||
get_be16(pb);
|
||||
/* modification of h263 codec version (!) */
|
||||
h263_hack_version = get_be32(pb);
|
||||
switch(h263_hack_version) {
|
||||
case 0x10000000:
|
||||
case 0x10003000:
|
||||
case 0x10003001:
|
||||
st->codec.sub_id = h263_hack_version;
|
||||
st->codec.codec_id = CODEC_ID_RV10;
|
||||
break;
|
||||
default:
|
||||
/* not handled */
|
||||
st->codec.codec_id = CODEC_ID_NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* skip codec info */
|
||||
size = url_ftell(pb) - codec_pos;
|
||||
url_fskip(pb, codec_data_size - size);
|
||||
break;
|
||||
case MKTAG('D', 'A', 'T', 'A'):
|
||||
goto header_end;
|
||||
default:
|
||||
/* unknown tag: skip it */
|
||||
url_fskip(pb, tag_size - 10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
header_end:
|
||||
rm->nb_packets = get_be32(pb); /* number of packets */
|
||||
if (!rm->nb_packets && (flags & 4))
|
||||
rm->nb_packets = 3600 * 25;
|
||||
get_be32(pb); /* next data header */
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
av_free(s->streams[i]);
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int get_num(ByteIOContext *pb, int *len)
|
||||
{
|
||||
int n, n1;
|
||||
|
||||
n = get_be16(pb);
|
||||
(*len)-=2;
|
||||
if (n >= 0x4000) {
|
||||
return n - 0x4000;
|
||||
} else {
|
||||
n1 = get_be16(pb);
|
||||
(*len)-=2;
|
||||
return (n << 16) | n1;
|
||||
}
|
||||
}
|
||||
|
||||
static int rm_read_packet(AVFormatContext *s, AVPacket *pkt)
|
||||
{
|
||||
RMContext *rm = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
AVStream *st;
|
||||
int len, num, timestamp, i, tmp, j;
|
||||
UINT8 *ptr;
|
||||
int flags;
|
||||
|
||||
redo:
|
||||
if (rm->nb_packets == 0)
|
||||
return -EIO;
|
||||
get_be16(pb);
|
||||
len = get_be16(pb);
|
||||
if (len < 12)
|
||||
return -EIO;
|
||||
num = get_be16(pb);
|
||||
timestamp = get_be32(pb);
|
||||
get_byte(pb); /* reserved */
|
||||
flags = get_byte(pb); /* flags */
|
||||
rm->nb_packets--;
|
||||
len -= 12;
|
||||
|
||||
st = NULL;
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
st = s->streams[i];
|
||||
if (num == st->id)
|
||||
break;
|
||||
}
|
||||
if (i == s->nb_streams) {
|
||||
/* skip packet if unknown number */
|
||||
url_fskip(pb, len);
|
||||
goto redo;
|
||||
}
|
||||
|
||||
if (st->codec.codec_type == CODEC_TYPE_VIDEO) {
|
||||
int full_frame, h, pic_num;
|
||||
|
||||
h= get_byte(pb);
|
||||
if ((h & 0xc0) == 0xc0) {
|
||||
int len2, pos;
|
||||
full_frame = 1;
|
||||
len2= get_num(pb, &len);
|
||||
pos = get_num(pb, &len);
|
||||
//printf("pos:%d\n",len);
|
||||
len -= 2;
|
||||
} else {
|
||||
int seq, frame_size, pos;
|
||||
full_frame = 0;
|
||||
seq = get_byte(pb);
|
||||
frame_size = get_num(pb, &len);
|
||||
pos = get_num(pb, &len);
|
||||
//printf("seq:%d, size:%d, pos:%d\n",seq,frame_size,pos);
|
||||
len -= 3;
|
||||
}
|
||||
/* picture number */
|
||||
pic_num= get_byte(pb);
|
||||
|
||||
//XXX/FIXME/HACK, demuxer should be fixed to send complete frames ...
|
||||
if(st->codec.slice_offset==NULL) st->codec.slice_offset= (int*)malloc(sizeof(int));
|
||||
st->codec.slice_count= full_frame;
|
||||
st->codec.slice_offset[0]= 0;
|
||||
}
|
||||
|
||||
av_new_packet(pkt, len);
|
||||
pkt->stream_index = i;
|
||||
get_buffer(pb, pkt->data, len);
|
||||
|
||||
/* for AC3, needs to swap bytes */
|
||||
if (st->codec.codec_id == CODEC_ID_AC3) {
|
||||
ptr = pkt->data;
|
||||
for(j=0;j<len;j+=2) {
|
||||
tmp = ptr[0];
|
||||
ptr[0] = ptr[1];
|
||||
ptr[1] = tmp;
|
||||
ptr += 2;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm_read_close(AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm_probe(AVProbeData *p)
|
||||
{
|
||||
/* check file header */
|
||||
if (p->buf_size <= 32)
|
||||
return 0;
|
||||
if (p->buf[0] == '.' && p->buf[1] == 'R' &&
|
||||
p->buf[2] == 'M' && p->buf[3] == 'F' &&
|
||||
p->buf[4] == 0 && p->buf[5] == 0)
|
||||
return AVPROBE_SCORE_MAX;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVInputFormat rm_iformat = {
|
||||
"rm",
|
||||
"rm format",
|
||||
sizeof(RMContext),
|
||||
rm_probe,
|
||||
rm_read_header,
|
||||
rm_read_packet,
|
||||
rm_read_close,
|
||||
};
|
||||
|
||||
static AVOutputFormat rm_oformat = {
|
||||
"rm",
|
||||
"rm format",
|
||||
"audio/x-pn-realaudio",
|
||||
"rm,ra",
|
||||
sizeof(RMContext),
|
||||
CODEC_ID_AC3,
|
||||
CODEC_ID_RV10,
|
||||
rm_write_header,
|
||||
rm_write_packet,
|
||||
rm_write_trailer,
|
||||
};
|
||||
|
||||
int rm_init(void)
|
||||
{
|
||||
av_register_input_format(&rm_iformat);
|
||||
av_register_output_format(&rm_oformat);
|
||||
return 0;
|
||||
}
|
||||
687
libavformat/rtp.c
Normal file
687
libavformat/rtp.c
Normal file
@@ -0,0 +1,687 @@
|
||||
/*
|
||||
* RTP input/output format
|
||||
* Copyright (c) 2002 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#ifndef __BEOS__
|
||||
# include <arpa/inet.h>
|
||||
#else
|
||||
# include "barpainet.h"
|
||||
#endif
|
||||
#include <netdb.h>
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
|
||||
/* TODO: - add RTCP statistics reporting (should be optional).
|
||||
|
||||
- add support for h263/mpeg4 packetized output : IDEA: send a
|
||||
buffer to 'rtp_write_packet' contains all the packets for ONE
|
||||
frame. Each packet should have a four byte header containing
|
||||
the length in big endian format (same trick as
|
||||
'url_open_dyn_packet_buf')
|
||||
*/
|
||||
|
||||
#define RTP_VERSION 2
|
||||
|
||||
#define RTP_MAX_SDES 256 /* maximum text length for SDES */
|
||||
|
||||
/* RTCP paquets use 0.5 % of the bandwidth */
|
||||
#define RTCP_TX_RATIO_NUM 5
|
||||
#define RTCP_TX_RATIO_DEN 1000
|
||||
|
||||
typedef enum {
|
||||
RTCP_SR = 200,
|
||||
RTCP_RR = 201,
|
||||
RTCP_SDES = 202,
|
||||
RTCP_BYE = 203,
|
||||
RTCP_APP = 204
|
||||
} rtcp_type_t;
|
||||
|
||||
typedef enum {
|
||||
RTCP_SDES_END = 0,
|
||||
RTCP_SDES_CNAME = 1,
|
||||
RTCP_SDES_NAME = 2,
|
||||
RTCP_SDES_EMAIL = 3,
|
||||
RTCP_SDES_PHONE = 4,
|
||||
RTCP_SDES_LOC = 5,
|
||||
RTCP_SDES_TOOL = 6,
|
||||
RTCP_SDES_NOTE = 7,
|
||||
RTCP_SDES_PRIV = 8,
|
||||
RTCP_SDES_IMG = 9,
|
||||
RTCP_SDES_DOOR = 10,
|
||||
RTCP_SDES_SOURCE = 11
|
||||
} rtcp_sdes_type_t;
|
||||
|
||||
enum RTPPayloadType {
|
||||
RTP_PT_ULAW = 0,
|
||||
RTP_PT_GSM = 3,
|
||||
RTP_PT_G723 = 4,
|
||||
RTP_PT_ALAW = 8,
|
||||
RTP_PT_S16BE_STEREO = 10,
|
||||
RTP_PT_S16BE_MONO = 11,
|
||||
RTP_PT_MPEGAUDIO = 14,
|
||||
RTP_PT_JPEG = 26,
|
||||
RTP_PT_H261 = 31,
|
||||
RTP_PT_MPEGVIDEO = 32,
|
||||
RTP_PT_MPEG2TS = 33,
|
||||
RTP_PT_H263 = 34, /* old H263 encapsulation */
|
||||
RTP_PT_PRIVATE = 96,
|
||||
};
|
||||
|
||||
typedef struct RTPContext {
|
||||
int payload_type;
|
||||
UINT32 ssrc;
|
||||
UINT16 seq;
|
||||
UINT32 timestamp;
|
||||
UINT32 base_timestamp;
|
||||
UINT32 cur_timestamp;
|
||||
int max_payload_size;
|
||||
/* rtcp sender statistics receive */
|
||||
INT64 last_rtcp_ntp_time;
|
||||
UINT32 last_rtcp_timestamp;
|
||||
/* rtcp sender statistics */
|
||||
unsigned int packet_count;
|
||||
unsigned int octet_count;
|
||||
unsigned int last_octet_count;
|
||||
int first_packet;
|
||||
/* buffer for output */
|
||||
UINT8 buf[RTP_MAX_PACKET_LENGTH];
|
||||
UINT8 *buf_ptr;
|
||||
} RTPContext;
|
||||
|
||||
int rtp_get_codec_info(AVCodecContext *codec, int payload_type)
|
||||
{
|
||||
switch(payload_type) {
|
||||
case RTP_PT_ULAW:
|
||||
codec->codec_id = CODEC_ID_PCM_MULAW;
|
||||
codec->channels = 1;
|
||||
codec->sample_rate = 8000;
|
||||
break;
|
||||
case RTP_PT_ALAW:
|
||||
codec->codec_id = CODEC_ID_PCM_ALAW;
|
||||
codec->channels = 1;
|
||||
codec->sample_rate = 8000;
|
||||
break;
|
||||
case RTP_PT_S16BE_STEREO:
|
||||
codec->codec_id = CODEC_ID_PCM_S16BE;
|
||||
codec->channels = 2;
|
||||
codec->sample_rate = 44100;
|
||||
break;
|
||||
case RTP_PT_S16BE_MONO:
|
||||
codec->codec_id = CODEC_ID_PCM_S16BE;
|
||||
codec->channels = 1;
|
||||
codec->sample_rate = 44100;
|
||||
break;
|
||||
case RTP_PT_MPEGAUDIO:
|
||||
codec->codec_id = CODEC_ID_MP2;
|
||||
break;
|
||||
case RTP_PT_JPEG:
|
||||
codec->codec_id = CODEC_ID_MJPEG;
|
||||
break;
|
||||
case RTP_PT_MPEGVIDEO:
|
||||
codec->codec_id = CODEC_ID_MPEG1VIDEO;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return < 0 if unknown payload type */
|
||||
int rtp_get_payload_type(AVCodecContext *codec)
|
||||
{
|
||||
int payload_type;
|
||||
|
||||
/* compute the payload type */
|
||||
payload_type = -1;
|
||||
switch(codec->codec_id) {
|
||||
case CODEC_ID_PCM_MULAW:
|
||||
payload_type = RTP_PT_ULAW;
|
||||
break;
|
||||
case CODEC_ID_PCM_ALAW:
|
||||
payload_type = RTP_PT_ALAW;
|
||||
break;
|
||||
case CODEC_ID_PCM_S16BE:
|
||||
if (codec->channels == 1) {
|
||||
payload_type = RTP_PT_S16BE_MONO;
|
||||
} else if (codec->channels == 2) {
|
||||
payload_type = RTP_PT_S16BE_STEREO;
|
||||
}
|
||||
break;
|
||||
case CODEC_ID_MP2:
|
||||
case CODEC_ID_MP3LAME:
|
||||
payload_type = RTP_PT_MPEGAUDIO;
|
||||
break;
|
||||
case CODEC_ID_MJPEG:
|
||||
payload_type = RTP_PT_JPEG;
|
||||
break;
|
||||
case CODEC_ID_MPEG1VIDEO:
|
||||
payload_type = RTP_PT_MPEGVIDEO;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return payload_type;
|
||||
}
|
||||
|
||||
static inline UINT32 decode_be32(const UINT8 *p)
|
||||
{
|
||||
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
|
||||
}
|
||||
|
||||
static inline UINT32 decode_be64(const UINT8 *p)
|
||||
{
|
||||
return ((UINT64)decode_be32(p) << 32) | decode_be32(p + 4);
|
||||
}
|
||||
|
||||
static int rtcp_parse_packet(AVFormatContext *s1, const unsigned char *buf, int len)
|
||||
{
|
||||
RTPContext *s = s1->priv_data;
|
||||
|
||||
if (buf[1] != 200)
|
||||
return -1;
|
||||
s->last_rtcp_ntp_time = decode_be64(buf + 8);
|
||||
s->last_rtcp_timestamp = decode_be32(buf + 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an RTP packet directly sent as raw data. Can only be used if
|
||||
* 'raw' is given as input file
|
||||
* @param s1 media file context
|
||||
* @param pkt returned packet
|
||||
* @param buf input buffer
|
||||
* @param len buffer len
|
||||
* @return zero if no error.
|
||||
*/
|
||||
int rtp_parse_packet(AVFormatContext *s1, AVPacket *pkt,
|
||||
const unsigned char *buf, int len)
|
||||
{
|
||||
RTPContext *s = s1->priv_data;
|
||||
unsigned int ssrc, h;
|
||||
int payload_type, seq, delta_timestamp;
|
||||
AVStream *st;
|
||||
UINT32 timestamp;
|
||||
|
||||
if (len < 12)
|
||||
return -1;
|
||||
|
||||
if ((buf[0] & 0xc0) != (RTP_VERSION << 6))
|
||||
return -1;
|
||||
if (buf[1] >= 200 && buf[1] <= 204) {
|
||||
rtcp_parse_packet(s1, buf, len);
|
||||
return -1;
|
||||
}
|
||||
payload_type = buf[1] & 0x7f;
|
||||
seq = (buf[2] << 8) | buf[3];
|
||||
timestamp = decode_be32(buf + 4);
|
||||
ssrc = decode_be32(buf + 8);
|
||||
|
||||
if (s->payload_type < 0) {
|
||||
s->payload_type = payload_type;
|
||||
|
||||
if (payload_type == RTP_PT_MPEG2TS) {
|
||||
/* XXX: special case : not a single codec but a whole stream */
|
||||
return -1;
|
||||
} else {
|
||||
st = av_new_stream(s1, 0);
|
||||
if (!st)
|
||||
return -1;
|
||||
rtp_get_codec_info(&st->codec, payload_type);
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: we can handle only one payload type */
|
||||
if (s->payload_type != payload_type)
|
||||
return -1;
|
||||
#if defined(DEBUG) || 1
|
||||
if (seq != ((s->seq + 1) & 0xffff)) {
|
||||
printf("RTP: PT=%02x: bad cseq %04x expected=%04x\n",
|
||||
payload_type, seq, ((s->seq + 1) & 0xffff));
|
||||
}
|
||||
s->seq = seq;
|
||||
#endif
|
||||
len -= 12;
|
||||
buf += 12;
|
||||
st = s1->streams[0];
|
||||
switch(st->codec.codec_id) {
|
||||
case CODEC_ID_MP2:
|
||||
/* better than nothing: skip mpeg audio RTP header */
|
||||
if (len <= 4)
|
||||
return -1;
|
||||
h = decode_be32(buf);
|
||||
len -= 4;
|
||||
buf += 4;
|
||||
av_new_packet(pkt, len);
|
||||
memcpy(pkt->data, buf, len);
|
||||
break;
|
||||
case CODEC_ID_MPEG1VIDEO:
|
||||
/* better than nothing: skip mpeg audio RTP header */
|
||||
if (len <= 4)
|
||||
return -1;
|
||||
h = decode_be32(buf);
|
||||
buf += 4;
|
||||
len -= 4;
|
||||
if (h & (1 << 26)) {
|
||||
/* mpeg2 */
|
||||
if (len <= 4)
|
||||
return -1;
|
||||
buf += 4;
|
||||
len -= 4;
|
||||
}
|
||||
av_new_packet(pkt, len);
|
||||
memcpy(pkt->data, buf, len);
|
||||
break;
|
||||
default:
|
||||
av_new_packet(pkt, len);
|
||||
memcpy(pkt->data, buf, len);
|
||||
break;
|
||||
}
|
||||
|
||||
if (s->last_rtcp_ntp_time != AV_NOPTS_VALUE) {
|
||||
/* compute pts from timestamp with received ntp_time */
|
||||
delta_timestamp = timestamp - s->last_rtcp_timestamp;
|
||||
/* XXX: do conversion, but not needed for mpeg at 90 KhZ */
|
||||
pkt->pts = s->last_rtcp_ntp_time + delta_timestamp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtp_read_header(AVFormatContext *s1,
|
||||
AVFormatParameters *ap)
|
||||
{
|
||||
RTPContext *s = s1->priv_data;
|
||||
s->payload_type = -1;
|
||||
s->last_rtcp_ntp_time = AV_NOPTS_VALUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtp_read_packet(AVFormatContext *s1, AVPacket *pkt)
|
||||
{
|
||||
char buf[RTP_MAX_PACKET_LENGTH];
|
||||
int ret;
|
||||
|
||||
/* XXX: needs a better API for packet handling ? */
|
||||
for(;;) {
|
||||
ret = url_read(url_fileno(&s1->pb), buf, sizeof(buf));
|
||||
if (ret < 0)
|
||||
return AVERROR_IO;
|
||||
if (rtp_parse_packet(s1, pkt, buf, ret) == 0)
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtp_read_close(AVFormatContext *s1)
|
||||
{
|
||||
// RTPContext *s = s1->priv_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtp_probe(AVProbeData *p)
|
||||
{
|
||||
if (strstart(p->filename, "rtp://", NULL))
|
||||
return AVPROBE_SCORE_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* rtp output */
|
||||
|
||||
static int rtp_write_header(AVFormatContext *s1)
|
||||
{
|
||||
RTPContext *s = s1->priv_data;
|
||||
int payload_type, max_packet_size;
|
||||
AVStream *st;
|
||||
|
||||
if (s1->nb_streams != 1)
|
||||
return -1;
|
||||
st = s1->streams[0];
|
||||
|
||||
payload_type = rtp_get_payload_type(&st->codec);
|
||||
if (payload_type < 0)
|
||||
payload_type = RTP_PT_PRIVATE; /* private payload type */
|
||||
s->payload_type = payload_type;
|
||||
|
||||
s->base_timestamp = random();
|
||||
s->timestamp = s->base_timestamp;
|
||||
s->ssrc = random();
|
||||
s->first_packet = 1;
|
||||
|
||||
max_packet_size = url_fget_max_packet_size(&s1->pb);
|
||||
if (max_packet_size <= 12)
|
||||
return AVERROR_IO;
|
||||
s->max_payload_size = max_packet_size - 12;
|
||||
|
||||
switch(st->codec.codec_id) {
|
||||
case CODEC_ID_MP2:
|
||||
case CODEC_ID_MP3LAME:
|
||||
s->buf_ptr = s->buf + 4;
|
||||
s->cur_timestamp = 0;
|
||||
break;
|
||||
case CODEC_ID_MPEG1VIDEO:
|
||||
s->cur_timestamp = 0;
|
||||
break;
|
||||
default:
|
||||
s->buf_ptr = s->buf;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* send an rtcp sender report packet */
|
||||
static void rtcp_send_sr(AVFormatContext *s1, INT64 ntp_time)
|
||||
{
|
||||
RTPContext *s = s1->priv_data;
|
||||
#if defined(DEBUG)
|
||||
printf("RTCP: %02x %Lx %x\n", s->payload_type, ntp_time, s->timestamp);
|
||||
#endif
|
||||
put_byte(&s1->pb, (RTP_VERSION << 6));
|
||||
put_byte(&s1->pb, 200);
|
||||
put_be16(&s1->pb, 6); /* length in words - 1 */
|
||||
put_be32(&s1->pb, s->ssrc);
|
||||
put_be64(&s1->pb, ntp_time);
|
||||
put_be32(&s1->pb, s->timestamp);
|
||||
put_be32(&s1->pb, s->packet_count);
|
||||
put_be32(&s1->pb, s->octet_count);
|
||||
put_flush_packet(&s1->pb);
|
||||
}
|
||||
|
||||
/* send an rtp packet. sequence number is incremented, but the caller
|
||||
must update the timestamp itself */
|
||||
static void rtp_send_data(AVFormatContext *s1, UINT8 *buf1, int len)
|
||||
{
|
||||
RTPContext *s = s1->priv_data;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("rtp_send_data size=%d\n", len);
|
||||
#endif
|
||||
|
||||
/* build the RTP header */
|
||||
put_byte(&s1->pb, (RTP_VERSION << 6));
|
||||
put_byte(&s1->pb, s->payload_type & 0x7f);
|
||||
put_be16(&s1->pb, s->seq);
|
||||
put_be32(&s1->pb, s->timestamp);
|
||||
put_be32(&s1->pb, s->ssrc);
|
||||
|
||||
put_buffer(&s1->pb, buf1, len);
|
||||
put_flush_packet(&s1->pb);
|
||||
|
||||
s->seq++;
|
||||
s->octet_count += len;
|
||||
s->packet_count++;
|
||||
}
|
||||
|
||||
/* send an integer number of samples and compute time stamp and fill
|
||||
the rtp send buffer before sending. */
|
||||
static void rtp_send_samples(AVFormatContext *s1,
|
||||
UINT8 *buf1, int size, int sample_size)
|
||||
{
|
||||
RTPContext *s = s1->priv_data;
|
||||
int len, max_packet_size, n;
|
||||
|
||||
max_packet_size = (s->max_payload_size / sample_size) * sample_size;
|
||||
/* not needed, but who nows */
|
||||
if ((size % sample_size) != 0)
|
||||
av_abort();
|
||||
while (size > 0) {
|
||||
len = (max_packet_size - (s->buf_ptr - s->buf));
|
||||
if (len > size)
|
||||
len = size;
|
||||
|
||||
/* copy data */
|
||||
memcpy(s->buf_ptr, buf1, len);
|
||||
s->buf_ptr += len;
|
||||
buf1 += len;
|
||||
size -= len;
|
||||
n = (s->buf_ptr - s->buf);
|
||||
/* if buffer full, then send it */
|
||||
if (n >= max_packet_size) {
|
||||
rtp_send_data(s1, s->buf, n);
|
||||
s->buf_ptr = s->buf;
|
||||
/* update timestamp */
|
||||
s->timestamp += n / sample_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: we suppose that exactly one frame is given as argument here */
|
||||
/* XXX: test it */
|
||||
static void rtp_send_mpegaudio(AVFormatContext *s1,
|
||||
UINT8 *buf1, int size)
|
||||
{
|
||||
RTPContext *s = s1->priv_data;
|
||||
AVStream *st = s1->streams[0];
|
||||
int len, count, max_packet_size;
|
||||
|
||||
max_packet_size = s->max_payload_size;
|
||||
|
||||
/* test if we must flush because not enough space */
|
||||
len = (s->buf_ptr - s->buf);
|
||||
if ((len + size) > max_packet_size) {
|
||||
if (len > 4) {
|
||||
rtp_send_data(s1, s->buf, s->buf_ptr - s->buf);
|
||||
s->buf_ptr = s->buf + 4;
|
||||
/* 90 KHz time stamp */
|
||||
s->timestamp = s->base_timestamp +
|
||||
(s->cur_timestamp * 90000LL) / st->codec.sample_rate;
|
||||
}
|
||||
}
|
||||
|
||||
/* add the packet */
|
||||
if (size > max_packet_size) {
|
||||
/* big packet: fragment */
|
||||
count = 0;
|
||||
while (size > 0) {
|
||||
len = max_packet_size - 4;
|
||||
if (len > size)
|
||||
len = size;
|
||||
/* build fragmented packet */
|
||||
s->buf[0] = 0;
|
||||
s->buf[1] = 0;
|
||||
s->buf[2] = count >> 8;
|
||||
s->buf[3] = count;
|
||||
memcpy(s->buf + 4, buf1, len);
|
||||
rtp_send_data(s1, s->buf, len + 4);
|
||||
size -= len;
|
||||
buf1 += len;
|
||||
count += len;
|
||||
}
|
||||
} else {
|
||||
if (s->buf_ptr == s->buf + 4) {
|
||||
/* no fragmentation possible */
|
||||
s->buf[0] = 0;
|
||||
s->buf[1] = 0;
|
||||
s->buf[2] = 0;
|
||||
s->buf[3] = 0;
|
||||
}
|
||||
memcpy(s->buf_ptr, buf1, size);
|
||||
s->buf_ptr += size;
|
||||
}
|
||||
s->cur_timestamp += st->codec.frame_size;
|
||||
}
|
||||
|
||||
/* NOTE: a single frame must be passed with sequence header if
|
||||
needed. XXX: use slices. */
|
||||
static void rtp_send_mpegvideo(AVFormatContext *s1,
|
||||
UINT8 *buf1, int size)
|
||||
{
|
||||
RTPContext *s = s1->priv_data;
|
||||
AVStream *st = s1->streams[0];
|
||||
int len, h, max_packet_size;
|
||||
UINT8 *q;
|
||||
|
||||
max_packet_size = s->max_payload_size;
|
||||
|
||||
while (size > 0) {
|
||||
/* XXX: more correct headers */
|
||||
h = 0;
|
||||
if (st->codec.sub_id == 2)
|
||||
h |= 1 << 26; /* mpeg 2 indicator */
|
||||
q = s->buf;
|
||||
*q++ = h >> 24;
|
||||
*q++ = h >> 16;
|
||||
*q++ = h >> 8;
|
||||
*q++ = h;
|
||||
|
||||
if (st->codec.sub_id == 2) {
|
||||
h = 0;
|
||||
*q++ = h >> 24;
|
||||
*q++ = h >> 16;
|
||||
*q++ = h >> 8;
|
||||
*q++ = h;
|
||||
}
|
||||
|
||||
len = max_packet_size - (q - s->buf);
|
||||
if (len > size)
|
||||
len = size;
|
||||
|
||||
memcpy(q, buf1, len);
|
||||
q += len;
|
||||
|
||||
/* 90 KHz time stamp */
|
||||
/* XXX: overflow */
|
||||
s->timestamp = s->base_timestamp +
|
||||
(s->cur_timestamp * 90000LL * FRAME_RATE_BASE) / st->codec.frame_rate;
|
||||
rtp_send_data(s1, s->buf, q - s->buf);
|
||||
|
||||
buf1 += len;
|
||||
size -= len;
|
||||
}
|
||||
s->cur_timestamp++;
|
||||
}
|
||||
|
||||
static void rtp_send_raw(AVFormatContext *s1,
|
||||
UINT8 *buf1, int size)
|
||||
{
|
||||
RTPContext *s = s1->priv_data;
|
||||
AVStream *st = s1->streams[0];
|
||||
int len, max_packet_size;
|
||||
|
||||
max_packet_size = s->max_payload_size;
|
||||
|
||||
while (size > 0) {
|
||||
len = max_packet_size;
|
||||
if (len > size)
|
||||
len = size;
|
||||
|
||||
/* 90 KHz time stamp */
|
||||
/* XXX: overflow */
|
||||
s->timestamp = s->base_timestamp +
|
||||
(s->cur_timestamp * 90000LL * FRAME_RATE_BASE) / st->codec.frame_rate;
|
||||
rtp_send_data(s1, buf1, len);
|
||||
|
||||
buf1 += len;
|
||||
size -= len;
|
||||
}
|
||||
s->cur_timestamp++;
|
||||
}
|
||||
|
||||
/* write an RTP packet. 'buf1' must contain a single specific frame. */
|
||||
static int rtp_write_packet(AVFormatContext *s1, int stream_index,
|
||||
UINT8 *buf1, int size, int force_pts)
|
||||
{
|
||||
RTPContext *s = s1->priv_data;
|
||||
AVStream *st = s1->streams[0];
|
||||
int rtcp_bytes;
|
||||
INT64 ntp_time;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("%d: write len=%d\n", stream_index, size);
|
||||
#endif
|
||||
|
||||
/* XXX: mpeg pts hardcoded. RTCP send every 0.5 seconds */
|
||||
rtcp_bytes = ((s->octet_count - s->last_octet_count) * RTCP_TX_RATIO_NUM) /
|
||||
RTCP_TX_RATIO_DEN;
|
||||
if (s->first_packet || rtcp_bytes >= 28) {
|
||||
/* compute NTP time */
|
||||
ntp_time = force_pts; // ((INT64)force_pts << 28) / 5625
|
||||
rtcp_send_sr(s1, ntp_time);
|
||||
s->last_octet_count = s->octet_count;
|
||||
s->first_packet = 0;
|
||||
}
|
||||
|
||||
switch(st->codec.codec_id) {
|
||||
case CODEC_ID_PCM_MULAW:
|
||||
case CODEC_ID_PCM_ALAW:
|
||||
case CODEC_ID_PCM_U8:
|
||||
case CODEC_ID_PCM_S8:
|
||||
rtp_send_samples(s1, buf1, size, 1 * st->codec.channels);
|
||||
break;
|
||||
case CODEC_ID_PCM_U16BE:
|
||||
case CODEC_ID_PCM_U16LE:
|
||||
case CODEC_ID_PCM_S16BE:
|
||||
case CODEC_ID_PCM_S16LE:
|
||||
rtp_send_samples(s1, buf1, size, 2 * st->codec.channels);
|
||||
break;
|
||||
case CODEC_ID_MP2:
|
||||
case CODEC_ID_MP3LAME:
|
||||
rtp_send_mpegaudio(s1, buf1, size);
|
||||
break;
|
||||
case CODEC_ID_MPEG1VIDEO:
|
||||
rtp_send_mpegvideo(s1, buf1, size);
|
||||
break;
|
||||
default:
|
||||
/* better than nothing : send the codec raw data */
|
||||
rtp_send_raw(s1, buf1, size);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtp_write_trailer(AVFormatContext *s1)
|
||||
{
|
||||
// RTPContext *s = s1->priv_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
AVInputFormat rtp_demux = {
|
||||
"rtp",
|
||||
"RTP input format",
|
||||
sizeof(RTPContext),
|
||||
rtp_probe,
|
||||
rtp_read_header,
|
||||
rtp_read_packet,
|
||||
rtp_read_close,
|
||||
.flags = AVFMT_NOHEADER,
|
||||
};
|
||||
|
||||
AVOutputFormat rtp_mux = {
|
||||
"rtp",
|
||||
"RTP output format",
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof(RTPContext),
|
||||
CODEC_ID_PCM_MULAW,
|
||||
CODEC_ID_NONE,
|
||||
rtp_write_header,
|
||||
rtp_write_packet,
|
||||
rtp_write_trailer,
|
||||
};
|
||||
|
||||
int rtp_init(void)
|
||||
{
|
||||
av_register_output_format(&rtp_mux);
|
||||
av_register_input_format(&rtp_demux);
|
||||
return 0;
|
||||
}
|
||||
40
libavformat/rtp.h
Normal file
40
libavformat/rtp.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* RTP definitions
|
||||
* Copyright (c) 2002 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef RTP_H
|
||||
#define RTP_H
|
||||
|
||||
#define RTP_MIN_PACKET_LENGTH 12
|
||||
#define RTP_MAX_PACKET_LENGTH 1500 /* XXX: suppress this define */
|
||||
|
||||
int rtp_init(void);
|
||||
int rtp_get_codec_info(AVCodecContext *codec, int payload_type);
|
||||
int rtp_get_payload_type(AVCodecContext *codec);
|
||||
int rtp_parse_packet(AVFormatContext *s1, AVPacket *pkt,
|
||||
const unsigned char *buf, int len);
|
||||
|
||||
extern AVOutputFormat rtp_mux;
|
||||
extern AVInputFormat rtp_demux;
|
||||
|
||||
int rtp_get_local_port(URLContext *h);
|
||||
int rtp_set_remote_url(URLContext *h, const char *uri);
|
||||
void rtp_get_file_handles(URLContext *h, int *prtp_fd, int *prtcp_fd);
|
||||
|
||||
extern URLProtocol rtp_protocol;
|
||||
|
||||
#endif /* RTP_H */
|
||||
300
libavformat/rtpproto.c
Normal file
300
libavformat/rtpproto.c
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* RTP network protocol
|
||||
* Copyright (c) 2002 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#ifndef __BEOS__
|
||||
# include <arpa/inet.h>
|
||||
#else
|
||||
# include "barpainet.h"
|
||||
#endif
|
||||
#include <netdb.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define RTP_TX_BUF_SIZE (64 * 1024)
|
||||
#define RTP_RX_BUF_SIZE (128 * 1024)
|
||||
|
||||
typedef struct RTPContext {
|
||||
URLContext *rtp_hd, *rtcp_hd;
|
||||
int rtp_fd, rtcp_fd;
|
||||
} RTPContext;
|
||||
|
||||
/**
|
||||
* If no filename is given to av_open_input_file because you want to
|
||||
* get the local port first, then you must call this function to set
|
||||
* the remote server address.
|
||||
*
|
||||
* @param s1 media file context
|
||||
* @param uri of the remote server
|
||||
* @return zero if no error.
|
||||
*/
|
||||
int rtp_set_remote_url(URLContext *h, const char *uri)
|
||||
{
|
||||
RTPContext *s = h->priv_data;
|
||||
char hostname[256];
|
||||
int port;
|
||||
|
||||
char buf[1024];
|
||||
char path[1024];
|
||||
|
||||
url_split(NULL, 0, hostname, sizeof(hostname), &port,
|
||||
path, sizeof(path), uri);
|
||||
|
||||
snprintf(buf, sizeof(buf), "udp://%s:%d%s", hostname, port, path);
|
||||
udp_set_remote_url(s->rtp_hd, buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "udp://%s:%d%s", hostname, port + 1, path);
|
||||
udp_set_remote_url(s->rtcp_hd, buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* add option to url of the form:
|
||||
"http://host:port/path?option1=val1&option2=val2... */
|
||||
void url_add_option(char *buf, int buf_size, const char *fmt, ...)
|
||||
{
|
||||
char buf1[1024];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (strchr(buf, '?'))
|
||||
pstrcat(buf, buf_size, "&");
|
||||
else
|
||||
pstrcat(buf, buf_size, "?");
|
||||
vsnprintf(buf1, sizeof(buf1), fmt, ap);
|
||||
pstrcat(buf, buf_size, buf1);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void build_udp_url(char *buf, int buf_size,
|
||||
const char *hostname, int port,
|
||||
int local_port, int multicast, int ttl)
|
||||
{
|
||||
snprintf(buf, buf_size, "udp://%s:%d", hostname, port);
|
||||
if (local_port >= 0)
|
||||
url_add_option(buf, buf_size, "localport=%d", local_port);
|
||||
if (multicast)
|
||||
url_add_option(buf, buf_size, "multicast=1", multicast);
|
||||
if (ttl >= 0)
|
||||
url_add_option(buf, buf_size, "ttl=%d", ttl);
|
||||
}
|
||||
|
||||
/*
|
||||
* url syntax: rtp://host:port[?option=val...]
|
||||
* option: 'multicast=1' : enable multicast
|
||||
* 'ttl=n' : set the ttl value (for multicast only)
|
||||
* 'localport=n' : set the local port to n
|
||||
*
|
||||
*/
|
||||
static int rtp_open(URLContext *h, const char *uri, int flags)
|
||||
{
|
||||
RTPContext *s;
|
||||
int port, is_output, is_multicast, ttl, local_port;
|
||||
char hostname[256];
|
||||
char buf[1024];
|
||||
char path[1024];
|
||||
const char *p;
|
||||
|
||||
is_output = (flags & URL_WRONLY);
|
||||
|
||||
s = av_mallocz(sizeof(RTPContext));
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
h->priv_data = s;
|
||||
|
||||
url_split(NULL, 0, hostname, sizeof(hostname), &port,
|
||||
path, sizeof(path), uri);
|
||||
/* extract parameters */
|
||||
is_multicast = 0;
|
||||
ttl = -1;
|
||||
local_port = -1;
|
||||
p = strchr(uri, '?');
|
||||
if (p) {
|
||||
is_multicast = find_info_tag(buf, sizeof(buf), "multicast", p);
|
||||
if (find_info_tag(buf, sizeof(buf), "ttl", p)) {
|
||||
ttl = strtol(buf, NULL, 10);
|
||||
}
|
||||
if (find_info_tag(buf, sizeof(buf), "localport", p)) {
|
||||
local_port = strtol(buf, NULL, 10);
|
||||
}
|
||||
}
|
||||
|
||||
build_udp_url(buf, sizeof(buf),
|
||||
hostname, port, local_port, is_multicast, ttl);
|
||||
if (url_open(&s->rtp_hd, buf, flags) < 0)
|
||||
goto fail;
|
||||
local_port = udp_get_local_port(s->rtp_hd);
|
||||
/* XXX: need to open another connexion if the port is not even */
|
||||
|
||||
/* well, should suppress localport in path */
|
||||
|
||||
build_udp_url(buf, sizeof(buf),
|
||||
hostname, port + 1, local_port + 1, is_multicast, ttl);
|
||||
if (url_open(&s->rtcp_hd, buf, flags) < 0)
|
||||
goto fail;
|
||||
|
||||
/* just to ease handle access. XXX: need to suppress direct handle
|
||||
access */
|
||||
s->rtp_fd = udp_get_file_handle(s->rtp_hd);
|
||||
s->rtcp_fd = udp_get_file_handle(s->rtcp_hd);
|
||||
|
||||
h->max_packet_size = url_get_max_packet_size(s->rtp_hd);
|
||||
h->is_streamed = 1;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (s->rtp_hd)
|
||||
url_close(s->rtp_hd);
|
||||
if (s->rtcp_hd)
|
||||
url_close(s->rtcp_hd);
|
||||
av_free(s);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int rtp_read(URLContext *h, UINT8 *buf, int size)
|
||||
{
|
||||
RTPContext *s = h->priv_data;
|
||||
struct sockaddr_in from;
|
||||
int from_len, len, fd_max, n;
|
||||
fd_set rfds;
|
||||
#if 0
|
||||
for(;;) {
|
||||
from_len = sizeof(from);
|
||||
len = recvfrom (s->rtp_fd, buf, size, 0,
|
||||
(struct sockaddr *)&from, &from_len);
|
||||
if (len < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
continue;
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#else
|
||||
for(;;) {
|
||||
/* build fdset to listen to RTP and RTCP packets */
|
||||
FD_ZERO(&rfds);
|
||||
fd_max = s->rtp_fd;
|
||||
FD_SET(s->rtp_fd, &rfds);
|
||||
if (s->rtcp_fd > fd_max)
|
||||
fd_max = s->rtcp_fd;
|
||||
FD_SET(s->rtcp_fd, &rfds);
|
||||
n = select(fd_max + 1, &rfds, NULL, NULL, NULL);
|
||||
if (n > 0) {
|
||||
/* first try RTCP */
|
||||
if (FD_ISSET(s->rtcp_fd, &rfds)) {
|
||||
from_len = sizeof(from);
|
||||
len = recvfrom (s->rtcp_fd, buf, size, 0,
|
||||
(struct sockaddr *)&from, &from_len);
|
||||
if (len < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
continue;
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* then RTP */
|
||||
if (FD_ISSET(s->rtp_fd, &rfds)) {
|
||||
from_len = sizeof(from);
|
||||
len = recvfrom (s->rtp_fd, buf, size, 0,
|
||||
(struct sockaddr *)&from, &from_len);
|
||||
if (len < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
continue;
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return len;
|
||||
}
|
||||
|
||||
static int rtp_write(URLContext *h, UINT8 *buf, int size)
|
||||
{
|
||||
RTPContext *s = h->priv_data;
|
||||
int ret;
|
||||
URLContext *hd;
|
||||
|
||||
if (buf[1] >= 200 && buf[1] <= 204) {
|
||||
/* RTCP payload type */
|
||||
hd = s->rtcp_hd;
|
||||
} else {
|
||||
/* RTP payload type */
|
||||
hd = s->rtp_hd;
|
||||
}
|
||||
|
||||
ret = url_write(hd, buf, size);
|
||||
#if 0
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 10 * 1000000;
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rtp_close(URLContext *h)
|
||||
{
|
||||
RTPContext *s = h->priv_data;
|
||||
|
||||
url_close(s->rtp_hd);
|
||||
url_close(s->rtcp_hd);
|
||||
av_free(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the local port used by the RTP connexion
|
||||
* @param s1 media file context
|
||||
* @return the local port number
|
||||
*/
|
||||
int rtp_get_local_port(URLContext *h)
|
||||
{
|
||||
RTPContext *s = h->priv_data;
|
||||
return udp_get_local_port(s->rtp_hd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rtp and rtcp file handles for select() usage to wait for several RTP
|
||||
* streams at the same time.
|
||||
* @param h media file context
|
||||
*/
|
||||
void rtp_get_file_handles(URLContext *h, int *prtp_fd, int *prtcp_fd)
|
||||
{
|
||||
RTPContext *s = h->priv_data;
|
||||
|
||||
*prtp_fd = s->rtp_fd;
|
||||
*prtcp_fd = s->rtcp_fd;
|
||||
}
|
||||
|
||||
URLProtocol rtp_protocol = {
|
||||
"rtp",
|
||||
rtp_open,
|
||||
rtp_read,
|
||||
rtp_write,
|
||||
NULL, /* seek */
|
||||
rtp_close,
|
||||
};
|
||||
1163
libavformat/rtsp.c
Normal file
1163
libavformat/rtsp.c
Normal file
File diff suppressed because it is too large
Load Diff
86
libavformat/rtsp.h
Normal file
86
libavformat/rtsp.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* RTSP definitions
|
||||
* Copyright (c) 2002 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef RTSP_H
|
||||
#define RTSP_H
|
||||
|
||||
/* RTSP handling */
|
||||
enum RTSPStatusCode {
|
||||
#define DEF(n, c, s) c = n,
|
||||
#include "rtspcodes.h"
|
||||
#undef DEF
|
||||
};
|
||||
|
||||
enum RTSPProtocol {
|
||||
RTSP_PROTOCOL_RTP_UDP = 0,
|
||||
RTSP_PROTOCOL_RTP_TCP = 1,
|
||||
RTSP_PROTOCOL_RTP_UDP_MULTICAST = 2,
|
||||
};
|
||||
|
||||
#define RTSP_DEFAULT_PORT 554
|
||||
#define RTSP_MAX_TRANSPORTS 8
|
||||
|
||||
typedef struct RTSPTransportField {
|
||||
int interleaved_min, interleaved_max; /* interleave ids, if TCP transport */
|
||||
int port_min, port_max; /* RTP ports */
|
||||
int client_port_min, client_port_max; /* RTP ports */
|
||||
int server_port_min, server_port_max; /* RTP ports */
|
||||
int ttl; /* ttl value */
|
||||
UINT32 destination; /* destination IP address */
|
||||
enum RTSPProtocol protocol;
|
||||
} RTSPTransportField;
|
||||
|
||||
typedef struct RTSPHeader {
|
||||
int content_length;
|
||||
enum RTSPStatusCode status_code; /* response code from server */
|
||||
int nb_transports;
|
||||
RTSPTransportField transports[RTSP_MAX_TRANSPORTS];
|
||||
int seq; /* sequence number */
|
||||
char session_id[512];
|
||||
} RTSPHeader;
|
||||
|
||||
/* the callback can be used to extend the connection setup/teardown step */
|
||||
enum RTSPCallbackAction {
|
||||
RTSP_ACTION_SERVER_SETUP,
|
||||
RTSP_ACTION_SERVER_TEARDOWN,
|
||||
RTSP_ACTION_CLIENT_SETUP,
|
||||
RTSP_ACTION_CLIENT_TEARDOWN,
|
||||
};
|
||||
|
||||
typedef struct RTSPActionServerSetup {
|
||||
UINT32 ipaddr;
|
||||
char transport_option[512];
|
||||
} RTSPActionServerSetup;
|
||||
|
||||
typedef int FFRTSPCallback(enum RTSPCallbackAction action,
|
||||
const char *session_id,
|
||||
char *buf, int buf_size,
|
||||
void *arg);
|
||||
|
||||
void rtsp_set_callback(FFRTSPCallback *rtsp_cb);
|
||||
|
||||
int rtsp_init(void);
|
||||
void rtsp_parse_line(RTSPHeader *reply, const char *buf);
|
||||
|
||||
extern int rtsp_abort_req;
|
||||
extern int rtsp_default_protocols;
|
||||
extern int rtsp_rtp_port_min;
|
||||
extern int rtsp_rtp_port_max;
|
||||
extern FFRTSPCallback *ff_rtsp_callback;
|
||||
|
||||
#endif /* RTSP_H */
|
||||
11
libavformat/rtspcodes.h
Normal file
11
libavformat/rtspcodes.h
Normal file
@@ -0,0 +1,11 @@
|
||||
DEF(200, RTSP_STATUS_OK, "OK")
|
||||
DEF(405, RTSP_STATUS_METHOD, "Method Not Allowed")
|
||||
DEF(453, RTSP_STATUS_BANDWIDTH, "Not Enough Bandwidth")
|
||||
DEF(454, RTSP_STATUS_SESSION, "Session Not Found")
|
||||
DEF(455, RTSP_STATUS_STATE, "Method Not Valid in This State")
|
||||
DEF(459, RTSP_STATUS_AGGREGATE, "Aggregate operation not allowed")
|
||||
DEF(460, RTSP_STATUS_ONLY_AGGREGATE, "Only aggregate operation allowed")
|
||||
DEF(461, RTSP_STATUS_TRANSPORT, "Unsupported transport")
|
||||
DEF(500, RTSP_STATUS_INTERNAL, "Internal Server Error")
|
||||
DEF(503, RTSP_STATUS_SERVICE, "Service Unavailable")
|
||||
DEF(505, RTSP_STATUS_VERSION, "RTSP Version not supported")
|
||||
1004
libavformat/strptime.c
Normal file
1004
libavformat/strptime.c
Normal file
File diff suppressed because it is too large
Load Diff
32
libavformat/strptime.h
Normal file
32
libavformat/strptime.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/* strptime.h
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Ethereal - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@ethereal.com>
|
||||
* Copyright 1998 Gerald Combs
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __STRPTIME_H__
|
||||
#define __STRPTIME_H__
|
||||
|
||||
/*
|
||||
* Version of "strptime()", for the benefit of OSes that don't have it.
|
||||
*/
|
||||
extern char *strptime(const char *, const char *, struct tm *);
|
||||
|
||||
#endif
|
||||
571
libavformat/swf.c
Normal file
571
libavformat/swf.c
Normal file
@@ -0,0 +1,571 @@
|
||||
/*
|
||||
* Flash Compatible Streaming Format
|
||||
* Copyright (c) 2000 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
|
||||
/* should have a generic way to indicate probable size */
|
||||
#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_LONG 0x100
|
||||
|
||||
/* flags for shape definition */
|
||||
#define FLAG_MOVETO 0x01
|
||||
#define FLAG_SETFILL0 0x02
|
||||
#define FLAG_SETFILL1 0x04
|
||||
|
||||
/* character id used */
|
||||
#define BITMAP_ID 0
|
||||
#define SHAPE_ID 1
|
||||
|
||||
typedef struct {
|
||||
offset_t duration_pos;
|
||||
offset_t tag_pos;
|
||||
int tag;
|
||||
} SWFContext;
|
||||
|
||||
static void put_swf_tag(AVFormatContext *s, int tag)
|
||||
{
|
||||
SWFContext *swf = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
|
||||
swf->tag_pos = url_ftell(pb);
|
||||
swf->tag = tag;
|
||||
/* reserve some room for the tag */
|
||||
if (tag & TAG_LONG) {
|
||||
put_le16(pb, 0);
|
||||
put_le32(pb, 0);
|
||||
} else {
|
||||
put_le16(pb, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void put_swf_end_tag(AVFormatContext *s)
|
||||
{
|
||||
SWFContext *swf = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
offset_t pos;
|
||||
int tag_len, tag;
|
||||
|
||||
pos = url_ftell(pb);
|
||||
tag_len = pos - swf->tag_pos - 2;
|
||||
tag = swf->tag;
|
||||
url_fseek(pb, swf->tag_pos, SEEK_SET);
|
||||
if (tag & TAG_LONG) {
|
||||
tag &= ~TAG_LONG;
|
||||
put_le16(pb, (tag << 6) | 0x3f);
|
||||
put_le32(pb, tag_len - 4);
|
||||
} else {
|
||||
assert(tag_len < 0x3f);
|
||||
put_le16(pb, (tag << 6) | tag_len);
|
||||
}
|
||||
url_fseek(pb, pos, SEEK_SET);
|
||||
}
|
||||
|
||||
static inline void max_nbits(int *nbits_ptr, int val)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (val == 0)
|
||||
return;
|
||||
val = abs(val);
|
||||
n = 1;
|
||||
while (val != 0) {
|
||||
n++;
|
||||
val >>= 1;
|
||||
}
|
||||
if (n > *nbits_ptr)
|
||||
*nbits_ptr = n;
|
||||
}
|
||||
|
||||
static void put_swf_rect(ByteIOContext *pb,
|
||||
int xmin, int xmax, int ymin, int ymax)
|
||||
{
|
||||
PutBitContext p;
|
||||
UINT8 buf[256];
|
||||
int nbits, mask;
|
||||
|
||||
init_put_bits(&p, buf, sizeof(buf), NULL, NULL);
|
||||
|
||||
nbits = 0;
|
||||
max_nbits(&nbits, xmin);
|
||||
max_nbits(&nbits, xmax);
|
||||
max_nbits(&nbits, ymin);
|
||||
max_nbits(&nbits, ymax);
|
||||
mask = (1 << nbits) - 1;
|
||||
|
||||
/* rectangle info */
|
||||
put_bits(&p, 5, nbits);
|
||||
put_bits(&p, nbits, xmin & mask);
|
||||
put_bits(&p, nbits, xmax & mask);
|
||||
put_bits(&p, nbits, ymin & mask);
|
||||
put_bits(&p, nbits, ymax & mask);
|
||||
|
||||
flush_put_bits(&p);
|
||||
put_buffer(pb, buf, pbBufPtr(&p) - p.buf);
|
||||
}
|
||||
|
||||
static void put_swf_line_edge(PutBitContext *pb, int dx, int dy)
|
||||
{
|
||||
int nbits, mask;
|
||||
|
||||
put_bits(pb, 1, 1); /* edge */
|
||||
put_bits(pb, 1, 1); /* line select */
|
||||
nbits = 2;
|
||||
max_nbits(&nbits, dx);
|
||||
max_nbits(&nbits, dy);
|
||||
|
||||
mask = (1 << nbits) - 1;
|
||||
put_bits(pb, 4, nbits - 2); /* 16 bits precision */
|
||||
if (dx == 0) {
|
||||
put_bits(pb, 1, 0);
|
||||
put_bits(pb, 1, 1);
|
||||
put_bits(pb, nbits, dy & mask);
|
||||
} else if (dy == 0) {
|
||||
put_bits(pb, 1, 0);
|
||||
put_bits(pb, 1, 0);
|
||||
put_bits(pb, nbits, dx & mask);
|
||||
} else {
|
||||
put_bits(pb, 1, 1);
|
||||
put_bits(pb, nbits, dx & mask);
|
||||
put_bits(pb, nbits, dy & mask);
|
||||
}
|
||||
}
|
||||
|
||||
#define FRAC_BITS 16
|
||||
|
||||
/* put matrix (not size optimized */
|
||||
static void put_swf_matrix(ByteIOContext *pb,
|
||||
int a, int b, int c, int d, int tx, int ty)
|
||||
{
|
||||
PutBitContext p;
|
||||
UINT8 buf[256];
|
||||
|
||||
init_put_bits(&p, buf, sizeof(buf), NULL, NULL);
|
||||
|
||||
put_bits(&p, 1, 1); /* a, d present */
|
||||
put_bits(&p, 5, 20); /* nb bits */
|
||||
put_bits(&p, 20, a);
|
||||
put_bits(&p, 20, d);
|
||||
|
||||
put_bits(&p, 1, 1); /* b, c present */
|
||||
put_bits(&p, 5, 20); /* nb bits */
|
||||
put_bits(&p, 20, c);
|
||||
put_bits(&p, 20, b);
|
||||
|
||||
put_bits(&p, 5, 20); /* nb bits */
|
||||
put_bits(&p, 20, tx);
|
||||
put_bits(&p, 20, ty);
|
||||
|
||||
flush_put_bits(&p);
|
||||
put_buffer(pb, buf, pbBufPtr(&p) - p.buf);
|
||||
}
|
||||
|
||||
/* XXX: handle audio only */
|
||||
static int swf_write_header(AVFormatContext *s)
|
||||
{
|
||||
SWFContext *swf;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
AVCodecContext *enc, *audio_enc, *video_enc;
|
||||
PutBitContext p;
|
||||
UINT8 buf1[256];
|
||||
int i, width, height, rate;
|
||||
|
||||
swf = av_malloc(sizeof(SWFContext));
|
||||
if (!swf)
|
||||
return -1;
|
||||
s->priv_data = swf;
|
||||
|
||||
video_enc = NULL;
|
||||
audio_enc = NULL;
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
enc = &s->streams[i]->codec;
|
||||
if (enc->codec_type == CODEC_TYPE_AUDIO)
|
||||
audio_enc = enc;
|
||||
else
|
||||
video_enc = enc;
|
||||
}
|
||||
|
||||
if (!video_enc) {
|
||||
/* currenty, cannot work correctly if audio only */
|
||||
width = 320;
|
||||
height = 200;
|
||||
rate = 10 * FRAME_RATE_BASE;
|
||||
} else {
|
||||
width = video_enc->width;
|
||||
height = video_enc->height;
|
||||
rate = video_enc->frame_rate;
|
||||
}
|
||||
|
||||
put_tag(pb, "FWS");
|
||||
put_byte(pb, 4); /* version (should use 4 for mpeg audio support) */
|
||||
put_le32(pb, DUMMY_FILE_SIZE); /* dummy size
|
||||
(will be patched if not streamed) */
|
||||
|
||||
put_swf_rect(pb, 0, width, 0, height);
|
||||
put_le16(pb, (rate * 256) / FRAME_RATE_BASE); /* frame rate */
|
||||
swf->duration_pos = url_ftell(pb);
|
||||
put_le16(pb, (UINT16)(DUMMY_DURATION * (INT64)rate / FRAME_RATE_BASE)); /* frame count */
|
||||
|
||||
/* define a shape with the jpeg inside */
|
||||
|
||||
put_swf_tag(s, TAG_DEFINESHAPE);
|
||||
|
||||
put_le16(pb, SHAPE_ID); /* ID of shape */
|
||||
/* bounding rectangle */
|
||||
put_swf_rect(pb, 0, width, 0, height);
|
||||
/* style info */
|
||||
put_byte(pb, 1); /* one fill style */
|
||||
put_byte(pb, 0x41); /* clipped bitmap fill */
|
||||
put_le16(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_byte(pb, 0); /* no line style */
|
||||
|
||||
/* shape drawing */
|
||||
init_put_bits(&p, buf1, sizeof(buf1), NULL, NULL);
|
||||
put_bits(&p, 4, 1); /* one fill bit */
|
||||
put_bits(&p, 4, 0); /* zero line bit */
|
||||
|
||||
put_bits(&p, 1, 0); /* not an edge */
|
||||
put_bits(&p, 5, FLAG_MOVETO | FLAG_SETFILL0);
|
||||
put_bits(&p, 5, 1); /* nbits */
|
||||
put_bits(&p, 1, 0); /* X */
|
||||
put_bits(&p, 1, 0); /* Y */
|
||||
put_bits(&p, 1, 1); /* set fill style 1 */
|
||||
|
||||
/* draw the rectangle ! */
|
||||
put_swf_line_edge(&p, width, 0);
|
||||
put_swf_line_edge(&p, 0, height);
|
||||
put_swf_line_edge(&p, -width, 0);
|
||||
put_swf_line_edge(&p, 0, -height);
|
||||
|
||||
/* end of shape */
|
||||
put_bits(&p, 1, 0); /* not an edge */
|
||||
put_bits(&p, 5, 0);
|
||||
|
||||
flush_put_bits(&p);
|
||||
put_buffer(pb, buf1, pbBufPtr(&p) - p.buf);
|
||||
|
||||
put_swf_end_tag(s);
|
||||
|
||||
|
||||
if (audio_enc) {
|
||||
int v;
|
||||
|
||||
/* start sound */
|
||||
|
||||
v = 0;
|
||||
switch(audio_enc->sample_rate) {
|
||||
case 11025:
|
||||
v |= 1 << 2;
|
||||
break;
|
||||
case 22050:
|
||||
v |= 2 << 2;
|
||||
break;
|
||||
case 44100:
|
||||
v |= 3 << 2;
|
||||
break;
|
||||
default:
|
||||
/* not supported */
|
||||
av_free(swf);
|
||||
return -1;
|
||||
}
|
||||
if (audio_enc->channels == 2)
|
||||
v |= 1;
|
||||
v |= 0x20; /* mp3 compressed */
|
||||
v |= 0x02; /* 16 bits */
|
||||
|
||||
put_swf_tag(s, TAG_STREAMHEAD);
|
||||
put_byte(&s->pb, 0);
|
||||
put_byte(&s->pb, v);
|
||||
put_le16(&s->pb, (audio_enc->sample_rate * FRAME_RATE_BASE) / rate); /* avg samples per frame */
|
||||
|
||||
|
||||
put_swf_end_tag(s);
|
||||
}
|
||||
|
||||
put_flush_packet(&s->pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int swf_write_video(AVFormatContext *s,
|
||||
AVCodecContext *enc, UINT8 *buf, int size)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
static int tag_id = 0;
|
||||
|
||||
if (enc->frame_number > 1) {
|
||||
/* remove the shape */
|
||||
put_swf_tag(s, TAG_REMOVEOBJECT);
|
||||
put_le16(pb, SHAPE_ID); /* shape ID */
|
||||
put_le16(pb, 1); /* depth */
|
||||
put_swf_end_tag(s);
|
||||
|
||||
/* free the bitmap */
|
||||
put_swf_tag(s, TAG_FREECHARACTER);
|
||||
put_le16(pb, BITMAP_ID);
|
||||
put_swf_end_tag(s);
|
||||
}
|
||||
|
||||
put_swf_tag(s, TAG_JPEG2 | TAG_LONG);
|
||||
|
||||
put_le16(pb, tag_id); /* ID of the image */
|
||||
|
||||
/* a dummy jpeg header seems to be required */
|
||||
put_byte(pb, 0xff);
|
||||
put_byte(pb, 0xd8);
|
||||
put_byte(pb, 0xff);
|
||||
put_byte(pb, 0xd9);
|
||||
/* write the jpeg image */
|
||||
put_buffer(pb, buf, size);
|
||||
|
||||
put_swf_end_tag(s);
|
||||
|
||||
/* draw the shape */
|
||||
|
||||
put_swf_tag(s, TAG_PLACEOBJECT);
|
||||
put_le16(pb, SHAPE_ID); /* shape ID */
|
||||
put_le16(pb, 1); /* depth */
|
||||
put_swf_matrix(pb, 1 << FRAC_BITS, 0, 0, 1 << FRAC_BITS, 0, 0);
|
||||
put_swf_end_tag(s);
|
||||
|
||||
/* output the frame */
|
||||
put_swf_tag(s, TAG_SHOWFRAME);
|
||||
put_swf_end_tag(s);
|
||||
|
||||
put_flush_packet(&s->pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int swf_write_audio(AVFormatContext *s, UINT8 *buf, int size)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
|
||||
put_swf_tag(s, TAG_STREAMBLOCK | TAG_LONG);
|
||||
|
||||
put_buffer(pb, buf, size);
|
||||
|
||||
put_swf_end_tag(s);
|
||||
put_flush_packet(&s->pb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int swf_write_packet(AVFormatContext *s, int stream_index,
|
||||
UINT8 *buf, int size, int force_pts)
|
||||
{
|
||||
AVCodecContext *codec = &s->streams[stream_index]->codec;
|
||||
if (codec->codec_type == CODEC_TYPE_AUDIO)
|
||||
return swf_write_audio(s, buf, size);
|
||||
else
|
||||
return swf_write_video(s, codec, buf, size);
|
||||
}
|
||||
|
||||
static int swf_write_trailer(AVFormatContext *s)
|
||||
{
|
||||
SWFContext *swf = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
AVCodecContext *enc, *video_enc;
|
||||
int file_size, i;
|
||||
|
||||
video_enc = NULL;
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
enc = &s->streams[i]->codec;
|
||||
if (enc->codec_type == CODEC_TYPE_VIDEO)
|
||||
video_enc = enc;
|
||||
}
|
||||
|
||||
put_swf_tag(s, TAG_END);
|
||||
put_swf_end_tag(s);
|
||||
|
||||
put_flush_packet(&s->pb);
|
||||
|
||||
/* patch file size and number of frames if not streamed */
|
||||
if (!url_is_streamed(&s->pb) && video_enc) {
|
||||
file_size = url_ftell(pb);
|
||||
url_fseek(pb, 4, SEEK_SET);
|
||||
put_le32(pb, file_size);
|
||||
url_fseek(pb, swf->duration_pos, SEEK_SET);
|
||||
put_le16(pb, video_enc->frame_number);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************/
|
||||
/* just to extract MP3 from swf */
|
||||
|
||||
static int get_swf_tag(ByteIOContext *pb, int *len_ptr)
|
||||
{
|
||||
int tag, len;
|
||||
|
||||
if (url_feof(pb))
|
||||
return -1;
|
||||
|
||||
tag = get_le16(pb);
|
||||
len = tag & 0x3f;
|
||||
tag = tag >> 6;
|
||||
if (len == 0x3f) {
|
||||
len = get_le32(pb);
|
||||
}
|
||||
*len_ptr = len;
|
||||
return tag;
|
||||
}
|
||||
|
||||
|
||||
static int swf_probe(AVProbeData *p)
|
||||
{
|
||||
/* check file header */
|
||||
if (p->buf_size <= 16)
|
||||
return 0;
|
||||
if (p->buf[0] == 'F' && p->buf[1] == 'W' &&
|
||||
p->buf[2] == 'S')
|
||||
return AVPROBE_SCORE_MAX;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int swf_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
int nbits, len, frame_rate, tag, v;
|
||||
AVStream *st;
|
||||
|
||||
if ((get_be32(pb) & 0xffffff00) != MKBETAG('F', 'W', 'S', 0))
|
||||
return -EIO;
|
||||
get_le32(pb);
|
||||
/* skip rectangle size */
|
||||
nbits = get_byte(pb) >> 3;
|
||||
len = (4 * nbits - 3 + 7) / 8;
|
||||
url_fskip(pb, len);
|
||||
frame_rate = get_le16(pb);
|
||||
get_le16(pb); /* frame count */
|
||||
|
||||
for(;;) {
|
||||
tag = get_swf_tag(pb, &len);
|
||||
if (tag < 0) {
|
||||
fprintf(stderr, "No streaming found in SWF\n");
|
||||
return -EIO;
|
||||
}
|
||||
if (tag == TAG_STREAMHEAD) {
|
||||
/* streaming found */
|
||||
get_byte(pb);
|
||||
v = get_byte(pb);
|
||||
get_le16(pb);
|
||||
/* if mp3 streaming found, OK */
|
||||
if ((v & 0x20) != 0) {
|
||||
st = av_mallocz(sizeof(AVStream));
|
||||
if (!st)
|
||||
return -ENOMEM;
|
||||
if (v & 0x01)
|
||||
st->codec.channels = 2;
|
||||
else
|
||||
st->codec.channels = 1;
|
||||
s->nb_streams = 1;
|
||||
s->streams[0] = st;
|
||||
|
||||
switch((v>> 2) & 0x03) {
|
||||
case 1:
|
||||
st->codec.sample_rate = 11025;
|
||||
break;
|
||||
case 2:
|
||||
st->codec.sample_rate = 22050;
|
||||
break;
|
||||
case 3:
|
||||
st->codec.sample_rate = 44100;
|
||||
break;
|
||||
default:
|
||||
av_free(st);
|
||||
return -EIO;
|
||||
}
|
||||
st->codec.codec_type = CODEC_TYPE_AUDIO;
|
||||
st->codec.codec_id = CODEC_ID_MP2;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
url_fskip(pb, len);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int swf_read_packet(AVFormatContext *s, AVPacket *pkt)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
int tag, len;
|
||||
|
||||
for(;;) {
|
||||
tag = get_swf_tag(pb, &len);
|
||||
if (tag < 0)
|
||||
return -EIO;
|
||||
if (tag == TAG_STREAMBLOCK) {
|
||||
av_new_packet(pkt, len);
|
||||
get_buffer(pb, pkt->data, pkt->size);
|
||||
break;
|
||||
} else {
|
||||
url_fskip(pb, len);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int swf_read_close(AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVInputFormat swf_iformat = {
|
||||
"swf",
|
||||
"Flash format",
|
||||
0,
|
||||
swf_probe,
|
||||
swf_read_header,
|
||||
swf_read_packet,
|
||||
swf_read_close,
|
||||
};
|
||||
|
||||
static AVOutputFormat swf_oformat = {
|
||||
"swf",
|
||||
"Flash format",
|
||||
"application/x-shockwave-flash",
|
||||
"swf",
|
||||
sizeof(SWFContext),
|
||||
CODEC_ID_MP2,
|
||||
CODEC_ID_MJPEG,
|
||||
swf_write_header,
|
||||
swf_write_packet,
|
||||
swf_write_trailer,
|
||||
};
|
||||
|
||||
int swf_init(void)
|
||||
{
|
||||
av_register_input_format(&swf_iformat);
|
||||
av_register_output_format(&swf_oformat);
|
||||
return 0;
|
||||
}
|
||||
176
libavformat/tcp.c
Normal file
176
libavformat/tcp.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* TCP protocol
|
||||
* Copyright (c) 2002 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#ifndef __BEOS__
|
||||
# include <arpa/inet.h>
|
||||
#else
|
||||
# include "barpainet.h"
|
||||
#endif
|
||||
#include <netdb.h>
|
||||
|
||||
typedef struct TCPContext {
|
||||
int fd;
|
||||
} TCPContext;
|
||||
|
||||
/* resolve host with also IP address parsing */
|
||||
int resolve_host(struct in_addr *sin_addr, const char *hostname)
|
||||
{
|
||||
struct hostent *hp;
|
||||
|
||||
if ((inet_aton(hostname, sin_addr)) == 0) {
|
||||
hp = gethostbyname(hostname);
|
||||
if (!hp)
|
||||
return -1;
|
||||
memcpy (sin_addr, hp->h_addr, sizeof(struct in_addr));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return non zero if error */
|
||||
static int tcp_open(URLContext *h, const char *uri, int flags)
|
||||
{
|
||||
struct sockaddr_in dest_addr;
|
||||
char hostname[1024], *q;
|
||||
int port, fd = -1;
|
||||
TCPContext *s;
|
||||
const char *p;
|
||||
|
||||
s = av_malloc(sizeof(TCPContext));
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
h->priv_data = s;
|
||||
p = uri;
|
||||
if (!strstart(p, "tcp://", &p))
|
||||
goto fail;
|
||||
q = hostname;
|
||||
while (*p != ':' && *p != '/' && *p != '\0') {
|
||||
if ((q - hostname) < sizeof(hostname) - 1)
|
||||
*q++ = *p;
|
||||
p++;
|
||||
}
|
||||
*q = '\0';
|
||||
if (*p != ':')
|
||||
goto fail;
|
||||
p++;
|
||||
port = strtoul(p, (char **)&p, 10);
|
||||
if (port <= 0 || port >= 65536)
|
||||
goto fail;
|
||||
|
||||
dest_addr.sin_family = AF_INET;
|
||||
dest_addr.sin_port = htons(port);
|
||||
if (resolve_host(&dest_addr.sin_addr, hostname) < 0)
|
||||
goto fail;
|
||||
|
||||
fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (fd < 0)
|
||||
goto fail;
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&dest_addr,
|
||||
sizeof(dest_addr)) < 0)
|
||||
goto fail;
|
||||
|
||||
s->fd = fd;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
av_free(s);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int tcp_read(URLContext *h, UINT8 *buf, int size)
|
||||
{
|
||||
TCPContext *s = h->priv_data;
|
||||
int size1, len;
|
||||
|
||||
size1 = size;
|
||||
while (size > 0) {
|
||||
#ifdef CONFIG_BEOS_NETSERVER
|
||||
len = recv (s->fd, buf, size, 0);
|
||||
#else
|
||||
len = read (s->fd, buf, size);
|
||||
#endif
|
||||
if (len < 0) {
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
#ifdef __BEOS__
|
||||
return errno;
|
||||
#else
|
||||
return -errno;
|
||||
#endif
|
||||
else
|
||||
continue;
|
||||
} else if (len == 0) {
|
||||
break;
|
||||
}
|
||||
size -= len;
|
||||
buf += len;
|
||||
}
|
||||
return size1 - size;
|
||||
}
|
||||
|
||||
static int tcp_write(URLContext *h, UINT8 *buf, int size)
|
||||
{
|
||||
TCPContext *s = h->priv_data;
|
||||
int ret, size1;
|
||||
|
||||
size1 = size;
|
||||
while (size > 0) {
|
||||
#ifdef CONFIG_BEOS_NETSERVER
|
||||
ret = send (s->fd, buf, size, 0);
|
||||
#else
|
||||
ret = write (s->fd, buf, size);
|
||||
#endif
|
||||
if (ret < 0 && errno != EINTR && errno != EAGAIN)
|
||||
#ifdef __BEOS__
|
||||
return errno;
|
||||
#else
|
||||
return -errno;
|
||||
#endif
|
||||
size -= ret;
|
||||
buf += ret;
|
||||
}
|
||||
return size1 - size;
|
||||
}
|
||||
|
||||
static int tcp_close(URLContext *h)
|
||||
{
|
||||
TCPContext *s = h->priv_data;
|
||||
#ifdef CONFIG_BEOS_NETSERVER
|
||||
closesocket(s->fd);
|
||||
#else
|
||||
close(s->fd);
|
||||
#endif
|
||||
av_free(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
URLProtocol tcp_protocol = {
|
||||
"tcp",
|
||||
tcp_open,
|
||||
tcp_read,
|
||||
tcp_write,
|
||||
NULL, /* seek */
|
||||
tcp_close,
|
||||
};
|
||||
272
libavformat/udp.c
Normal file
272
libavformat/udp.c
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* UDP prototype streaming system
|
||||
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#ifndef __BEOS__
|
||||
# include <arpa/inet.h>
|
||||
#else
|
||||
# include "barpainet.h"
|
||||
#endif
|
||||
#include <netdb.h>
|
||||
|
||||
typedef struct {
|
||||
int udp_fd;
|
||||
int ttl;
|
||||
int is_multicast;
|
||||
int local_port;
|
||||
struct ip_mreq mreq;
|
||||
struct sockaddr_in dest_addr;
|
||||
} UDPContext;
|
||||
|
||||
#define UDP_TX_BUF_SIZE 32768
|
||||
|
||||
/**
|
||||
* If no filename is given to av_open_input_file because you want to
|
||||
* get the local port first, then you must call this function to set
|
||||
* the remote server address.
|
||||
*
|
||||
* url syntax: udp://host:port[?option=val...]
|
||||
* option: 'multicast=1' : enable multicast
|
||||
* 'ttl=n' : set the ttl value (for multicast only)
|
||||
* 'localport=n' : set the local port
|
||||
*
|
||||
* @param s1 media file context
|
||||
* @param uri of the remote server
|
||||
* @return zero if no error.
|
||||
*/
|
||||
int udp_set_remote_url(URLContext *h, const char *uri)
|
||||
{
|
||||
UDPContext *s = h->priv_data;
|
||||
char hostname[256];
|
||||
int port;
|
||||
|
||||
url_split(NULL, 0, hostname, sizeof(hostname), &port, NULL, 0, uri);
|
||||
|
||||
/* set the destination address */
|
||||
if (resolve_host(&s->dest_addr.sin_addr, hostname) < 0)
|
||||
return -EIO;
|
||||
s->dest_addr.sin_family = AF_INET;
|
||||
s->dest_addr.sin_port = htons(port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the local port used by the UDP connexion
|
||||
* @param s1 media file context
|
||||
* @return the local port number
|
||||
*/
|
||||
int udp_get_local_port(URLContext *h)
|
||||
{
|
||||
UDPContext *s = h->priv_data;
|
||||
return s->local_port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the udp file handle for select() usage to wait for several RTP
|
||||
* streams at the same time.
|
||||
* @param h media file context
|
||||
*/
|
||||
int udp_get_file_handle(URLContext *h)
|
||||
{
|
||||
UDPContext *s = h->priv_data;
|
||||
return s->udp_fd;
|
||||
}
|
||||
|
||||
/* put it in UDP context */
|
||||
/* return non zero if error */
|
||||
static int udp_open(URLContext *h, const char *uri, int flags)
|
||||
{
|
||||
struct sockaddr_in my_addr, my_addr1;
|
||||
char hostname[1024];
|
||||
int port, udp_fd = -1, tmp;
|
||||
UDPContext *s = NULL;
|
||||
int is_output, len;
|
||||
const char *p;
|
||||
char buf[256];
|
||||
|
||||
h->is_streamed = 1;
|
||||
|
||||
is_output = (flags & URL_WRONLY);
|
||||
|
||||
s = av_malloc(sizeof(UDPContext));
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
h->priv_data = s;
|
||||
s->ttl = 16;
|
||||
s->is_multicast = 0;
|
||||
p = strchr(uri, '?');
|
||||
if (p) {
|
||||
s->is_multicast = find_info_tag(buf, sizeof(buf), "multicast", p);
|
||||
if (find_info_tag(buf, sizeof(buf), "ttl", p)) {
|
||||
s->ttl = strtol(buf, NULL, 10);
|
||||
}
|
||||
if (find_info_tag(buf, sizeof(buf), "localport", p)) {
|
||||
s->local_port = strtol(buf, NULL, 10);
|
||||
}
|
||||
}
|
||||
|
||||
/* fill the dest addr */
|
||||
url_split(NULL, 0, hostname, sizeof(hostname), &port, NULL, 0, uri);
|
||||
|
||||
/* XXX: fix url_split */
|
||||
if (hostname[0] == '\0' || hostname[0] == '?') {
|
||||
/* only accepts null hostname if input */
|
||||
if (s->is_multicast || (flags & URL_WRONLY))
|
||||
goto fail;
|
||||
} else {
|
||||
udp_set_remote_url(h, uri);
|
||||
}
|
||||
|
||||
udp_fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (udp_fd < 0)
|
||||
goto fail;
|
||||
|
||||
my_addr.sin_family = AF_INET;
|
||||
my_addr.sin_addr.s_addr = htonl (INADDR_ANY);
|
||||
if (s->is_multicast && !(h->flags & URL_WRONLY)) {
|
||||
/* special case: the bind must be done on the multicast address port */
|
||||
my_addr.sin_port = s->dest_addr.sin_port;
|
||||
} else {
|
||||
my_addr.sin_port = htons(s->local_port);
|
||||
}
|
||||
|
||||
/* the bind is needed to give a port to the socket now */
|
||||
if (bind(udp_fd,(struct sockaddr *)&my_addr, sizeof(my_addr)) < 0)
|
||||
goto fail;
|
||||
|
||||
len = sizeof(my_addr1);
|
||||
getsockname(udp_fd, (struct sockaddr *)&my_addr1, &len);
|
||||
s->local_port = ntohs(my_addr1.sin_port);
|
||||
|
||||
#ifndef CONFIG_BEOS_NETSERVER
|
||||
if (s->is_multicast) {
|
||||
if (h->flags & URL_WRONLY) {
|
||||
/* output */
|
||||
if (setsockopt(udp_fd, IPPROTO_IP, IP_MULTICAST_TTL,
|
||||
&s->ttl, sizeof(s->ttl)) < 0) {
|
||||
perror("IP_MULTICAST_TTL");
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
/* input */
|
||||
memset(&s->mreq, 0, sizeof(s->mreq));
|
||||
s->mreq.imr_multiaddr = s->dest_addr.sin_addr;
|
||||
s->mreq.imr_interface.s_addr = htonl (INADDR_ANY);
|
||||
if (setsockopt(udp_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
|
||||
&s->mreq, sizeof(s->mreq)) < 0) {
|
||||
perror("rtp: IP_ADD_MEMBERSHIP");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (is_output) {
|
||||
/* limit the tx buf size to limit latency */
|
||||
tmp = UDP_TX_BUF_SIZE;
|
||||
if (setsockopt(udp_fd, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp)) < 0) {
|
||||
perror("setsockopt sndbuf");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
s->udp_fd = udp_fd;
|
||||
h->max_packet_size = 1472; /* XXX: probe it ? */
|
||||
return 0;
|
||||
fail:
|
||||
if (udp_fd >= 0)
|
||||
#ifdef CONFIG_BEOS_NETSERVER
|
||||
closesocket(udp_fd);
|
||||
#else
|
||||
close(udp_fd);
|
||||
#endif
|
||||
av_free(s);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int udp_read(URLContext *h, UINT8 *buf, int size)
|
||||
{
|
||||
UDPContext *s = h->priv_data;
|
||||
struct sockaddr_in from;
|
||||
int from_len, len;
|
||||
|
||||
for(;;) {
|
||||
from_len = sizeof(from);
|
||||
len = recvfrom (s->udp_fd, buf, size, 0,
|
||||
(struct sockaddr *)&from, &from_len);
|
||||
if (len < 0) {
|
||||
if (errno != EAGAIN && errno != EINTR)
|
||||
return -EIO;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int udp_write(URLContext *h, UINT8 *buf, int size)
|
||||
{
|
||||
UDPContext *s = h->priv_data;
|
||||
int ret;
|
||||
|
||||
for(;;) {
|
||||
ret = sendto (s->udp_fd, buf, size, 0,
|
||||
(struct sockaddr *) &s->dest_addr,
|
||||
sizeof (s->dest_addr));
|
||||
if (ret < 0) {
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
return -EIO;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static int udp_close(URLContext *h)
|
||||
{
|
||||
UDPContext *s = h->priv_data;
|
||||
|
||||
#ifndef CONFIG_BEOS_NETSERVER
|
||||
if (s->is_multicast && !(h->flags & URL_WRONLY)) {
|
||||
if (setsockopt(s->udp_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
|
||||
&s->mreq, sizeof(s->mreq)) < 0) {
|
||||
perror("IP_DROP_MEMBERSHIP");
|
||||
}
|
||||
}
|
||||
close(s->udp_fd);
|
||||
#else
|
||||
closesocket(s->udp_fd);
|
||||
#endif
|
||||
av_free(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
URLProtocol udp_protocol = {
|
||||
"udp",
|
||||
udp_open,
|
||||
udp_read,
|
||||
udp_write,
|
||||
NULL, /* seek */
|
||||
udp_close,
|
||||
};
|
||||
1280
libavformat/utils.c
Normal file
1280
libavformat/utils.c
Normal file
File diff suppressed because it is too large
Load Diff
326
libavformat/wav.c
Normal file
326
libavformat/wav.c
Normal file
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* WAV encoder and decoder
|
||||
* Copyright (c) 2001, 2002 Fabrice Bellard.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "avformat.h"
|
||||
#include "avi.h"
|
||||
|
||||
const CodecTag codec_wav_tags[] = {
|
||||
{ CODEC_ID_MP2, 0x50 },
|
||||
{ CODEC_ID_MP3LAME, 0x55 },
|
||||
{ CODEC_ID_AC3, 0x2000 },
|
||||
{ CODEC_ID_PCM_S16LE, 0x01 },
|
||||
{ CODEC_ID_PCM_U8, 0x01 }, /* must come after s16le in this list */
|
||||
{ CODEC_ID_PCM_ALAW, 0x06 },
|
||||
{ CODEC_ID_PCM_MULAW, 0x07 },
|
||||
{ CODEC_ID_ADPCM_MS, 0x02 },
|
||||
{ CODEC_ID_ADPCM_IMA_WAV, 0x11 },
|
||||
{ CODEC_ID_WMAV1, 0x160 },
|
||||
{ CODEC_ID_WMAV2, 0x161 },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
/* WAVEFORMATEX header */
|
||||
/* returns the size or -1 on error */
|
||||
int put_wav_header(ByteIOContext *pb, AVCodecContext *enc)
|
||||
{
|
||||
int tag, bps, blkalign, bytespersec;
|
||||
int hdrsize = 18;
|
||||
|
||||
tag = codec_get_tag(codec_wav_tags, enc->codec_id);
|
||||
if (tag == 0)
|
||||
return -1;
|
||||
put_le16(pb, tag);
|
||||
put_le16(pb, enc->channels);
|
||||
put_le32(pb, enc->sample_rate);
|
||||
if (enc->codec_id == CODEC_ID_PCM_U8 ||
|
||||
enc->codec_id == CODEC_ID_PCM_ALAW ||
|
||||
enc->codec_id == CODEC_ID_PCM_MULAW) {
|
||||
bps = 8;
|
||||
} else if (enc->codec_id == CODEC_ID_MP2 || enc->codec_id == CODEC_ID_MP3LAME) {
|
||||
bps = 0;
|
||||
} else if (enc->codec_id == CODEC_ID_ADPCM_IMA_WAV || enc->codec_id == CODEC_ID_ADPCM_MS) {
|
||||
bps = 4;
|
||||
} else {
|
||||
bps = 16;
|
||||
}
|
||||
|
||||
if (enc->codec_id == CODEC_ID_MP2 || enc->codec_id == CODEC_ID_MP3LAME) {
|
||||
blkalign = 1;
|
||||
//blkalign = 144 * enc->bit_rate/enc->sample_rate;
|
||||
} else if (enc->block_align != 0) { /* specified by the codec */
|
||||
blkalign = enc->block_align;
|
||||
} else
|
||||
blkalign = enc->channels*bps >> 3;
|
||||
if (enc->codec_id == CODEC_ID_PCM_U8 ||
|
||||
enc->codec_id == CODEC_ID_PCM_S16LE) {
|
||||
bytespersec = enc->sample_rate * blkalign;
|
||||
} else {
|
||||
bytespersec = enc->bit_rate / 8;
|
||||
}
|
||||
put_le32(pb, bytespersec); /* bytes per second */
|
||||
put_le16(pb, blkalign); /* block align */
|
||||
put_le16(pb, bps); /* bits per sample */
|
||||
if (enc->codec_id == CODEC_ID_MP3LAME) {
|
||||
put_le16(pb, 12); /* wav_extra_size */
|
||||
hdrsize += 12;
|
||||
put_le16(pb, 1); /* wID */
|
||||
put_le32(pb, 2); /* fdwFlags */
|
||||
put_le16(pb, 1152); /* nBlockSize */
|
||||
put_le16(pb, 1); /* nFramesPerBlock */
|
||||
put_le16(pb, 1393); /* nCodecDelay */
|
||||
} else if (enc->codec_id == CODEC_ID_MP2) {
|
||||
put_le16(pb, 22); /* wav_extra_size */
|
||||
hdrsize += 22;
|
||||
put_le16(pb, 2); /* fwHeadLayer */
|
||||
put_le32(pb, enc->bit_rate); /* dwHeadBitrate */
|
||||
put_le16(pb, enc->channels == 2 ? 1 : 8); /* fwHeadMode */
|
||||
put_le16(pb, 0); /* fwHeadModeExt */
|
||||
put_le16(pb, 1); /* wHeadEmphasis */
|
||||
put_le16(pb, 16); /* fwHeadFlags */
|
||||
put_le32(pb, 0); /* dwPTSLow */
|
||||
put_le32(pb, 0); /* dwPTSHigh */
|
||||
} else if (enc->codec_id == CODEC_ID_ADPCM_IMA_WAV) {
|
||||
put_le16(pb, 2); /* wav_extra_size */
|
||||
put_le16(pb, ((enc->block_align - 4 * enc->channels) / (4 * enc->channels)) * 8 + 1); /* wSamplesPerBlock */
|
||||
} else
|
||||
put_le16(pb, 0); /* wav_extra_size */
|
||||
|
||||
return hdrsize;
|
||||
}
|
||||
|
||||
void get_wav_header(ByteIOContext *pb, AVCodecContext *codec,
|
||||
int has_extra_data)
|
||||
{
|
||||
int id;
|
||||
|
||||
id = get_le16(pb);
|
||||
codec->codec_type = CODEC_TYPE_AUDIO;
|
||||
codec->codec_tag = id;
|
||||
codec->fourcc = id;
|
||||
codec->channels = get_le16(pb);
|
||||
codec->sample_rate = get_le32(pb);
|
||||
codec->bit_rate = get_le32(pb) * 8;
|
||||
codec->block_align = get_le16(pb);
|
||||
codec->frame_bits = get_le16(pb); /* bits per sample */
|
||||
codec->codec_id = wav_codec_get_id(id, codec->frame_bits);
|
||||
if (has_extra_data) {
|
||||
codec->extradata_size = get_le16(pb);
|
||||
if (codec->extradata_size > 0) {
|
||||
codec->extradata = av_mallocz(codec->extradata_size);
|
||||
get_buffer(pb, codec->extradata, codec->extradata_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int wav_codec_get_id(unsigned int tag, int bps)
|
||||
{
|
||||
int id;
|
||||
id = codec_get_id(codec_wav_tags, tag);
|
||||
if (id <= 0)
|
||||
return id;
|
||||
/* handle specific u8 codec */
|
||||
if (id == CODEC_ID_PCM_S16LE && bps == 8)
|
||||
id = CODEC_ID_PCM_U8;
|
||||
return id;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
offset_t data;
|
||||
} WAVContext;
|
||||
|
||||
static int wav_write_header(AVFormatContext *s)
|
||||
{
|
||||
WAVContext *wav = s->priv_data;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
offset_t fmt;
|
||||
|
||||
put_tag(pb, "RIFF");
|
||||
put_le32(pb, 0); /* file length */
|
||||
put_tag(pb, "WAVE");
|
||||
|
||||
/* format header */
|
||||
fmt = start_tag(pb, "fmt ");
|
||||
if (put_wav_header(pb, &s->streams[0]->codec) < 0) {
|
||||
av_free(wav);
|
||||
return -1;
|
||||
}
|
||||
end_tag(pb, fmt);
|
||||
|
||||
/* data header */
|
||||
wav->data = start_tag(pb, "data");
|
||||
|
||||
put_flush_packet(pb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wav_write_packet(AVFormatContext *s, int stream_index_ptr,
|
||||
UINT8 *buf, int size, int force_pts)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
put_buffer(pb, buf, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wav_write_trailer(AVFormatContext *s)
|
||||
{
|
||||
ByteIOContext *pb = &s->pb;
|
||||
WAVContext *wav = s->priv_data;
|
||||
offset_t file_size;
|
||||
|
||||
if (!url_is_streamed(&s->pb)) {
|
||||
end_tag(pb, wav->data);
|
||||
|
||||
/* update file size */
|
||||
file_size = url_ftell(pb);
|
||||
url_fseek(pb, 4, SEEK_SET);
|
||||
put_le32(pb, (UINT32)(file_size - 8));
|
||||
url_fseek(pb, file_size, SEEK_SET);
|
||||
|
||||
put_flush_packet(pb);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return the size of the found tag */
|
||||
/* XXX: > 2GB ? */
|
||||
static int find_tag(ByteIOContext *pb, UINT32 tag1)
|
||||
{
|
||||
unsigned int tag;
|
||||
int size;
|
||||
|
||||
for(;;) {
|
||||
if (url_feof(pb))
|
||||
return -1;
|
||||
tag = get_le32(pb);
|
||||
size = get_le32(pb);
|
||||
if (tag == tag1)
|
||||
break;
|
||||
url_fseek(pb, size, SEEK_CUR);
|
||||
}
|
||||
if (size < 0)
|
||||
size = 0x7fffffff;
|
||||
return size;
|
||||
}
|
||||
|
||||
static int wav_probe(AVProbeData *p)
|
||||
{
|
||||
/* check file header */
|
||||
if (p->buf_size <= 32)
|
||||
return 0;
|
||||
if (p->buf[0] == 'R' && p->buf[1] == 'I' &&
|
||||
p->buf[2] == 'F' && p->buf[3] == 'F' &&
|
||||
p->buf[8] == 'W' && p->buf[9] == 'A' &&
|
||||
p->buf[10] == 'V' && p->buf[11] == 'E')
|
||||
return AVPROBE_SCORE_MAX;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* wav input */
|
||||
static int wav_read_header(AVFormatContext *s,
|
||||
AVFormatParameters *ap)
|
||||
{
|
||||
int size;
|
||||
unsigned int tag;
|
||||
ByteIOContext *pb = &s->pb;
|
||||
AVStream *st;
|
||||
|
||||
/* check RIFF header */
|
||||
tag = get_le32(pb);
|
||||
|
||||
if (tag != MKTAG('R', 'I', 'F', 'F'))
|
||||
return -1;
|
||||
get_le32(pb); /* file size */
|
||||
tag = get_le32(pb);
|
||||
if (tag != MKTAG('W', 'A', 'V', 'E'))
|
||||
return -1;
|
||||
|
||||
/* parse fmt header */
|
||||
size = find_tag(pb, MKTAG('f', 'm', 't', ' '));
|
||||
if (size < 0)
|
||||
return -1;
|
||||
st = av_new_stream(s, 0);
|
||||
if (!st)
|
||||
return AVERROR_NOMEM;
|
||||
|
||||
get_wav_header(pb, &st->codec, (size >= 18));
|
||||
|
||||
size = find_tag(pb, MKTAG('d', 'a', 't', 'a'));
|
||||
if (size < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MAX_SIZE 4096
|
||||
|
||||
static int wav_read_packet(AVFormatContext *s,
|
||||
AVPacket *pkt)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (url_feof(&s->pb))
|
||||
return -EIO;
|
||||
if (av_new_packet(pkt, MAX_SIZE))
|
||||
return -EIO;
|
||||
pkt->stream_index = 0;
|
||||
|
||||
ret = get_buffer(&s->pb, pkt->data, pkt->size);
|
||||
if (ret < 0)
|
||||
av_free_packet(pkt);
|
||||
/* note: we need to modify the packet size here to handle the last
|
||||
packet */
|
||||
pkt->size = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wav_read_close(AVFormatContext *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AVInputFormat wav_iformat = {
|
||||
"wav",
|
||||
"wav format",
|
||||
0,
|
||||
wav_probe,
|
||||
wav_read_header,
|
||||
wav_read_packet,
|
||||
wav_read_close,
|
||||
};
|
||||
|
||||
static AVOutputFormat wav_oformat = {
|
||||
"wav",
|
||||
"wav format",
|
||||
"audio/x-wav",
|
||||
"wav",
|
||||
sizeof(WAVContext),
|
||||
CODEC_ID_PCM_S16LE,
|
||||
CODEC_ID_NONE,
|
||||
wav_write_header,
|
||||
wav_write_packet,
|
||||
wav_write_trailer,
|
||||
};
|
||||
|
||||
int wav_init(void)
|
||||
{
|
||||
av_register_input_format(&wav_iformat);
|
||||
av_register_output_format(&wav_oformat);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user