From patchwork Sun May 10 15:49:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Needham <06needhamt@gmail.com> X-Patchwork-Id: 19598 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 2C42544A1F6 for ; Sun, 10 May 2020 18:50:00 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 0BFA86881FC; Sun, 10 May 2020 18:50:00 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f42.google.com (mail-wr1-f42.google.com [209.85.221.42]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 32F356881EC for ; Sun, 10 May 2020 18:49:53 +0300 (EEST) Received: by mail-wr1-f42.google.com with SMTP id y16so605552wrs.3 for ; Sun, 10 May 2020 08:49:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=to:from:subject:message-id:date:user-agent:mime-version :content-language; bh=eA9SgUWJVLntB+yf0P4ZeR5E4GAYblBlpse0UmhS53s=; b=e8twp9YNTHGZSzH4KIGe0o5/9p8VWiLQNRtyRD+OFtiu6wYA8xd80cSdn/LvAoVZLc /xdvEZ4phdoi3dtD0kAcTzTABtB1/rDI5adVO6Ye1eGgvSqFlaCBvP3ZJEx0opW1oRo5 KSSfUi6gFT2ApkPrPMaNCXxs1P1QElK+2xVgdRkS23J3G+yskd0Oaa03rkh90w3JGn1M r9V++eaEENvz6UhHpCfKdt4iU1rgaKbbi04AQqGMVFZ/K1BM8KvIjmNYZzZ6JB9NC6V6 PdHm4X6wp53pJeuRW/dtLPL32jqqwSqQhVsSd5nIY+jQVdVQ+7Msd0TsAm3VltmGXb2J yW/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:to:from:subject:message-id:date:user-agent :mime-version:content-language; bh=eA9SgUWJVLntB+yf0P4ZeR5E4GAYblBlpse0UmhS53s=; b=gu2keDfDhixOuNWBC+xRL5QJsxHqtXvvBMAe7N9RyzNwDkPcXx9194Hb293x00ZPj3 mdSQvxPPdz9OEZL/lI71Rd+f9BqntQL2eAtubwK9x3p531Ak/UmIp4sPBbqdroBFofzT jhkYF3/01FWmw5l3XJE+iz7Z5XgnnXSrrkrT0E0VGCAqSdYVDCb50CdUM0LVJM5uDflu I8sSI/AQc55FS0V04V5K19d50bf7F2hzL2vlfdKqUCPvgkINMkK0856fox0b2NMkTEHr 2uhSf7gZj1nCvYPrJux8gkmBir454Iq3IybyrApSc+Fel4LtVxPrSu9Z1LVmtpH3yEQs zeZA== X-Gm-Message-State: AGi0PuaEVfjGB9wMqb0z1RFBM9DmdoSN8K/+vCqVTu2MiCA9nSn2A3+r H6pBIv9x4z6s4pqG/V/mkjCWyfCoCns= X-Google-Smtp-Source: APiQypL3K8qEtnHvVsmFywSY8xFaY2r4ZyVRhBBQuWJO6T25IC68XZGnFtnJx/YAtB1pzZRFdOeI3g== X-Received: by 2002:adf:82c1:: with SMTP id 59mr13428101wrc.377.1589125792418; Sun, 10 May 2020 08:49:52 -0700 (PDT) Received: from [192.168.1.106] ([51.194.52.10]) by smtp.gmail.com with ESMTPSA id i1sm13208505wrx.22.2020.05.10.08.49.50 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Sun, 10 May 2020 08:49:51 -0700 (PDT) To: ffmpeg-devel@ffmpeg.org From: Thomas Needham <06needhamt@gmail.com> Message-ID: <11af14c3-1b4e-b1f6-30ec-f9d75bb96382@gmail.com> Date: Sun, 10 May 2020 16:49:50 +0100 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Thunderbird/68.6.0 MIME-Version: 1.0 Content-Language: en-US Subject: [FFmpeg-devel] [PATCH V3] avformat: Add Dynacolor MVC Demuxer X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Please see new version of the patch with the changes addressed. Thanks Tom From f91f5931fcc381cbcddb8abc03dde3f81c33a085 Mon Sep 17 00:00:00 2001 From: Tom Needham <06needhamt@gmail.com> Date: Sun, 10 May 2020 16:42:00 +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@gmail.com> --- Changelog | 1 + doc/general.texi | 1 + libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/dynacolor.c | 411 +++++++++++++++++++++++++++++++++++++++ libavformat/dynacolor.h | 209 ++++++++++++++++++++ libavformat/version.h | 2 +- 7 files changed, 625 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 : - 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..05a32b5299 --- /dev/null +++ b/libavformat/dynacolor.c @@ -0,0 +1,411 @@ +/* + * 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 +#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" + +int ff_dyna_read_packet_header(AVFormatContext *ctx, AVIOContext *pb, unsigned char *pes_data, DynacolorPesHeader *pes, + unsigned int *size, time_t *time, DynacolorHeader *header, unsigned int *basicIdx_H, unsigned int *basicIdx_L, + unsigned char first) +{ + int ret = 0; + unsigned int stream_format; + + *(basicIdx_H) = avio_rl32(pb); + + header->Basic.Header1 = *(basicIdx_H)&0xFF; + header->Basic.Header2 = *(basicIdx_H) >> 8 & 0xFF; + header->Basic.reserved = *(basicIdx_H) >> 16 & 0x0F; + header->Basic.Source = *(basicIdx_H) >> 20 & 0x01; + header->Basic.WidthBase = *(basicIdx_H) >> 21 & 0x03; + header->Basic.Reserved0 = *(basicIdx_H) >> 23 & 0x01; + header->Basic.Channel = *(basicIdx_H) >> 24 & 0x02; + header->Basic.StreamType = *(basicIdx_H) >> 26 & 0x02; + header->Basic.QI = *(basicIdx_H) >> 28 & 0x0F; + + *size = avio_rl32(pb); + header->Basic.Size = *size; + + *time = (time_t)avio_rl32(pb); + header->Basic.Time = ((unsigned int) *time); + + *(basicIdx_L) = avio_rl32(pb); + + header->Basic.NTSC_PAL = *(basicIdx_L)&0x01; + header->Basic.ImgMode = *(basicIdx_L) >> 1 & 0x01; + header->Basic.Audio = *(basicIdx_L) >> 2 & 0x01; + header->Basic.DST = *(basicIdx_L) >> 3 & 0x01; + header->Basic.Covert = *(basicIdx_L) >> 4 & 0x01; + header->Basic.Vloss = *(basicIdx_L) >> 5 & 0x01; + header->Basic.AlarmIn = *(basicIdx_L) >> 6 & 0x01; + header->Basic.Motion = *(basicIdx_L) >> 7 & 0x01; + header->Basic.ExtraEnable = *(basicIdx_L) >> 8 & 0x01; + header->Basic.EventEnable = *(basicIdx_L) >> 9 & 0x01; + header->Basic.PPS = *(basicIdx_L) >> 10 & 0x40; + header->Basic.Type = *(basicIdx_L) >> 16 & 0x08; + header->Basic.Channel = *(basicIdx_L) >> 19 & 0x20; + header->Basic.Chksum = *(basicIdx_L) >> 24 & 0xFF; + + if (header->Basic.ExtraEnable) { + // File has DynacolorExtraIdx header so parse it + unsigned int start; + unsigned char end; + char checksum; + + start = avio_rl32(pb); + + header->Extra.IsDST_S_W = start & 0x01; + header->Extra.DST_Minutes = start >> 1 & 0xFF; + header->Extra.OverSpeed = start >> 9 & 0x01; + header->Extra.SkipPicCnt = start >> 10 & 0x20; + header->Extra.Exception = start >> 15 & 0x01; + header->Extra.SuperExtraSize = start >> 16 & 0xFFFF; + + header->Extra.Serno = avio_rl32(pb); + header->Extra.AlmIn[0] = (unsigned char)avio_r8(pb); + header->Extra.AlmIn[1] = (unsigned char)avio_r8(pb); + header->Extra.Vloss[0] = (unsigned char)avio_r8(pb); + header->Extra.Vloss[1] = (unsigned char)avio_r8(pb); + header->Extra.Motion[0] = (unsigned char)avio_r8(pb); + header->Extra.Motion[1] = (unsigned char)avio_r8(pb); + + end = (unsigned char)avio_r8(pb); + + header->Extra.IsDVRRec = end & 0x03; + header->Extra.TimeZone = (end >> 2) & 0x40; + + checksum = (char)avio_r8(pb); + + header->Basic.Chksum = checksum & 0xFF; + + header->SuperExtra.type = (int)avio_rl32(pb); + header->SuperExtra.remain_size = (int)avio_rl32(pb); + + if (avio_read(pb, header->SuperExtra.title, DYNACOLOR_SUPEREXTRA_TITLE_SIZE) != DYNACOLOR_SUPEREXTRA_TITLE_SIZE) + return AVERROR_INVALIDDATA; + + if (avio_read(pb, header->SuperExtra.extra_data, DYNACOLOR_SUPEREXTRA_EXTRA_DATA_SIZE) != DYNACOLOR_SUPEREXTRA_EXTRA_DATA_SIZE) + return AVERROR_INVALIDDATA; + + } else { + // File has no DynacolorExtraIdx header so skip it + av_log(ctx, AV_LOG_DEBUG, "Skipping DynacolorExtraIdx and DynacolorSuperExtraIdx Header\n"); + avio_skip(pb, DYNACOLOR_EXTRA_SIZE + DYNACOLOR_SUPEREXTRA_SIZE); + + memset(&header->Extra, 0xFF, DYNACOLOR_EXTRA_SIZE); + memset(&header->SuperExtra, 0xFF, DYNACOLOR_SUPEREXTRA_SIZE); + } + + if (avio_read(pb, pes_data, DYNACOLOR_PES_HEADER_SIZE) != DYNACOLOR_PES_HEADER_SIZE) + return AVERROR_INVALIDDATA; + + ret = ff_dyna_build_pes_header(ctx, pes_data); + + if (ret) { + av_log(ctx, AV_LOG_ERROR, "Failed to Build PES Header\n"); + return AVERROR_INVALIDDATA; + } else { + if(first) { + stream_format = ff_dyna_get_stream_format(ctx, header); + if (!stream_format) { + avpriv_report_missing_feature(ctx, "Format %02X,", stream_format); + return AVERROR_PATCHWELCOME; + } else if (stream_format == AVERROR_INVALIDDATA) { + av_log(ctx, AV_LOG_ERROR, "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, DynacolorHeader *header) +{ + DynacolorContext *priv = ctx->priv_data; + 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, DYNACOLOR_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, DYNACOLOR_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, DYNACOLOR_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, DYNACOLOR_AUDIO_FORMAT_PREFIX); + + if (!ret) { + avpriv_report_missing_feature(ctx, "Format %02X,", priv->pes_header.format_id); + return AVERROR_PATCHWELCOME; + } else { + av_log(ctx, AV_LOG_DEBUG, "Successfully Validated AUDIO PES Header\n"); + return DYNACOLOR_JPEG_FORMAT_PREFIX; + } + } else { + av_log(ctx, AV_LOG_DEBUG, "Successfully Validated JPEG PES Header\n"); + return DYNACOLOR_JPEG_FORMAT_PREFIX; + } + } else { + av_log(ctx, AV_LOG_DEBUG, "Successfully Validated MPEG4 PES Header\n"); + return DYNACOLOR_MPEG4_FORMAT_PREFIX; + } + } else { + av_log(ctx, AV_LOG_DEBUG, "Successfully Validated H264 PES Header\n"); + return DYNACOLOR_H264_FORMAT_PREFIX; + } + + return 0; +} + +int ff_dyna_build_pes_header(AVFormatContext *ctx, uint8_t *pes_data) +{ + DynacolorContext *priv = ctx->priv_data; + + priv->pes_header.start_code0 = pes_data[0]; + priv->pes_header.start_code1 = pes_data[1]; + priv->pes_header.start_code2 = pes_data[2]; + priv->pes_header.format_id = pes_data[3] & 0x0F; + priv->pes_header.channel_id = pes_data[3] >> 8 & 0x0F; + + priv->pes_header.unused_0 = MKTAG(pes_data[4], pes_data[5], pes_data[6], pes_data[7]); + priv->pes_header.unused_1 = MKTAG(pes_data[8], pes_data[9], pes_data[10], pes_data[11]); + priv->pes_header.unused_2 = MKTAG(pes_data[12], pes_data[13], pes_data[14], pes_data[15]); + priv->pes_header.unused_3 = MKTAG(pes_data[16], pes_data[17], pes_data[18], pes_data[19]); + priv->pes_header.unused_4 = MKTAG(pes_data[20], pes_data[21], pes_data[22], pes_data[23]); + + priv->pes_header.size_bit7to0 = pes_data[24]; + priv->pes_header.size_bit10to8 = pes_data[25] & 0x08; + priv->pes_header.size_bit14to11 = pes_data[26] & 0xF; + priv->pes_header.size_bit21to15 = pes_data[27]; + priv->pes_header.size_marker0 = pes_data[28] & 0x01; + priv->pes_header.size_marker1 = pes_data[29] & 0x01; + priv->pes_header.picture_type = pes_data[30]; + priv->pes_header.is_interleaved = pes_data[31] & 0x01; + priv->pes_header.field_id = pes_data[31] >> 1 & 0x03; + + return 0; +} + +int ff_dyna_check_pes_packet_header(AVFormatContext *ctx, int format_prefix) +{ + DynacolorContext *dyna = ctx->priv_data; + DynacolorPesHeader *pes = &dyna->pes_header; + + return pes->format_id == format_prefix; +} + +static int dyna_read_probe(const AVProbeData *p) +{ + int magic = MKTAG(p->buf[0], p->buf[1], p->buf[2], p->buf[3]); + + if (magic == 0x20103240) + 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; + + 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 *pes_data; + DynacolorPesHeader *pes; + unsigned int size; + + unsigned int basicIdx_H, basicIdx_L; + + pes_data = av_malloc_array(DYNACOLOR_PES_HEADER_SIZE, 1); + pes = av_malloc(sizeof(DynacolorPesHeader)); + + ret = ff_dyna_read_packet_header(ctx, pb, pes_data, pes, &size, &time, + &priv->header, &basicIdx_H, &basicIdx_L, 1); + + if (ret) { + ret = AVERROR_INVALIDDATA; + goto end; + } + + if (priv->pes_header.format_id == DYNACOLOR_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) { + ret = AVERROR_INVALIDDATA; + goto end; + } + + } else if (priv->pes_header.format_id == DYNACOLOR_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) { + ret = AVERROR_INVALIDDATA; + goto end; + } + + } else if (priv->pes_header.format_id == DYNACOLOR_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) { + ret = AVERROR_INVALIDDATA; + goto end; + } + + } else if (priv->pes_header.format_id == DYNACOLOR_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) { + ret = AVERROR_INVALIDDATA; + goto end; + } + } + +end: + av_free(pes_data); + av_free(pes); + + return ret; +} + +static int dyna_read_packet(AVFormatContext *ctx, AVPacket *pkt) +{ + int ret = 0; + DynacolorContext *priv = ctx->priv_data; + AVIOContext *pb = ctx->pb; + DynacolorPesHeader *pes; + + time_t time; + unsigned char *pes_data; + unsigned char *pkt_data; + unsigned int size; + + unsigned int basicIdx_H, basicIdx_L; + + pes_data = av_malloc_array(DYNACOLOR_PES_HEADER_SIZE, 1); + pes = av_malloc(sizeof(DynacolorPesHeader)); + + ret = ff_dyna_read_packet_header(ctx, pb, pes_data, pes, &size, &time, + &priv->header, &basicIdx_H, &basicIdx_L, 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); + + pkt_data = av_malloc(size); + ret = avio_read(pb, pkt_data, size); + + if (ret != size) + goto end; + + ret = av_packet_from_data(pkt, pkt_data, size); + + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Error Building Packet\n"); + goto end; + } + +end: + av_free(pes_data); + av_free(pes); + + 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; + int64_t ret = 0L; + + 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); + + ret = avio_seek(ctx->pb, size, SEEK_SET); + + if(ret < 0) + return ret; + + 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..a620ed7a2e --- /dev/null +++ b/libavformat/dynacolor.h @@ -0,0 +1,209 @@ +/* + * 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 DynacolorBasicIdx header] + [16-byte DynacolorExtraIdx header] + ['SuperExtraSize' bytes] + [32-byte PES header] + [video body] +// The 'SuperExtraSize' is defined in the 'DynacolorExtraIdx' structure to store the additional information, and usually equals zero. + +#define DYNACOLOR_PES_HEADER_SIZE 32 +#define DYNACOLOR_EXTRA_SIZE 16 +#define DYNACOLOR_SUPEREXTRA_SIZE 108 +#define DYNACOLOR_SUPEREXTRA_TITLE_SIZE 12 +#define DYNACOLOR_SUPEREXTRA_EXTRA_DATA_SIZE 96 + +#define DYNACOLOR_AUDIO_FORMAT_PREFIX 0x0C +#define DYNACOLOR_MPEG4_FORMAT_PREFIX 0x0D +#define DYNACOLOR_JPEG_FORMAT_PREFIX 0x0E +#define DYNACOLOR_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] + +} DynacolorBasicIdx; + +// 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 DynacolorBasicIdx = 0, it means analog camera's status. If Source = 1, it means IP camera's. + char Vloss[2]; // 16 channel, if Source of DynacolorBasicIdx = 0, it means analog camera's status. If Source = 1, it means IP camera's. + char Motion[2]; // 16 channel, if Source of DynacolorBasicIdx = 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; + +} DynacolorExtraIdx; + +// Super Extra Index : +// type = 0 +// remain_size = 108 +// structure to store cam title & extra data(GPS/Text) + +typedef struct +{ + + // the type for DynacolorExtraIdx is fixed to 0 + int type; + + // the remain_size for DynacolorExtraIdx 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]; + +} DynacolorSuperExtraIdx; + +typedef struct +{ + DynacolorBasicIdx Basic; + DynacolorExtraIdx Extra; + DynacolorSuperExtraIdx SuperExtra; +} DynacolorHeader; + +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; + +} DynacolorPesHeader; + +typedef struct { + DynacolorHeader header; + DynacolorPesHeader pes_header; +} DynacolorContext; + +int ff_dyna_read_packet_header(AVFormatContext *ctx, AVIOContext *pb, unsigned char *pes_data, DynacolorPesHeader *pes, unsigned int *size, + time_t *time, DynacolorHeader *cdata_B, unsigned int *basicIdx_H, unsigned int *basicIdx_L, + unsigned char first); +int ff_dyna_get_stream_format(AVFormatContext *ctx, DynacolorHeader *header); +int ff_dyna_build_pes_header(AVFormatContext *ctx, uint8_t *pes_data); +int ff_dyna_check_pes_packet_header(AVFormatContext *ctx, int format_prefix); + +#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, \