📄 swfdec_flv_decoder.c
字号:
/* Swfdec * Copyright (C) 2007 Benjamin Otte <otte@gnome.org> * * 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.1 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., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include "swfdec_flv_decoder.h"#include "swfdec_audio_internal.h"#include "swfdec_bits.h"#include "swfdec_debug.h"enum { SWFDEC_STATE_HEADER, /* need to parse header */ SWFDEC_STATE_LAST_TAG, /* read size of last tag */ SWFDEC_STATE_TAG, /* read next tag */ SWFDEC_STATE_EOF /* file is complete */};typedef struct _SwfdecFlvVideoTag SwfdecFlvVideoTag;typedef struct _SwfdecFlvAudioTag SwfdecFlvAudioTag;typedef struct _SwfdecFlvDataTag SwfdecFlvDataTag;struct _SwfdecFlvVideoTag { guint timestamp; /* milliseconds */ SwfdecVideoFormat format; /* format in use */ int frame_type; /* 0: undefined, 1: keyframe, 2: iframe, 3: H263 disposable iframe */ SwfdecBuffer * buffer; /* buffer for this data */};struct _SwfdecFlvAudioTag { guint timestamp; /* milliseconds */ SwfdecAudioFormat format; /* format in use */ gboolean width; /* TRUE for 16bit, FALSE for 8bit */ SwfdecAudioOut original_format; /* channel/rate information */ SwfdecBuffer * buffer; /* buffer for this data */};struct _SwfdecFlvDataTag { guint timestamp; /* milliseconds */ SwfdecBuffer * buffer; /* buffer containing raw AMF data */};G_DEFINE_TYPE (SwfdecFlvDecoder, swfdec_flv_decoder, SWFDEC_TYPE_DECODER)static voidswfdec_flv_decoder_dispose (GObject *object){ SwfdecFlvDecoder *flv = SWFDEC_FLV_DECODER (object); guint i; if (flv->audio) { for (i = 0; i < flv->audio->len; i++) { SwfdecFlvAudioTag *tag = &g_array_index (flv->audio, SwfdecFlvAudioTag, i); swfdec_buffer_unref (tag->buffer); } g_array_free (flv->audio, TRUE); flv->audio = NULL; } if (flv->video) { for (i = 0; i < flv->video->len; i++) { SwfdecFlvVideoTag *tag = &g_array_index (flv->video, SwfdecFlvVideoTag, i); swfdec_buffer_unref (tag->buffer); } g_array_free (flv->video, TRUE); flv->video = NULL; } if (flv->data) { for (i = 0; i < flv->data->len; i++) { SwfdecFlvDataTag *tag = &g_array_index (flv->data, SwfdecFlvDataTag, i); swfdec_buffer_unref (tag->buffer); } g_array_free (flv->data, TRUE); flv->data = NULL; } G_OBJECT_CLASS (swfdec_flv_decoder_parent_class)->dispose (object);}static SwfdecStatusswfdec_flv_decoder_parse_header (SwfdecFlvDecoder *flv){ SwfdecDecoder *dec = SWFDEC_DECODER (flv); SwfdecBuffer *buffer; SwfdecBits bits; guint version, header_length; gboolean has_audio, has_video; /* NB: we're still reading from the original queue, since deflating is not initialized yet */ buffer = swfdec_buffer_queue_peek (dec->queue, 9); if (buffer == NULL) return SWFDEC_STATUS_NEEDBITS; swfdec_bits_init (&bits, buffer); /* Check if we're really an FLV file */ if (swfdec_bits_get_u8 (&bits) != 'F' || swfdec_bits_get_u8 (&bits) != 'L' || swfdec_bits_get_u8 (&bits) != 'V') { swfdec_buffer_unref (buffer); return SWFDEC_STATUS_ERROR; } version = swfdec_bits_get_u8 (&bits); swfdec_bits_getbits (&bits, 5); has_audio = swfdec_bits_getbit (&bits); swfdec_bits_getbit (&bits); has_video = swfdec_bits_getbit (&bits); header_length = swfdec_bits_get_bu32 (&bits); swfdec_buffer_unref (buffer); if (header_length < 9) { SWFDEC_ERROR ("invalid header length %u, must be 9 or greater", header_length); /* FIXME: treat as error or ignore? */ return SWFDEC_STATUS_ERROR; } buffer = swfdec_buffer_queue_pull (dec->queue, header_length); if (buffer == NULL) return SWFDEC_STATUS_NEEDBITS; swfdec_buffer_unref (buffer); SWFDEC_LOG ("parsing flv stream"); SWFDEC_LOG (" version %u", version); SWFDEC_LOG (" with%s audio", has_audio ? "" : "out"); SWFDEC_LOG (" with%s video", has_video ? "" : "out"); flv->version = version; if (has_audio) { flv->audio = g_array_new (FALSE, FALSE, sizeof (SwfdecFlvAudioTag)); } if (has_video) { flv->video = g_array_new (FALSE, FALSE, sizeof (SwfdecFlvVideoTag)); } flv->state = SWFDEC_STATE_LAST_TAG; return SWFDEC_STATUS_OK;}static SwfdecStatusswfdec_flv_decoder_parse_last_tag (SwfdecFlvDecoder *flv){ SwfdecDecoder *dec = SWFDEC_DECODER (flv); SwfdecBuffer *buffer; SwfdecBits bits; guint last_tag; buffer = swfdec_buffer_queue_pull (dec->queue, 4); if (buffer == NULL) return SWFDEC_STATUS_NEEDBITS; swfdec_bits_init (&bits, buffer); last_tag = swfdec_bits_get_bu32 (&bits); SWFDEC_LOG ("last tag was %u bytes", last_tag); swfdec_buffer_unref (buffer); flv->state = SWFDEC_STATE_TAG; return SWFDEC_STATUS_OK;}static guintswfdec_flv_decoder_find_video (SwfdecFlvDecoder *flv, guint timestamp){ guint min, max; g_assert (flv->video); min = 0; max = flv->video->len; while (max - min > 1) { guint cur = (max + min) / 2; SwfdecFlvVideoTag *tag = &g_array_index (flv->video, SwfdecFlvVideoTag, cur); if (tag->timestamp > timestamp) { max = cur; } else { min = cur; } } return min;}static guintswfdec_flv_decoder_find_audio (SwfdecFlvDecoder *flv, guint timestamp){ guint min, max; g_assert (flv->audio); min = 0; max = flv->audio->len; while (max - min > 1) { guint cur = (max + min) / 2; SwfdecFlvAudioTag *tag = &g_array_index (flv->audio, SwfdecFlvAudioTag, cur); if (tag->timestamp > timestamp) { max = cur; } else { min = cur; } } return min;}static guintswfdec_flv_decoder_find_data (SwfdecFlvDecoder *flv, guint timestamp){ guint min, max; g_assert (flv->data); min = 0; max = flv->data->len; while (max - min > 1) { guint cur = (max + min) / 2; SwfdecFlvDataTag *tag = &g_array_index (flv->data, SwfdecFlvDataTag, cur); if (tag->timestamp > timestamp) { max = cur; } else { min = cur; } } return min;}static SwfdecStatusswfdec_flv_decoder_parse_video_tag (SwfdecFlvDecoder *flv, SwfdecBits *bits, guint timestamp){ SwfdecDecoder *dec = SWFDEC_DECODER (flv); SwfdecFlvVideoTag tag; if (flv->video == NULL) { SWFDEC_ERROR ("wow, video tags in an FLV without video!"); return SWFDEC_STATUS_OK; } tag.timestamp = timestamp; tag.frame_type = swfdec_bits_getbits (bits, 4); tag.format = swfdec_bits_getbits (bits, 4); tag.buffer = swfdec_bits_get_buffer (bits, -1); SWFDEC_LOG (" format: %u", tag.format); SWFDEC_LOG (" frame type: %u", tag.frame_type); if (tag.buffer == NULL) { SWFDEC_WARNING ("no buffer, ignoring"); return SWFDEC_STATUS_OK; } if (flv->video->len == 0) { g_array_append_val (flv->video, tag); } else if (g_array_index (flv->video, SwfdecFlvVideoTag, flv->video->len - 1).timestamp < tag.timestamp) { g_array_append_val (flv->video, tag); } else { guint idx; SWFDEC_WARNING ("timestamps of video buffers not increasing (last was %u, now %u)", g_array_index (flv->video, SwfdecFlvVideoTag, flv->video->len - 1).timestamp, tag.timestamp); idx = swfdec_flv_decoder_find_video (flv, tag.timestamp); g_array_insert_val (flv->video, idx, tag); } if (dec->width == 0 && dec->height == 0) { SwfdecFlvVideoTag *t = &g_array_index (flv->video, SwfdecFlvVideoTag, 0); SwfdecVideoDecoder *decoder; cairo_surface_t *surface; /* nice hack... */ decoder = swfdec_video_decoder_new (t->format); if (decoder == NULL) return SWFDEC_STATUS_OK; surface = swfdec_video_decoder_decode (decoder, t->buffer); if (surface == NULL) return SWFDEC_STATUS_OK; dec->width = cairo_image_surface_get_width (surface); dec->height = cairo_image_surface_get_height (surface); swfdec_video_decoder_free (decoder); cairo_surface_destroy (surface); return SWFDEC_STATUS_INIT; } else { return SWFDEC_STATUS_IMAGE; }}static voidswfdec_flv_decoder_parse_audio_tag (SwfdecFlvDecoder *flv, SwfdecBits *bits, guint timestamp){ SwfdecFlvAudioTag tag; int rate; gboolean stereo; if (flv->audio == NULL) { SWFDEC_ERROR ("wow, audio tags in an FLV without audio!"); return; } tag.timestamp = timestamp; tag.format = swfdec_bits_getbits (bits, 4); rate = 44100 / (1 << (3 - swfdec_bits_getbits (bits, 2))); tag.width = swfdec_bits_getbit (bits); stereo = swfdec_bits_getbit (bits); tag.original_format = SWFDEC_AUDIO_OUT_GET (stereo ? 2 : 1, rate); tag.buffer = swfdec_bits_get_buffer (bits, -1); SWFDEC_LOG (" format: %u", (guint) tag.format); SWFDEC_LOG (" rate: %d", rate); SWFDEC_LOG (" channels: %d", stereo ? 2 : 1); if (tag.buffer == NULL) { SWFDEC_WARNING ("no buffer, ignoring"); return; } if (flv->audio->len == 0) { g_array_append_val (flv->audio, tag); } else if (g_array_index (flv->audio, SwfdecFlvAudioTag, flv->audio->len - 1).timestamp < tag.timestamp) { g_array_append_val (flv->audio, tag); } else { guint idx; SWFDEC_WARNING ("timestamps of audio buffers not increasing (last was %u, now %u)", g_array_index (flv->audio, SwfdecFlvAudioTag, flv->audio->len - 1).timestamp, tag.timestamp); idx = swfdec_flv_decoder_find_audio (flv, tag.timestamp); g_array_insert_val (flv->audio, idx, tag); }}static voidswfdec_flv_decoder_parse_data_tag (SwfdecFlvDecoder *flv, SwfdecBits *bits, guint timestamp){ SwfdecFlvDataTag tag; if (flv->data == NULL) { flv->data = g_array_new (FALSE, FALSE, sizeof (SwfdecFlvDataTag)); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -