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.c292
1 files changed, 199 insertions, 93 deletions
diff --git a/src/demuxers/demux_flv.c b/src/demuxers/demux_flv.c
index bdb33d21d..d3da20136 100644
--- a/src/demuxers/demux_flv.c
+++ b/src/demuxers/demux_flv.c
@@ -21,10 +21,10 @@
/*
* Flash Video (.flv) File Demuxer
* by Mike Melanson (melanson@pcisys.net) and
- * Claudio Ciccani (klan@directfb.org)
+ * Claudio Ciccani (klan@users.sf.net)
*
* For more information on the FLV file format, visit:
- * http://download.macromedia.com/pub/flash/flash_file_format_specification.pdf
+ * http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v9.pdf
*/
#ifdef HAVE_CONFIG_H
@@ -85,7 +85,7 @@ typedef struct {
off_t filesize;
flv_index_entry_t *index;
- int num_indices;
+ unsigned int num_indices;
unsigned int cur_pts;
@@ -110,14 +110,20 @@ typedef struct {
#define FLV_SOUND_FORMAT_ADPCM 0x01
#define FLV_SOUND_FORMAT_MP3 0x02
#define FLV_SOUND_FORMAT_PCM_LE 0x03
+#define FLV_SOUND_FORMAT_NELLY16 0x04 /* Nellymoser 16KHz */
#define FLV_SOUND_FORMAT_NELLY8 0x05 /* Nellymoser 8KHz */
#define FLV_SOUND_FORMAT_NELLY 0x06 /* Nellymoser */
+#define FLV_SOUND_FORMAT_ALAW 0x07 /* G.711 A-LAW */
+#define FLV_SOUND_FORMAT_MULAW 0x08 /* G.711 MU-LAW */
+#define FLV_SOUND_FORMAT_AAC 0x0a
+#define FLV_SOUND_FORMAT_MP38 0x0e /* MP3 8KHz */
#define FLV_VIDEO_FORMAT_FLV1 0x02 /* Sorenson H.263 */
#define FLV_VIDEO_FORMAT_SCREEN 0x03
#define FLV_VIDEO_FORMAT_VP6 0x04 /* On2 VP6 */
#define FLV_VIDEO_FORMAT_VP6A 0x05 /* On2 VP6 with alphachannel */
#define FLV_VIDEO_FORMAT_SCREEN2 0x06
+#define FLV_VIDEO_FORMAT_H264 0x07
#define FLV_DATA_TYPE_NUMBER 0x00
#define FLV_DATA_TYPE_BOOL 0x01
@@ -209,7 +215,7 @@ static int parse_flv_var(demux_flv_t *this,
unsigned char *end = buf + size;
char *str;
unsigned char type;
- int len, num;
+ unsigned int len, num;
if (size < 1)
return 0;
@@ -283,6 +289,8 @@ static int parse_flv_var(demux_flv_t *this,
str = tmp + 2;
tmp += len + 2;
len = parse_flv_var(this, tmp, end-tmp, str, len);
+ if (!len)
+ return 0;
tmp += len;
}
if (*tmp++ != FLV_DATA_TYPE_ENDOBJECT)
@@ -298,6 +306,8 @@ static int parse_flv_var(demux_flv_t *this,
str = tmp + 2;
tmp += len + 2;
len = parse_flv_var(this, tmp, end-tmp, str, len);
+ if (!len)
+ return 0;
tmp += len;
}
break;
@@ -306,10 +316,14 @@ static int parse_flv_var(demux_flv_t *this,
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));
- this->num_indices = num;
+ if (!this->index || this->num_indices != num) {
+ if (this->index)
+ free(this->index);
+ this->index = calloc(num, sizeof(flv_index_entry_t));
+ if (!this->index)
+ return 0;
+ this->num_indices = num;
+ }
for (num = 0; num < this->num_indices && tmp < end; num++) {
if (*tmp++ == FLV_DATA_TYPE_NUMBER) {
lprintf(" got number (%f)\n", BE_F64(tmp));
@@ -320,19 +334,27 @@ static int parse_flv_var(demux_flv_t *this,
break;
}
if (key && keylen == 13 && !strncmp(key, "filepositions", 13)) {
- if (this->index && this->num_indices == num) {
- for (num = 0; num < this->num_indices && tmp < end; num++) {
- if (*tmp++ == FLV_DATA_TYPE_NUMBER) {
- lprintf(" got number (%f)\n", BE_F64(tmp));
- this->index[num].offset = BE_F64(tmp);
- tmp += 8;
- }
+ if (!this->index || this->num_indices != num) {
+ if (this->index)
+ free(this->index);
+ this->index = calloc(num, sizeof(flv_index_entry_t));
+ if (!this->index)
+ return 0;
+ this->num_indices = num;
+ }
+ for (num = 0; num < this->num_indices && tmp < end; num++) {
+ if (*tmp++ == FLV_DATA_TYPE_NUMBER) {
+ lprintf(" got number (%f)\n", BE_F64(tmp));
+ this->index[num].offset = BE_F64(tmp);
+ tmp += 8;
}
- break;
}
+ break;
}
while (num-- && tmp < end) {
len = parse_flv_var(this, tmp, end-tmp, NULL, 0);
+ if (!len)
+ return 0;
tmp += len;
}
break;
@@ -349,12 +371,12 @@ static int parse_flv_var(demux_flv_t *this,
}
static void parse_flv_script(demux_flv_t *this, int size) {
- unsigned char *buf = xine_xmalloc(size);
+ unsigned char *buf = malloc(size);
unsigned char *tmp = buf;
unsigned char *end = buf + size;
int len;
- if (this->input->read(this->input, buf, size ) != size) {
+ if (!buf || this->input->read(this->input, buf, size ) != size) {
this->status = DEMUX_FINISHED;
free(buf);
return;
@@ -413,11 +435,24 @@ static int read_flv_packet(demux_flv_t *this, int preview) {
buf_type = BUF_AUDIO_FLVADPCM;
break;
case FLV_SOUND_FORMAT_MP3:
+ case FLV_SOUND_FORMAT_MP38:
buf_type = BUF_AUDIO_MPEG;
break;
case FLV_SOUND_FORMAT_PCM_LE:
buf_type = BUF_AUDIO_LPCM_LE;
break;
+ case FLV_SOUND_FORMAT_ALAW:
+ buf_type = BUF_AUDIO_ALAW;
+ break;
+ case FLV_SOUND_FORMAT_MULAW:
+ buf_type = BUF_AUDIO_MULAW;
+ break;
+ case FLV_SOUND_FORMAT_AAC:
+ buf_type = BUF_AUDIO_AAC;
+ /* AAC extra header */
+ this->input->read(this->input, buffer, 1 );
+ remaining_bytes--;
+ break;
default:
lprintf(" unsupported audio format (%d)...\n", buffer[0] >> 4);
buf_type = BUF_AUDIO_UNKNOWN;
@@ -448,8 +483,17 @@ static int read_flv_packet(demux_flv_t *this, int preview) {
}
remaining_bytes--;
- if ((buffer[0] >> 4) == 0x01)
- buf_flags = BUF_FLAG_KEYFRAME;
+ switch ((buffer[0] >> 4)) {
+ case 0x01:
+ buf_flags = BUF_FLAG_KEYFRAME;
+ break;
+ case 0x05:
+ /* skip server command */
+ this->input->seek(this->input, remaining_bytes, SEEK_CUR);
+ continue;
+ default:
+ break;
+ }
this->videocodec = buffer[0] & 0x0F; /* override */
switch (this->videocodec) {
@@ -468,6 +512,12 @@ static int read_flv_packet(demux_flv_t *this, int preview) {
this->input->read(this->input, buffer, 4);
remaining_bytes -= 4;
break;
+ case FLV_VIDEO_FORMAT_H264:
+ buf_type = BUF_VIDEO_H264;
+ /* AVC extra header */
+ this->input->read(this->input, buffer, 4);
+ remaining_bytes -= 4;
+ break;
default:
lprintf(" unsupported video format (%d)...\n", buffer[0] & 0x0F);
buf_type = BUF_VIDEO_UNKNOWN;
@@ -495,6 +545,23 @@ static int read_flv_packet(demux_flv_t *this, int preview) {
bih->biSize++;
buf->size++;
}
+ else if (buf_type == BUF_VIDEO_H264 && buffer[0] == 0) {
+ /* AVC sequence header */
+ if (remaining_bytes > buf->max_size-buf->size) {
+ xprintf(this->xine, XINE_VERBOSITY_LOG,
+ _("sequence header too big (%u bytes)!\n"), remaining_bytes);
+ this->input->read(this->input, buf->content+buf->size, buf->max_size-buf->size);
+ this->input->seek(this->input, remaining_bytes-buf->max_size-buf->size, SEEK_CUR);
+ bih->biSize = buf->max_size;
+ buf->size = buf->max_size;
+ }
+ else {
+ this->input->read(this->input, buf->content+buf->size, remaining_bytes);
+ bih->biSize += remaining_bytes;
+ buf->size += remaining_bytes;
+ }
+ remaining_bytes = 0;
+ }
fifo->put(fifo, buf);
this->got_video_header = 1;
}
@@ -502,9 +569,9 @@ static int read_flv_packet(demux_flv_t *this, int preview) {
case FLV_TAG_TYPE_SCRIPT:
lprintf(" got script tag...\n");
- parse_flv_script(this, remaining_bytes);
-
if (preview) {
+ parse_flv_script(this, remaining_bytes);
+
/* 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);
@@ -521,11 +588,21 @@ static int read_flv_packet(demux_flv_t *this, int preview) {
buf->type = BUF_AUDIO_FLVADPCM;
break;
case FLV_SOUND_FORMAT_MP3:
+ case FLV_SOUND_FORMAT_MP38:
buf->type = BUF_AUDIO_MPEG;
break;
case FLV_SOUND_FORMAT_PCM_LE:
buf->type = BUF_AUDIO_LPCM_LE;
break;
+ case FLV_SOUND_FORMAT_ALAW:
+ buf->type = BUF_AUDIO_ALAW;
+ break;
+ case FLV_SOUND_FORMAT_MULAW:
+ buf->type = BUF_AUDIO_MULAW;
+ break;
+ case FLV_SOUND_FORMAT_AAC:
+ buf->type = BUF_AUDIO_AAC;
+ break;
default:
buf->type = BUF_AUDIO_UNKNOWN;
break;
@@ -533,9 +610,10 @@ static int read_flv_packet(demux_flv_t *this, int preview) {
buf->size = 0;
this->audio_fifo->put(this->audio_fifo, buf);
this->got_audio_header = 1;
+ lprintf(" got audio header from metadata...\n");
}
- if (!this->got_video_header && this->videocodec) {
+ if (!this->got_video_header && this->videocodec && this->videocodec != FLV_VIDEO_FORMAT_H264) {
xine_bmiheader *bih;
buf = this->video_fifo->buffer_pool_alloc(this->video_fifo);
buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER |
@@ -567,10 +645,13 @@ static int read_flv_packet(demux_flv_t *this, int preview) {
}
this->video_fifo->put(this->video_fifo, buf);
this->got_video_header = 1;
+ lprintf(" got video header from metadata...\n");
}
return this->status;
- }
+ }
+ /* no preview */
+ this->input->seek(this->input, remaining_bytes, SEEK_CUR);
continue;
default:
@@ -582,33 +663,61 @@ static int read_flv_packet(demux_flv_t *this, int preview) {
while (remaining_bytes) {
buf = fifo->buffer_pool_alloc(fifo);
buf->type = buf_type;
- buf->pts = (int64_t) pts * 90;
-
- 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)) {
buf->extra_info->input_normpos =
(int)((double)this->input->get_current_pos(this->input) * 65535.0 / this->size);
}
+
+ if ((buf_type == BUF_VIDEO_H264 || buf_type == BUF_AUDIO_AAC) && buffer[0] == 0) {
+ /* AVC/AAC sequence header */
+ buf->pts = 0;
+ buf->size = 0;
+
+ buf->decoder_flags = BUF_FLAG_SPECIAL | BUF_FLAG_HEADER;
+ if (preview)
+ buf->decoder_flags |= BUF_FLAG_PREVIEW;
- if (remaining_bytes > buf->max_size)
- buf->size = buf->max_size;
- else
- buf->size = remaining_bytes;
- 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;
-
- if (this->input->read(this->input, buf->content, buf->size) != buf->size) {
- buf->free_buffer(buf);
- this->status = DEMUX_FINISHED;
- break;
+ buf->decoder_info[1] = BUF_SPECIAL_DECODER_CONFIG;
+ buf->decoder_info[2] = MIN(remaining_bytes, buf->max_size);
+ buf->decoder_info_ptr[2] = buf->mem;
+
+ if (this->input->read(this->input, buf->mem, buf->decoder_info[2]) != buf->decoder_info[2]) {
+ buf->free_buffer(buf);
+ this->status = DEMUX_FINISHED;
+ break;
+ }
+
+ if (remaining_bytes > buf->max_size) {
+ xprintf(this->xine, XINE_VERBOSITY_LOG,
+ _("sequence header too big (%u bytes)!\n"), remaining_bytes);
+ this->input->seek(this->input, remaining_bytes-buf->max_size, SEEK_CUR);
+ }
+ remaining_bytes = 0;
+ }
+ else {
+ buf->pts = (int64_t) pts * 90;
+ if (!preview)
+ check_newpts(this, buf->pts, (tag_type == FLV_TAG_TYPE_VIDEO));
+
+ if (remaining_bytes > buf->max_size)
+ buf->size = buf->max_size;
+ else
+ buf->size = remaining_bytes;
+ 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;
+
+ if (this->input->read(this->input, buf->content, buf->size) != buf->size) {
+ buf->free_buffer(buf);
+ this->status = DEMUX_FINISHED;
+ break;
+ }
}
fifo->put(fifo, buf);
@@ -631,8 +740,8 @@ static void seek_flv_file(demux_flv_t *this, off_t seek_pos, int seek_pts) {
lprintf(" seeking %s to %d...\n",
do_rewind ? "backward" : "forward", seek_pts);
-
- if (seek_pts == 0) {
+
+ if (seek_pos == 0 && seek_pts == 0) {
this->input->seek(this->input, this->start, SEEK_SET);
this->cur_pts = 0;
return;
@@ -658,11 +767,9 @@ static void seek_flv_file(demux_flv_t *this, off_t seek_pos, int seek_pts) {
this->input->seek(this->input, this->index[i].offset-4, SEEK_SET);
this->cur_pts = this->index[i].pts;
- return;
}
}
-
- if (seek_pos && this->videocodec) {
+ else if (seek_pos && this->videocodec && abs(seek_pts-this->cur_pts) > 300000) {
off_t pos, size;
pos = this->input->get_current_pos(this->input);
@@ -697,49 +804,50 @@ static void seek_flv_file(demux_flv_t *this, off_t seek_pos, int seek_pts) {
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;
- int ptag_size;
+ else if (seek_pts) {
+ while (do_rewind ? (seek_pts < this->cur_pts) : (seek_pts > this->cur_pts)) {
+ unsigned char tag_type;
+ int data_size;
+ int ptag_size;
- if (next_tag)
- this->input->seek(this->input, next_tag, SEEK_CUR);
+ if (next_tag)
+ this->input->seek(this->input, next_tag, SEEK_CUR);
- len = this->input->read(this->input, buffer, 16);
- if (len != 16) {
- len = (len < 0) ? 0 : len;
- break;
- }
+ len = this->input->read(this->input, buffer, 16);
+ if (len != 16) {
+ len = (len < 0) ? 0 : len;
+ break;
+ }
- ptag_size = _X_BE_32(&buffer[0]);
- tag_type = buffer[4];
- data_size = _X_BE_24(&buffer[5]);
- pts = _X_BE_24(&buffer[8]) | (buffer[11] << 24);
+ ptag_size = _X_BE_32(&buffer[0]);
+ tag_type = buffer[4];
+ 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 */
- next_tag = -(ptag_size + 16 + 4);
- }
- else {
- next_tag = data_size - 1;
- }
+ if (do_rewind) {
+ if (!ptag_size)
+ break; /* beginning of movie */
+ next_tag = -(ptag_size + 16 + 4);
+ }
+ else {
+ next_tag = data_size - 1;
+ }
- if (this->flags & FLV_FLAG_HAS_VIDEO) {
- /* sync to video key frame */
- if (tag_type != FLV_TAG_TYPE_VIDEO || (buffer[15] >> 4) != 0x01)
- continue;
- lprintf(" video keyframe found at %d...\n", pts);
+ if (this->flags & FLV_FLAG_HAS_VIDEO) {
+ /* sync to video key frame */
+ if (tag_type != FLV_TAG_TYPE_VIDEO || (buffer[15] >> 4) != 0x01)
+ continue;
+ lprintf(" video keyframe found at %d...\n", pts);
+ }
+ this->cur_pts = pts;
}
- this->cur_pts = pts;
- }
- /* seek back to the beginning of the tag */
- this->input->seek(this->input, -len, SEEK_CUR);
+ /* seek back to the beginning of the tag */
+ this->input->seek(this->input, -len, SEEK_CUR);
- lprintf( " seeked to %d.\n", pts);
+ lprintf( " seeked to %d.\n", pts);
+ }
}
@@ -789,8 +897,12 @@ static int demux_flv_seek (demux_plugin_t *this_gen,
this->status = DEMUX_OK;
if (INPUT_IS_SEEKABLE(this->input)) {
- if (start_pos && !start_time)
- start_time = (int64_t) this->length * start_pos / 65535;
+ if (start_pos && !start_time) {
+ if (this->length)
+ start_time = (int64_t) this->length * start_pos / 65535;
+ else if (this->index)
+ start_time = this->index[(int)(start_pos * (this->num_indices-1) / 65535)].pts;
+ }
if (!this->length || start_time < this->length) {
seek_flv_file(this, start_pos, start_time);
@@ -838,7 +950,7 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
input_plugin_t *input) {
demux_flv_t *this;
- this = xine_xmalloc(sizeof (demux_flv_t));
+ this = calloc(1, sizeof(demux_flv_t));
this->xine = stream->xine;
this->stream = stream;
this->input = input;
@@ -857,12 +969,6 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
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_CONTENT:
case METHOD_EXPLICIT:
if (!open_flv_file(this)) {
@@ -906,7 +1012,7 @@ static void class_dispose (demux_class_t *this_gen) {
static void *init_plugin (xine_t *xine, void *data) {
demux_flv_class_t *this;
- this = xine_xmalloc (sizeof (demux_flv_class_t));
+ this = calloc(1, sizeof(demux_flv_class_t));
this->demux_class.open_plugin = open_plugin;
this->demux_class.get_description = get_description;