source: svn/trunk/newcon3bcm2_21bu/dta/src/nexus/bsettop_audio.c @ 2

Last change on this file since 2 was 2, checked in by phkim, 11 years ago

1.phkim

  1. revision copy newcon3sk r27
  • Property svn:executable set to *
File size: 17.0 KB
Line 
1/***************************************************************************
2 *     Copyright (c) 2012, Broadcom Corporation
3 *     All Rights Reserved
4 *     Confidential Property of Broadcom Corporation
5 *
6 *  THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE
7 *  AGREEMENT  BETWEEN THE USER AND BROADCOM.  YOU HAVE NO RIGHT TO USE OR
8 *  EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT.
9 *
10 * $brcm_Workfile:  $
11 * $brcm_Revision:  $
12 * $brcm_Date: $
13 *
14 * Module Description:
15 *
16 * Revision History:
17 *
18 * $brcm_Log:  $
19 *
20 ***************************************************************************/
21
22#include "nexus_platform.h"
23#include "nexus_audio_decoder.h"
24#include "nexus_stc_channel.h"
25#include "nexus_timebase.h"
26#include "nexus_auto_volume_level.h"
27#include "nexus_audio_capture.h"
28
29#include "bstd.h"
30#include "bkni.h"
31#include "ts_psi.h"
32#include "bavc.h"
33
34#include "bsettop.h"
35#include "bsettop_stream_n_priv.h"
36
37BDBG_MODULE(bdecode_audio);
38
39/* following definition should be matched the level of application's vol */
40#define vol_min                 0
41#define vol_max                 32
42#define MAX_EBUFFER             200 /* for 500 msec */
43
44struct baudio_decode
45{
46        NEXUS_AudioDecoderHandle pcmDecoder;
47        NEXUS_AudioDecoderHandle compressedDecoder;
48        NEXUS_AutoVolumeLevelHandle avlHandle;
49        NEXUS_AudioCaptureHandle captureHandle;
50
51        NEXUS_AudioDecoderStartSettings audioSettings;
52               
53        NEXUS_PlatformConfiguration platformConfig;
54
55        bool initialized;
56        baudio_decode_config config;
57        bool audio_decoding;
58        bool start_compressed;
59        bstream_info_t  *p_stream_info;
60        bstream_t               stream;
61
62        bool locked;
63        int first_pts_cnt;
64        int tsm_failed_cnt;
65        int ewptr;
66        long energy[MAX_EBUFFER];
67};
68
69static struct baudio_decode s_audio;
70static bresult baudio_decode_p_set_dolby(baudio_decode_t audio, baudio_dolby_settings dolby, baudio_aac_downmix aac_downmix);
71#ifdef CONFIG_AUD_PWR
72static void baudio_decode_p_capture_cb(void *pparam, int iparam);
73#endif
74static void baudio_decode_switch_mai_output(baudio_decode_t audio, bool compressed);
75static void baudio_decode_lock_cb(void *pparam, int iparam)
76{
77        NEXUS_AudioDecoderStatus status;
78        baudio_decode_t audio = (baudio_decode_t)pparam;
79        BSTD_UNUSED(iparam);
80
81        NEXUS_AudioDecoder_GetStatus(audio->pcmDecoder, &status);
82        audio->locked = status.locked; 
83        BDBG_MSG(("Lock status : %d", audio->locked));
84}
85
86static void baudio_decode_first_pts_cb(void *pparam, int iparam)
87{
88        baudio_decode_t audio = (baudio_decode_t)pparam;
89        BSTD_UNUSED(iparam);
90        audio->first_pts_cnt++;                 
91        BDBG_MSG(("First PTS : %d", audio->first_pts_cnt));
92}
93
94static void baudio_decode_tsm_failed_cb(void *pparam, int iparam)
95{
96        baudio_decode_t audio = (baudio_decode_t)pparam;
97        BSTD_UNUSED(iparam);
98        audio->tsm_failed_cnt++;
99        BDBG_MSG(("TSM failed : %d", audio->tsm_failed_cnt));
100}
101
102baudio_decode_t baudio_decode_open(bobject_t id)
103{
104        NEXUS_AutoVolumeLevelSettings avlSettings;
105        NEXUS_AudioDecoderSettings decoderSettings;
106#ifdef CONFIG_AUD_PWR
107        NEXUS_AudioCaptureStartSettings captureSettings;
108#endif
109       
110        BSTD_UNUSED(id);
111
112        BKNI_Memset(&s_audio, 0, sizeof(s_audio));
113
114        NEXUS_Platform_GetConfiguration(&s_audio.platformConfig);
115
116        s_audio.pcmDecoder = NEXUS_AudioDecoder_Open(0, NULL);
117        s_audio.compressedDecoder = NEXUS_AudioDecoder_Open(1, NULL);
118        s_audio.avlHandle = NEXUS_AutoVolumeLevel_Open(NULL);
119        s_audio.captureHandle = NEXUS_AudioCapture_Open(0, NULL);
120
121        NEXUS_AutoVolumeLevel_AddInput(s_audio.avlHandle, 
122                NEXUS_AudioDecoder_GetConnector(s_audio.pcmDecoder, NEXUS_AudioDecoderConnectorType_eStereo));
123        NEXUS_AudioOutput_AddInput(NEXUS_AudioDac_GetConnector(s_audio.platformConfig.outputs.audioDacs[0]),
124                NEXUS_AutoVolumeLevel_GetConnector(s_audio.avlHandle));
125        NEXUS_AudioOutput_AddInput(NEXUS_HdmiOutput_GetAudioConnector(s_audio.platformConfig.outputs.hdmi[0]),
126                NEXUS_AutoVolumeLevel_GetConnector(s_audio.avlHandle));
127#if NEXUS_NUM_RFM_OUTPUTS
128        NEXUS_AudioOutput_AddInput(NEXUS_Rfm_GetAudioConnector(s_audio.platformConfig.outputs.rfm[0]),
129                NEXUS_AutoVolumeLevel_GetConnector(s_audio.avlHandle));
130#endif
131        NEXUS_AutoVolumeLevel_GetSettings(s_audio.avlHandle, &avlSettings);
132        avlSettings.enabled = false;
133        NEXUS_AutoVolumeLevel_SetSettings(s_audio.avlHandle, &avlSettings);
134
135        s_audio.config.avl = avlSettings.enabled;
136        s_audio.config.avl_target = avlSettings.target;
137        s_audio.config.avl_fixedboost = avlSettings.fixedBoost;
138        s_audio.config.avl_lowerbound = avlSettings.lowerBound;
139
140#ifdef CONFIG_AUD_PWR
141        NEXUS_AudioOutput_AddInput(NEXUS_AudioCapture_GetConnector(s_audio.captureHandle),
142                NEXUS_AutoVolumeLevel_GetConnector(s_audio.avlHandle));
143
144        NEXUS_AudioCapture_GetDefaultStartSettings(&captureSettings);
145        captureSettings.dataCallback.callback = baudio_decode_p_capture_cb;
146        captureSettings.dataCallback.context = &s_audio.capture;
147        NEXUS_AudioCapture_Start(s_audio.captureHandle, &captureSettings);             
148#endif
149       
150        NEXUS_AudioDecoder_GetSettings(s_audio.pcmDecoder, &decoderSettings);
151        decoderSettings.firstPts.callback = baudio_decode_first_pts_cb;
152        decoderSettings.firstPts.context = &s_audio;
153        decoderSettings.lockChanged.callback = baudio_decode_lock_cb;
154        decoderSettings.lockChanged.context = &s_audio; 
155        decoderSettings.ptsError.callback = baudio_decode_tsm_failed_cb;
156        decoderSettings.ptsError.context = &s_audio;
157        NEXUS_AudioDecoder_SetSettings(s_audio.pcmDecoder, &decoderSettings);
158
159        return &s_audio;
160}
161
162void baudio_decode_stop(baudio_decode_t audio)
163{
164        if (!audio->audio_decoding)
165                return;
166
167        NEXUS_AudioDecoder_Stop(audio->pcmDecoder);
168        if (audio->start_compressed) {
169                NEXUS_AudioDecoder_Stop(audio->compressedDecoder);
170                baudio_decode_switch_mai_output(audio, false);
171        }
172        audio->audio_decoding = false;
173        audio->start_compressed = false;       
174}
175
176bresult baudio_decode_start(baudio_decode_t audio, bdisplay_t display, bstream_t p_stream)
177{
178        bstream_status status;
179        bstream_info_t *stream_info;
180        NEXUS_TimebaseSettings timebaseSettings;
181
182        BDBG_ASSERT(p_stream);
183        BSTD_UNUSED(display);
184        audio->stream = p_stream;
185
186        bstream_get_status(p_stream, &status);
187        stream_info = bstream_get_info(p_stream);
188        audio->p_stream_info = stream_info;
189
190        if ((status.mpeg.audio[0].pid == 0) || !stream_info)
191                return eBAPP_RESULT_FAILURE;
192       
193        if (audio->audio_decoding)
194                baudio_decode_stop(audio);
195
196        BDBG_ASSERT(stream_info->audioPidChannel);
197        BDBG_ASSERT(stream_info->stcChannel);
198
199        if (((status.mpeg.audio[0].format == NEXUS_AudioCodec_eAc3) ||
200                 (status.mpeg.audio[0].format == NEXUS_AudioCodec_eAc3Plus)) &&
201                audio->config.hdmi_compress) 
202        {
203                audio->start_compressed = true;
204                baudio_decode_switch_mai_output(audio, true);
205        }       
206        NEXUS_AudioDecoder_GetDefaultStartSettings(&audio->audioSettings);
207        audio->audioSettings.codec = status.mpeg.audio[0].format;;
208        audio->audioSettings.pidChannel = stream_info->audioPidChannel;
209        audio->audioSettings.stcChannel = stream_info->stcChannel;
210
211        if (status.mpeg.video[0].pid == 0)
212        {
213                NEXUS_Timebase_GetSettings(NEXUS_Timebase_e0, &timebaseSettings);
214                timebaseSettings.sourceType = NEXUS_TimebaseSourceType_ePcr;
215                timebaseSettings.sourceSettings.pcr.pidChannel = stream_info->pcrPidChannel;
216                timebaseSettings.sourceSettings.pcr.maxPcrError = 0xff;
217                timebaseSettings.sourceSettings.pcr.trackRange = NEXUS_TimebaseTrackRange_e61ppm;
218                NEXUS_Timebase_SetSettings(NEXUS_Timebase_e0, &timebaseSettings);
219        }
220
221
222        NEXUS_AudioDecoder_Start(audio->pcmDecoder, &audio->audioSettings);
223        audio->audio_decoding = true;
224        audio->config.copyright = 0;
225
226        if (audio->start_compressed )
227        {
228                NEXUS_AudioDecoder_Start(audio->compressedDecoder, &audio->audioSettings);
229        } 
230        return b_ok;
231}
232
233bresult baudio_decode_set_config(baudio_decode_t audio, const baudio_decode_config *config)
234{
235        NEXUS_AudioDecoderSettings settings;
236        NEXUS_AutoVolumeLevelSettings avlSettings;
237       
238        int lvol, rvol;
239
240        NEXUS_AudioDecoder_GetSettings(audio->pcmDecoder, &settings);
241        if (audio->pcmDecoder) {
242        /*
243                baudio_decode_set_ac3_status(audio, config->copyright, config->category_code);
244        */
245        }
246        if ((audio->config.left_volume != config->left_volume) ||
247                (audio->config.right_volume != config->right_volume) ||
248                (audio->config.mute != config->mute))
249        {
250                lvol = config->left_volume;
251                rvol = config->right_volume;
252               
253                if (lvol < vol_min) lvol = vol_min;
254                else if (lvol > vol_max) lvol = vol_max;
255                if (rvol < vol_min) rvol = vol_min;
256                else if (rvol > vol_max) rvol = vol_max;
257
258                settings.muted = config->mute; 
259                /* TODO: need adjustment to meet comcast volume level */       
260                lvol = (lvol + NEXUS_AUDIO_VOLUME_LINEAR_MIN - vol_min) * 
261                                        (NEXUS_AUDIO_VOLUME_LINEAR_NORMAL - NEXUS_AUDIO_VOLUME_LINEAR_MIN) / (vol_max-vol_min);
262                rvol = (rvol + NEXUS_AUDIO_VOLUME_LINEAR_MIN - vol_min) * 
263                                        (NEXUS_AUDIO_VOLUME_LINEAR_NORMAL - NEXUS_AUDIO_VOLUME_LINEAR_MIN) / (vol_max-vol_min);
264
265                settings.volumeMatrix[NEXUS_AudioChannel_eLeft][NEXUS_AudioChannel_eLeft] = lvol;
266                settings.volumeMatrix[NEXUS_AudioChannel_eRight][NEXUS_AudioChannel_eRight] = rvol;
267                settings.volumeMatrix[NEXUS_AudioChannel_eLeft][NEXUS_AudioChannel_eRight] = 0;
268                settings.volumeMatrix[NEXUS_AudioChannel_eRight][NEXUS_AudioChannel_eLeft] = 0; 
269        }
270
271        if (audio->config.mono_mix != config->mono_mix)
272        {
273                settings.outputMode = (config->mono_mix)?NEXUS_AudioDecoderOutputMode_e1_0:NEXUS_AudioDecoderOutputMode_e2_0;
274        }
275        if (audio->config.dualmono != config->dualmono)
276        {
277                switch (config->dualmono) {
278                        case baudio_dualmono_left: settings.dualMonoMode = NEXUS_AudioDecoderDualMonoMode_eLeft; break;
279                        case baudio_dualmono_right: settings.dualMonoMode = NEXUS_AudioDecoderDualMonoMode_eRight; break;
280                        case baudio_dualmono_monomix: settings.dualMonoMode = NEXUS_AudioDecoderDualMonoMode_eMix; break;
281                        case baudio_dualmono_stereo:
282                        default:
283                                settings.dualMonoMode = NEXUS_AudioDecoderDualMonoMode_eStereo;
284                                break;
285                }
286        }       
287        NEXUS_AudioDecoder_SetSettings(audio->pcmDecoder, &settings);
288        baudio_decode_p_set_dolby(audio, config->dolby, config->aac_downmix);
289
290        if ((audio->config.avl != config->avl) ||
291                (audio->config.avl_target != config->avl_target) ||
292                (audio->config.avl_lowerbound != config->avl_lowerbound) ||
293                (audio->config.avl_fixedboost != config->avl_fixedboost)) 
294        {
295                NEXUS_AutoVolumeLevel_GetSettings(audio->avlHandle, &avlSettings);
296                avlSettings.enabled = config->avl;
297                avlSettings.target = config->avl_target;
298                avlSettings.fixedBoost = config->avl_fixedboost;
299                avlSettings.lowerBound = config->avl_lowerbound;
300                NEXUS_AutoVolumeLevel_SetSettings(audio->avlHandle, &avlSettings);
301        }
302        audio->config = *config;
303        return b_ok;
304}
305
306bresult baudio_decode_get_status(baudio_decode_t audio, baudio_decode_status *status)
307{
308        NEXUS_AudioDecoderStatus astatus;
309        bstream_status stream_status;
310
311        NEXUS_AudioDecoder_GetStatus(audio->pcmDecoder, &astatus);
312
313        if (audio->stream)
314                bstream_get_status(audio->stream, &stream_status);
315        else
316                BKNI_Memset(&stream_status, 0, sizeof(bstream_status));
317
318        status->fifo_depth = astatus.fifoDepth;
319        status->fifo_size = astatus.fifoSize;
320        status->pts = astatus.pts;
321        status->stc = astatus.pts + astatus.ptsStcDifference;
322        status->pid = stream_status.mpeg.audio[0].pid;
323
324        if (stream_status.mpeg.audio[0].format == NEXUS_AudioCodec_eAc3 || stream_status.mpeg.audio[0].format == NEXUS_AudioCodec_eAc3Plus)
325                status->copyright = astatus.codecStatus.ac3.copyright;
326        else
327                status->copyright = 0;
328       
329        return b_ok;
330}
331
332void baudio_decode_get_config(baudio_decode_t audio, baudio_decode_config *config)
333{
334        BSTD_UNUSED(audio);
335        *config = audio->config;
336}
337
338
339void baudio_decode_get_avl_level(baudio_decode_t audio, int num_energy, long *level)
340{
341        int start_idx, remain;
342        BDBG_ASSERT(level);
343
344        if(num_energy>MAX_EBUFFER) return;
345        start_idx = audio->ewptr-num_energy;
346       
347        if (start_idx >= 0) {
348                BKNI_Memcpy(level, &audio->energy[start_idx], num_energy*sizeof(long));
349        }
350        else {
351                remain = -1*start_idx;
352                BKNI_Memcpy(level, &audio->energy[MAX_EBUFFER+start_idx], remain*sizeof(long));
353                BKNI_Memcpy(level+remain, &audio->energy[0], audio->ewptr*sizeof(long));
354        }
355}
356
357const bsettop_av_stream_type_t s_audio_stream_types[] =
358{
359    { TS_PSI_ST_11172_3_Audio,  BAVC_AudioCompressionStd_eMpegL1, 0, "MPEG"},
360    { TS_PSI_ST_13818_3_Audio,  BAVC_AudioCompressionStd_eMpegL1,    0,"MPEG"},
361    { TS_PSI_ST_ATSC_AC3,       BAVC_AudioCompressionStd_eAc3,          0,"AC3"},
362    { TS_PSI_ST_ATSC_EAC3,      BAVC_AudioCompressionStd_eAc3Plus,      0,"AC3+"}
363};
364const int s_audio_stream_types_num = sizeof(s_audio_stream_types)/sizeof(s_audio_stream_types[0]);
365bsettop_av_stream_type_t *bdecode_supported_audio(unsigned char stream_type)
366{
367    int i;
368    for (i = 0; i < s_audio_stream_types_num; ++i)
369    {
370        BDBG_MSG(("Table Entry[%d]: 0x%02x,0x%02x %s\n",i,s_audio_stream_types[i].format,s_audio_stream_types[i].codec_id,s_audio_stream_types[i].format_name));
371        if (stream_type == s_audio_stream_types[i].format)
372        {
373            BDBG_MSG(("Audio format[0x%02x]:  %s\n",s_audio_stream_types[i].format,s_audio_stream_types[i].format_name));
374            return(bsettop_av_stream_type_t*)&s_audio_stream_types[i];
375        }
376    }
377    BDBG_MSG(("Unsupported Audio format[0x%02x]\n",stream_type));
378    return NULL;
379}
380
381/*
382 * Dolby stuff
383 */
384static NEXUS_AudioDecoderDolbyDrcMode baudio_p_map_drc_mode(baudio_dolby_drc_mode mode)
385{
386        switch (mode)
387        {
388                case baudio_dolby_drc_mode_line: return NEXUS_AudioDecoderDolbyDrcMode_eLine;
389                case baudio_dolby_drc_mode_custom_a: return NEXUS_AudioDecoderDolbyDrcMode_eCustomA;
390                case baudio_dolby_drc_mode_custom_d: return NEXUS_AudioDecoderDolbyDrcMode_eCustomD;
391                default: break;
392        }
393        return NEXUS_AudioDecoderDolbyDrcMode_eRf;
394}
395
396static NEXUS_AudioDecoderDolbyStereoDownmixMode baudio_p_map_stereo_downmix_mode(baudio_dolby_stereo_downmix_mode mode)
397{
398        switch (mode)
399        {
400                case baudio_dolby_stereo_downmix_mode_dolby_surround_compatible:
401                        return NEXUS_AudioDecoderDolbyStereoDownmixMode_eDolbySurroundCompatible;
402                case baudio_dolby_stereo_downmix_mode_standard:
403                        return NEXUS_AudioDecoderDolbyStereoDownmixMode_eStandard;
404                default: break;
405        }
406        return NEXUS_AudioDecoderDolbyStereoDownmixMode_eAutomatic;
407}
408static bresult baudio_decode_p_set_dolby(baudio_decode_t audio, baudio_dolby_settings dolby, baudio_aac_downmix aac_downmix)
409{
410        NEXUS_AudioDecoderCodecSettings settings;
411        NEXUS_AudioDecoderDolbySettings *dolbySettings;
412        NEXUS_AudioCodec codec ;
413        bstream_status status;
414        NEXUS_AudioDecoderDolbyDrcMode drc;
415        NEXUS_AudioDecoderDolbyStereoDownmixMode stereo;
416
417        BSTD_UNUSED(aac_downmix);
418
419        if ( (audio->config.dolby.stereo_downmix_mode != dolby.stereo_downmix_mode) ||
420                 (audio->config.dolby.drc_mode != dolby.drc_mode) ||
421                 (audio->config.dolby.cut != dolby.cut) || (audio->config.dolby.dialog_norm != dolby.dialog_norm))
422        {
423                bstream_get_status(audio->stream, &status);
424                codec = (NEXUS_AudioCodec)status.mpeg.audio[0].format;
425                if ((codec != NEXUS_AudioCodec_eAc3) && (codec != NEXUS_AudioCodec_eAc3Plus)) 
426                        return b_ok;
427
428                NEXUS_AudioDecoder_GetCodecSettings(audio->pcmDecoder, codec, &settings);
429                if (settings.codec == NEXUS_AudioCodec_eAc3) 
430                        dolbySettings = &settings.codecSettings.ac3;
431                else
432                        dolbySettings = &settings.codecSettings.ac3Plus;
433
434                /* baudio_dolby_stereo_downmix_mode should match NEXUS_AudioDecoderDolbyStereoDownmixMode */
435                stereo = baudio_p_map_stereo_downmix_mode(dolby.stereo_downmix_mode);
436                drc = baudio_p_map_drc_mode(dolby.drc_mode);
437
438                dolbySettings->drcMode = drc;
439                dolbySettings->stereoDownmixMode = stereo;
440                dolbySettings->cut = dolby.cut;
441                dolbySettings->boost = dolby.boost;
442                dolbySettings->dialogNormalization = dolby.dialog_norm;
443                NEXUS_AudioDecoder_SetCodecSettings(audio->pcmDecoder, &settings);
444        }       
445
446        return b_ok;   
447}
448
449/*
450 * Summary:
451 * power management for RAAGA block
452 */
453void baudio_decode_standby(baudio_decode_t audio, bool standby)
454{
455        BSTD_UNUSED(audio);
456        BSTD_UNUSED(standby);
457}
458
459#ifdef CONFIG_AUD_PWR
460#define NESUB 48 /* 1ms in 48KHz sample rate */
461#include "avl_dspfunc.h"
462static void baudio_decode_p_capture_cb(void *pparam, int iparam)
463{
464        NEXUS_AudioCaptureHandle capture = (NEXUS_AudioCaptureHandle)pparam;
465       
466        for (;;)
467        {
468                void *pBuffer;
469                size_t bufferSize;
470
471                NEXUS_AudioCapture_GetBuffer(capture, (void **)&pBuffer, &bufferSize);
472                if (bufferSize>0) {
473                        NEXUS_AudioCapture_ReadComplete(capture, bufferSize);
474                }
475                else break;
476        }
477}
478#endif
479
480void baudio_decode_test_tone(baudio_decode_t audio, 
481                                                         uint32_t *samples, /* array of samples, num_samples long */
482                                                         uint32_t num_samples, /* Number of samples, should be less than sample array size */
483                                                         bool leftChannel,      /* true - program left channel, flase program right channel */
484                                                         bool enable /* enable/disable hifidac tone test */
485                                                        )
486{
487        BSTD_UNUSED(audio);
488        BSTD_UNUSED(samples);
489        BSTD_UNUSED(num_samples);
490        BSTD_UNUSED(leftChannel);
491        BSTD_UNUSED(enable);
492}
493
494static void baudio_decode_switch_mai_output(baudio_decode_t audio, bool compressed)
495{
496        if (compressed) {
497                NEXUS_AudioOutput_RemoveAllInputs(NEXUS_HdmiOutput_GetAudioConnector(audio->platformConfig.outputs.hdmi[0]));
498                NEXUS_AudioOutput_AddInput(NEXUS_HdmiOutput_GetAudioConnector(audio->platformConfig.outputs.hdmi[0]),
499                        NEXUS_AudioDecoder_GetConnector(audio->compressedDecoder, NEXUS_AudioDecoderConnectorType_eCompressed));
500        }
501        else {
502                NEXUS_AudioOutput_RemoveAllInputs(NEXUS_HdmiOutput_GetAudioConnector(audio->platformConfig.outputs.hdmi[0]));
503                NEXUS_AudioOutput_AddInput(NEXUS_HdmiOutput_GetAudioConnector(audio->platformConfig.outputs.hdmi[0]),
504                        NEXUS_AutoVolumeLevel_GetConnector(audio->avlHandle));
505        }
506}
Note: See TracBrowser for help on using the repository browser.