diff options
Diffstat (limited to 'src/demuxers/demux_flv.c')
-rw-r--r-- | src/demuxers/demux_flv.c | 299 |
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 } }; + |