summaryrefslogtreecommitdiff
path: root/src/demuxers/demux_flv.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/demuxers/demux_flv.c')
-rw-r--r--src/demuxers/demux_flv.c299
1 files changed, 205 insertions, 94 deletions
diff --git a/src/demuxers/demux_flv.c b/src/demuxers/demux_flv.c
index 0588b408e..e6a7e234a 100644
--- a/src/demuxers/demux_flv.c
+++ b/src/demuxers/demux_flv.c
@@ -15,7 +15,7 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
*/
/*
@@ -25,8 +25,6 @@
*
* For more information on the FLV file format, visit:
* http://download.macromedia.com/pub/flash/flash_file_format_specification.pdf
- *
- * $Id: demux_flv.c,v 1.21 2007/03/17 11:29:43 klan Exp $
*/
#ifdef HAVE_CONFIG_H
@@ -44,10 +42,10 @@
/*
#define LOG
*/
-#include "xine_internal.h"
-#include "xineutils.h"
-#include "compat.h"
-#include "demux.h"
+#include <xine/xine_internal.h>
+#include <xine/xineutils.h>
+#include <xine/compat.h>
+#include <xine/demux.h>
#include "bswap.h"
#include "group_games.h"
@@ -70,13 +68,21 @@ typedef struct {
off_t start; /* in bytes */
off_t size; /* in bytes */
- unsigned char got_video;
- unsigned char got_audio;
+ unsigned char got_video_header;
+ unsigned char got_audio_header;
unsigned int length; /* in ms */
int width;
int height;
- double framerate;
+ int duration;
+ int videocodec;
+
+ int samplerate;
+ int samplesize;
+ int stereo;
+ int audiocodec;
+
+ off_t filesize;
flv_index_entry_t *index;
int num_indices;
@@ -180,13 +186,10 @@ static int open_flv_file(demux_flv_t *this) {
return 0;
}
- this->start = BE_32(&buffer[5]);
+ this->start = _X_BE_32(&buffer[5]);
this->size = this->input->get_length(this->input);
- if (INPUT_IS_SEEKABLE(this->input))
- this->input->seek(this->input, this->start, SEEK_SET);
- else if (this->start > 9)
- this->input->seek(this->input, this->start-9, SEEK_CUR);
+ this->input->seek(this->input, this->start, SEEK_SET);
lprintf(" qualified FLV file, repositioned @ offset 0x%" PRIxMAX "\n",
(intmax_t)this->start);
@@ -196,7 +199,7 @@ static int open_flv_file(demux_flv_t *this) {
#define BE_F64(buf) ({\
union { uint64_t q; double d; } _tmp;\
- _tmp.q = BE_64(buf);\
+ _tmp.q = _X_BE_64(buf);\
_tmp.d;\
})\
@@ -230,10 +233,37 @@ static int parse_flv_var(demux_flv_t *this,
_x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT, this->height);
}
else if (keylen == 9 && !strncmp(key, "framerate", 9)) {
- this->framerate = val;
+ if (val > 0) {
+ this->duration = 90000.0 / val;
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_FRAME_DURATION, this->duration);
+ }
}
else if (keylen == 13 && !strncmp(key, "videodatarate", 13)) {
- _x_stream_info_set(this->stream, XINE_STREAM_INFO_BITRATE, val*1000.0);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_BITRATE, val*1000.0);
+ }
+ else if (keylen == 12 && !strncmp(key, "videocodecid", 12)) {
+ this->videocodec = val;
+ }
+ else if (keylen == 15 && !strncmp(key, "audiosamplerate", 15)) {
+ this->samplerate = val;
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE, this->samplerate);
+ }
+ else if (keylen == 15 && !strncmp(key, "audiosamplesize", 15)) {
+ this->samplesize = val;
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_BITS, this->samplesize);
+ }
+ else if (keylen == 5 && !strncmp(key, "stereo", 5)) {
+ this->stereo = val;
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_CHANNELS, this->stereo ? 2 : 1);
+ }
+ else if (keylen == 13 && !strncmp(key, "audiodatarate", 13)) {
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_BITRATE, val*1000.0);
+ }
+ else if (keylen == 12 && !strncmp(key, "audiocodecid", 12)) {
+ this->audiocodec = val;
+ }
+ else if (keylen == 8 && !strncmp(key, "filesize", 8)) {
+ this->filesize = val;
}
}
tmp += 8;
@@ -244,11 +274,11 @@ static int parse_flv_var(demux_flv_t *this,
break;
case FLV_DATA_TYPE_STRING:
lprintf(" got string (%s)\n", tmp+2);
- len = BE_16(tmp);
+ len = _X_BE_16(tmp);
tmp += len + 2;
break;
case FLV_DATA_TYPE_OBJECT:
- while ((len = BE_16(tmp)) && tmp < end) {
+ while ((len = _X_BE_16(tmp)) && tmp < end) {
lprintf(" got object var (%s)\n", tmp+2);
str = tmp + 2;
tmp += len + 2;
@@ -259,12 +289,12 @@ static int parse_flv_var(demux_flv_t *this,
return 0;
break;
case FLV_DATA_TYPE_ECMARRAY:
- lprintf(" got EMCA array (%d indices)\n", BE_32(tmp));
- num = BE_32(tmp);
+ lprintf(" got EMCA array (%d indices)\n", _X_BE_32(tmp));
+ num = _X_BE_32(tmp);
tmp += 4;
while (num-- && tmp < end) {
lprintf(" got array key (%s)\n", tmp+2);
- len = BE_16(tmp);
+ len = _X_BE_16(tmp);
str = tmp + 2;
tmp += len + 2;
len = parse_flv_var(this, tmp, end-tmp, str, len);
@@ -272,13 +302,12 @@ static int parse_flv_var(demux_flv_t *this,
}
break;
case FLV_DATA_TYPE_ARRAY:
- lprintf(" got array (%d indices)\n", BE_32(tmp));
- num = BE_32(tmp);
+ lprintf(" got array (%d indices)\n", _X_BE_32(tmp));
+ num = _X_BE_32(tmp);
tmp += 4;
if (key && keylen == 5 && !strncmp(key, "times", 5)) {
- if (this->index)
- free (this->index);
- this->index = xine_xmalloc(num*sizeof(flv_index_entry_t));
+ free (this->index);
+ this->index = xine_xcalloc(num, sizeof(flv_index_entry_t));
this->num_indices = num;
for (num = 0; num < this->num_indices && tmp < end; num++) {
if (*tmp++ == FLV_DATA_TYPE_NUMBER) {
@@ -307,7 +336,7 @@ static int parse_flv_var(demux_flv_t *this,
}
break;
case FLV_DATA_TYPE_DATE:
- lprintf(" got date (%"PRId64", %d)\n", BE_64(tmp), BE_16(tmp+8));
+ lprintf(" got date (%"PRId64", %d)\n", _X_BE_64(tmp), _X_BE_16(tmp+8));
tmp += 10;
break;
default:
@@ -340,7 +369,7 @@ static void parse_flv_script(demux_flv_t *this, int size) {
free(buf);
}
-static int read_flv_packet(demux_flv_t *this) {
+static int read_flv_packet(demux_flv_t *this, int preview) {
fifo_buffer_t *fifo = NULL;
buf_element_t *buf = NULL;
unsigned char buffer[12];
@@ -359,8 +388,8 @@ static int read_flv_packet(demux_flv_t *this) {
}
tag_type = buffer[0];
- remaining_bytes = BE_24(&buffer[1]);
- pts = BE_24(&buffer[4]) | (buffer[7] << 24);
+ remaining_bytes = _X_BE_24(&buffer[1]);
+ pts = _X_BE_24(&buffer[4]) | (buffer[7] << 24);
lprintf(" tag_type = 0x%02X, 0x%X bytes, pts %u\n",
tag_type, remaining_bytes, pts/90);
@@ -374,7 +403,8 @@ static int read_flv_packet(demux_flv_t *this) {
}
remaining_bytes--;
- switch (buffer[0] >> 4) {
+ this->audiocodec = buffer[0] >> 4; /* override */
+ switch (this->audiocodec) {
case FLV_SOUND_FORMAT_PCM_BE:
buf_type = BUF_AUDIO_LPCM_BE;
break;
@@ -394,20 +424,18 @@ static int read_flv_packet(demux_flv_t *this) {
}
fifo = this->audio_fifo;
- if (!this->got_audio) {
+ if (preview && !this->got_audio_header) {
/* send init info to audio decoder */
buf = fifo->buffer_pool_alloc(fifo);
- buf->decoder_flags = BUF_FLAG_HEADER|BUF_FLAG_STDHEADER|BUF_FLAG_FRAME_END;
+ buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER | BUF_FLAG_FRAME_END;
buf->decoder_info[0] = 0;
buf->decoder_info[1] = 44100 >> (3 - ((buffer[0] >> 2) & 3)); /* samplerate */
buf->decoder_info[2] = (buffer[0] & 2) ? 16 : 8; /* bits per sample */
buf->decoder_info[3] = (buffer[0] & 1) + 1; /* channels */
buf->size = 0; /* no extra data */
buf->type = buf_type;
-
fifo->put(fifo, buf);
-
- this->got_audio = 1;
+ this->got_audio_header = 1;
}
break;
@@ -422,7 +450,8 @@ static int read_flv_packet(demux_flv_t *this) {
if ((buffer[0] >> 4) == 0x01)
buf_flags = BUF_FLAG_KEYFRAME;
- switch (buffer[0] & 0x0F) {
+ this->videocodec = buffer[0] & 0x0F; /* override */
+ switch (this->videocodec) {
case FLV_VIDEO_FORMAT_FLV1:
buf_type = BUF_VIDEO_FLV1;
break;
@@ -444,15 +473,15 @@ static int read_flv_packet(demux_flv_t *this) {
break;
}
- fifo = this->video_fifo;
- if (!this->got_video) {
+ fifo = this->video_fifo;
+ if (preview && !this->got_video_header) {
xine_bmiheader *bih;
/* send init info to video decoder; send the bitmapinfo header to the decoder
* primarily as a formality since there is no real data inside */
buf = fifo->buffer_pool_alloc(fifo);
buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER |
BUF_FLAG_FRAMERATE | BUF_FLAG_FRAME_END;
- buf->decoder_info[0] = this->framerate ? (90000.0/this->framerate) : 0;
+ buf->decoder_info[0] = this->duration;
bih = (xine_bmiheader *) buf->content;
memset(bih, 0, sizeof(xine_bmiheader));
bih->biSize = sizeof(xine_bmiheader);
@@ -465,16 +494,82 @@ static int read_flv_packet(demux_flv_t *this) {
bih->biSize++;
buf->size++;
}
-
fifo->put(fifo, buf);
-
- this->got_video = 1;
+ this->got_video_header = 1;
}
break;
case FLV_TAG_TYPE_SCRIPT:
lprintf(" got script tag...\n");
parse_flv_script(this, remaining_bytes);
+
+ if (preview) {
+ /* send init info to decoders using script information as reference */
+ if (!this->got_audio_header && this->audiocodec) {
+ buf = this->audio_fifo->buffer_pool_alloc(this->audio_fifo);
+ buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER | BUF_FLAG_FRAME_END;
+ buf->decoder_info[0] = 0;
+ buf->decoder_info[1] = this->samplerate;
+ buf->decoder_info[2] = this->samplesize;
+ buf->decoder_info[3] = this->stereo ? 2 : 1;
+ switch (this->audiocodec) {
+ case FLV_SOUND_FORMAT_PCM_BE:
+ buf->type = BUF_AUDIO_LPCM_BE;
+ break;
+ case FLV_SOUND_FORMAT_ADPCM:
+ buf->type = BUF_AUDIO_FLVADPCM;
+ break;
+ case FLV_SOUND_FORMAT_MP3:
+ buf->type = BUF_AUDIO_MPEG;
+ break;
+ case FLV_SOUND_FORMAT_PCM_LE:
+ buf->type = BUF_AUDIO_LPCM_LE;
+ break;
+ default:
+ buf->type = BUF_AUDIO_UNKNOWN;
+ break;
+ }
+ buf->size = 0;
+ this->audio_fifo->put(this->audio_fifo, buf);
+ this->got_audio_header = 1;
+ }
+
+ if (!this->got_video_header && this->videocodec) {
+ xine_bmiheader *bih;
+ buf = this->video_fifo->buffer_pool_alloc(this->video_fifo);
+ buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER |
+ BUF_FLAG_FRAMERATE | BUF_FLAG_FRAME_END;
+ buf->decoder_info[0] = this->duration;
+ switch (this->videocodec) {
+ case FLV_VIDEO_FORMAT_FLV1:
+ buf->type = BUF_VIDEO_FLV1;
+ break;
+ case FLV_VIDEO_FORMAT_VP6:
+ case FLV_VIDEO_FORMAT_VP6A:
+ buf->type = BUF_VIDEO_VP6F;
+ break;
+ default:
+ buf->type = BUF_VIDEO_UNKNOWN;
+ break;
+ }
+ buf->size = sizeof(xine_bmiheader);
+ bih = (xine_bmiheader *) buf->content;
+ memset(bih, 0, sizeof(xine_bmiheader));
+ bih->biSize = sizeof(xine_bmiheader);
+ bih->biWidth = this->width;
+ bih->biHeight = this->height;
+ if (buf->type == BUF_VIDEO_VP6F) {
+ *((uint8_t *)buf->content+buf->size) = ((16-(this->width&15)) << 4) |
+ ((16-(this->height&15)) & 0xf);
+ bih->biSize++;
+ buf->size++;
+ }
+ this->video_fifo->put(this->video_fifo, buf);
+ this->got_video_header = 1;
+ }
+
+ return this->status;
+ }
continue;
default:
@@ -487,7 +582,9 @@ static int read_flv_packet(demux_flv_t *this) {
buf = fifo->buffer_pool_alloc(fifo);
buf->type = buf_type;
buf->pts = (int64_t) pts * 90;
- check_newpts(this, buf->pts, (tag_type == FLV_TAG_TYPE_VIDEO));
+
+ if (!preview)
+ check_newpts(this, buf->pts, (tag_type == FLV_TAG_TYPE_VIDEO));
buf->extra_info->input_time = pts;
if (this->input->get_length(this->input)) {
@@ -502,6 +599,8 @@ static int read_flv_packet(demux_flv_t *this) {
remaining_bytes -= buf->size;
buf->decoder_flags = buf_flags;
+ if (preview)
+ buf->decoder_flags |= BUF_FLAG_PREVIEW;
if (!remaining_bytes)
buf->decoder_flags |= BUF_FLAG_FRAME_END;
@@ -521,12 +620,13 @@ static int read_flv_packet(demux_flv_t *this) {
return this->status;
}
-static void seek_flv_file(demux_flv_t *this, int seek_pts) {
+static void seek_flv_file(demux_flv_t *this, off_t seek_pos, int seek_pts) {
unsigned char buffer[16];
unsigned int pts = this->cur_pts;
int len = 0;
int next_tag = 0;
int do_rewind = (seek_pts < this->cur_pts);
+ int i;
lprintf(" seeking %s to %d...\n",
do_rewind ? "backward" : "forward", seek_pts);
@@ -538,8 +638,6 @@ static void seek_flv_file(demux_flv_t *this, int seek_pts) {
}
if (this->index) {
- int i;
-
if (do_rewind) {
for (i = this->num_indices-1; i > 0; i--) {
if (this->index[i-1].pts < seek_pts)
@@ -563,6 +661,43 @@ static void seek_flv_file(demux_flv_t *this, int seek_pts) {
}
}
+ if (seek_pos && this->videocodec) {
+ off_t pos, size;
+
+ pos = this->input->get_current_pos(this->input);
+ size = this->filesize ? : this->input->get_length(this->input);
+ this->input->seek(this->input, (uint64_t)size * seek_pos / 65535, SEEK_SET);
+ lprintf(" resyncing...\n");
+
+ /* resync */
+ for (i = 0; i < 200000; i++) {
+ uint8_t buf[4];
+
+ if (this->input->read(this->input, buf, 1) < 1) {
+ this->status = DEMUX_FINISHED;
+ return;
+ }
+ if (buf[0] == FLV_TAG_TYPE_VIDEO) {
+ this->input->seek(this->input, 7, SEEK_CUR);
+ if (this->input->read(this->input, buf, 4) < 4) {
+ this->status = DEMUX_FINISHED;
+ return;
+ }
+ /* check StreamID and CodecID */
+ if ( _X_ME_32(buf) == ME_FOURCC(0, 0, 0, (this->videocodec | 0x10)) ) {
+ this->input->seek(this->input, -16, SEEK_CUR);
+ lprintf(" ...resynced after %d bytes\n", i);
+ return;
+ }
+ this->input->seek(this->input, -11, SEEK_CUR);
+ }
+ }
+
+ lprintf(" ...resync failed!\n");
+ this->input->seek(this->input, pos, SEEK_SET);
+ return;
+ }
+
while (do_rewind ? (seek_pts < this->cur_pts) : (seek_pts > this->cur_pts)) {
unsigned char tag_type;
int data_size;
@@ -577,10 +712,10 @@ static void seek_flv_file(demux_flv_t *this, int seek_pts) {
break;
}
- ptag_size = BE_32(&buffer[0]);
+ ptag_size = _X_BE_32(&buffer[0]);
tag_type = buffer[4];
- data_size = BE_24(&buffer[5]);
- pts = BE_24(&buffer[8]) | (buffer[11] << 24);
+ data_size = _X_BE_24(&buffer[5]);
+ pts = _X_BE_24(&buffer[8]) | (buffer[11] << 24);
if (do_rewind) {
if (!ptag_size) break; /* beginning of movie */
@@ -609,7 +744,7 @@ static void seek_flv_file(demux_flv_t *this, int seek_pts) {
static int demux_flv_send_chunk(demux_plugin_t *this_gen) {
demux_flv_t *this = (demux_flv_t *) this_gen;
- return read_flv_packet(this);
+ return read_flv_packet(this, 0);
}
static void demux_flv_send_headers(demux_plugin_t *this_gen) {
@@ -620,6 +755,8 @@ static void demux_flv_send_headers(demux_plugin_t *this_gen) {
this->audio_fifo = this->stream->audio_fifo;
this->status = DEMUX_OK;
+
+ this->buf_flag_seek = 1;
/* load stream information */
_x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO,
@@ -632,10 +769,10 @@ static void demux_flv_send_headers(demux_plugin_t *this_gen) {
/* find first audio/video packets and send headers */
for (i = 0; i < 20; i++) {
- if (read_flv_packet(this) != DEMUX_OK)
+ if (read_flv_packet(this, 1) != DEMUX_OK)
break;
- if (((this->flags & FLV_FLAG_HAS_VIDEO) && this->got_video) &&
- ((this->flags & FLV_FLAG_HAS_AUDIO) && this->got_audio)) {
+ if (((this->flags & FLV_FLAG_HAS_VIDEO) && this->got_video_header) &&
+ ((this->flags & FLV_FLAG_HAS_AUDIO) && this->got_audio_header)) {
lprintf(" headers sent...\n");
break;
}
@@ -654,7 +791,7 @@ static int demux_flv_seek (demux_plugin_t *this_gen,
start_time = (int64_t) this->length * start_pos / 65535;
if (!this->length || start_time < this->length) {
- seek_flv_file(this, start_time);
+ seek_flv_file(this, start_pos, start_time);
if (playing) {
this->buf_flag_seek = 1;
@@ -669,8 +806,7 @@ static int demux_flv_seek (demux_plugin_t *this_gen,
static void demux_flv_dispose (demux_plugin_t *this_gen) {
demux_flv_t *this = (demux_flv_t *) this_gen;
- if (this->index)
- free(this->index);
+ free(this->index);
free(this);
}
@@ -717,13 +853,7 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
this->status = DEMUX_FINISHED;
switch (stream->content_detection_method) {
- case METHOD_BY_EXTENSION:
- if (!_x_demux_check_extension(input->get_mrl(input), "flv")) {
- free (this);
- return NULL;
- }
-
- /* falling through is intended */
+ case METHOD_BY_MRL:
case METHOD_BY_CONTENT:
case METHOD_EXPLICIT:
if (!open_flv_file(this)) {
@@ -740,39 +870,19 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
return &this->demux_plugin;
}
-static const char *get_description (demux_class_t *this_gen) {
- return "Flash Video file demux plugin";
-}
-
-static const char *get_identifier (demux_class_t *this_gen) {
- return "FLV";
-}
-
-static const char *get_extensions (demux_class_t *this_gen) {
- return "flv";
-}
-
-static const char *get_mimetypes (demux_class_t *this_gen) {
- return "video/x-flv: flv: Flash video;";
-}
-
-static void class_dispose (demux_class_t *this_gen) {
- demux_flv_class_t *this = (demux_flv_class_t *) this_gen;
-
- free (this);
-}
-
static void *init_plugin (xine_t *xine, void *data) {
demux_flv_class_t *this;
this = xine_xmalloc (sizeof (demux_flv_class_t));
this->demux_class.open_plugin = open_plugin;
- this->demux_class.get_description = get_description;
- this->demux_class.get_identifier = get_identifier;
- this->demux_class.get_mimetypes = get_mimetypes;
- this->demux_class.get_extensions = get_extensions;
- this->demux_class.dispose = class_dispose;
+ this->demux_class.description = N_("Flash Video file demux plugin");
+ this->demux_class.identifier = "FLV";
+ this->demux_class.mimetypes = "video/x-flv: flv: Flash video;"
+ "video/flv: flv: Flash video;"
+ "application/x-flash-video: flv: Flash video;";
+ this->demux_class.extensions = "flv";
+ this->demux_class.dispose = default_demux_class_dispose;
return this;
}
@@ -786,6 +896,7 @@ static const demuxer_info_t demux_info_flv = {
const plugin_info_t xine_plugin_info[] EXPORTED = {
/* type, API, "name", version, special_info, init_function */
- { PLUGIN_DEMUX, 26, "flashvideo", XINE_VERSION_CODE, &demux_info_flv, init_plugin },
+ { PLUGIN_DEMUX, 27, "flashvideo", XINE_VERSION_CODE, &demux_info_flv, init_plugin },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
+