This demuxer adds support for demuxing files in the Dynacolor format such as the sample located at:
http://samples.ffmpeg.org/camera-dvr/dynacolor/dynacolor-camera-sample However, some decode errors are showing on the resulting MPEG4 stream. I don't know whether this is a bug with the demuxer or the file as there is only one sample but the output results in a 1 second mp4 file that is playable in VLC media player. Signed-off-by: Tom Needham <06needh...@gmail.com> --- Changelog | 1 + doc/general.texi | 1 + libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/dynacolor.c | 560 +++++++++++++++++++++++++++++++++++++++ libavformat/dynacolor.h | 308 +++++++++++++++++++++ libavformat/version.h | 2 +- 7 files changed, 873 insertions(+), 1 deletion(-) create mode 100644 libavformat/dynacolor.c create mode 100644 libavformat/dynacolor.h diff --git a/Changelog b/Changelog index 711861bda9..79d39494c9 100644 --- a/Changelog +++ b/Changelog @@ -54,6 +54,7 @@ version <next>: - DERF demuxer - CRI HCA decoder - CRI HCA demuxer +- Dynacolor MVC Demuxer version 4.2: diff --git a/doc/general.texi b/doc/general.texi index 752618a00b..4eb4716d87 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -452,6 +452,7 @@ library: @item DXA @tab @tab X @tab This format is used in the non-Windows version of the Feeble Files game and different game cutscenes repacked for use with ScummVM. +@item Dynacolor MVC @tab @tab X @item Electronic Arts cdata @tab @tab X @item Electronic Arts Multimedia @tab @tab X @tab Used in various EA games; files have extensions like WVE and UV2. diff --git a/libavformat/Makefile b/libavformat/Makefile index 8fd0d43721..4d1ca8b7ed 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -169,6 +169,7 @@ OBJS-$(CONFIG_DV_MUXER) += dvenc.o OBJS-$(CONFIG_DVBSUB_DEMUXER) += dvbsub.o rawdec.o OBJS-$(CONFIG_DVBTXT_DEMUXER) += dvbtxt.o rawdec.o OBJS-$(CONFIG_DXA_DEMUXER) += dxa.o +OBJS-$(CONFIG_DYNACOLOR_DEMUXER) += dynacolor.o OBJS-$(CONFIG_EA_CDATA_DEMUXER) += eacdata.o OBJS-$(CONFIG_EA_DEMUXER) += electronicarts.o OBJS-$(CONFIG_EAC3_DEMUXER) += ac3dec.o rawdec.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 39d2c352f5..50f3926b05 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -131,6 +131,7 @@ extern AVOutputFormat ff_dv_muxer; extern AVInputFormat ff_dvbsub_demuxer; extern AVInputFormat ff_dvbtxt_demuxer; extern AVInputFormat ff_dxa_demuxer; +extern AVInputFormat ff_dynacolor_demuxer; extern AVInputFormat ff_ea_demuxer; extern AVInputFormat ff_ea_cdata_demuxer; extern AVInputFormat ff_eac3_demuxer; diff --git a/libavformat/dynacolor.c b/libavformat/dynacolor.c new file mode 100644 index 0000000000..059323e9f3 --- /dev/null +++ b/libavformat/dynacolor.c @@ -0,0 +1,560 @@ +/* + * Dynacolor MVC Demuxer + * Copyright (c) 2020 Tom Needham + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <time.h> +#include "avformat.h" +#include "internal.h" +#include "dynacolor.h" +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/mathematics.h" +#include "libavutil/timecode.h" +#include "libavutil/avassert.h" + +//max total size=sizeof(tHeader)+sizeof(tSuperExtraIdx1)+PENTAMICRO_PES_HEADER_SIZE+frame size +inline unsigned char dyna_callback_checksum(tBasicIdx *header) +{ + int i; + unsigned char chksum = 0, *pHeader; + pHeader = (unsigned char *)header; + if (*pHeader == '@' && *(pHeader + 1) == '2') + for (i = 0; i < sizeof(tBasicIdx) - 1; ++i) + { + chksum ^= *(pHeader + i); + } + return chksum; +} + +inline unsigned char dyna_extra_checksum(tBasicIdx *header) +{ + int i; + unsigned char chksum = 0, *pHeader; + pHeader = (unsigned char *)header; + if (*pHeader == '@' && *(pHeader + 1) == '2') + for (i = 0; i < sizeof(tBasicIdx) - 1; ++i) + { + chksum ^= *(pHeader + i) & 0xFF; + } + return chksum; +} + +void dyna_make_drv_header(tHeader *pHdr, int serno, unsigned int size, int pic_type) +{ + // + // init pHdr + // + memset(pHdr, 0, sizeof(tHeader)); + + // + // set basic header + // + pHdr->Basic.Header1 = '@'; + pHdr->Basic.Header2 = '2'; + + // + // + // FIXME : + // Set QI as fixed value here + // - 0: default(720x240), 1: 360x240, 2: 720x480 + // + pHdr->Basic.QI = (pHdr->Basic.WidthBase == 0) ? 2 : 1; + // + // pic_type + // - get from raw data + // + pHdr->Basic.Type = pic_type; //frame type 1=I, 2=P,3=B + pHdr->Basic.Source = 1; //for IP dev + pHdr->Basic.Size = size; //drv header + frame + // + // Time info + // + pHdr->Basic.Time = time(NULL); + // + // NTSC_PAL + // - 7 bit : 0:NTSC/1:PAL + // + pHdr->Basic.NTSC_PAL = 1; //dyna_get_video_type(&pHdr->Basic); //0=NTSC,1=PAL + // + // Audio + // - 5 bit : 0:Video/1:Audio + // + pHdr->Basic.Audio = 0; //audio=1. video=0 + pHdr->Basic.ExtraEnable = 1; + // + // set extra header + // + pHdr->Extra.Serno = serno; + // + // TODO : event status + // + if (pHdr->Basic.Motion) + pHdr->Extra.Motion[0] |= 0x01; + if (pHdr->Basic.AlarmIn) + pHdr->Extra.AlmIn[0] |= 0x01; + + pHdr->Extra.SuperExtraSize = sizeof(tSuperExtraIdx); + pHdr->Basic.Chksum = dyna_callback_checksum(&pHdr->Basic); + pHdr->Extra.Chksum = dyna_extra_checksum(&pHdr->Basic); +} + +//Audio: +tHeader *dyna_get_audio_header(tHeader *frame, unsigned int chksum, unsigned int audioserno) +{ + //audio is ms adpcm. Each package size is 32(tHeader) +2048(ms adpcm). + tHeader *Header = frame; + Header->Basic.Header1 = '@'; + Header->Basic.Header2 = '2'; + Header->Basic.Size = 2048; // + Header->Basic.Time = time(NULL); + Header->Basic.Audio = 1; // 5 bit : 0:Video/1:Audio^M + Header->Basic.ExtraEnable = 1; + Header->Extra.Serno = audioserno++; + + for (unsigned int i = 0; i < sizeof(tBasicIdx) - 1; ++i) + chksum ^= (unsigned int)(frame + i); + + Header->Basic.Chksum = chksum; + return Header; +} + +int dyna_read_file_header(AVFormatContext *ctx, AVIOContext *pb, unsigned char *pesData, tPesHeader *pes, + unsigned int *size, time_t *time, tHeader *cdata_L, unsigned int *basicIdx_H, unsigned int *basicIdx_L) +{ + int ret = 0; + unsigned int stream_format; + + av_log(ctx, AV_LOG_DEBUG, "Parsing tBasicIdx Header \n"); + + *(basicIdx_H) = avio_rl32(pb); + + cdata_L->Basic.Header1 = *(basicIdx_H)&0xFF; + cdata_L->Basic.Header2 = *(basicIdx_H) >> 8 & 0xFF; + cdata_L->Basic.reserved = *(basicIdx_H) >> 16 & 0x0F; + cdata_L->Basic.Source = *(basicIdx_H) >> 20 & 0x01; + cdata_L->Basic.WidthBase = *(basicIdx_H) >> 21 & 0x03; + cdata_L->Basic.Reserved0 = *(basicIdx_H) >> 23 & 0x01; + cdata_L->Basic.Channel = *(basicIdx_H) >> 24 & 0x02; + cdata_L->Basic.StreamType = *(basicIdx_H) >> 26 & 0x02; + cdata_L->Basic.QI = *(basicIdx_H) >> 28 & 0x0F; + + *size = avio_rl32(pb); + cdata_L->Basic.Size = *size; + + *time = (time_t)avio_rl32(pb); + cdata_L->Basic.Time = ((unsigned int) *time); + + *(basicIdx_L) = avio_rl32(pb); + + cdata_L->Basic.NTSC_PAL = *(basicIdx_L)&0x01; + cdata_L->Basic.ImgMode = *(basicIdx_L) >> 1 & 0x01; + cdata_L->Basic.Audio = *(basicIdx_L) >> 2 & 0x01; + cdata_L->Basic.DST = *(basicIdx_L) >> 3 & 0x01; + cdata_L->Basic.Covert = *(basicIdx_L) >> 4 & 0x01; + cdata_L->Basic.Vloss = *(basicIdx_L) >> 5 & 0x01; + cdata_L->Basic.AlarmIn = *(basicIdx_L) >> 6 & 0x01; + cdata_L->Basic.Motion = *(basicIdx_L) >> 7 & 0x01; + cdata_L->Basic.ExtraEnable = *(basicIdx_L) >> 8 & 0x01; + cdata_L->Basic.EventEnable = *(basicIdx_L) >> 9 & 0x01; + cdata_L->Basic.PPS = *(basicIdx_L) >> 10 & 0x40; + cdata_L->Basic.Type = *(basicIdx_L) >> 16 & 0x08; + cdata_L->Basic.Channel = *(basicIdx_L) >> 19 & 0x20; + cdata_L->Basic.Chksum = *(basicIdx_L) >> 24 & 0xFF; + + av_log(ctx, AV_LOG_DEBUG, "tBasicIdx Header: %d \n", *(basicIdx_H) | *(basicIdx_L)); + + if (cdata_L->Basic.ExtraEnable) + { + // File has tExtraIdx header so parse it + unsigned int start; + unsigned char end; + char checksum; + + av_log(ctx, AV_LOG_DEBUG, "Parsing tExtraIdx Header \n"); + + start = avio_rl32(pb); + cdata_L->Extra.IsDST_S_W = start & 0x01; + cdata_L->Extra.DST_Minutes = start >> 1 & 0xFF; + cdata_L->Extra.OverSpeed = start >> 9 & 0x01; + cdata_L->Extra.SkipPicCnt = start >> 10 & 0x20; + cdata_L->Extra.Exception = start >> 15 & 0x01; + cdata_L->Extra.SuperExtraSize = start >> 16 & 0xFFFF; + + cdata_L->Extra.Serno = avio_rl32(pb); + cdata_L->Extra.AlmIn[0] = (unsigned char)avio_r8(pb); + cdata_L->Extra.AlmIn[1] = (unsigned char)avio_r8(pb); + cdata_L->Extra.Vloss[0] = (unsigned char)avio_r8(pb); + cdata_L->Extra.Vloss[1] = (unsigned char)avio_r8(pb); + cdata_L->Extra.Motion[0] = (unsigned char)avio_r8(pb); + cdata_L->Extra.Motion[1] = (unsigned char)avio_r8(pb); + + end = (unsigned char)avio_r8(pb); + cdata_L->Extra.IsDVRRec = end & 0x03; + cdata_L->Extra.TimeZone = end >> 2 & 0x40; + + checksum = (char)avio_r8(pb); + + av_log(ctx, AV_LOG_DEBUG, "tExtraIdx SuperExtraSize %i \n", cdata_L->Extra.SuperExtraSize); + av_log(ctx, AV_LOG_DEBUG, "tExtraIdx First 4 %d \n", start); + av_log(ctx, AV_LOG_DEBUG, "tExtraIdx Alarm In %i \n", (short)(cdata_L->Extra.AlmIn[1] << 8 | cdata_L->Extra.AlmIn[0])); + av_log(ctx, AV_LOG_DEBUG, "tExtraIdx Video Loss %i \n", (short)(cdata_L->Extra.Vloss[1] << 8 | cdata_L->Extra.Vloss[0])); + av_log(ctx, AV_LOG_DEBUG, "tExtraIdx Motion %i \n", (short)(cdata_L->Extra.Motion[1] << 8 | cdata_L->Extra.Motion[0])); + av_log(ctx, AV_LOG_DEBUG, "tExtraIdx End %d \n", end); + av_log(ctx, AV_LOG_DEBUG, "tExtraIdx Checksum %d \n", checksum); + + av_log(ctx, AV_LOG_DEBUG, "Parsing tSuperExtraIdx Header \n"); + + cdata_L->SuperExtra.type = (int)avio_rl32(pb); + cdata_L->SuperExtra.remain_size = (int)avio_rl32(pb); + + av_log(ctx, AV_LOG_DEBUG, "tSuperExtraIdx type: %d \n", cdata_L->SuperExtra.type); + av_log(ctx, AV_LOG_DEBUG, "tSuperExtraIdx remain_size: %i \n", cdata_L->SuperExtra.remain_size); + + if (avio_read(pb, cdata_L->SuperExtra.title, PENTAMICRO_SUPEREXTRA_TITLE_SIZE) != PENTAMICRO_SUPEREXTRA_TITLE_SIZE) + return AVERROR_INVALIDDATA; + + if (avio_read(pb, cdata_L->SuperExtra.extra_data, PENTAMICRO_SUPEREXTRA_EXTRA_DATA_SIZE) != PENTAMICRO_SUPEREXTRA_EXTRA_DATA_SIZE) + return AVERROR_INVALIDDATA; + + av_log(ctx, AV_LOG_DEBUG, "tSuperExtraIdx title: %s \n", cdata_L->SuperExtra.title); + av_log(ctx, AV_LOG_DEBUG, "tSuperExtraIdx extra_data: %s \n", cdata_L->SuperExtra.extra_data); + + av_log(ctx, AV_LOG_DEBUG, "Successfully Parsed tSuperExtraIdx Header \n"); + } + else + { + // File has no tExtraIdx header so skip 16 bytes + av_log(ctx, AV_LOG_DEBUG, "Skipping tExtraIdx and tSuperExtraIdx Header \n"); + avio_skip(pb, PENTAMICRO_EXTRA_SIZE + PENTAMICRO_SUPEREXTRA_SIZE); + + memset(&cdata_L->Extra, 0xFF, sizeof(tExtraIdx)); + } + + av_log(ctx, AV_LOG_DEBUG, "Size %d \n", *size); + av_log(ctx, AV_LOG_DEBUG, "Time %ld \n", *time); + + if (avio_read(pb, pesData, PENTAMICRO_PES_HEADER_SIZE) != PENTAMICRO_PES_HEADER_SIZE) + return AVERROR_INVALIDDATA; + + // Try And Build PES Header + av_log(ctx, AV_LOG_DEBUG, "Building PES Header \n"); + ret = dyna_make_pes_packet_header(ctx, pesData, cdata_L->Basic.Type, *size, 0x01, 0x00); + + if (ret) + { + av_log(ctx, AV_LOG_DEBUG, "Failed to Build PES Header \n"); + return AVERROR_INVALIDDATA; + } + else + { + av_log(ctx, AV_LOG_DEBUG, "Successfully Built PES Header Determining Stream Format \n"); + + stream_format = dyna_get_stream_format(ctx, cdata_L, size); + if (!stream_format) + { + av_log(ctx, AV_LOG_WARNING, "File Has Unkown Stream Format: 0x%02X", stream_format); + return AVERROR_PATCHWELCOME; + } + else if (stream_format == AVERROR_INVALIDDATA) + { + av_log(ctx, AV_LOG_WARNING, "Could not Determine Stream Format \n"); + return AVERROR_INVALIDDATA; + } + + av_log(ctx, AV_LOG_DEBUG, "File Has 0x%02X Stream Format \n", stream_format); + } + + return ret; +} + +int dyna_get_stream_format(AVFormatContext *ctx, tHeader *cdata_L, unsigned int *size) +{ + int ret = 0; + + // Try And Build Header for H264 Format + av_log(ctx, AV_LOG_DEBUG, "Validating H264 PES Header \n"); + ret = dyna_check_pes_packet_header(ctx, cdata_L->Basic.Type, *size, 0x01, 0x00, PENTAMICRO_H264_FORMAT_PREFIX); + + if (!ret) + { + // Format was not H264 so try And Build Header for MPEG4 Format + av_log(ctx, AV_LOG_DEBUG, "Validating MPEG4 PES Header \n"); + ret = dyna_check_pes_packet_header(ctx, cdata_L->Basic.Type, *size, 0x01, 0x00, PENTAMICRO_MPEG4_FORMAT_PREFIX); + + if (!ret) + { + // Format was not MPEG4 or H264 so try And Build Header for JPEG Format + av_log(ctx, AV_LOG_DEBUG, "Validating JPEG PES Header \n"); + ret = dyna_check_pes_packet_header(ctx, cdata_L->Basic.Type, *size, 0x01, 0x00, PENTAMICRO_JPEG_FORMAT_PREFIX); + + if (!ret) + { + // Format was not MPEG4 or H264 or JPEG so try And Build Header for AUDIO Format + av_log(ctx, AV_LOG_DEBUG, "Validating AUDIO PES Header \n"); + ret = dyna_check_pes_packet_header(ctx, cdata_L->Basic.Type, *size, 0x01, 0x00, PENTAMICRO_AUDIO_FORMAT_PREFIX); + + if (!ret) + { + return AVERROR_INVALIDDATA; + } + else + { + av_log(ctx, AV_LOG_DEBUG, "Successfully Validated AUDIO PES Header \n"); + return PENTAMICRO_JPEG_FORMAT_PREFIX; + } + } + else + { + av_log(ctx, AV_LOG_DEBUG, "Successfully Validated JPEG PES Header \n"); + return PENTAMICRO_JPEG_FORMAT_PREFIX; + } + } + else + { + av_log(ctx, AV_LOG_DEBUG, "Successfully Validated MPEG4 PES Header \n"); + return PENTAMICRO_MPEG4_FORMAT_PREFIX; + } + } + else + { + av_log(ctx, AV_LOG_DEBUG, "Successfully Validated H264 PES Header \n"); + return PENTAMICRO_H264_FORMAT_PREFIX; + } + + return 0; +} + +int dyna_check_pes_packet_header(AVFormatContext *ctx, int pic_type, + unsigned int size, long long dts, int stream_id, int format_prefix) +{ + + DynacolorContext *dyna = ctx->priv_data; + tPesHeader *pes = &dyna->pes_header; + + // the size should contain this 32-byte PES header. + size += PENTAMICRO_PES_HEADER_SIZE; + + return pes->format_id == format_prefix; +} + +int dyna_make_pes_packet_header(AVFormatContext *ctx, unsigned char *pes, int pic_type, + unsigned int size, long long dts, int stream_id) +{ + DynacolorContext *dyna = ctx->priv_data; + tPesHeader *hdr = &dyna->pes_header; + + // + // the size should contain this 32-byte PES header. + // + size += PENTAMICRO_PES_HEADER_SIZE; + + hdr->start_code0 = pes[0]; + hdr->start_code1 = pes[1]; + hdr->start_code2 = pes[2]; + hdr->format_id = pes[3] & 0x0F; + hdr->channel_id = pes[3] >> 8 & 0x0F; + + hdr->unused_0 = (unsigned int)(pes[4] << 24 | pes[5] << 16 | pes[6] << 8 | pes[7]); + hdr->unused_1 = (unsigned int)(pes[8] << 24 | pes[9] << 16 | pes[10] << 8 | pes[11]); + hdr->unused_2 = (unsigned int)(pes[12] << 24 | pes[13] << 16 | pes[14] << 8 | pes[15]); + hdr->unused_3 = (unsigned int)(pes[16] << 24 | pes[17] << 16 | pes[18] << 8 | pes[19]); + hdr->unused_4 = (unsigned int)(pes[20] << 24 | pes[21] << 16 | pes[22] << 8 | pes[23]); + + hdr->size_bit7to0 = pes[24]; + hdr->size_bit10to8 = pes[25] & 0x08; + hdr->size_bit14to11 = pes[26] & 0xF; + hdr->size_bit21to15 = (unsigned int)pes[27] & 0x80; // Cast To Suppress Overflow Warning + hdr->size_marker0 = pes[28] & 0x01; + hdr->size_marker1 = pes[29] & 0x01; + hdr->picture_type = pes[30]; + hdr->is_interleaved = pes[31] & 0x01; + hdr->field_id = pes[31] >> 1 & 0x03; + + return 0; + +} // end dyna_make_pes_packet_header() + + +static int dyna_read_probe(const AVProbeData *p) +{ + unsigned char* bytes = av_malloc(p->buf_size); + + for (int i = 0; i < p->buf_size; i++) { + bytes[i] = p->buf[i]; + } + + if(bytes[0] == 0x40 && bytes[1] == 0x32 && bytes[2] == 0x10 && bytes[3] == 0x20) + return AVPROBE_SCORE_MAX; + + return 0; +} + +static int dyna_read_header(AVFormatContext *ctx) +{ + int ret = 0; + DynacolorContext *priv = ctx->priv_data; + AVIOContext *pb = ctx->pb; + AVIOContext *start = NULL; + + AVStream *vstream = NULL; + AVCodec *vcodec = NULL; + AVCodecParameters *vcodec_params = NULL; + + AVStream *astream = NULL; + AVCodec *acodec = NULL; + AVCodecParameters *acodec_params = NULL; + + time_t time; + unsigned char *pesData; + tPesHeader *pes; + unsigned int size; + + unsigned int basicIdx_H, basicIdx_L; + + start = (AVIOContext *)av_malloc(sizeof(AVIOContext)); + memcpy(start, pb, sizeof(AVIOContext)); + + pesData = (unsigned char *)av_malloc_array(PENTAMICRO_PES_HEADER_SIZE, 1); + pes = (tPesHeader *)av_malloc(sizeof(tPesHeader)); + + ret = dyna_read_file_header(ctx, pb, pesData, pes, &size, &time, + &priv->header, &basicIdx_H, &basicIdx_L); + + if(ret) + return AVERROR_INVALIDDATA; + + memset(&priv->header, 0xFF, sizeof(tHeader)); + + av_log(ctx, AV_LOG_DEBUG, "Dynacolor Header Successfully Parsed Detecting Streams... \n"); + + if (priv->pes_header.format_id == PENTAMICRO_JPEG_FORMAT_PREFIX) + { + av_log(ctx, AV_LOG_DEBUG, "Demuxing JPEG Video Stream \n"); + + vcodec = avcodec_find_decoder(AV_CODEC_ID_MJPEG); + vstream = avformat_new_stream(ctx, vcodec); + vcodec_params = vstream->codecpar; + + if (!vstream || !vcodec_params) + return AVERROR_INVALIDDATA; + } + else if (priv->pes_header.format_id == PENTAMICRO_MPEG4_FORMAT_PREFIX) + { + av_log(ctx, AV_LOG_DEBUG, "Demuxing MPEG4 Video Stream \n"); + + vcodec = avcodec_find_decoder(AV_CODEC_ID_MPEG4); + vstream = avformat_new_stream(ctx, vcodec); + vcodec_params = vstream->codecpar; + + if (!vstream || !vcodec_params) + return AVERROR_INVALIDDATA; + } + else if (priv->pes_header.format_id == PENTAMICRO_H264_FORMAT_PREFIX) + { + av_log(ctx, AV_LOG_DEBUG, "Demuxing H264 Video Stream \n"); + + vcodec = avcodec_find_decoder(AV_CODEC_ID_H264); + vstream = avformat_new_stream(ctx, vcodec); + vcodec_params = vstream->codecpar; + + if (!vstream || !vcodec_params) + return AVERROR_INVALIDDATA; + } + else if (priv->pes_header.format_id == PENTAMICRO_AUDIO_FORMAT_PREFIX) + { + av_log(ctx, AV_LOG_DEBUG, "Demuxing Audio Stream \n"); + + acodec = avcodec_find_decoder(AV_CODEC_ID_PCM_F16LE); + astream = avformat_new_stream(ctx, acodec); + acodec_params = astream->codecpar; + + if (!astream || !acodec_params) + return AVERROR_INVALIDDATA; + } + + return 0; +} + +static int dyna_read_packet(AVFormatContext *ctx, AVPacket *pkt) +{ + int ret = 0; + DynacolorContext *priv = ctx->priv_data; + AVIOContext *pb = ctx->pb; + uint8_t* pkt_data; + unsigned int size; + + avio_read(pb, (uint8_t*)&priv->pes_header, PENTAMICRO_PES_HEADER_SIZE); + + size = (priv->pes_header.size_bit7to0 & 0xFF) + | ((priv->pes_header.size_bit10to8 & 0x04)) << 15 + | ((priv->pes_header.size_bit14to11 & 0x08) << 11) + | ((priv->pes_header.size_bit21to15 & 0x7F) << 8); + + av_log(ctx, AV_LOG_DEBUG, "Size = %i \n", size); + pkt_data = (uint8_t*) av_malloc(size + 1); + + ret = avio_read(pb, pkt_data, size); + av_log(ctx, AV_LOG_DEBUG, "first ret = %i \n", ret); + + if(ret != size) + return ret; + + ret = av_packet_from_data(pkt, pkt_data, size); + av_log(ctx, AV_LOG_DEBUG, "second ret = %i \n", ret); + + if(ret < 0) + return ret; + + return ret; +} + +static int dyna_read_close(AVFormatContext *ctx) +{ + return 0; +} + +static int dyna_read_seek(AVFormatContext *ctx, int stream_index, + int64_t timestamp, int flags) +{ + DynacolorContext *priv = ctx->priv_data; + unsigned int size = 0; + + size = (priv->pes_header.size_bit7to0 & 0xFF) + | ((priv->pes_header.size_bit10to8 & 0x04)) << 15 + | ((priv->pes_header.size_bit14to11 & 0x08) << 11) + | ((priv->pes_header.size_bit21to15 & 0x7F) << 8); + + if (avio_seek(ctx->pb, size, SEEK_SET) < 0) + return -1; + + return 0; +} + +AVInputFormat ff_dynacolor_demuxer = { + .name = "dynacolor", + .long_name = NULL_IF_CONFIG_SMALL("Dynacolor MVC"), + .priv_data_size = sizeof(DynacolorContext), + .read_probe = dyna_read_probe, + .read_header = dyna_read_header, + .read_packet = dyna_read_packet, + .read_close = dyna_read_close, + .read_seek = dyna_read_seek, + .extensions = "dyna,dyn", +}; diff --git a/libavformat/dynacolor.h b/libavformat/dynacolor.h new file mode 100644 index 0000000000..de28a51bc9 --- /dev/null +++ b/libavformat/dynacolor.h @@ -0,0 +1,308 @@ +/* + * Dynacolor MVC Demuxer + * Copyright (c) 2020 Tom Needham + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_DYNACOLOR_H +#define AVFORMAT_DYNACOLOR_H + +#include "avformat.h" + +// +// The data structure of the streaming video: +// [16-byte tBasicIdx header] + [16-byte tExtraIdx header] + ['SuperExtraSize' bytes] + [32-byte PES header] + [video body] +// The 'SuperExtraSize' is defined in the 'tExtraIdx' structure to store the additional information, and usually equals zero. + +#define PENTAMICRO_PES_HEADER_SIZE 32 +#define PENTAMICRO_EXTRA_SIZE 16 +#define PENTAMICRO_SUPEREXTRA_SIZE 108 +#define PENTAMICRO_SUPEREXTRA_TITLE_SIZE 12 +#define PENTAMICRO_SUPEREXTRA_EXTRA_DATA_SIZE 96 + +#define PENTAMICRO_AUDIO_FORMAT_PREFIX 0x0C +#define PENTAMICRO_MPEG4_FORMAT_PREFIX 0x0D +#define PENTAMICRO_JPEG_FORMAT_PREFIX 0x0E +#define PENTAMICRO_H264_FORMAT_PREFIX 0x0F + +// +// Big Endian +// DG600 uses big-endian CPU +// +// Basic Idx: 16 bytes +// +typedef struct +{ + unsigned int Header1 : 8; // [2] "@X" -- X:device type, DG600 is "6" @6 + unsigned int Header2 : 8; // [2] "@X" -- X:device type, DG600 is "6" @6 + unsigned int reserved : 4; // 4 bits + unsigned int Source : 1; // 1 bit : (0 - DVR, 1 - IP Dev) + unsigned int WidthBase : 2; // 0:720 , 1:704, 2:640 + unsigned int Reserved0 : 1; // 1 bit : (0 - Disable(backward compatibile), 1 - Enable) + // + // this is basically for VSS use since VSS needs to support 96 channels, and + // there is originally 5 bits only for channel number in the header. + // With these two extra bits, the channel number becomes ((Channel_Ext<<5)|Channel). + // + unsigned int Channel_Ext : 2; + unsigned int StreamType : 2; // 0-NormalStream, 1-VStream, 2-DualStream + unsigned int QI : 4; // 4 bits : QI (0~16) + /////////////////end//////////////////////////////////////////////////////// + unsigned int Size; // [4] + unsigned int Time; // [4] + //total 4 byte//start/////////////////////////////////////////////////////// + unsigned int NTSC_PAL : 1; // 1 bit : 0:NTSC/1:PAL + unsigned int ImgMode : 1; // 1 bit : 0:Live/1:Playback + unsigned int Audio : 1; // 1 bit : 0:Video/1:Audio + unsigned int DST : 1; // 1 bit : DST + unsigned int Covert : 1; // 1 bit : Covert + unsigned int Vloss : 1; // 1 bit : Video loss + unsigned int AlarmIn : 1; // 1 bit : Alarm In + unsigned int Motion : 1; // 1 bit : Motion + + unsigned int ExtraEnable : 1; // 1 bit : 0: no extra 1: have extra + unsigned int EventEnable : 1; // 1 bit : 0: Normal status 1: Event duration + unsigned int PPS : 6; // 6 bits : Per-channel PPS Idx (0~127 level) + + unsigned int Type : 3; // 3 bits : type (0~7) 0: none 1: I-pic, 2: P-Pic, 3: B-Pic + unsigned int Channel : 5; // 5 bits : Channel No (0~31) + unsigned int Chksum : 8; // [1] + +} tBasicIdx; +// +// Extra Idx: 16 bytes +// +typedef struct +{ + unsigned int DST_Minutes : 8; // 0~7 = 0 ~ 120 minutes + unsigned int IsDST_S_W : 1; // 0 = summer time , 1 =winter time + unsigned int OverSpeed : 1; + unsigned int SkipPicCnt : 5; + // + // use one bit to represent exception alarm + // - can't get exactually exception no here + // + unsigned int Exception : 1; + unsigned int SuperExtraSize : 16; + //total 4 byte//end/////////////////////////////////////////////////////// + + unsigned int Serno; // [4] + // + // Original tEventInfo16CH was broken 'cause of the byte alignment problem + // - use the following directly + // + // ===== Start ===== + // + // the first bit can descript first channel's evnet status, so that it can totally script 16 channels' status. + // if IP camera, the first bit also descripts the first IP cmaera's status. + char AlmIn[2]; // 16 channel, if Source of tBasicIdx = 0, it means analog camera's status. If Source = 1, it means IP camera's. + char Vloss[2]; // 16 channel, if Source of tBasicIdx = 0, it means analog camera's status. If Source = 1, it means IP camera's. + char Motion[2]; // 16 channel, if Source of tBasicIdx = 0, it means analog camera's status. If Source = 1, it means IP camera's. + + // + // ===== End ===== + // + unsigned char IsDVRRec : 2; + // + // For TimeZone setting + // - 0 means OFF (backward compatibility) + // - Delta : 30 min + // - Start from : -12:00 + // - Aka. + // 1 : -12:00 (-720 min) + // 2 : -11:30 (-690 min) + // 3 : -11:00 (-660 min) + // .... + // 25 : +00:00 (+000 min) + // .... + // 41 : +08:00 (+480 min) + // 42 : +08:30 (+510 min) + // 43 : +09:00 (+540 min) + // .... + // 51 : +13:00 (+780 min) + // + unsigned char TimeZone : 6; + char Chksum; + +} tExtraIdx; + +// +// Super Extra Index : +// type = 0 +// remain_size = 108 +// structure to store cam title & extra data(GPS/Text) +// +typedef struct +{ + // + // the type for tSuperExtraIdx is fixed to 0 + // + int type; + // + // the remain_size for tSuperExtraIdx is fixed to : 12 + 96 = 108 + // + int remain_size; + // + // CAM_TIT_LEN + // + char title[12]; + // + // merge original account0 ~ account3 + // - GPS & Text support will use the same place + // - REC_INDEX_EXTRA_TYPE, REC_INDEX_EXTRA_CH, REC_INDEX_EXTRA_LENGTH are + // used for some specific info + // - refer to list.h for detail + // + char extra_data[96]; + +} tSuperExtraIdx; + +typedef struct +{ + tBasicIdx Basic; + tExtraIdx Extra; + tSuperExtraIdx SuperExtra; +} tHeader; + +//According to tBasicIdx's Source and Audio, you can understand when to use tIPCamAudioExtraIdx. +// Source = 1 and Audio = 1 means the audio data is from IP camera. +// And Channel to know the audio data is from which one IP camera. + +typedef struct +{ + unsigned int DST_Minutes : 8; // 0~7 = 0 ~ 120 minutes + unsigned int IsDST_S_W : 1; // 0 = summer time , 1 =winter time + unsigned int OverSpeed : 1; + unsigned int SkipPicCnt : 5; + // + // use one bit to represent exception alarm + // - can't get exactually exception no here + // + unsigned int Exception : 1; + unsigned int SuperExtraSize : 16; + //total 4 byte//end/////////////////////////////////////////////////////// + + unsigned int Serno; // [4] + // + // Original tEventInfo16CH was broken 'cause of the byte alignment problem + // - use the following directly + // + // ===== Start ===== + // + // the first bit can descript first channel's evnet status, so that it can totally script 16 channels' status. + // if IP camera, the first bit also descripts the first IP cmaera's status. + unsigned short FormatTag; // audio format tag followed MS's definition + unsigned short BitsPerSample; // bits per sample (eg. 8, 16, or 24) + unsigned short SamplesPerSec; // samples per second (eg. 8000, 16000, or 44100) + // + // ===== End ===== + // + unsigned char IsDVRRec : 2; + // + // For TimeZone setting + // - 0 means OFF (backward compatibility) + // - Delta : 30 min + // - Start from : -12:00 + // - Aka. + // 1 : -12:00 (-720 min) + // 2 : -11:30 (-690 min) + // 3 : -11:00 (-660 min) + // .... + // 25 : +00:00 (+000 min) + // .... + // 41 : +08:00 (+480 min) + // 42 : +08:30 (+510 min) + // 43 : +09:00 (+540 min) + // .... + // 51 : +13:00 (+780 min) + // + unsigned char TimeZone : 6; + char Chksum; + +} tIPCamAudioExtraIdx; + +typedef struct +{ + // 4 bytes + unsigned int start_code0 : 8; // must be 0x00 + unsigned int start_code1 : 8; // must be 0x00 + unsigned int start_code2 : 8; // must be 0x01 + unsigned int format_id : 4; // JPEG:0xd, MPEG4:0xe, H.264:0xf, Audio:0xc + unsigned int channel_id : 4; // channel id from 0 to 15 for CH1 to CH16 + // 4 bytes + unsigned int unused_0; + // 4 bytes + unsigned int unused_1; + // 4 bytes + unsigned int unused_2; + // 4 bytes + unsigned int unused_3; + // 4 bytes + unsigned int unused_4; + // + // size of the video body and this PES header (total 3 bytes including the markers) + // + unsigned int size_bit7to0 : 8; // from bit7 to bit0 + unsigned int size_bit10to8 : 3; // from bit10 to bit8 + unsigned int size_bit14to11 : 4; // from bit14 to bit11 + unsigned int size_bit21to15 : 7; // from bit21 to bit15 + unsigned int size_marker0 : 1; // must be 0x1 + unsigned int size_marker1 : 1; // must be 0x1 + // 1 byte for the picture type + unsigned int picture_type : 8; // 1: I-slice, 2: P-slice, 3: B-slice + + unsigned int is_interleaved : 1; // 0: even/odd fields are separated horizontally + // 1: even/odd fields are interleaved + unsigned int field_id : 2; // 0: odd field, 1: even field, 2/3: undefined + unsigned int unused_5 : 29; + +} tPesHeader; + +typedef struct { + tHeader header; + tPesHeader pes_header; +} DynacolorContext; + +// Function Defs + +//stream_id ane dts can be set to 0. +//size is the frame size. +//pic_type=pHdr->Basic.Type + +int dyna_check_pes_packet_header(AVFormatContext *ctx, int pic_type, + unsigned int size, long long dts, int stream_id, int format_prefix); + +int dyna_make_pes_packet_header(AVFormatContext *ctx, unsigned char *pes, int pic_type, + unsigned int size, long long dts, int stream_id); + +void dyna_make_drv_header(tHeader *pHdr, int serno, unsigned int size, int pic_type); + +tHeader *dyna_get_audio_header(tHeader *frame, unsigned int chksum, unsigned int audioserno); + +static int dyna_read_probe(const AVProbeData *p); +static int dyna_read_header(AVFormatContext *ctx); +static int dyna_read_packet(AVFormatContext *ctx, AVPacket *pkt); +static int dyna_read_close(AVFormatContext *ctx); +static int dyna_read_seek(AVFormatContext *ctx, int stream_index, + int64_t timestamp, int flags); + +int dyna_read_file_header(AVFormatContext *ctx, AVIOContext *pb, unsigned char *pesData, tPesHeader *pes, unsigned int *size, + time_t *time, tHeader *cdata_B, unsigned int *basicIdx_H, unsigned int *basicIdx_L); +int dyna_get_stream_format(AVFormatContext *ctx, tHeader *cdata_L, unsigned int *size); + +#endif diff --git a/libavformat/version.h b/libavformat/version.h index 18c2f5fec2..493a0b337f 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -32,7 +32,7 @@ // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) // Also please add any ticket numbers that you believe might be affected here #define LIBAVFORMAT_VERSION_MAJOR 58 -#define LIBAVFORMAT_VERSION_MINOR 42 +#define LIBAVFORMAT_VERSION_MINOR 43 #define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ -- 2.21.0.windows.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".