
Recherche avancée
Médias (1)
-
Publier une image simplement
13 avril 2011, par ,
Mis à jour : Février 2012
Langue : français
Type : Video
Autres articles (111)
-
Les autorisations surchargées par les plugins
27 avril 2010, parMediaspip core
autoriser_auteur_modifier() afin que les visiteurs soient capables de modifier leurs informations sur la page d’auteurs -
Ajouter notes et légendes aux images
7 février 2011, parPour pouvoir ajouter notes et légendes aux images, la première étape est d’installer le plugin "Légendes".
Une fois le plugin activé, vous pouvez le configurer dans l’espace de configuration afin de modifier les droits de création / modification et de suppression des notes. Par défaut seuls les administrateurs du site peuvent ajouter des notes aux images.
Modification lors de l’ajout d’un média
Lors de l’ajout d’un média de type "image" un nouveau bouton apparait au dessus de la prévisualisation (...) -
Encoding and processing into web-friendly formats
13 avril 2011, parMediaSPIP automatically converts uploaded files to internet-compatible formats.
Video files are encoded in MP4, Ogv and WebM (supported by HTML5) and MP4 (supported by Flash).
Audio files are encoded in MP3 and Ogg (supported by HTML5) and MP3 (supported by Flash).
Where possible, text is analyzed in order to retrieve the data needed for search engine detection, and then exported as a series of image files.
All uploaded files are stored online in their original format, so you can (...)
Sur d’autres sites (7364)
-
Encoiding Video Frame Error with VAAPI encoding [on hold]
26 octobre 2018, par Tcor5I’m attempting to convert one of the FFMPEG Muxing examples (https://ffmpeg.org/doxygen/2.8/muxing_8c.html) to use VAAPI hardware encoding, using https://www.ffmpeg.org/doxygen/trunk/vaapi_transcode_8c-example.html as an example.
The problem is when I call avcodec_open2, it returns :
Error encoding video frame : Input/output errorI believe I have set up all the HW contexts and frames appropriately but it seems the encoding is failing due to an invalid VASurface ID.
Does anyone have any tips on how to resolve/troubleshoot this ?Thanks
This is the entire shell printout :
[AVHWDeviceContext @ 0x55acd607dd40] Opened VA display via X11 display :0.
[AVHWDeviceContext @ 0x55acd607dd40] libva: VA-API version 1.1.0
[AVHWDeviceContext @ 0x55acd607dd40] libva: va_getDriverName() returns 0
[AVHWDeviceContext @ 0x55acd607dd40] libva: Trying to open /usr/lib/x86_64-linux-gnu/dri/i965_drv_video.so
[AVHWDeviceContext @ 0x55acd607dd40] libva: Found init function __vaDriverInit_1_1
[AVHWDeviceContext @ 0x55acd607dd40] libva: va_openDriver() returns 0
[AVHWDeviceContext @ 0x55acd607dd40] Initialised VAAPI connection: version 1.1
[AVHWDeviceContext @ 0x55acd607dd40] Format 0x32315659 -> yuv420p.
[AVHWDeviceContext @ 0x55acd607dd40] Format 0x30323449 -> yuv420p.
[AVHWDeviceContext @ 0x55acd607dd40] Format 0x3231564e -> nv12.
[AVHWDeviceContext @ 0x55acd607dd40] Format 0x32595559 -> yuyv422.
[AVHWDeviceContext @ 0x55acd607dd40] Format 0x59565955 -> uyvy422.
[AVHWDeviceContext @ 0x55acd607dd40] Format 0x48323234 -> yuv422p.
[AVHWDeviceContext @ 0x55acd607dd40] Format 0x58424752 -> rgb0.
[AVHWDeviceContext @ 0x55acd607dd40] Format 0x58524742 -> bgr0.
[AVHWDeviceContext @ 0x55acd607dd40] Format 0x30313050 -> p010le.
[AVHWDeviceContext @ 0x55acd607dd40] Matched "Intel i965 driver for Intel(R) Skylake - 2.1.0" as known driver "Intel i965 (Quick Sync)".
[AVHWDeviceContext @ 0x55acd609dc40] Opened VA display via X11 display :0.
[AVHWDeviceContext @ 0x55acd609dc40] libva: VA-API version 1.1.0
[AVHWDeviceContext @ 0x55acd609dc40] libva: va_getDriverName() returns 0
[AVHWDeviceContext @ 0x55acd609dc40] libva: Trying to open /usr/lib/x86_64-linux-gnu/dri/i965_drv_video.so
[AVHWDeviceContext @ 0x55acd609dc40] libva: Found init function __vaDriverInit_1_1
[AVHWDeviceContext @ 0x55acd609dc40] libva: va_openDriver() returns 0
[AVHWDeviceContext @ 0x55acd609dc40] Initialised VAAPI connection: version 1.1
[AVHWDeviceContext @ 0x55acd609dc40] Format 0x32315659 -> yuv420p.
[AVHWDeviceContext @ 0x55acd609dc40] Format 0x30323449 -> yuv420p.
[AVHWDeviceContext @ 0x55acd609dc40] Format 0x3231564e -> nv12.
[AVHWDeviceContext @ 0x55acd609dc40] Format 0x32595559 -> yuyv422.
[AVHWDeviceContext @ 0x55acd609dc40] Format 0x59565955 -> uyvy422.
[AVHWDeviceContext @ 0x55acd609dc40] Format 0x48323234 -> yuv422p.
[AVHWDeviceContext @ 0x55acd609dc40] Format 0x58424752 -> rgb0.
[AVHWDeviceContext @ 0x55acd609dc40] Format 0x58524742 -> bgr0.
[AVHWDeviceContext @ 0x55acd609dc40] Format 0x30313050 -> p010le.
[AVHWDeviceContext @ 0x55acd609dc40] Matched "Intel i965 driver for Intel(R) Skylake - 2.1.0" as known driver "Intel i965 (Quick Sync)".
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x4000000.
[AVHWFramesContext @ 0x55acd60bbb00] Direct mapping possible.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x4000001.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x4000002.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x4000003.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x4000004.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x4000005.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x4000006.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x4000007.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x4000008.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x4000009.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x400000a.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x400000b.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x400000c.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x400000d.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x400000e.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x400000f.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x4000010.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x4000011.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x4000012.
[AVHWFramesContext @ 0x55acd60bbb00] Created surface 0x4000013.
[h264_vaapi @ 0x55acd60ba3c0] Using nv12 as format of reconstructed frames.
[AVHWFramesContext @ 0x55acd60bcec0] Created surface 0x4000014.
[AVHWFramesContext @ 0x55acd60bcec0] Direct mapping possible.
[AVHWFramesContext @ 0x55acd60bcec0] Created surface 0x4000015.
[AVHWFramesContext @ 0x55acd60bcec0] Created surface 0x4000016.
[AVHWFramesContext @ 0x55acd60bcec0] Created surface 0x4000017.
[AVHWFramesContext @ 0x55acd60bcec0] Created surface 0x4000018.
[AVHWFramesContext @ 0x55acd60bcec0] Created surface 0x4000019.
[AVHWFramesContext @ 0x55acd60bcec0] Created surface 0x400001a.
[h264_vaapi @ 0x55acd60ba3c0] Using variable-bitrate = 400000 bps.
[h264_vaapi @ 0x55acd60ba3c0] Encode frame: 352x288 (0).
[h264_vaapi @ 0x55acd60ba3c0] Pictures: IDR (0/0)
[h264_vaapi @ 0x55acd60ba3c0] Issuing encode for pic 0/0 as type IDR.
[h264_vaapi @ 0x55acd60ba3c0] No reference pictures.
[h264_vaapi @ 0x55acd60ba3c0] Input surface is 0.
[h264_vaapi @ 0x55acd60ba3c0] Recon surface is 0x400001a.
[h264_vaapi @ 0x55acd60ba3c0] Allocated output buffer 0x8000000
[h264_vaapi @ 0x55acd60ba3c0] Output buffer is 0x8000000.
[h264_vaapi @ 0x55acd60ba3c0] Param buffer (27) is 0x8000001.
[h264_vaapi @ 0x55acd60ba3c0] Param buffer (27) is 0x8000002.
[h264_vaapi @ 0x55acd60ba3c0] Param buffer (27) is 0x8000003.
[h264_vaapi @ 0x55acd60ba3c0] Param buffer (27) is 0x8000004.
[h264_vaapi @ 0x55acd60ba3c0] Param buffer (22) is 0x8000005.
[h264_vaapi @ 0x55acd60ba3c0] Param buffer (23) is 0x8000006.
[h264_vaapi @ 0x55acd60ba3c0] Packed header buffer (1) is 0x8000007/0x8000008 (376 bits).
[h264_vaapi @ 0x55acd60ba3c0] Packed header buffer (4) is 0x8000009/0x800000a (944 bits).
[h264_vaapi @ 0x55acd60ba3c0] Packed header buffer (3) is 0x800000b/0x800000c (72 bits).
[h264_vaapi @ 0x55acd60ba3c0] Param buffer (24) is 0x800000d.
[h264_vaapi @ 0x55acd60ba3c0] Failed to begin picture encode issue: 6 (invalid VASurfaceID).
[h264_vaapi @ 0x55acd60ba3c0] Encode failed: -5.
Error encoding video frame: Input/output errorThis is the code that i’m using :
#include
#include
#include
#include
#include <libavutil></libavutil>avassert.h>
#include <libavutil></libavutil>channel_layout.h>
#include <libavutil></libavutil>opt.h>
#include <libavutil></libavutil>mathematics.h>
#include <libavutil></libavutil>timestamp.h>
#include <libavformat></libavformat>avformat.h>
#include <libswscale></libswscale>swscale.h>
#include <libswresample></libswresample>swresample.h>
//HW Encoding
#include <libavcodec></libavcodec>avcodec.h>
#include <libavutil></libavutil>pixdesc.h>
#include <libavutil></libavutil>hwcontext.h>
#define STREAM_DURATION 10.0
#define STREAM_FRAME_RATE 25 /* 25 images/s */
/* default pix_fmt */
#define STREAM_PIX_FMT AV_PIX_FMT_VAAPI
#define SCALE_FLAGS SWS_BICUBIC
#define LINUX_LIVE_STREAM // enables linux live stream
#define MAX_NAME_LEN 256
static AVBufferRef *hw_device_ctx = NULL; //HW Encoding
// a wrapper around a single output AVStream
typedef struct OutputStream {
AVStream *st;
AVCodecContext *enc;
/* pts of the next frame that will be generated */
int64_t next_pts;
int samples_count;
AVFrame *frame;
AVFrame *tmp_frame;
float t, tincr, tincr2;
struct SwsContext *sws_ctx;
struct SwrContext *swr_ctx;
} OutputStream;
static int set_hwframe_ctx(AVCodecContext *c, AVBufferRef *hw_device_ctx)
{
AVBufferRef *hw_frames_ref;
AVHWFramesContext *frames_ctx = NULL;
int err = 0;
if (!(hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) {
fprintf(stderr, "Failed to create VAAPI frame context.\n");
return -1;
}
frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data);
frames_ctx->format = AV_PIX_FMT_VAAPI;
frames_ctx->sw_format = AV_PIX_FMT_NV12;
frames_ctx->width = 352;
frames_ctx->height = 288;
frames_ctx->initial_pool_size = 20;
if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) {
fprintf(stderr, "Failed to initialize VAAPI frame context."
"Error code: %s\n",av_err2str(err));
av_buffer_unref(&hw_frames_ref);
return err;
}
c->hw_frames_ctx = av_buffer_ref(hw_frames_ref);
if (!c->hw_frames_ctx)
err = AVERROR(ENOMEM);
return err;
}
/* Add an output stream. */
static void add_stream(OutputStream *ost, AVFormatContext *oc,
AVCodec **codec,
enum AVCodecID codec_id)
{
AVCodecContext *c;
int i;
int ret; //HW Encoding
const char *enc_name = "h264_vaapi";//HW encoding
//HW Encoding
ret = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, NULL, NULL, 0);
if (ret < 0) {
fprintf(stderr, "Failed to create a VAAPI device. Error code: %s\n", av_err2str(ret));
return -1;
}
*codec = avcodec_find_encoder_by_name(enc_name);//HW encoding codec
if (!(*codec)) {
fprintf(stderr, "Could not find encoder.\n");
exit(1);
}
ost->st = avformat_new_stream(oc, NULL);
if (!ost->st) {
fprintf(stderr, "Could not allocate stream\n");
exit(1);
}
ost->st->id = oc->nb_streams-1;
c = avcodec_alloc_context3(*codec);
if (!c) {
fprintf(stderr, "Could not alloc an encoding context\n");
exit(1);
}
ost->enc = c;
switch ((*codec)->type) {
case AVMEDIA_TYPE_VIDEO:
c->codec_id = codec_id;
c->bit_rate = 400000;
/* Resolution must be a multiple of two. */
c->width = 352;
c->height = 288;
ost->st->time_base = (AVRational){ 1, STREAM_FRAME_RATE };
c->time_base = ost->st->time_base;
c->gop_size = 12;
c->pix_fmt = STREAM_PIX_FMT;
if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
c->max_b_frames = 2;
}
if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
c->mb_decision = 2;
}
break;
default:
break;
}
if ((ret = set_hwframe_ctx(c, hw_device_ctx)) < 0) {
fprintf(stderr, "Failed to set hwframe context.\n");
exit(1);
}
if (oc->oformat->flags & AVFMT_GLOBALHEADER)
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
/* video output */
static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height)
{
AVFrame *picture;
int ret;
picture = av_frame_alloc();
if (!picture)
return NULL;
picture->format = AV_PIX_FMT_YUV420P;
picture->width = width;
picture->height = height;
printf ("pix_fmt = %d \n", pix_fmt);
printf ("picture ->format (pix_fmt) = %d \n", picture->format);
/* allocate the buffers for the frame data */
ret = av_frame_get_buffer(picture, 32);
if (ret < 0) {
fprintf(stderr, "Could not allocate frame data.\n");
printf ("error : %d \n", ret);
exit(1);
}
return picture;
}
static void open_video(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg)
{
int ret;
AVCodecContext *c = ost->enc;
AVDictionary *opt = NULL;
av_dict_copy(&opt, opt_arg, 0);
/* open the codec */
ret = avcodec_open2(c, codec, &opt);
av_dict_free(&opt);
if (ret < 0) {
fprintf(stderr, "Could not open video codec: %s\n", av_err2str(ret));
exit(1);
}
/* allocate and init a re-usable frame */
printf ("line #%d\n", __LINE__);
ost->frame = alloc_picture(c->pix_fmt, c->width, c->height);
if (!ost->frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
printf ("line #%d\n", __LINE__);
/* If the output format is not YUV420P, then a temporary YUV420P
* picture is needed too. It is then converted to the required
* output format. */
ost->tmp_frame = NULL;
if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
ost->tmp_frame = alloc_picture(AV_PIX_FMT_YUV420P, c->width, c->height);
if (!ost->tmp_frame) {
fprintf(stderr, "Could not allocate temporary picture\n");
exit(1);
}
}
/* copy the stream parameters to the muxer */
ret = avcodec_parameters_from_context(ost->st->codecpar, c);
if (ret < 0) {
fprintf(stderr, "Could not copy the stream parameters\n");
exit(1);
}
}
/* Prepare a dummy image. */
static void fill_yuv_image(AVFrame *pict, int frame_index,
int width, int height)
{
int x, y, i;
i = frame_index;
/* Y */
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
pict->data[0][y * pict->linesize[0] + x] = x + y + i * 3;
/* Cb and Cr */
for (y = 0; y < height / 2; y++) {
for (x = 0; x < width / 2; x++) {
pict->data[1][y * pict->linesize[1] + x] = 128 + y + i * 2;
}
}
}
static AVFrame *get_video_frame(OutputStream *ost)
{
AVCodecContext *c = ost->enc;
if (av_compare_ts(ost->next_pts, c->time_base,
STREAM_DURATION, (AVRational){ 1, 1 }) >= 0)
return NULL;
if (av_frame_make_writable(ost->frame) < 0)
exit(1);
if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
/* as we only generate a YUV420P picture, we must convert it
* to the codec pixel format if needed */
if (!ost->sws_ctx) {
ost->sws_ctx = sws_getContext(c->width, c->height,
AV_PIX_FMT_YUV420P,
c->width, c->height,
AV_PIX_FMT_NV12,
SCALE_FLAGS, NULL, NULL, NULL);
if (!ost->sws_ctx) {
fprintf(stderr,
"Could not initialize the conversion context\n");
exit(1);
}
}
printf ("line #%d\n", __LINE__);
fill_yuv_image(ost->tmp_frame, ost->next_pts, c->width, c->height);
printf ("line #%d\n", __LINE__);
sws_scale(ost->sws_ctx, (const uint8_t * const *) ost->tmp_frame->data,
ost->tmp_frame->linesize, 0, c->height, ost->frame->data,
ost->frame->linesize);
printf ("line #%d\n", __LINE__);
} else {
fill_yuv_image(ost->frame, ost->next_pts, c->width, c->height);
}
printf ("line #%d\n", __LINE__);
ost->frame->pts = ost->next_pts++;
return ost->frame;
}
/*
* encode one video frame and send it to the muxer
* return 1 when encoding is finished, 0 otherwise
*/
static int write_video_frame(AVFormatContext *oc, OutputStream *ost)
{
int ret;
AVCodecContext *c;
AVFrame *frame;
int got_packet = 0;
AVPacket pkt = { 0 };
AVStream *st;
printf ("line #%d\n", __LINE__);
c = ost->enc;
printf ("line #%d\n", __LINE__);
frame = get_video_frame(ost);
printf ("line #%d\n", __LINE__);
av_init_packet(&pkt);
/* encode the image */
ret = avcodec_encode_video2(c, &pkt, frame, &got_packet);
if (ret < 0) {
fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret));
exit(1);
}
if (got_packet) {
av_packet_rescale_ts(&pkt, c->time_base, ost->st->time_base);
pkt.stream_index = ost->st->index;
ret = av_interleaved_write_frame(oc, &pkt);
} else {
ret = 0;
}
if (ret < 0) {
fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret));
exit(1);
}
return (frame || got_packet) ? 0 : 1;
}
static void close_stream(AVFormatContext *oc, OutputStream *ost)
{
avcodec_free_context(&ost->enc);
av_frame_free(&ost->frame);
av_frame_free(&ost->tmp_frame);
sws_freeContext(ost->sws_ctx);
swr_free(&ost->swr_ctx);
}
/**************************************************************/
/* media file output */
int main(int argc, char **argv)
{
OutputStream video_st = { 0 }, audio_st = { 0 };
const char *filename;
AVOutputFormat *fmt;
AVFormatContext *oc;
AVCodec *video_codec, *enc_codec;
int ret;
int have_video = 0;
int encode_video = 0;
AVDictionary *opt = NULL;
int i;
av_log_set_level(AV_LOG_DEBUG); //Debug flag
if (argc < 2) {
printf("usage: %s output_file\n"
"API example program to output a media file with libavformat.\n"
"This program generates a synthetic audio and video stream, encodes and\n"
"muxes them into a file named output_file.\n"
"The output format is automatically guessed according to the file extension.\n"
"Raw images can also be output by using '%%d' in the filename.\n"
"\n", argv[0]);
return 1;
}
filename = argv[1];
for (i = 2; i+1 < argc; i+=2) {
if (!strcmp(argv[i], "-flags") || !strcmp(argv[i], "-fflags"))
av_dict_set(&opt, argv[i]+1, argv[i+1], 0);
}
//HW Encoding
ret = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, NULL, NULL, 0);
if (ret < 0) {
fprintf(stderr, "Failed to create a VAAPI device. Error code: %s\n", av_err2str(ret));
return -1;
}
if (!(video_codec = avcodec_find_encoder_by_name("h264_vaapi"))) {
printf(stderr, "Could not find encoder h264_vaapi");
ret = -1;
}
/* allocate the output media context */
avformat_alloc_output_context2(&oc, NULL, NULL, filename);
if (!oc) {
printf("Could not deduce output format from file extension: using MPEG.\n");
avformat_alloc_output_context2(&oc, NULL, "mpeg", filename);
}
if (!oc)
return 1;
fmt = oc->oformat;
/* Add the audio and video streams using the default format codecs
* and initialize the codecs. */
if (fmt->video_codec != AV_CODEC_ID_NONE) {
add_stream(&video_st, oc, &video_codec, fmt->video_codec);
have_video = 1;
encode_video = 1;
}
/* Now that all the parameters are set, we can open the audio and
* video codecs and allocate the necessary encode buffers. */
if (have_video)
open_video(oc, video_codec, &video_st, opt);
av_dump_format(oc, 0, filename, 1);
/* open the output file, if needed */
if (!(fmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE);
if (ret < 0) {
fprintf(stderr, "Could not open '%s': %s\n", filename,
av_err2str(ret));
return 1;
}
}
/* Write the stream header, if any. */
ret = avformat_write_header(oc, &opt);
if (ret < 0) {
fprintf(stderr, "Error occurred when opening output file: %s\n",
av_err2str(ret));
return 1;
}
printf ("line #%d\n", __LINE__);
while (encode_video) {
/* select the stream to encode */
if (encode_video &&
(av_compare_ts(video_st.next_pts, video_st.enc->time_base,
video_st.next_pts, video_st.enc->time_base) <= 0)) {
printf ("line #%d\n", __LINE__);
encode_video = !write_video_frame(oc, &video_st);
printf ("line #%d\n", __LINE__);
}
}
av_write_trailer(oc);
/* Close each codec. */
if (have_video)
close_stream(oc, &video_st);
if (!(fmt->flags & AVFMT_NOFILE))
/* Close the output file. */
avio_closep(&oc->pb);
/* free the stream */
avformat_free_context(oc);
av_buffer_unref(&hw_device_ctx);
return 0;
} -
Ode to the Gravis Ultrasound
1er août 2011, par Multimedia Mike — GeneralWARNING : This post is a bunch of nostalgia. Feel free to follow along if you recall the DOS days of the early-mid 1990s.
I finally let go of my Gravis Ultrasound MAX sound card a little while ago. It felt like the end of an era for me, even though I had scarcely used the card in recent memory.
The Beginning
What is the Gravis Ultrasound ? Only the finest PC sound card from the classic DOS days. Back in the day (very early 1990s), most consumer PC sound cards were Yamaha OPL FM synthesizers paired with a basic digital to analog converter (DAC). Gravis, a company known for game controllers, dared to break with the dominant paradigm of Sound Blaster clones and create a sound card that had 32 digital channels.
I heard about the GUS sometime in 1992 through one of the dominant online services at the time, Prodigy. Through the message boards, I learned of a promotion with Electronic Arts in which customers could pre-order a GUS at a certain discount along with 2 EA games from a selected catalog (with progressive discounts when ordering more games from the list). I know I got the DOS version of PowerMonger ; I think the other was Night Shift, though that doesn’t seem to be an EA title.Anyway, 1992 saw many maddening delays of the GUS hardware. Finally, reports of GUS shipments began to trickle into the Prodigy message forums. Then one day in November, 1992, mine arrived. Into the 286 machine it went and a valiant attempt at software installation was made. A friend and I fought with the software late into the evening, trying to make this thing work reasonably. I remember grabbing a pair of old headphones sitting near the computer that were used for an ancient (even for the time) portable radio. That was the only means of sound reproduction we had available at that moment. And it still sounded incredible.
After graduating to progressively superior headphones, I would later return to that original pair only to feel my ears were being physically assaulted. Strange, they sounded fine that first night I was trying to make the GUS work. I guess this was my first understanding that the degree to which one is a snobby audiophile is all a matter of hard-earned experience.
Technology
The GUS was powered by something called a GF1 which was supposed to use a technology called wavetable synthesis. In the early days, I thought (and I wasn’t alone in this) that this meant that the GF1 chip had a bunch of digitized instrument samples stored in the ASIC. That wasn’t it.However, it did feature 32 digital channels at a time when most PC audio cards had 2 (plus that Yamaha FM synthesizer). There was some hemming and hawing about how the original GUS couldn’t drive all 32 channels at a full 44.1 kHz ("CD quality") playback rate. It’s true— if 14 channels were enabled, all could be played at 44.1 kHz. Enabling more channels started progressive degradation and with all 32 channels, each was only playing at around 19 kHz. Still, from my emerging game programmer perspective, that allowed for 8-channel tracker music and 6 channels of sound effects, all at the vaunted CD level of quality.
Games and Compatibility
The primary reason to have a discrete sound card was for entertainment applications — ahem, games. GUS support was pretty sketchy out of the gate (ostensibly a major reason for the card’s delay). While many sound cards offered Sound Blaster emulation by basically having the same hardware as Sound Blaster cards, the GUS took a software route towards emulating the SB. To do this required a program called the Sound Blaster Operating System, or SBOS.Oh, how awesome it was to hear the program exclaim "SBOS installed !" And how harshly it grated on your nerves after the 200th time hearing it due to so many reboots and fiddling with options to make your games work. Also, I’ve always wondered if there’s something special about sampling an ’s’ sound — does it strain the sampling frequency range ? Perhaps the phrase was sampled at too low a bitrate because the ’s’ sounds didn’t come through very clearly, which is something you notice after hundreds of iterations when there are 3 ’s’ sounds in the phrase.
Fortunately, SBOS became less relevant with the advent of Mega-Em, a separate emulator which intercepted calls to Roland MIDI systems and routed them to the very capable GUS. Roland-supporting games sounded beautiful.
Eventually, more and more DOS games were released with native Gravis support, sometimes with the help of The Miles Sound System (from our friends at Rad Game Tools — you know, the people behind Smacker and Bink). The library changelog is quite the trip down PC memory lane.
An important area where the GUS shined brightly was that of demos and music trackers. The emerging PC demo scene embraced the powerful GUS (aided, no doubt, by Gravis’ sponsorship of the community) and the coolest computer art and music of the time natively supported the card.
Programming
At this point in my life, I was a budding programmer in high school and was fairly intent on programming video games. So far, I had figured out how to make a few blips using a borrowed Sound Blaster card. I went to great lengths to learn how to program the Gravis Ultrasound.Oh you kids today, with your easy access to information at the tips of your fingers thanks to Google and the broader internet. I had to track down whatever information I could find through a combination of Prodigy message boards and local dialup BBSes and FidoNet message bases. Gravis was initially tight-lipped about programming information for its powerful card, as was de rigueur of hardware companies (something that largely persists to this day). But Gravis eventually saw an opportunity to one-up encumbent Creative Labs and released a full SDK for the Ultrasound. I wanted the SDK badly.
So it was early-mid 1993. Gravis released an SDK. I heard that it was available on their support BBS. Their BBS with a long distance phone number. If memory serves, the SDK was only in the neighborhood of 1.5 Mbytes. That takes a long time to transfer via a 2400 baud modem at a time when long distance phone charges were still a thing and not insubstantial.
Luckily, they also put the SDK on something called an ’FTP site’. Fortunately, about this time, I had the opportunity to get some internet access via the local university.
Indeed, my entire motivation for initially wanting to get on the internet was to obtain special programming information. Is that nerdy enough for you ?
I see that the GUS SDK is still available via the Gravis FTP site. The file GUSDK222.ZIP is dated 1998 and is less than a megabyte.
Next Generation : CD Support
So I had my original GUS by the end of 1992. That was just the first iteration of the Gravis Ultrasound. The next generation was the GUS MAX. When I was ready to get into the CD-ROM era, this was what I wanted in my computer. This is because the GUS MAX had CD-ROM support. This is odd to think about now when all optical drives have SATA interfaces and (P)ATA interfaces before that— what did CD-ROM compatibility mean back then ? I wasn’t quite sure. But in early 1995, I headed over to Computer City (R.I.P.) and bought a new GUS MAX and Sony double-speed CD-ROM drive to install in the family’s PC.
About the "CD-ROM compatibility" : It seems that there were numerous competing interfaces in the early days of CD-ROM technology. The GUS MAX simply integrated 3 different CD-ROM controllers onto the audio card. This was superfluous to me since the Sony drive came with an appropriate controller card anyway, though I didn’t figure out that the extra controller card was unnecessary until after I installed it. No matter ; computers of the day were rife with expansion ports.
The 3 different CD-ROM controllers on the GUS MAX
Explaining The Difference
It was difficult to explain the difference in quality to those who didn’t really care. Sometime during 1995, I picked up a quasi-promotional CD-ROM called "The Gravis Ultrasound Experience" from Babbage’s computer store (remember when that was a thing ?). As most PC software had been distributed on floppy discs up until this point, this CD-ROM was an embarrassment of riches. Tons of game demos, scene demos, tracker music, and all the latest GUS drivers and support software.Further, the CD-ROM had a number of red book CD audio tracks that illustrated the difference between Sound Blaster cards and the GUS. I remember loaning this to a tech-savvy coworker who disbelieved how awesome the GUS was. The coworker took it home, listened to it, and wholly agreed that the GUS audio sounded better than the SB audio in the comparison — and was thoroughly confused because she was hearing this audio emanating from her Sound Blaster. It was the difference between real-time and pre-rendered audio, I suppose, but I failed to convey that message. I imagine the same issue comes up even today regarding real-time video rendering vs., e.g., a pre-rendered HD cinematic posted on YouTube.
Regrettably, I can’t find that CD-ROM anymore which leads me to believe that the coworker never gave it back. Too bad, because it was quite the treasure trove.
Aftermath
According to folklore I’ve heard, Gravis couldn’t keep up as the world changed to Windows and failed to deliver decent drivers. Indeed, I remember trying to keep my GUS in service under Windows 95 well into 1998 but eventually relented and installed some kind of more appropriate sound card that was better supported under Windows.Of course, audio output capability has been standard issue for any PC for at least 10 years and many people aren’t even aware that discrete sound cards still exist. Real-time audio rendering has become less essential as full musical tracks can be composed and compressed into PCM format and delivered with the near limitless space afforded by optical storage.
A few years ago, it was easy to pick up old GUS cards on eBay for cheap. As of this writing, there are only a few and they’re pricy (but perhaps not selling). Maybe I was just viewing during the trough of no value a few years ago.
Nowadays, of course, anyone interested in studying the old GUS or getting a nostalgia fix need only boot up the always-excellent DOSBox emulator which provides remarkable GUS emulation support.
-
Dreamcast SD Adapter and DreamShell
31 décembre 2014, par Multimedia Mike — Sega DreamcastNope ! I’m never going to let go of the Sega Dreamcast hacking. When I was playing around with Dreamcast hacking early last year, I became aware that there is such a thing as an SD card adapter for the DC that plugs into the port normally reserved for the odd DC link cable. Of course I wanted to see what I could do with it.
The primary software that leverages the DC SD adapter is called DreamShell. Working with this adapter and the software requires some skill and guesswork. Searching for these topics tends to turn up results from various forums where people are trying to cargo-cult their way to solutions. I have a strange feeling that this post might become the unofficial English-language documentation on the matter.
Use Cases
What can you do with this thing ? Undoubtedly, the primary use is for backing up (ripping) the contents of GD-ROMs (the custom optical format used for the DC) and playing those backed up (ripped) copies. Presumably, users of this device leverage the latter use case more than the former, i.e., download ripped games, load them on the SD card, and launch them using DreamShell.However, there are other uses such as multimedia playback, system exploration, BIOS reprogramming, high-level programming, and probably a few other things I haven’t figured out yet.
Delivery
I put in an order via the dc-sd.com website and in about 2 short months, the item arrived from China. This marked my third lifetime delivery from China and curiously, all 3 of the shipments have pertained to the Sega Dreamcast.
I thought it was very interesting that this adapter came in such complete packaging. The text is all in Chinese, though the back states “Windows 98 / ME / 2000 / XP, Mac OS 9.1, LINUX2.4”. That’s what tipped me off that they must have just cannibalized some old USB SD card readers and packaging in order to create these. Closer inspection of the internals through the translucent pink case confirms this.
Usage
According to its change log, DreamShell has been around for a long time with version 1.0.0 released in February of 2004. The current version is 4.0.0 RC3. There are several downloads available :- DreamShell 4.0 RC 3 CDI Image
- DreamShell 4.0 RC 3 + Boot Loader
- DreamShell 4.0 RC 3 + Core CDI image
Option #2 worked for me. It contains a CDI disc image and the DreamShell files in a directory named DS/.
Burn the CDI to a CD-R in the normal way you would burn a bootable Dreamcast disc from a CDI image. This is open-ended and left as an exercise to the reader, since there are many procedures depending on platform. On Linux, I used a small script I found once called burncdi-dc.sh.
Then, copy the contents of the DS/ folder to an SD card. As for filesystem, FAT16 and FAT32 are both known to work. The files in DS/ should land in the root of the SD card ; the folder DS/ should not be in the root.
Plug the SD card into the DC SD adapter and plug the adapter in the link cable port on the back of the Dreamcast. Then, boot the disc. If it works, you will see this minor corruption of the usual Sega licensing screen :
Then, there will be a brief white-on-black text screen that explains the booting process :
Then, there will be the main DreamShell logo :
Finally, you will land on the DreamShell main desktop :
Skepticism
At first, I was supremely skeptical of the idea that this SD adapter could perform speedily enough to play games reasonably. This was predicated on the observation that my DC coder’s cable that I used to use for homebrew development could not transfer faster than 115200 bits/second, amounting to about 11 kbytes/sec. I assumed that this was a fundamental limitation of the link port.In fact, I ripped a few of my Dreamcast discs over a decade ago and still have those rips lying around. So I copied the ISO image of Resident Evil : Code Veronica — the game I personally played most on the DC — to the SD card (anywhere works) and used the “ISO loader” icon seen on the desktop above to launch the game.
It works :
The opening FMV plays at full speed. Everything loads as fast as I remember. I was quite surprised.
Digression : My assumptions about serial speeds have often been mistaken. 10 years ago, I heard stories about how we would soon be able to watch streaming video on our cell phones. I scoffed because I thought the 56K limitation of dialup modems was some sort of fundamental speed-of-light type of limitation for telephony bandwidth, wired or wireless.
The desktop menu also includes a ‘speedtest’ tool that profiles the write and read performance of your preferred storage medium. For my fastest SD card (a PNY 2 GB card) :
This is probably more representative of the true adapter bandwidth as reading and writing is a good deal faster through more modern interfaces on PC and Mac with this same card.
Look at the other options on the speedtest console. Hard drive ? Apparently, it’s possible, but it requires a good deal more hardware hacking than just purchasing this SD adapter.
Ripping
As you can see from the Resident Evil screenshot, playing games works quite nicely. How about ripping ? I’m pleased to say that DreamShell has a beautiful ripping interface :
Enter a name for the disc (or read the disc label), select the storage medium, and let it, well, rip. It indicates which track it’s working on and the Sega logo acts as a progress bar, shading blue as the track rip progresses.
I’m finally, efficiently, archiving that collection of Sega Dreamcast demo discs ; I’m hoping they’ll eventually find a home at the Internet Archive. How is overall ripping performance ? Usually about 38-40 minutes to rip a full 900-1000 MB. That certainly beats the 27-28 hours that were required when I performed the ripping at 11 kbytes/sec via the DC coders cable.
All is well until I get a sector reading error :
That’s when it can come in handy to have 3 DC consoles (see ?! not crazy !).
Other Uses
There’s a file explorer. You can browse the filesystem of the SD card, visual memory unit, or the CD portion of the GD-ROM (would be more useful if it accessed the GD area). There are FFmpeg files included. So I threw a random Cinepak file and random MPEG-1 file at it to see what happens. MPEG-1 didn’t do anything, but this Cinepak file from some Sierra game played handily :
If you must enter strings, it helps to have a Dreamcast keyboard (which I do). Failing that, here’s a glimpse of the onscreen keyboard that DreamShell equips :
Learning to use it is a game in itself.
There is an option of installing DreamShell in the BIOS. I did not attempt this. I don’t know if it’s possible (not like there’s a lot of documentation)– perhaps a custom BIOS modchip is needed. But here’s what the screen looks like :
There is also a plain console to interact with (better have a physical keyboard). There are numerous file manipulation commands and custom system interaction commands. I see one interesting command called ‘addr’ that looks useful for dumping memory regions to a file.
A Lua language interpreter is also built in. I would love to play with this if I could ascertain whether DreamShell provided Dreamcast-specific APIs.
Tips And Troubleshooting
I have 3 Dreamcast consoles, affectionately named Terran, Protoss, and Zerg after the StarCraft II stickers with which they are adorned. Some seem to work better than others. Protoss seemed to be able to boot the DreamShell disc more reliably than the others. However, I was alarmed when it couldn’t boot one morning when it was churning the previous day.I think the problem is that it was just cold. That seemed to be the issue. I put in a normal GD-ROM and let it warm up on that disc for awhile and then DreamShell booted fine. So that’s my piece of cargo-culting troubleshooting advice.