[FFmpeg-devel] [PATCH] avformat: Add Dynacolor MVC Demuxer

Tom Needham 06needhamt at gmail.com
Fri Mar 27 13:51:30 EET 2020


Please See the updated patch below which applies as expected.

>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

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..394e36adbe
--- /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
+inline 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;
+}
+
+inline 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..8c59c7bf5d
--- /dev/null
+++ b/libavformat/dynacolor.h
@@ -0,0 +1,273 @@
+/*
+ * 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
+
+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.19.1.windows.1


On Sun, 22 Mar 2020 at 22:03, Carl Eugen Hoyos <ceffmpeg at gmail.com> wrote:

> Am So., 22. März 2020 um 18:29 Uhr schrieb Tom Needham <
> 06needhamt at gmail.com>:
> >
> > 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.
>
> I don't know but a demuxer that produces some useful output is better
> than no demuxer.
>
> > +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];
> > +    }
>
> This is not useful, please look at other probe functions.
>
> > +    if(bytes[0] == 0x40 && bytes[1] == 0x32 && bytes[2] == 0x10 &&
> > bytes[3] == 0x20)
> > +        return AVPROBE_SCORE_MAX;
>
> This should return MAX/2 for 32 bit.
>
> > +
> > +    return 0;
> > +}
>
> Your patch has many style issues, please run tools/patcheck
> and look at other files.
> This includes comments, camel-case variable names and
> strange "{" "}" placement.
>
> Carl Eugen
> _______________________________________________
> 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