[FFmpeg-devel] [PATCH V2] avformat: Add Dynacolor MVC Demuxer
Tom Needham
06needhamt at gmail.com
Sat Mar 28 15:32:42 EET 2020
Hi Michael
I have fixed the build errors you mentioned by not inlining those functions
so they do not get relocated. Updated patch below.
>From 4c17850cb2546487d789b86f511a30bdef40f817 Mon Sep 17 00:00:00 2001
From: Tom Needham <06needhamt at gmail.com>
Date: Sun, 21 Jul 2019 21:11:19 +0100
Subject: [PATCH] avformat: Add Dynacolor MVC Demuxer
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 <06needhamt at gmail.com>
---
Changelog | 1 +
doc/general.texi | 1 +
libavformat/Makefile | 1 +
libavformat/allformats.c | 1 +
libavformat/dynacolor.c | 497 +++++++++++++++++++++++++++++++++++++++
libavformat/dynacolor.h | 276 ++++++++++++++++++++++
libavformat/version.h | 2 +-
7 files changed, 778 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.
+ at 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..cccbbf82f2
--- /dev/null
+++ b/libavformat/dynacolor.c
@@ -0,0 +1,497 @@
+/*
+ * 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
+unsigned char ff_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;
+}
+
+unsigned char ff_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 ff_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';
+ pHdr->Basic.QI = (pHdr->Basic.WidthBase) ? 1 : 2;
+ 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
+ pHdr->Basic.Time = time(NULL);
+ pHdr->Basic.NTSC_PAL = 1; //dyna_get_video_type(&pHdr->Basic);
//0=NTSC,1=PAL
+ pHdr->Basic.Audio = 0; //audio=1. video=0
+ pHdr->Basic.ExtraEnable = 1;
+ pHdr->Basic.Chksum = ff_dyna_callback_checksum(&pHdr->Basic);
+
+ // Set extra header
+ pHdr->Extra.Serno = serno;
+ if (pHdr->Basic.Motion)
+ pHdr->Extra.Motion[0] |= 0x01;
+ if (pHdr->Basic.AlarmIn)
+ pHdr->Extra.AlmIn[0] |= 0x01;
+
+ pHdr->Extra.SuperExtraSize = sizeof(tSuperExtraIdx);
+ pHdr->Extra.Chksum = ff_dyna_extra_checksum(&pHdr->Basic);
+}
+
+tHeader *ff_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
+ 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 ff_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 = ff_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 = ff_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 ff_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 = ff_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 = ff_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 = ff_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 = ff_dyna_check_pes_packet_header(ctx,
cdata_L->Basic.Type, *size, 0x01, 0x00, PENTAMICRO_AUDIO_FORMAT_PREFIX);
+
+ if (!ret) {
+ return AVERROR_PATCHWELCOME;
+ } 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 ff_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 ff_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;
+}
+
+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 = av_malloc(sizeof(AVIOContext));
+ memcpy(start, pb, sizeof(AVIOContext));
+
+ pesData = av_malloc_array(PENTAMICRO_PES_HEADER_SIZE, 1);
+ pes = av_malloc(sizeof(tPesHeader));
+
+ ret = ff_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 = 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..1c3837e434
--- /dev/null
+++ b/libavformat/dynacolor.h
@@ -0,0 +1,276 @@
+/*
+ * 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)
+
+ unsigned int Size; // [4]
+ unsigned int Time; // [4]
+
+ 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;
+
+ unsigned int Serno; // [4]
+
+ // Original tEventInfo16CH was broken 'cause of the byte alignment
problem
+ // - use the following directly
+ // 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.
+
+ 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;
+
+
+ unsigned int Serno;
+ // Original tEventInfo16CH was broken 'cause of the byte alignment
problem
+ // use the following directly
+ // 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)
+
+ 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
+
+ unsigned int unused_0;
+ unsigned int unused_1;
+ unsigned int unused_2;
+ unsigned int unused_3;
+ 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
+
+unsigned char ff_dyna_callback_checksum(tBasicIdx *header);
+unsigned char ff_dyna_extra_checksum(tBasicIdx *header);
+
+int ff_dyna_check_pes_packet_header(AVFormatContext *ctx, int pic_type,
+ unsigned int size, long long dts, int
stream_id, int format_prefix);
+
+int ff_dyna_make_pes_packet_header(AVFormatContext *ctx, unsigned char
*pes, int pic_type,
+ unsigned int size, long long dts, int
stream_id);
+
+void ff_dyna_make_drv_header(tHeader *pHdr, int serno, unsigned int size,
int pic_type);
+
+tHeader *ff_dyna_get_audio_header(tHeader *frame, unsigned int chksum,
unsigned int audioserno);
+
+int ff_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 ff_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
On Fri, 27 Mar 2020 at 17:53, Michael Niedermayer <michael at niedermayer.cc>
wrote:
> On Thu, Mar 26, 2020 at 10:28:32AM +0000, Tom Needham wrote:
> > Thanks for your feedback. Please see the attached patch with all the
> style
> > changes addressed which also applies properly.
>
> > Changelog | 1
> > doc/general.texi | 1
> > libavformat/Makefile | 1
> > libavformat/allformats.c | 1
> > libavformat/dynacolor.c | 497
> +++++++++++++++++++++++++++++++++++++++++++++++
> > libavformat/dynacolor.h | 273 +++++++++++++++++++++++++
> > libavformat/version.h | 2
> > 7 files changed, 775 insertions(+), 1 deletion(-)
> > 373e3c7f910ae4d2d005ca0f764afa8f41c0adf4
> 0001-avformat-Add-Dynacolor-MVC-Demuxer.patch
> > From 8f741899b7f6b3884d613f622a548b353af2489e Mon Sep 17 00:00:00 2001
> > From: Tom Needham <06needhamt at gmail.com>
> > Date: Sun, 21 Jul 2019 21:11:19 +0100
> > Subject: [PATCH] avformat: Add Dynacolor MVC Demuxer
> >
> > 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 <06needhamt at gmail.com>
> > ---
> > Changelog | 1 +
> > doc/general.texi | 1 +
> > libavformat/Makefile | 1 +
> > libavformat/allformats.c | 1 +
> > libavformat/dynacolor.c | 497 +++++++++++++++++++++++++++++++++++++++
> > libavformat/dynacolor.h | 273 +++++++++++++++++++++
> > libavformat/version.h | 2 +-
> > 7 files changed, 775 insertions(+), 1 deletion(-)
> > create mode 100644 libavformat/dynacolor.c
> > create mode 100644 libavformat/dynacolor.h
>
> fails to build on mingw64 with --disable-debug --disable-stripping
> --enable-small
>
> libavformat/libavformat.a(dynacolor.o):dynacolor.c:(.text+0x200):
> undefined reference to `ff_dyna_callback_checksum'
> libavformat/libavformat.a(dynacolor.o):dynacolor.c:(.text+0x200):
> relocation truncated to fit: R_X86_64_PC32 against undefined symbol
> `ff_dyna_callback_checksum'
> libavformat/libavformat.a(dynacolor.o):dynacolor.c:(.text+0x227):
> undefined reference to `ff_dyna_extra_checksum'
> libavformat/libavformat.a(dynacolor.o):dynacolor.c:(.text+0x227):
> relocation truncated to fit: R_X86_64_PC32 against undefined symbol
> `ff_dyna_extra_checksum'
> libavformat/libavformat.a(dynacolor.o):dynacolor.c:(.text+0x200):
> undefined reference to `ff_dyna_callback_checksum'
> libavformat/libavformat.a(dynacolor.o):dynacolor.c:(.text+0x200):
> relocation truncated to fit: R_X86_64_PC32 against undefined symbol
> `ff_dyna_callback_checksum'
> libavformat/libavformat.a(dynacolor.o):dynacolor.c:(.text+0x227):
> undefined reference to `ff_dyna_extra_checksum'
> libavformat/libavformat.a(dynacolor.o):dynacolor.c:(.text+0x227):
> relocation truncated to fit: R_X86_64_PC32 against undefined symbol
> `ff_dyna_extra_checksum'
>
>
> [...]
> --
> Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> Complexity theory is the science of finding the exact solution to an
> approximation. Benchmarking OTOH is finding an approximation of the exact
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
More information about the ffmpeg-devel
mailing list