ALT Linux Bugzilla
– Attachment 3066 Details for
Bug 17888
x-midi support
New bug
|
Search
|
[?]
|
Help
Register
|
Log In
[x]
|
Forgot Password
Login:
[x]
|
EN
|
RU
[patch]
xine-lib-1.1.4-timidity.patch
xine-lib-1.1.4-timidity.patch (text/plain), 20.85 KB, created by
Valery Inozemtsev
on 2008-11-14 11:42:30 MSK
(
hide
)
Description:
xine-lib-1.1.4-timidity.patch
Filename:
MIME Type:
Creator:
Valery Inozemtsev
Created:
2008-11-14 11:42:30 MSK
Size:
20.85 KB
patch
obsolete
>--- xine-lib-hg-20080905/configure.ac.timidity 2008-11-14 00:07:22 +0300 >+++ xine-lib-hg-20080905/configure.ac 2008-11-14 00:08:35 +0300 >@@ -1486,6 +1486,26 @@ fi > AM_CONDITIONAL(HAVE_OSS, test "x$have_ossaudio" = "xyes") > > >+ >+dnl --------------------------------------------- >+dnl Midi support >+dnl --------------------------------------------- >+AC_ARG_ENABLE([midi], >+ AC_HELP_STRING([--enable-midi], [enable midi support]), >+ [with_midi=$enableval], [with_midi=no]) >+if test "x$with_midi" = "xyes"; then >+ AC_CHECK_LIB(timidity, mid_init, >+ [ AC_CHECK_HEADER(timidity.h, >+ [ have_libtimidity=yes >+ AC_DEFINE(HAVE_TIMIDITY,1,[Define this if you have timidity (libtimidity) installed]) >+ LIBTIMIDITY_LIBS="-ltimidity" ], >+ AC_MSG_RESULT([*** All midi dependent parts will be enabled ***]))], >+ AC_MSG_RESULT([*** All midi dependent parts will be enabled ***])) >+ AC_SUBST(LIBTIMIDITY_LIBS) >+fi >+AM_CONDITIONAL(HAVE_TIMIDITY, test x"$have_libtimidity" = "xyes") >+ >+ > dnl --------------------------------------------- > dnl Alsa support > dnl --------------------------------------------- >@@ -2937,6 +2957,10 @@ if test "x$enable_a52dec" = "xyes"; then > echo " - ac3 (internal library)" > fi > fi >+if test x"$have_libtimidity" = "xyes"; then >+ echo " - midi (external library)" >+fi >+ > echo "" > > dnl video decoders >--- xine-lib-hg-20080905/src/demuxers/Makefile.am.timidity 2008-09-05 23:26:59 +0400 >+++ xine-lib-hg-20080905/src/demuxers/Makefile.am 2008-11-14 00:10:13 +0300 >@@ -113,8 +113,8 @@ xineplug_dmx_audio_la_SOURCES = group_au > demux_vox.c demux_wav.c demux_ac3.c id3.c \ > demux_aac.c demux_mod.c demux_flac.c \ > demux_mpc.c demux_dts.c demux_shn.c \ >- demux_tta.c >-xineplug_dmx_audio_la_LIBADD = $(XINE_LIB) $(LTLIBINTL) $(LIBMODPLUG_LIBS) >+ demux_tta.c demux_midi.c >+xineplug_dmx_audio_la_LIBADD = $(XINE_LIB) $(LTLIBINTL) $(LIBMODPLUG_LIBS) $(LIBTIMIDITY_LIBS) > xineplug_dmx_audio_la_CFLAGS = $(AM_CFLAGS) $(LIBMODPLUG_CFLAGS) > > xineplug_dmx_yuv_frames_la_SOURCES = demux_yuv_frames.c >--- xine-lib-hg-20080905/src/demuxers/demux_midi.c.timidity 2008-11-14 00:08:35 +0300 >+++ xine-lib-hg-20080905/src/demuxers/demux_midi.c 2008-11-14 00:08:35 +0300 >@@ -0,0 +1,523 @@ >+/* >+ * Copyright (C) 2001-2005 the xine project >+ * >+ * This file is part of xine, a free video player. >+ * >+ * xine 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. >+ * >+ * xine 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 >+ */ >+ >+/* >+ * Midi File Demuxer by Jian Shi (jianshi@redflag-linux.com) >+ * based on Midi and WAV specs that are available far and wide >+ * require libtimidity to convert Midi sequence to WAV samples >+ * >+ */ >+ >+#ifdef HAVE_CONFIG_H >+#include "config.h" >+#endif >+ >+#ifdef HAVE_TIMIDITY >+ >+#include <stdio.h> >+#include <fcntl.h> >+#include <unistd.h> >+#include <string.h> >+#include <stdlib.h> >+#include <ctype.h> >+ >+#include <timidity.h> >+ >+#include "xine_internal.h" >+#include "xineutils.h" >+#include "demux.h" >+#include "buffer.h" >+#include "bswap.h" >+#include "group_audio.h" >+ >+#define MIDI_SIGNATURE_SIZE 14 >+#define RIFF_SIGNATURE_SIZE 20 >+#define RMI_SIGNATURE_SIZE (RIFF_SIGNATURE_SIZE + MIDI_SIGNATURE_SIZE) >+#define PREFERED_FRAME_SIZE 4096 >+ >+#define CONVERT_WAVE_RATE 44100 >+#define CONVERT_WAVE_BITPERSAMPLE 16 >+#define CONVERT_WAVE_CHANNEL 2 >+ >+typedef struct { >+ demux_plugin_t demux_plugin; >+ xine_stream_t *stream; >+ fifo_buffer_t *video_fifo; >+ fifo_buffer_t *audio_fifo; >+ input_plugin_t *input; >+ int status; >+ >+ int start_pos; /* current position ,presented by time(ms) */ >+ >+ xine_waveformatex *wave_header; >+ char *wave_frame_buf; /* wave samples for midi */ >+ int wave_frame_size; /* the frame buf size ,presented by byte */ >+ int wave_length; /* wave length presented by time(ms) */ >+ >+ MidSong *midsong; /* the mid-stream from libtimidity */ >+ MidSongOptions convert_opts; /* convertion params */ >+ >+ int audio_type; /* the type of the instance of buf_element_t */ >+ int seek_flag; /* this is set true when a seek occurred */ >+} demux_midi_t; >+ >+typedef struct { >+ demux_class_t demux_class; >+} demux_midi_class_t; >+ >+static int demux_midi_get_stream_length(demux_plugin_t * this_gen); >+ >+/* returns 1 if the MIDI file was opened successfully, 0 otherwise */ >+static int >+open_midi_file(demux_midi_t * this) >+{ >+ int count = 0; >+ char *rawbuf = NULL; >+ char* title = NULL; >+ char* comment = NULL; >+ uint32_t rawbuf_len = 0; >+ uint16_t mtrk_count = 0; >+ uint32_t mtrk_len = 0; >+ uint32_t header_size = 0; >+ >+ header_size = RMI_SIGNATURE_SIZE; >+ rawbuf = (char *) malloc(header_size); >+ if (rawbuf == NULL) >+ return 0; >+ >+ /* strikly check the signature and the consistance for the MIDI file */ >+ if (_x_demux_read_header(this->input, rawbuf, header_size) != >+ header_size) { >+ free(rawbuf); >+ return 0; >+ } >+ >+ if (rawbuf[0] == 'M' && rawbuf[1] == 'T' && >+ rawbuf[2] == 'h' && rawbuf[3] == 'd'){ >+ this->input->seek(this->input, MIDI_SIGNATURE_SIZE, SEEK_SET); >+ } >+ else if(rawbuf[0] == 'R' && rawbuf[1] == 'I' && >+ rawbuf[2] == 'F' && rawbuf[3] == 'F' && >+ rawbuf[8] == 'R' && rawbuf[9] == 'M' && >+ rawbuf[10] == 'I' && rawbuf[11] == 'D' && >+ rawbuf[12] == 'd' && rawbuf[13] == 'a' && >+ rawbuf[14] == 't' && rawbuf[15] == 'a' && >+ rawbuf[20] == 'M' && rawbuf[21] == 'T' && >+ rawbuf[22] == 'h' && rawbuf[23] == 'd'){ >+ char* tmp_buf = (char*)malloc(MIDI_SIGNATURE_SIZE); >+ memcpy(tmp_buf, rawbuf + RIFF_SIGNATURE_SIZE, MIDI_SIGNATURE_SIZE); >+ free(rawbuf); >+ rawbuf = tmp_buf; >+ this->input->seek(this->input, RMI_SIGNATURE_SIZE, SEEK_SET); >+ } >+ else{ >+ free(rawbuf); >+ return 0; >+ } >+ >+ rawbuf_len = MIDI_SIGNATURE_SIZE; >+ mtrk_count = *(uint16_t *) (rawbuf + 10); >+ mtrk_count = mtrk_count >> 8 | mtrk_count << 8; >+ while (count < mtrk_count) { >+ char mtrk_header[8]; >+ >+ if (this->input->read(this->input, mtrk_header, 8) != 8) { >+ free(rawbuf); >+ return 0; >+ } >+ >+ if (mtrk_header[0] != 'M' || mtrk_header[1] != 'T' || >+ mtrk_header[2] != 'r' || mtrk_header[3] != 'k'){ >+ free(rawbuf); >+ return 0; >+ } >+ mtrk_len = *(uint32_t *) (mtrk_header + 4); >+ mtrk_len = (mtrk_len >> 24 & 0x000000FF) | >+ (mtrk_len >> 8 & 0x0000FF00) | >+ (mtrk_len << 8 & 0x00FF0000) | >+ (mtrk_len << 24 & 0xFF000000); >+ >+ rawbuf = (char *) realloc(rawbuf, rawbuf_len + mtrk_len + 8); >+ if(rawbuf == NULL){ >+ free(rawbuf); >+ return 0; >+ } >+ >+ memcpy(rawbuf + rawbuf_len, mtrk_header, 8); >+ rawbuf_len += 8; >+ if (this->input-> >+ read(this->input, rawbuf + rawbuf_len, >+ mtrk_len) != mtrk_len) { >+ free(rawbuf); >+ return 0; >+ } >+ >+ rawbuf_len += mtrk_len; >+ count++; >+ } >+ >+ this->wave_header = >+ (xine_waveformatex *) malloc(sizeof (xine_waveformatex)); >+ this->wave_header->wFormatTag = 0x0001; >+ this->wave_header->nChannels = CONVERT_WAVE_CHANNEL; >+ this->wave_header->nSamplesPerSec = CONVERT_WAVE_RATE; >+ this->wave_header->nAvgBytesPerSec = >+ CONVERT_WAVE_RATE * CONVERT_WAVE_CHANNEL * >+ CONVERT_WAVE_BITPERSAMPLE / 8; >+ this->wave_header->nBlockAlign = >+ CONVERT_WAVE_BITPERSAMPLE * CONVERT_WAVE_CHANNEL / 8; >+ this->wave_header->wBitsPerSample = CONVERT_WAVE_BITPERSAMPLE; >+ this->wave_header->cbSize = 0; >+ >+ this->convert_opts.rate = CONVERT_WAVE_RATE; >+ this->convert_opts.format = MID_AUDIO_S16LSB; >+ this->convert_opts.channels = CONVERT_WAVE_CHANNEL; >+ this->convert_opts.buffer_size = 4096; >+ >+ mid_init(NULL); >+ MidIStream *midi_stream = >+ mid_istream_open_mem((void *) rawbuf, rawbuf_len, 0); >+ >+ this->midsong = mid_song_load(midi_stream, &this->convert_opts); >+ mid_istream_close(midi_stream); >+ >+ free(rawbuf); >+ >+ if (this->midsong == NULL){ >+ mid_exit(); >+ return 0; >+ } >+ >+ title = mid_song_get_meta(this->midsong, MID_SONG_TEXT); >+ comment = mid_song_get_meta(this->midsong, MID_SONG_COPYRIGHT); >+ if (title) { >+ _x_meta_info_n_set(this->stream, XINE_META_INFO_TITLE, >+ title, strlen(title)); >+ } >+ if(comment){ >+ _x_meta_info_n_set(this->stream, XINE_META_INFO_COMMENT, >+ comment, strlen(comment)); >+ } >+ >+ this->wave_length = mid_song_get_total_time(this->midsong); >+ if (this->wave_length == 0){ >+ mid_exit(); >+ return 0; >+ } >+ >+ this->wave_frame_size = PREFERED_FRAME_SIZE / this->wave_header-> >+ nBlockAlign * this->wave_header->nBlockAlign; >+ this->wave_frame_buf = (char *) malloc(this->wave_frame_size); >+ if (this->wave_frame_buf == NULL){ >+ mid_exit(); >+ return 0; >+ } >+ >+ mid_song_start(this->midsong); >+ >+ this->start_pos = 0; >+ this->audio_type = >+ _x_formattag_to_buf_audio(0x0001 /* PCM WAVE FORMAT */ ); >+ >+ return 1; >+} >+ >+static int >+demux_midi_send_chunk(demux_plugin_t * this_gen) >+{ >+ demux_midi_t *this = (demux_midi_t *) this_gen; >+ >+ buf_element_t *buf = NULL; >+ unsigned int remaining_bytes = 0; >+ unsigned int local_start_pos = 0; >+ >+ remaining_bytes = >+ mid_song_read_wave(this->midsong, this->wave_frame_buf, >+ this->wave_frame_size); >+ if (remaining_bytes == 0){ >+ this->status = DEMUX_FINISHED; >+ return this->status; >+ } >+ >+ if (this->seek_flag) { >+ _x_demux_control_newpts(this->stream, this->start_pos * 90, >+ BUF_FLAG_SEEK); >+ this->seek_flag = 0; >+ } >+ >+ while (remaining_bytes) { >+ local_start_pos = this->start_pos; >+ if (!this->audio_fifo) { >+ this->status = DEMUX_FINISHED; >+ break; >+ } >+ >+ buf = this->audio_fifo->buffer_pool_alloc(this->audio_fifo); >+ bzero(buf->content, buf->max_size); >+ if (this->wave_length != 0) >+ buf->extra_info->input_normpos = >+ (int) ((double) local_start_pos * 65535 / >+ this->wave_length); >+ buf->extra_info->input_time = local_start_pos; >+ buf->pts = local_start_pos * 90; >+ >+ if (remaining_bytes > buf->max_size) >+ buf->size = buf->max_size; >+ else >+ buf->size = remaining_bytes; >+ >+ if (memcpy >+ (buf->content, >+ this->wave_frame_buf + (buf->size - remaining_bytes), >+ buf->size) != buf->content) { >+ buf->free_buffer(buf); >+ this->status = DEMUX_FINISHED; >+ break; >+ } >+ >+ >+ remaining_bytes -= buf->size; >+ if (remaining_bytes == 0){ >+ buf->decoder_flags |= BUF_FLAG_FRAME_END; >+ } >+ >+ buf->type = this->audio_type; >+ this->audio_fifo->put(this->audio_fifo, buf); >+ >+ } >+ this->start_pos = mid_song_get_time(this->midsong); >+ >+ return this->status; >+} >+ >+static void >+demux_midi_send_headers(demux_plugin_t * this_gen) >+{ >+ demux_midi_t *this = (demux_midi_t *) this_gen; >+ buf_element_t *buf; >+ >+ this->video_fifo = this->stream->video_fifo; >+ this->audio_fifo = this->stream->audio_fifo; >+ >+ this->status = DEMUX_OK; >+ >+ /* load stream information */ >+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO, 0); >+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_AUDIO, 1); >+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_CHANNELS, >+ this->wave_header->nChannels); >+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE, >+ this->wave_header->nSamplesPerSec); >+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_BITS, >+ this->wave_header->wBitsPerSample); >+ /* send start buffers */ >+ _x_demux_control_start(this->stream); >+ >+ /* send init info to decoders */ >+ if (this->audio_fifo) { >+ buf = this->audio_fifo->buffer_pool_alloc(this->audio_fifo); >+ buf->type = this->audio_type; >+ buf->decoder_flags = >+ BUF_FLAG_HEADER | BUF_FLAG_STDHEADER | BUF_FLAG_FRAME_END; >+ buf->decoder_info[0] = 0; >+ buf->decoder_info[1] = this->wave_header->nSamplesPerSec; >+ buf->decoder_info[2] = this->wave_header->wBitsPerSample; >+ buf->decoder_info[3] = this->wave_header->nChannels; >+ buf->content = (void *) this->wave_header; >+ buf->size = 16; /* the size of WAV fmt header */ >+ this->audio_fifo->put(this->audio_fifo, buf); >+ } >+} >+ >+static int >+demux_midi_seek(demux_plugin_t * this_gen, >+ off_t start_pos, int start_time, int playing) >+{ >+ >+ demux_midi_t *this = (demux_midi_t *) this_gen; >+ start_pos = (off_t) ((double) start_pos / 65535 * this->wave_length); >+ >+ this->seek_flag = 1; >+ this->status = DEMUX_OK; >+ _x_demux_flush_engine(this->stream); >+ >+ if (start_time != 0) { >+ start_pos = start_time; >+ } >+ >+ /* check the boundary offsets */ >+ if (start_pos <= 0) >+ this->start_pos = 0; >+ else if (start_pos >= this->wave_length) { >+ this->status = DEMUX_FINISHED; >+ return this->status; >+ } else { >+ this->start_pos = start_pos; >+ } >+ mid_song_seek(this->midsong, start_pos); >+ >+ return this->status; >+} >+ >+static void >+demux_midi_dispose(demux_plugin_t * this_gen) >+{ >+ demux_midi_t *this = (demux_midi_t *) this_gen; >+ free(this->wave_header); >+ free(this->wave_frame_buf); >+ mid_song_free(this->midsong); >+ free(this); >+} >+ >+static int >+demux_midi_get_status(demux_plugin_t * this_gen) >+{ >+ demux_midi_t *this = (demux_midi_t *) this_gen; >+ >+ return this->status; >+} >+ >+static int >+demux_midi_get_stream_length(demux_plugin_t * this_gen) >+{ >+ demux_midi_t *this = (demux_midi_t *) this_gen; >+ >+ return this->wave_length; >+} >+ >+static uint32_t >+demux_midi_get_capabilities(demux_plugin_t * this_gen) >+{ >+ return DEMUX_CAP_NOCAP; >+} >+ >+static int >+demux_midi_get_optional_data(demux_plugin_t * this_gen, >+ void *data, int data_type) >+{ >+ return DEMUX_OPTIONAL_UNSUPPORTED; >+} >+ >+static demux_plugin_t * >+open_plugin(demux_class_t * class_gen, xine_stream_t * stream, >+ input_plugin_t * input) >+{ >+ >+ demux_midi_t *this; >+ >+ this = xine_xmalloc(sizeof (demux_midi_t)); >+ this->stream = stream; >+ this->input = input; >+ >+ this->demux_plugin.send_headers = demux_midi_send_headers; >+ this->demux_plugin.send_chunk = demux_midi_send_chunk; >+ this->demux_plugin.seek = demux_midi_seek; >+ this->demux_plugin.dispose = demux_midi_dispose; >+ this->demux_plugin.get_status = demux_midi_get_status; >+ this->demux_plugin.get_stream_length = demux_midi_get_stream_length; >+ this->demux_plugin.get_capabilities = demux_midi_get_capabilities; >+ this->demux_plugin.get_optional_data = demux_midi_get_optional_data; >+ this->demux_plugin.demux_class = class_gen; >+ >+ this->status = DEMUX_FINISHED; >+ >+ switch (stream->content_detection_method) { >+ >+ case METHOD_BY_EXTENSION:{ >+ const char *extensions, *mrl; >+ >+ mrl = input->get_mrl(input); >+ extensions = class_gen->get_extensions(class_gen); >+ >+ if (!_x_demux_check_extension(mrl, extensions)) { >+ free(this); >+ return NULL; >+ } >+ break; >+ } >+ >+ case METHOD_BY_CONTENT: >+ case METHOD_EXPLICIT: >+ break; >+ >+ default: >+ free(this); >+ return NULL; >+ } >+ >+ if (open_midi_file(this) == 0) { >+ free(this); >+ return NULL; >+ } >+ >+ return &this->demux_plugin; >+} >+ >+static const char * >+get_description(demux_class_t * this_gen) >+{ >+ return "MIDI file demux plugin"; >+} >+ >+static const char * >+get_identifier(demux_class_t * this_gen) >+{ >+ return "MIDI"; >+} >+ >+static const char * >+get_extensions(demux_class_t * this_gen) >+{ >+ return "mid"; >+} >+ >+static const char * >+get_mimetypes(demux_class_t * this_gen) >+{ >+ return "audio/x-midi: midi: MIDI audio;" >+ "audio/x-midi: mid: MIDI audio;"; >+} >+ >+static void >+class_dispose(demux_class_t * this_gen) >+{ >+ demux_midi_class_t *this = (demux_midi_class_t *) this_gen; >+ free(this); >+} >+ >+void * >+demux_midi_init_plugin(xine_t * xine, void *data) >+{ >+ demux_midi_class_t *this; >+ >+ this = xine_xmalloc(sizeof (demux_midi_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; >+ >+ return this; >+} >+ >+#endif /* HAVE_TIMIDITY*/ >+ >--- xine-lib-hg-20080905/src/demuxers/group_audio.c.timidity 2008-11-14 00:07:37 +0300 >+++ xine-lib-hg-20080905/src/demuxers/group_audio.c 2008-11-14 00:08:35 +0300 >@@ -103,6 +103,12 @@ static const demuxer_info_t demux_info_w > 6 /* priority */ > }; > >+#ifdef HAVE_TIMIDITY >+demuxer_info_t demux_info_midi = { >+ 6 >+}; >+#endif >+ > #ifdef HAVE_MODPLUG > static const demuxer_info_t demux_info_mod = { > 10 /* priority */ >@@ -130,6 +136,9 @@ const plugin_info_t xine_plugin_info[] E > { PLUGIN_DEMUX, 26, "voc", XINE_VERSION_CODE, &demux_info_voc, demux_voc_init_plugin }, > { PLUGIN_DEMUX, 26, "vox", XINE_VERSION_CODE, &demux_info_vox, demux_vox_init_plugin }, > { PLUGIN_DEMUX, 26, "wav", XINE_VERSION_CODE, &demux_info_wav, demux_wav_init_plugin }, >+#ifdef HAVE_TIMIDITY >+ { PLUGIN_DEMUX, 26, "mid", XINE_VERSION_CODE, &demux_info_midi, demux_midi_init_plugin }, >+#endif > #ifdef HAVE_MODPLUG > { PLUGIN_DEMUX, 26, "mod", XINE_VERSION_CODE, &demux_info_mod, demux_mod_init_plugin }, > #endif >--- xine-lib-hg-20080905/src/demuxers/group_audio.h.timidity 2008-09-05 23:26:59 +0400 >+++ xine-lib-hg-20080905/src/demuxers/group_audio.h 2008-11-14 00:08:35 +0300 >@@ -39,6 +39,9 @@ void *demux_tta_init_plugin (xine_t *xin > void *demux_voc_init_plugin (xine_t *xine, void *data); > void *demux_vox_init_plugin (xine_t *xine, void *data); > void *demux_wav_init_plugin (xine_t *xine, void *data); >+#ifdef HAVE_TIMIDITY >+void *demux_midi_init_plugin (xine_t *xine, void *data); >+#endif > > #ifdef HAVE_NOSEFART > void *demux_nsf_init_plugin (xine_t *xine, void *data);
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 17888
: 3066