From 2d14fdcb3a53f7775543f8ef85cae5edf41d3637 Mon Sep 17 00:00:00 2001 From: reimar Date: Mon, 1 Aug 2005 18:52:20 +0000 Subject: Support more MythTV nuv files, based on Gentoo portage patch git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@16165 b3059339-0415-0410-9bf9-f77b7e298cf2 --- libmpdemux/demux_nuv.c | 135 +++++++++++++++++++++++++++++++++++++++++++---- libmpdemux/nuppelvideo.h | 45 ++++++++++++++++ 2 files changed, 170 insertions(+), 10 deletions(-) (limited to 'libmpdemux') diff --git a/libmpdemux/demux_nuv.c b/libmpdemux/demux_nuv.c index ce754c51eb..a156f28b56 100644 --- a/libmpdemux/demux_nuv.c +++ b/libmpdemux/demux_nuv.c @@ -43,6 +43,22 @@ typedef struct _nuv_info_t nuv_position_t *current_position; } nuv_priv_t; +/** + * \brief find best matching bitrate (in kbps) out of a table + * \param bitrate bitrate to find best match for + * \return best match from table + */ +static int nearestBitrate(int bitrate) { + const int rates[17] = {8000, 16000, 24000, 32000, 40000, 48000, 56000, + 64000, 80000, 96000, 112000, 128000, 160000, + 192000, 224000, 256000, 320000}; + int i; + for (i = 0; i < 16; i++) { + if ((rates[i] + rates[i + 1]) / 2 > bitrate) + break; + } + return rates[i]; +} /** * Seek to a position relative to the current position, indicated in time. @@ -154,12 +170,19 @@ int demux_nuv_fill_buffer ( demuxer_t *demuxer ) rtjpeg_frameheader.packetlength); #endif - /* Skip Seekpoint, Text and Sync for now */ + /* Skip Seekpoint, Extended header and Sync for now */ if ((rtjpeg_frameheader.frametype == 'R') || - (rtjpeg_frameheader.frametype == 'T') || + (rtjpeg_frameheader.frametype == 'X') || (rtjpeg_frameheader.frametype == 'S')) return 1; + /* Skip seektable and text (these have a payload) */ + if ((rtjpeg_frameheader.frametype == 'Q') || + (rtjpeg_frameheader.frametype == 'T')) { + stream_skip(demuxer->stream, rtjpeg_frameheader.packetlength); + return 1; + } + if (((rtjpeg_frameheader.frametype == 'D') && (rtjpeg_frameheader.comptype == 'R')) || (rtjpeg_frameheader.frametype == 'V')) @@ -181,9 +204,7 @@ int demux_nuv_fill_buffer ( demuxer_t *demuxer ) } else - /* copy PCM only */ - if (demuxer->audio && (rtjpeg_frameheader.frametype == 'A') && - (rtjpeg_frameheader.comptype == '0')) + if (demuxer->audio && (rtjpeg_frameheader.frametype == 'A')) { priv->current_audio_frame++; if (want_audio) { @@ -194,15 +215,99 @@ int demux_nuv_fill_buffer ( demuxer_t *demuxer ) orig_pos + 12, 0 ); } else { /* skip audio block */ - stream_seek ( demuxer->stream, - stream_tell ( demuxer->stream ) - + rtjpeg_frameheader.packetlength ); + stream_skip ( demuxer->stream, + rtjpeg_frameheader.packetlength ); } } return 1; } +/* Scan for the extended data in MythTV nuv streams */ +static int demux_xscan_nuv(demuxer_t* demuxer, int width, int height) { + int i; + int res = 0; + off_t orig_pos = stream_tell(demuxer->stream); + struct rtframeheader rtjpeg_frameheader; + struct extendeddata ext; + sh_video_t* sh_video = demuxer->video->sh; + sh_audio_t* sh_audio = demuxer->audio->sh; + + for (i = 0; i < 2; ++i) { + if (stream_read(demuxer->stream, (char*)&rtjpeg_frameheader, + sizeof(rtjpeg_frameheader)) < sizeof(rtjpeg_frameheader)) + goto out; + + if (rtjpeg_frameheader.frametype != 'X') + stream_skip(demuxer->stream, rtjpeg_frameheader.packetlength); + } + + if ( rtjpeg_frameheader.frametype != 'X' ) + goto out; + + if (rtjpeg_frameheader.packetlength != sizeof(ext)) { + mp_msg(MSGT_DEMUXER, MSGL_WARN, + "NUV extended frame does not have expected length, ignoring\n"); + goto out; + } + le2me_extendeddata(&ext); + + if (stream_read(demuxer->stream, (char*)&ext, sizeof(ext)) < sizeof(ext)) + goto out; + + if (ext.version != 1) { + mp_msg(MSGT_DEMUXER, MSGL_WARN, + "NUV extended frame has unknown version number (%d), ignoring\n", + ext.version); + goto out; + } + + mp_msg(MSGT_DEMUXER, MSGL_V, "Detected MythTV stream\n"); + + /* Video parameters */ + mp_msg(MSGT_DEMUXER, MSGL_V, "FOURCC: %c%c%c%c\n", + (ext.video_fourcc >> 24) & 0xff, + (ext.video_fourcc >> 16) & 0xff, + (ext.video_fourcc >> 8) & 0xff, + (ext.video_fourcc) & 0xff); + sh_video->format = ext.video_fourcc; + sh_video->i_bps = ext.lavc_bitrate; + + /* Audio parameters */ + if (ext.audio_fourcc == mmioFOURCC('L', 'A', 'M', 'E')) { + sh_audio->format = 0x55; + } else if (ext.audio_fourcc == mmioFOURCC('R', 'A', 'W', 'A')) { + sh_audio->format = 0x1; + } else { + mp_msg(MSGT_DEMUXER, MSGL_WARN, + "Unknown audio format 0x%x\n", ext.audio_fourcc); + } + + sh_audio->channels = ext.audio_channels; + sh_audio->samplerate = ext.audio_sample_rate; + sh_audio->i_bps = sh_audio->channels * sh_audio->samplerate * + ext.audio_bits_per_sample; + if (sh_audio->format != 0x1) + sh_audio->i_bps = nearestBitrate(sh_audio->i_bps / + ext.audio_compression_ratio); + sh_audio->wf->wFormatTag = sh_audio->format; + sh_audio->wf->nChannels = sh_audio->channels; + sh_audio->wf->nSamplesPerSec = sh_audio->samplerate; + sh_audio->wf->nAvgBytesPerSec = sh_audio->i_bps / 8; + sh_audio->wf->nBlockAlign = sh_audio->channels * 2; + sh_audio->wf->wBitsPerSample = ext.audio_bits_per_sample; + sh_audio->wf->cbSize = 0; + + mp_msg(MSGT_DEMUXER, MSGL_V, + "channels=%d bitspersample=%d samplerate=%d compression_ratio=%d\n", + ext.audio_channels, ext.audio_bits_per_sample, + ext.audio_sample_rate, ext.audio_compression_ratio); + return 1; +out: + stream_reset(demuxer->stream); + stream_seek(demuxer->stream, orig_pos); + return 0; +} demuxer_t* demux_open_nuv ( demuxer_t* demuxer ) { @@ -282,6 +387,13 @@ demuxer_t* demux_open_nuv ( demuxer_t* demuxer ) sh_audio->wf->cbSize = 0; } + /* Check for extended data (X frame) and read settings from it */ + if (!demux_xscan_nuv(demuxer, rtjpeg_fileheader.width, + rtjpeg_fileheader.height)) + /* Otherwise assume defaults */ + mp_msg(MSGT_DEMUXER, MSGL_V, "No NUV extended frame, using defaults\n"); + + priv->index_list = (nuv_position_t*) malloc(sizeof(nuv_position_t)); priv->index_list->frame = 0; priv->index_list->time = 0; @@ -304,9 +416,12 @@ int nuv_check_file ( demuxer_t* demuxer ) if(stream_read(demuxer->stream,(char*)&ns,sizeof(ns)) != sizeof(ns)) return 0; - if ( strncmp ( ns.finfo, "NuppelVideo", 12 ) ) + if ( strncmp ( ns.finfo, "NuppelVideo", 12 ) && + strncmp ( ns.finfo, "MythTVVideo", 12 ) ) return 0; /* Not a NuppelVideo file */ - if ( strncmp ( ns.version, "0.05", 5 ) ) + if ( strncmp ( ns.version, "0.05", 5 ) && + strncmp ( ns.version, "0.06", 5 ) && + strncmp ( ns.version, "0.07", 5 ) ) return 0; /* Wrong version NuppelVideo file */ /* Return to original position */ diff --git a/libmpdemux/nuppelvideo.h b/libmpdemux/nuppelvideo.h index 0f89b0c6f2..a67dd02aeb 100644 --- a/libmpdemux/nuppelvideo.h +++ b/libmpdemux/nuppelvideo.h @@ -71,6 +71,34 @@ typedef struct __attribute__((packed)) rtframeheader // R: do not use here! (fixed 'RTjjjjjjjjjjjjjj') } rtframeheader; +/* for MythTV */ +typedef struct __attribute__((packed)) extendeddata +{ + int version; // yes, this is repeated from the file header + int video_fourcc; // video encoding method used + int audio_fourcc; // audio encoding method used + // generic data + int audio_sample_rate; + int audio_bits_per_sample; + int audio_channels; + // codec specific + // mp3lame + int audio_compression_ratio; + int audio_quality; + // rtjpeg + int rtjpeg_quality; + int rtjpeg_luma_filter; + int rtjpeg_chroma_filter; + // libavcodec + int lavc_bitrate; + int lavc_qmin; + int lavc_qmax; + int lavc_maxqdiff; + // unused for later -- total size of 128 integers. + // new fields must be added at the end, above this comment. + int expansion[113]; +} extendeddata; + #define FRAMEHEADERSIZE sizeof(rtframeheader) #define FILEHEADERSIZE sizeof(rtfileheader) @@ -108,4 +136,21 @@ typedef struct audbuffertype (h)->timecode = le2me_32((h)->timecode); \ (h)->packetlength = le2me_32((h)->packetlength); \ } +#define le2me_extendeddata(h) { \ + (h)->version = le2me_32((h)->version); \ + (h)->video_fourcc = le2me_32((h)->video_fourcc); \ + (h)->audio_fourcc = le2me_32((h)->audio_fourcc); \ + (h)->audio_sample_rate = le2me_32((h)->audio_sample_rate); \ + (h)->audio_bits_per_sample = le2me_32((h)->audio_bits_per_sample);\ + (h)->audio_channels = le2me_32((h)->audio_channels); \ + (h)->audio_compression_ratio = le2me_32((h)->audio_compression_ratio);\ + (h)->audio_quality = le2me_32((h)->audio_quality); \ + (h)->rtjpeg_quality = le2me_32((h)->rtjpeg_quality); \ + (h)->rtjpeg_luma_filter = le2me_32((h)->rtjpeg_luma_filter); \ + (h)->rtjpeg_chroma_filter = le2me_32((h)->rtjpeg_chroma_filter);\ + (h)->lavc_bitrate = le2me_32((h)->lavc_bitrate); \ + (h)->lavc_qmin = le2me_32((h)->lavc_qmin); \ + (h)->lavc_qmax = le2me_32((h)->lavc_qmax); \ + (h)->lavc_maxqdiff = le2me_32((h)->lavc_maxqdiff); \ + } -- cgit v1.2.3