mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-28 19:20:35 +00:00
b0afaffc5d
This allows downstream of a payloader to know the RTP header's marker flag without first having to map the buffer and parse the RTP header. Especially inside RTP header extension implementations this can be useful to decide which packet corresponds to e.g. the last packet of a video frame. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1776>
1872 lines
54 KiB
C
1872 lines
54 KiB
C
/* GStreamer
|
|
* Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com>
|
|
* Copyright (C) <2008> Dejan Sakelsak <dejan.sakelsak@marand.si>
|
|
* Copyright (C) <2009> Janin Kolenc <janin.kolenc@marand.si>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <gst/rtp/gstrtpbuffer.h>
|
|
#include <gst/video/video.h>
|
|
|
|
#include "gstrtpelements.h"
|
|
#include "gstrtph263pay.h"
|
|
#include "gstrtputils.h"
|
|
|
|
typedef enum
|
|
{
|
|
GST_H263_FRAME_TYPE_I = 0,
|
|
GST_H263_FRAME_TYPE_P = 1,
|
|
GST_H263_FRAME_TYPE_PB = 2
|
|
} GstRtpH263PayFrameType;
|
|
|
|
typedef enum
|
|
{
|
|
GST_RTP_H263_PAYLOAD_PICTURE_FORMAT_RES1 = 0,
|
|
GST_RTP_H263_PAYLOAD_PICTURE_FORMAT_SQCIF = 1,
|
|
GST_RTP_H263_PAYLOAD_PICTURE_FORMAT_QCIF = 2,
|
|
GST_RTP_H263_PAYLOAD_PICTURE_FORMAT_CIF = 3,
|
|
GST_RTP_H263_PAYLOAD_PICTURE_FORMAT_4CIF = 4,
|
|
GST_RTP_H263_PAYLOAD_PICTURE_FORMAT_16CIF = 5,
|
|
GST_RTP_H263_PAYLOAD_PICTURE_FORMAT_RES2 = 6,
|
|
GST_H263_PAYLOAD_PICTURE_FORMAT_PLUS = 7
|
|
} GstRtpH263PayPictureFormat;
|
|
|
|
static const guint format_props[8][2] = { {254, 254},
|
|
{6, 8},
|
|
{9, 11},
|
|
{18, 22},
|
|
{18, 88},
|
|
{18, 352},
|
|
{254, 254},
|
|
{255, 255}
|
|
};
|
|
|
|
/*
|
|
* I-frame MCBPC table: code, mask, nbits, cb, cr, mb type -> 10 = undefined (because we have guint)
|
|
*/
|
|
#define MCBPC_I_LEN 9
|
|
#define MCBPC_I_WID 6
|
|
static const guint32 mcbpc_I[9][6] = {
|
|
{0x8000, 0x8000, 1, 0, 0, 3},
|
|
{0x2000, 0xe000, 3, 0, 1, 3},
|
|
{0x4000, 0xe000, 3, 1, 0, 3},
|
|
{0x6000, 0xe000, 3, 1, 1, 3},
|
|
{0x1000, 0xf000, 4, 0, 0, 4},
|
|
{0x0400, 0xfc00, 6, 0, 1, 4},
|
|
{0x0800, 0xfc00, 6, 1, 0, 4},
|
|
{0x0c00, 0xfc00, 6, 1, 1, 4},
|
|
{0x0080, 0xff80, 9, 10, 10, 10}
|
|
};
|
|
|
|
/*
|
|
* P-frame MCBPC table: code, mask, nbits, cb, cr, mb type -> 10 = undefined (because we have guint)
|
|
*/
|
|
#define MCBPC_P_LEN 21
|
|
#define MCBPC_P_WID 6
|
|
static const guint16 mcbpc_P[21][6] = {
|
|
{0x8000, 0x8000, 1, 0, 0, 0},
|
|
{0x3000, 0xf000, 4, 0, 1, 0},
|
|
{0x2000, 0xf000, 4, 1, 0, 0},
|
|
{0x1400, 0xfc00, 6, 1, 1, 0},
|
|
{0x6000, 0xe000, 3, 0, 0, 1},
|
|
{0x0e00, 0xfe00, 7, 0, 1, 1},
|
|
{0x0c00, 0xfe00, 7, 1, 0, 1},
|
|
{0x0280, 0xff80, 9, 1, 1, 1},
|
|
{0x4000, 0xe000, 3, 0, 0, 2},
|
|
{0x0a00, 0xfe00, 7, 0, 1, 2},
|
|
{0x0800, 0xfe00, 7, 1, 0, 2},
|
|
{0x0500, 0xff00, 8, 1, 1, 2},
|
|
{0x1800, 0xf800, 5, 0, 0, 3},
|
|
{0x0400, 0xff00, 8, 0, 1, 3},
|
|
{0x0300, 0xff00, 8, 1, 0, 3},
|
|
{0x0600, 0xfe00, 7, 1, 1, 3},
|
|
{0x1000, 0xfc00, 6, 0, 0, 4},
|
|
{0x0200, 0xff80, 9, 0, 1, 4},
|
|
{0x0180, 0xff80, 9, 1, 0, 4},
|
|
{0x0100, 0xff80, 9, 1, 1, 4},
|
|
{0x0080, 0xff80, 9, 10, 10, 10}
|
|
};
|
|
|
|
/*
|
|
* I-frame CBPY (code, mask, nbits, Y0, Y1, Y2, Y3)
|
|
*/
|
|
#define CBPY_LEN 16
|
|
#define CBPY_WID 7
|
|
static const guint8 cbpy_I[16][7] = {
|
|
{0x30, 0xf0, 4, 0, 0, 0, 0},
|
|
{0x28, 0xf8, 5, 0, 0, 0, 1},
|
|
{0x20, 0xf8, 5, 0, 0, 1, 0},
|
|
{0x90, 0xf0, 4, 0, 0, 1, 1},
|
|
{0x18, 0xf8, 5, 0, 1, 0, 0},
|
|
{0x70, 0xf0, 4, 0, 1, 0, 1},
|
|
{0x08, 0xfc, 6, 0, 1, 1, 0},
|
|
{0xb0, 0xf0, 4, 0, 1, 1, 1},
|
|
{0x10, 0xf8, 5, 1, 0, 0, 0},
|
|
{0x0c, 0xfc, 6, 1, 0, 0, 1},
|
|
{0x50, 0xf0, 4, 1, 0, 1, 0},
|
|
{0xa0, 0xf0, 4, 1, 0, 1, 1},
|
|
{0x40, 0xf0, 4, 1, 1, 0, 0},
|
|
{0x80, 0xf0, 4, 1, 1, 0, 1},
|
|
{0x60, 0xf0, 4, 1, 1, 1, 0},
|
|
{0xc0, 0xc0, 2, 1, 1, 1, 1}
|
|
};
|
|
|
|
/*
|
|
* P-frame CBPY (code, mask, nbits, Y0, Y1, Y2, Y3)
|
|
*/
|
|
static const guint8 cbpy_P[16][7] = {
|
|
{0x30, 0xf0, 4, 1, 1, 1, 1},
|
|
{0x28, 0xf8, 5, 1, 1, 1, 0},
|
|
{0x20, 0xf8, 5, 1, 1, 0, 1},
|
|
{0x90, 0xf0, 4, 1, 1, 0, 0},
|
|
{0x18, 0xf8, 5, 1, 0, 1, 1},
|
|
{0x70, 0xf0, 4, 1, 0, 1, 0},
|
|
{0x08, 0xfc, 6, 1, 0, 0, 1},
|
|
{0xb0, 0xf0, 4, 1, 0, 0, 0},
|
|
{0x10, 0xf8, 5, 0, 1, 1, 1},
|
|
{0x0c, 0xfc, 6, 0, 1, 1, 0},
|
|
{0x50, 0xf0, 4, 0, 1, 0, 1},
|
|
{0xa0, 0xf0, 4, 0, 1, 0, 0},
|
|
{0x40, 0xf0, 4, 0, 0, 1, 1},
|
|
{0x80, 0xf0, 4, 0, 0, 1, 0},
|
|
{0x60, 0xf0, 4, 0, 0, 0, 1},
|
|
{0xc0, 0xc0, 2, 0, 0, 0, 0}
|
|
};
|
|
|
|
/*
|
|
* Block TCOEF table (code, mask, nbits, LAST, RUN, LEVEL)
|
|
*/
|
|
#define TCOEF_LEN 103
|
|
#define TCOEF_WID 6
|
|
static const guint16 tcoef[103][6] = {
|
|
{0x8000, 0xc000, 3, 0, 0, 1},
|
|
{0xf000, 0xf000, 5, 0, 0, 2},
|
|
{0x5400, 0xfc00, 7, 0, 0, 3},
|
|
{0x2e00, 0xfe00, 8, 0, 0, 4},
|
|
{0x1f00, 0xff00, 9, 0, 0, 5},
|
|
{0x1280, 0xff80, 10, 0, 0, 6},
|
|
{0x1200, 0xff80, 10, 0, 0, 7},
|
|
{0x0840, 0xffc0, 11, 0, 0, 8},
|
|
{0x0800, 0xffc0, 11, 0, 0, 9},
|
|
{0x00e0, 0xffe0, 12, 0, 0, 10}, //10
|
|
{0x00c0, 0xffe0, 12, 0, 0, 11},
|
|
{0x0400, 0xffe0, 12, 0, 0, 12},
|
|
{0xc000, 0xe000, 4, 0, 1, 1},
|
|
{0x5000, 0xfc00, 7, 0, 1, 2},
|
|
{0x1e00, 0xff00, 9, 0, 1, 3},
|
|
{0x03c0, 0xffc0, 11, 0, 1, 4},
|
|
{0x0420, 0xffe0, 12, 0, 1, 5},
|
|
{0x0500, 0xfff0, 13, 0, 1, 6},
|
|
{0xe000, 0xf000, 5, 0, 2, 1},
|
|
{0x1d00, 0xff00, 9, 0, 2, 2}, //20
|
|
{0x0380, 0xffc0, 11, 0, 2, 3},
|
|
{0x0510, 0xfff0, 13, 0, 2, 4},
|
|
{0x6800, 0xf800, 6, 0, 3, 1},
|
|
{0x1180, 0xff80, 10, 0, 3, 2},
|
|
{0x0340, 0xffc0, 11, 0, 3, 3},
|
|
{0x6000, 0xf800, 6, 0, 4, 1},
|
|
{0x1100, 0xff80, 10, 0, 4, 2},
|
|
{0x0520, 0xfff0, 13, 0, 4, 3},
|
|
{0x5800, 0xf800, 6, 0, 5, 1},
|
|
{0x0300, 0xffc0, 11, 0, 5, 2}, // 30
|
|
{0x0530, 0xfff0, 13, 0, 5, 3},
|
|
{0x4c00, 0xfc00, 7, 0, 6, 1},
|
|
{0x02c0, 0xffc0, 11, 0, 6, 2},
|
|
{0x0540, 0xfff0, 13, 0, 6, 3},
|
|
{0x4800, 0xfc00, 7, 0, 7, 1},
|
|
{0x0280, 0xffc0, 11, 0, 7, 2},
|
|
{0x4400, 0xfc00, 7, 0, 8, 1},
|
|
{0x0240, 0xffc0, 11, 0, 8, 2},
|
|
{0x4000, 0xfc00, 7, 0, 9, 1},
|
|
{0x0200, 0xffc0, 11, 0, 9, 2}, // 40
|
|
{0x2c00, 0xfe00, 8, 0, 10, 1},
|
|
{0x0550, 0xfff0, 13, 0, 10, 2},
|
|
{0x2a00, 0xfe00, 8, 0, 11, 1},
|
|
{0x2800, 0xfe00, 8, 0, 12, 1},
|
|
{0x1c00, 0xff00, 9, 0, 13, 1},
|
|
{0x1b00, 0xff00, 9, 0, 14, 1},
|
|
{0x1080, 0xff80, 10, 0, 15, 1},
|
|
{0x1000, 0xff80, 10, 0, 16, 1},
|
|
{0x0f80, 0xff80, 10, 0, 17, 1},
|
|
{0x0f00, 0xff80, 10, 0, 18, 1}, // 50
|
|
{0x0e80, 0xff80, 10, 0, 19, 1},
|
|
{0x0e00, 0xff80, 10, 0, 20, 1},
|
|
{0x0d80, 0xff80, 10, 0, 21, 1},
|
|
{0x0d00, 0xff80, 10, 0, 22, 1},
|
|
{0x0440, 0xffe0, 12, 0, 23, 1},
|
|
{0x0460, 0xffe0, 12, 0, 24, 1},
|
|
{0x0560, 0xfff0, 13, 0, 25, 1},
|
|
{0x0570, 0xfff0, 13, 0, 26, 1},
|
|
{0x7000, 0xf000, 5, 1, 0, 1},
|
|
{0x0c80, 0xff80, 10, 1, 0, 2}, // 60
|
|
{0x00a0, 0xffe0, 12, 1, 0, 3},
|
|
{0x3c00, 0xfc00, 7, 1, 1, 1},
|
|
{0x0080, 0xffe0, 12, 1, 1, 2},
|
|
{0x3800, 0xfc00, 7, 1, 2, 1},
|
|
{0x3400, 0xfc00, 7, 1, 3, 1},
|
|
{0x3000, 0xfc00, 7, 1, 4, 1},
|
|
{0x2600, 0xfe00, 8, 1, 5, 1},
|
|
{0x2400, 0xfe00, 8, 1, 6, 1},
|
|
{0x2200, 0xfe00, 8, 1, 7, 1},
|
|
{0x2000, 0xfe00, 8, 1, 8, 1}, // 70
|
|
{0x1a00, 0xff00, 9, 1, 9, 1},
|
|
{0x1900, 0xff00, 9, 1, 10, 1},
|
|
{0x1800, 0xff00, 9, 1, 11, 1},
|
|
{0x1700, 0xff00, 9, 1, 12, 1},
|
|
{0x1600, 0xff00, 9, 1, 13, 1},
|
|
{0x1500, 0xff00, 9, 1, 14, 1},
|
|
{0x1400, 0xff00, 9, 1, 15, 1},
|
|
{0x1300, 0xff00, 9, 1, 16, 1},
|
|
{0x0c00, 0xff80, 10, 1, 17, 1},
|
|
{0x0b80, 0xff80, 10, 1, 18, 1}, // 80
|
|
{0x0b00, 0xff80, 10, 1, 19, 1},
|
|
{0x0a80, 0xff80, 10, 1, 20, 1},
|
|
{0x0a00, 0xff80, 10, 1, 21, 1},
|
|
{0x0980, 0xff80, 10, 1, 22, 1},
|
|
{0x0900, 0xff80, 10, 1, 23, 1},
|
|
{0x0880, 0xff80, 10, 1, 24, 1},
|
|
{0x01c0, 0xffc0, 11, 1, 25, 1},
|
|
{0x0180, 0xffc0, 11, 1, 26, 1},
|
|
{0x0140, 0xffc0, 11, 1, 27, 1},
|
|
{0x0100, 0xffc0, 11, 1, 28, 1}, // 90
|
|
{0x0480, 0xffe0, 12, 1, 29, 1},
|
|
{0x04a0, 0xffe0, 12, 1, 30, 1},
|
|
{0x04c0, 0xffe0, 12, 1, 31, 1},
|
|
{0x04e0, 0xffe0, 12, 1, 32, 1},
|
|
{0x0580, 0xfff0, 13, 1, 33, 1},
|
|
{0x0590, 0xfff0, 13, 1, 34, 1},
|
|
{0x05a0, 0xfff0, 13, 1, 35, 1},
|
|
{0x05b0, 0xfff0, 13, 1, 36, 1},
|
|
{0x05c0, 0xfff0, 13, 1, 37, 1},
|
|
{0x05d0, 0xfff0, 13, 1, 38, 1}, // 100
|
|
{0x05e0, 0xfff0, 13, 1, 39, 1},
|
|
{0x05f0, 0xfff0, 13, 1, 40, 1},
|
|
{0x0600, 0xfe00, 7, 0, 0xffff, 0xffff}
|
|
};
|
|
|
|
/*
|
|
* Motion vector code table (Code, mask, nbits, vector (halfpixel, two's complement), diff (halfpixel, two's complement))
|
|
*/
|
|
#define MVD_LEN 64
|
|
#define MVD_WID 5
|
|
static const guint16 mvd[64][5] = {
|
|
{0x0028, 0xfff8, 13, 0x0060, 0x0020},
|
|
{0x0038, 0xfff8, 13, 0x0061, 0x0021},
|
|
{0x0050, 0xfff0, 12, 0x0062, 0x0022},
|
|
{0x0070, 0xfff0, 12, 0x0063, 0x0023},
|
|
{0x0090, 0xfff0, 12, 0x0064, 0x0024},
|
|
{0x00b0, 0xfff0, 12, 0x0065, 0x0025},
|
|
{0x00d0, 0xfff0, 12, 0x0066, 0x0026},
|
|
{0x00f0, 0xfff0, 12, 0x0067, 0x0027},
|
|
{0x0120, 0xffe0, 11, 0x0068, 0x0028},
|
|
{0x0160, 0xffe0, 11, 0x0069, 0x0029},
|
|
{0x01a0, 0xffe0, 11, 0x006a, 0x002a},
|
|
{0x01e0, 0xffe0, 11, 0x006b, 0x002b},
|
|
{0x0220, 0xffe0, 11, 0x006c, 0x002c},
|
|
{0x0260, 0xffe0, 11, 0x006d, 0x002d},
|
|
{0x02a0, 0xffe0, 11, 0x006e, 0x002e},
|
|
{0x02e0, 0xffe0, 11, 0x006f, 0x002f},
|
|
{0x0320, 0xffe0, 11, 0x0070, 0x0030},
|
|
{0x0360, 0xffe0, 11, 0x0071, 0x0031},
|
|
{0x03a0, 0xffe0, 11, 0x0072, 0x0032},
|
|
{0x03e0, 0xffe0, 11, 0x0073, 0x0033},
|
|
{0x0420, 0xffe0, 11, 0x0074, 0x0034},
|
|
{0x0460, 0xffe0, 11, 0x0075, 0x0035},
|
|
{0x04c0, 0xffc0, 10, 0x0076, 0x0036},
|
|
{0x0540, 0xffc0, 10, 0x0077, 0x0037},
|
|
{0x05c0, 0xffc0, 10, 0x0078, 0x0038},
|
|
{0x0700, 0xff00, 8, 0x0079, 0x0039},
|
|
{0x0900, 0xff00, 8, 0x007a, 0x003a},
|
|
{0x0b00, 0xff00, 8, 0x007b, 0x003b},
|
|
{0x0e00, 0xfe00, 7, 0x007c, 0x003c},
|
|
{0x1800, 0xf800, 5, 0x007d, 0x003d},
|
|
{0x3000, 0xf000, 4, 0x007e, 0x003e},
|
|
{0x6000, 0xe000, 3, 0x007f, 0x003f},
|
|
{0x8000, 0x8000, 1, 0x0000, 0x0000},
|
|
{0x4000, 0xe000, 3, 0x0001, 0x0041},
|
|
{0x2000, 0xf000, 4, 0x0002, 0x0042},
|
|
{0x1000, 0xf800, 5, 0x0003, 0x0043},
|
|
{0x0c00, 0xfe00, 7, 0x0004, 0x0044},
|
|
{0x0a00, 0xff00, 8, 0x0005, 0x0045},
|
|
{0x0800, 0xff00, 8, 0x0006, 0x0046},
|
|
{0x0600, 0xff00, 8, 0x0007, 0x0047},
|
|
{0x0580, 0xffc0, 10, 0x0008, 0x0048},
|
|
{0x0500, 0xffc0, 10, 0x0009, 0x0049},
|
|
{0x0480, 0xffc0, 10, 0x000a, 0x004a},
|
|
{0x0440, 0xffe0, 11, 0x000b, 0x004b},
|
|
{0x0400, 0xffe0, 11, 0x000c, 0x004c},
|
|
{0x03c0, 0xffe0, 11, 0x000d, 0x004d},
|
|
{0x0380, 0xffe0, 11, 0x000e, 0x004e},
|
|
{0x0340, 0xffe0, 11, 0x000f, 0x004f},
|
|
{0x0300, 0xffe0, 11, 0x0010, 0x0050},
|
|
{0x02c0, 0xffe0, 11, 0x0011, 0x0051},
|
|
{0x0280, 0xffe0, 11, 0x0012, 0x0052},
|
|
{0x0240, 0xffe0, 11, 0x0013, 0x0053},
|
|
{0x0200, 0xffe0, 11, 0x0014, 0x0054},
|
|
{0x01c0, 0xffe0, 11, 0x0015, 0x0055},
|
|
{0x0180, 0xffe0, 11, 0x0016, 0x0056},
|
|
{0x0140, 0xffe0, 11, 0x0017, 0x0057},
|
|
{0x0100, 0xffe0, 11, 0x0018, 0x0058},
|
|
{0x00e0, 0xfff0, 12, 0x0019, 0x0059},
|
|
{0x00c0, 0xfff0, 12, 0x001a, 0x005a},
|
|
{0x00a0, 0xfff0, 12, 0x001b, 0x005b},
|
|
{0x0080, 0xfff0, 12, 0x001c, 0x005c},
|
|
{0x0060, 0xfff0, 12, 0x001d, 0x005d},
|
|
{0x0040, 0xfff0, 12, 0x001e, 0x005e},
|
|
{0x0030, 0xfff8, 13, 0x001f, 0x005f}
|
|
};
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (rtph263pay_debug);
|
|
#define GST_CAT_DEFAULT (rtph263pay_debug)
|
|
|
|
#define GST_RTP_HEADER_LEN 12
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_MODE_A_ONLY
|
|
};
|
|
|
|
static GstStaticPadTemplate gst_rtp_h263_pay_sink_template =
|
|
GST_STATIC_PAD_TEMPLATE ("sink",
|
|
GST_PAD_SINK,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS ("video/x-h263, "
|
|
"variant = (string) \"itu\", " "h263version = (string) \"h263\"")
|
|
);
|
|
|
|
static GstStaticPadTemplate gst_rtp_h263_pay_src_template =
|
|
GST_STATIC_PAD_TEMPLATE ("src",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS ("application/x-rtp, "
|
|
"media = (string) \"video\", "
|
|
"payload = (int) " GST_RTP_PAYLOAD_H263_STRING ", "
|
|
"clock-rate = (int) 90000, " "encoding-name = (string) \"H263\"; "
|
|
"application/x-rtp, "
|
|
"media = (string) \"video\", "
|
|
"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
|
|
"clock-rate = (int) 90000, " "encoding-name = (string) \"H263\"")
|
|
);
|
|
|
|
static void gst_rtp_h263_pay_finalize (GObject * object);
|
|
|
|
static gboolean gst_rtp_h263_pay_setcaps (GstRTPBasePayload * payload,
|
|
GstCaps * caps);
|
|
static void gst_rtp_h263_pay_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec);
|
|
static void gst_rtp_h263_pay_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec);
|
|
static GstFlowReturn gst_rtp_h263_pay_handle_buffer (GstRTPBasePayload *
|
|
payload, GstBuffer * buffer);
|
|
|
|
static void gst_rtp_h263_pay_boundry_init (GstRtpH263PayBoundry * boundry,
|
|
guint8 * start, guint8 * end, guint8 sbit, guint8 ebit);
|
|
static GstRtpH263PayGob *gst_rtp_h263_pay_gob_new (GstRtpH263PayBoundry *
|
|
boundry, guint gobn);
|
|
static GstRtpH263PayMB *gst_rtp_h263_pay_mb_new (GstRtpH263PayBoundry * boundry,
|
|
guint mba);
|
|
static GstRtpH263PayPackage *gst_rtp_h263_pay_package_new_empty ();
|
|
static GstRtpH263PayPackage *gst_rtp_h263_pay_package_new (guint8 * start,
|
|
guint8 * end, guint length, guint8 sbit, guint8 ebit, GstBuffer * outbuf,
|
|
gboolean marker);
|
|
|
|
static void gst_rtp_h263_pay_mb_destroy (GstRtpH263PayMB * mb);
|
|
static void gst_rtp_h263_pay_gob_destroy (GstRtpH263PayGob * gob, guint ind);
|
|
static void gst_rtp_h263_pay_context_destroy (GstRtpH263PayContext * context,
|
|
guint ind);
|
|
static void gst_rtp_h263_pay_package_destroy (GstRtpH263PayPackage * pack);
|
|
|
|
#define gst_rtp_h263_pay_parent_class parent_class
|
|
G_DEFINE_TYPE (GstRtpH263Pay, gst_rtp_h263_pay, GST_TYPE_RTP_BASE_PAYLOAD);
|
|
GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtph263pay, "rtph263pay",
|
|
GST_RANK_SECONDARY, GST_TYPE_RTP_H263_PAY, rtp_element_init (plugin));
|
|
|
|
static void
|
|
gst_rtp_h263_pay_class_init (GstRtpH263PayClass * klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
GstElementClass *gstelement_class;
|
|
GstRTPBasePayloadClass *gstrtpbasepayload_class;
|
|
|
|
gobject_class = (GObjectClass *) klass;
|
|
gstelement_class = (GstElementClass *) klass;
|
|
gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
|
|
|
|
gobject_class->finalize = gst_rtp_h263_pay_finalize;
|
|
|
|
gstrtpbasepayload_class->set_caps = gst_rtp_h263_pay_setcaps;
|
|
gstrtpbasepayload_class->handle_buffer = gst_rtp_h263_pay_handle_buffer;
|
|
gobject_class->set_property = gst_rtp_h263_pay_set_property;
|
|
gobject_class->get_property = gst_rtp_h263_pay_get_property;
|
|
|
|
g_object_class_install_property (G_OBJECT_CLASS (klass),
|
|
PROP_MODE_A_ONLY, g_param_spec_boolean ("modea-only",
|
|
"Fragment packets in mode A Only",
|
|
"Disable packetization modes B and C", DEFAULT_MODE_A,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
gst_element_class_add_static_pad_template (gstelement_class,
|
|
&gst_rtp_h263_pay_src_template);
|
|
gst_element_class_add_static_pad_template (gstelement_class,
|
|
&gst_rtp_h263_pay_sink_template);
|
|
|
|
gst_element_class_set_static_metadata (gstelement_class,
|
|
"RTP H263 packet payloader", "Codec/Payloader/Network/RTP",
|
|
"Payload-encodes H263 video in RTP packets (RFC 2190)",
|
|
"Neil Stratford <neils@vipadia.com>"
|
|
"Dejan Sakelsak <dejan.sakelsak@marand.si>");
|
|
|
|
GST_DEBUG_CATEGORY_INIT (rtph263pay_debug, "rtph263pay", 0,
|
|
"H263 RTP Payloader");
|
|
}
|
|
|
|
static void
|
|
gst_rtp_h263_pay_init (GstRtpH263Pay * rtph263pay)
|
|
{
|
|
GST_RTP_BASE_PAYLOAD_PT (rtph263pay) = GST_RTP_PAYLOAD_H263;
|
|
rtph263pay->prop_payload_mode = DEFAULT_MODE_A;
|
|
}
|
|
|
|
static void
|
|
gst_rtp_h263_pay_finalize (GObject * object)
|
|
{
|
|
GstRtpH263Pay *rtph263pay;
|
|
|
|
rtph263pay = GST_RTP_H263_PAY (object);
|
|
|
|
gst_buffer_replace (&rtph263pay->current_buffer, NULL);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static gboolean
|
|
gst_rtp_h263_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps)
|
|
{
|
|
GstStructure *s = gst_caps_get_structure (caps, 0);
|
|
gint width, height;
|
|
gchar *framesize = NULL;
|
|
gboolean res;
|
|
|
|
if (gst_structure_has_field (s, "width") &&
|
|
gst_structure_has_field (s, "height")) {
|
|
if (!gst_structure_get_int (s, "width", &width) || width <= 0) {
|
|
goto invalid_dimension;
|
|
}
|
|
|
|
if (!gst_structure_get_int (s, "height", &height) || height <= 0) {
|
|
goto invalid_dimension;
|
|
}
|
|
|
|
framesize = g_strdup_printf ("%d-%d", width, height);
|
|
}
|
|
|
|
gst_rtp_base_payload_set_options (payload, "video",
|
|
payload->pt != GST_RTP_PAYLOAD_H263, "H263", 90000);
|
|
|
|
if (framesize != NULL) {
|
|
res = gst_rtp_base_payload_set_outcaps (payload,
|
|
"a-framesize", G_TYPE_STRING, framesize, NULL);
|
|
} else {
|
|
res = gst_rtp_base_payload_set_outcaps (payload, NULL);
|
|
}
|
|
g_free (framesize);
|
|
|
|
return res;
|
|
|
|
/* ERRORS */
|
|
invalid_dimension:
|
|
{
|
|
GST_ERROR_OBJECT (payload, "Invalid width/height from caps");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_rtp_h263_pay_context_destroy (GstRtpH263PayContext * context, guint ind)
|
|
{
|
|
if (!context)
|
|
return;
|
|
|
|
if (context->gobs) {
|
|
guint i;
|
|
|
|
for (i = 0; i < format_props[ind][0]; i++) {
|
|
if (context->gobs[i]) {
|
|
gst_rtp_h263_pay_gob_destroy (context->gobs[i], ind);
|
|
}
|
|
}
|
|
|
|
g_free (context->gobs);
|
|
}
|
|
|
|
g_free (context);
|
|
}
|
|
|
|
static void
|
|
gst_rtp_h263_pay_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstRtpH263Pay *rtph263pay;
|
|
|
|
rtph263pay = GST_RTP_H263_PAY (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_MODE_A_ONLY:
|
|
rtph263pay->prop_payload_mode = g_value_get_boolean (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_rtp_h263_pay_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstRtpH263Pay *rtph263pay;
|
|
|
|
rtph263pay = GST_RTP_H263_PAY (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_MODE_A_ONLY:
|
|
g_value_set_boolean (value, rtph263pay->prop_payload_mode);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static GstRtpH263PayPackage *
|
|
gst_rtp_h263_pay_package_new_empty (void)
|
|
{
|
|
return (GstRtpH263PayPackage *) g_malloc0 (sizeof (GstRtpH263PayPackage));
|
|
}
|
|
|
|
static GstRtpH263PayPackage *
|
|
gst_rtp_h263_pay_package_new (guint8 * start, guint8 * end, guint length,
|
|
guint8 sbit, guint8 ebit, GstBuffer * outbuf, gboolean marker)
|
|
{
|
|
|
|
GstRtpH263PayPackage *package;
|
|
|
|
package = gst_rtp_h263_pay_package_new_empty ();
|
|
|
|
package->payload_start = start;
|
|
package->payload_end = end;
|
|
package->payload_len = length;
|
|
package->sbit = sbit;
|
|
package->ebit = ebit;
|
|
package->outbuf = outbuf;
|
|
package->marker = marker;
|
|
|
|
return package;
|
|
}
|
|
|
|
static void
|
|
gst_rtp_h263_pay_package_destroy (GstRtpH263PayPackage * pack)
|
|
{
|
|
if (pack)
|
|
g_free (pack);
|
|
}
|
|
|
|
static void
|
|
gst_rtp_h263_pay_boundry_init (GstRtpH263PayBoundry * boundry,
|
|
guint8 * start, guint8 * end, guint8 sbit, guint8 ebit)
|
|
{
|
|
boundry->start = start;
|
|
boundry->end = end;
|
|
boundry->sbit = sbit;
|
|
boundry->ebit = ebit;
|
|
}
|
|
|
|
static void
|
|
gst_rtp_h263_pay_splat_header_A (guint8 * header,
|
|
GstRtpH263PayPackage * package, GstRtpH263PayPic * piclayer)
|
|
{
|
|
|
|
GstRtpH263PayAHeader *a_header;
|
|
|
|
a_header = (GstRtpH263PayAHeader *) header;
|
|
|
|
a_header->f = 0;
|
|
a_header->p = 0;
|
|
a_header->sbit = package->sbit;
|
|
a_header->ebit = package->ebit;
|
|
a_header->src = GST_H263_PICTURELAYER_PLSRC (piclayer);
|
|
a_header->i = GST_H263_PICTURELAYER_PLTYPE (piclayer);
|
|
a_header->u = GST_H263_PICTURELAYER_PLUMV (piclayer);
|
|
a_header->s = GST_H263_PICTURELAYER_PLSAC (piclayer);
|
|
a_header->a = GST_H263_PICTURELAYER_PLAP (piclayer);
|
|
a_header->r1 = 0;
|
|
a_header->r2 = 0;
|
|
a_header->dbq = 0;
|
|
a_header->trb = 0;
|
|
a_header->tr = 0;
|
|
|
|
}
|
|
|
|
static void
|
|
gst_rtp_h263_pay_splat_header_B (guint8 * header,
|
|
GstRtpH263PayPackage * package, GstRtpH263PayPic * piclayer)
|
|
{
|
|
|
|
GstRtpH263PayBHeader *b_header;
|
|
|
|
b_header = (GstRtpH263PayBHeader *) header;
|
|
|
|
b_header->f = 1;
|
|
b_header->p = 0;
|
|
b_header->sbit = package->sbit;
|
|
b_header->ebit = package->ebit;
|
|
b_header->src = GST_H263_PICTURELAYER_PLSRC (piclayer);
|
|
b_header->quant = package->quant;
|
|
b_header->gobn = package->gobn;
|
|
b_header->mba1 = package->mba >> 6;
|
|
b_header->mba2 = package->mba & 0x003f;
|
|
b_header->r = 0;
|
|
b_header->i = GST_H263_PICTURELAYER_PLTYPE (piclayer);
|
|
b_header->u = GST_H263_PICTURELAYER_PLUMV (piclayer);
|
|
b_header->s = GST_H263_PICTURELAYER_PLSAC (piclayer);
|
|
b_header->a = GST_H263_PICTURELAYER_PLAP (piclayer);
|
|
|
|
b_header->hmv11 = 0;
|
|
b_header->hmv12 = 0;
|
|
b_header->vmv11 = 0;
|
|
b_header->vmv12 = 0;
|
|
b_header->hmv21 = 0;
|
|
b_header->hmv22 = 0;
|
|
b_header->vmv21 = 0;
|
|
|
|
if (package->nmvd > 0) {
|
|
//mvd[0]
|
|
b_header->hmv11 = (package->mvd[0] & 0x7f) >> 3;
|
|
b_header->hmv12 = (package->mvd[0] & 0x07);
|
|
//mvd[1]
|
|
b_header->vmv11 = (package->mvd[1] & 0x07f) >> 2;
|
|
b_header->vmv12 = (package->mvd[1] & 0x03);
|
|
|
|
if (package->nmvd == 8) {
|
|
//mvd[4]
|
|
b_header->hmv21 = (package->mvd[4] & 0x7f) >> 1;
|
|
b_header->hmv22 = (package->mvd[4] & 0x01);
|
|
//mvd[5]
|
|
b_header->vmv21 = (package->mvd[5] & 0x7f);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static gboolean
|
|
gst_rtp_h263_pay_gobfinder (GstRtpH263Pay * rtph263pay,
|
|
GstRtpH263PayBoundry * boundry)
|
|
{
|
|
guint8 *current;
|
|
guint range;
|
|
guint i;
|
|
|
|
current = boundry->end + 1;
|
|
range = (rtph263pay->data - current) + rtph263pay->available_data;
|
|
|
|
|
|
GST_DEBUG_OBJECT (rtph263pay,
|
|
"Searching for next GOB, data:%p, len:%u, payload_len:%p,"
|
|
" current:%p, range:%u", rtph263pay->data, rtph263pay->available_data,
|
|
boundry->end + 1, current, range);
|
|
|
|
/* If we are past the end, stop */
|
|
if (current >= rtph263pay->data + rtph263pay->available_data)
|
|
return FALSE;
|
|
|
|
for (i = 3; i < range - 3; i++) {
|
|
if ((current[i] == 0x0) &&
|
|
(current[i + 1] == 0x0) && (current[i + 2] >> 7 == 0x1)) {
|
|
GST_LOG_OBJECT (rtph263pay, "GOB end found at: %p start: %p len: %u",
|
|
current + i - 1, boundry->end + 1,
|
|
(guint) (current + i - boundry->end + 2));
|
|
gst_rtp_h263_pay_boundry_init (boundry, boundry->end + 1, current + i - 1,
|
|
0, 0);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (rtph263pay,
|
|
"Couldn't find any new GBSC in this frame, range:%u", range);
|
|
|
|
gst_rtp_h263_pay_boundry_init (boundry, boundry->end + 1,
|
|
(guint8 *) (rtph263pay->data + rtph263pay->available_data - 1), 0, 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GstRtpH263PayGob *
|
|
gst_rtp_h263_pay_gob_new (GstRtpH263PayBoundry * boundry, guint gobn)
|
|
{
|
|
GstRtpH263PayGob *gob;
|
|
|
|
gob = (GstRtpH263PayGob *) g_malloc0 (sizeof (GstRtpH263PayGob));
|
|
|
|
gob->start = boundry->start;
|
|
gob->end = boundry->end;
|
|
gob->length = boundry->end - boundry->start + 1;
|
|
gob->ebit = boundry->ebit;
|
|
gob->sbit = boundry->sbit;
|
|
gob->gobn = gobn;
|
|
gob->quant = 0;
|
|
gob->macroblocks = NULL;
|
|
gob->nmacroblocs = 0;
|
|
|
|
return gob;
|
|
}
|
|
|
|
static void
|
|
gst_rtp_h263_pay_gob_destroy (GstRtpH263PayGob * gob, guint ind)
|
|
{
|
|
|
|
if (!gob)
|
|
return;
|
|
|
|
if (gob->macroblocks) {
|
|
|
|
guint i;
|
|
|
|
for (i = 0; i < gob->nmacroblocs; i++) {
|
|
gst_rtp_h263_pay_mb_destroy (gob->macroblocks[i]);
|
|
}
|
|
|
|
g_free (gob->macroblocks);
|
|
}
|
|
|
|
g_free (gob);
|
|
}
|
|
|
|
/*
|
|
* decode MCBPC for I frames and return index in table or -1 if not found
|
|
*/
|
|
static gint
|
|
gst_rtp_h263_pay_decode_mcbpc_I (GstRtpH263Pay * rtph263pay, guint32 value)
|
|
{
|
|
|
|
gint i;
|
|
guint16 code;
|
|
|
|
code = value >> 16;
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "value:0x%08x, code:0x%04x", value, code);
|
|
|
|
for (i = 0; i < MCBPC_I_LEN; i++) {
|
|
if ((code & mcbpc_I[i][1]) == mcbpc_I[i][0]) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
GST_WARNING_OBJECT (rtph263pay, "Couldn't find code, returning -1");
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* decode MCBPC for P frames and return index in table or -1 if not found
|
|
*/
|
|
static gint
|
|
gst_rtp_h263_pay_decode_mcbpc_P (GstRtpH263Pay * rtph263pay, guint32 value)
|
|
{
|
|
|
|
gint i;
|
|
guint16 code;
|
|
|
|
code = value >> 16;
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "value:0x%08x, code:0x%04x", value, code);
|
|
|
|
for (i = 0; i < MCBPC_P_LEN; i++) {
|
|
if ((code & mcbpc_P[i][1]) == mcbpc_P[i][0]) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
GST_WARNING_OBJECT (rtph263pay, "Couldn't find code, returning -1");
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* decode CBPY and return index in table or -1 if not found
|
|
*/
|
|
static gint
|
|
gst_rtp_h263_pay_decode_cbpy (GstRtpH263Pay * rtph263pay, guint32 value,
|
|
const guint8 cbpy_table[16][7])
|
|
{
|
|
|
|
gint i;
|
|
guint8 code;
|
|
|
|
code = value >> 24;
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "value:0x%08x, code:0x%04x", value, code);
|
|
|
|
for (i = 0; i < CBPY_LEN; i++) {
|
|
if ((code & cbpy_table[i][1]) == cbpy_table[i][0]) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
GST_WARNING_OBJECT (rtph263pay, "Couldn't find code, returning -1");
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* decode MVD and return index in table or -1 if not found
|
|
*/
|
|
static gint
|
|
gst_rtp_h263_pay_decode_mvd (GstRtpH263Pay * rtph263pay, guint32 value)
|
|
{
|
|
|
|
gint i;
|
|
guint16 code;
|
|
|
|
code = value >> 16;
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "value:0x%08x, code:0x%04x", value, code);
|
|
|
|
for (i = 0; i < MVD_LEN; i++) {
|
|
if ((code & mvd[i][1]) == mvd[i][0]) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
GST_WARNING_OBJECT (rtph263pay, "Couldn't find code, returning -1");
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* decode TCOEF and return index in table or -1 if not found
|
|
*/
|
|
static gint
|
|
gst_rtp_h263_pay_decode_tcoef (GstRtpH263Pay * rtph263pay, guint32 value)
|
|
{
|
|
|
|
gint i;
|
|
guint16 code;
|
|
|
|
code = value >> 16;
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "value:0x%08x, code:0x%04x", value, code);
|
|
|
|
for (i = 0; i < TCOEF_LEN; i++) {
|
|
if ((code & tcoef[i][1]) == tcoef[i][0]) {
|
|
GST_TRACE_OBJECT (rtph263pay, "tcoef is %d", i);
|
|
return i;
|
|
}
|
|
}
|
|
|
|
GST_WARNING_OBJECT (rtph263pay, "Couldn't find code, returning -1");
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* the 32-bit register is like a window that we move right for "move_bits" to get the next v "data" h263 field
|
|
* "rest_bits" tells how many bits in the "data" byte address are still not used
|
|
*/
|
|
|
|
static gint
|
|
gst_rtp_h263_pay_move_window_right (GstRtpH263Pay * rtph263pay,
|
|
GstRtpH263PayContext * context, guint n, guint rest_bits,
|
|
guint8 ** orig_data, guint8 ** data_end)
|
|
{
|
|
|
|
GST_TRACE_OBJECT (rtph263pay,
|
|
"Moving window: 0x%08x from: %p for %d bits, rest_bits: %d, data_end %p",
|
|
context->window, context->win_end, n, rest_bits, *data_end);
|
|
|
|
if (n == 0)
|
|
return rest_bits;
|
|
|
|
while (n != 0 || context->win_end == ((*data_end) + 1)) {
|
|
guint8 b = context->win_end <= *data_end ? *context->win_end : 0;
|
|
if (rest_bits == 0) {
|
|
if (n > 8) {
|
|
context->window = (context->window << 8) | b;
|
|
n -= 8;
|
|
} else {
|
|
context->window = (context->window << n) | (b >> (8 - n));
|
|
rest_bits = 8 - n;
|
|
if (rest_bits == 0)
|
|
context->win_end++;
|
|
break;
|
|
}
|
|
} else {
|
|
if (n > rest_bits) {
|
|
context->window = (context->window << rest_bits) |
|
|
(b & (((guint) pow (2.0, (double) rest_bits)) - 1));
|
|
n -= rest_bits;
|
|
rest_bits = 0;
|
|
} else {
|
|
context->window = (context->window << n) |
|
|
((b & (((guint) pow (2.0, (double) rest_bits)) - 1)) >>
|
|
(rest_bits - n));
|
|
rest_bits -= n;
|
|
if (rest_bits == 0)
|
|
context->win_end++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
context->win_end++;
|
|
}
|
|
|
|
*orig_data = context->win_end - 4;
|
|
|
|
GST_TRACE_OBJECT (rtph263pay,
|
|
"Window moved to %p with value: 0x%08x and orig_data: %p rest_bits: %d",
|
|
context->win_end, context->window, *orig_data, rest_bits);
|
|
return rest_bits;
|
|
}
|
|
|
|
/*
|
|
* Find the start of the next MB (end of the current MB)
|
|
* returns the number of excess bits and stores the end of the MB in end
|
|
* data must be placed on first MB byte
|
|
*/
|
|
static GstRtpH263PayMB *
|
|
gst_rtp_h263_pay_B_mbfinder (GstRtpH263Pay * rtph263pay,
|
|
GstRtpH263PayContext * context, GstRtpH263PayGob * gob,
|
|
GstRtpH263PayMB * macroblock, guint mba)
|
|
{
|
|
guint mb_type_index;
|
|
guint cbpy_type_index;
|
|
guint tcoef_type_index;
|
|
GstRtpH263PayMB *mac;
|
|
GstRtpH263PayBoundry boundry;
|
|
|
|
gst_rtp_h263_pay_boundry_init (&boundry, macroblock->end,
|
|
macroblock->end, 8 - macroblock->ebit, macroblock->ebit);
|
|
mac = gst_rtp_h263_pay_mb_new (&boundry, mba);
|
|
|
|
if (mac->sbit == 8) {
|
|
mac->start++;
|
|
// mac->end++;
|
|
mac->sbit = 0;
|
|
}
|
|
|
|
GST_LOG_OBJECT (rtph263pay,
|
|
"current_pos:%p, end:%p, rest_bits:%d, window:0x%08x", mac->start,
|
|
mac->end, macroblock->ebit, context->window);
|
|
|
|
if (context->piclayer->ptype_pictype == 0) {
|
|
//We have an I frame
|
|
gint i;
|
|
guint last;
|
|
guint ind;
|
|
|
|
//Step 2 decode MCBPC I
|
|
mb_type_index =
|
|
gst_rtp_h263_pay_decode_mcbpc_I (rtph263pay, context->window);
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "MCBPC index: %d", mb_type_index);
|
|
if (mb_type_index == -1) {
|
|
GST_ERROR_OBJECT (rtph263pay, "MB index shouldn't be -1 in window: %08x",
|
|
context->window);
|
|
goto beach;
|
|
}
|
|
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context,
|
|
mcbpc_I[mb_type_index][2], mac->ebit, &mac->end, &gob->end);
|
|
|
|
mac->mb_type = mcbpc_I[mb_type_index][5];
|
|
|
|
if (mb_type_index == 8) {
|
|
GST_TRACE_OBJECT (rtph263pay, "Stuffing skipping rest of MB header");
|
|
return mac;
|
|
}
|
|
//Step 3 decode CBPY I
|
|
cbpy_type_index =
|
|
gst_rtp_h263_pay_decode_cbpy (rtph263pay, context->window, cbpy_I);
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "CBPY index: %d", cbpy_type_index);
|
|
if (cbpy_type_index == -1) {
|
|
GST_ERROR_OBJECT (rtph263pay,
|
|
"CBPY index shouldn't be -1 in window: %08x", context->window);
|
|
goto beach;
|
|
}
|
|
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context,
|
|
cbpy_I[cbpy_type_index][2], mac->ebit, &mac->end, &gob->end);
|
|
|
|
//Step 4 decode rest of MB
|
|
//MB type 1 and 4 have DQUANT - we store it for packaging purposes
|
|
if (mcbpc_I[mb_type_index][5] == 4) {
|
|
GST_TRACE_OBJECT (rtph263pay, "Shifting DQUANT");
|
|
|
|
mac->quant = (context->window >> 30);
|
|
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context, 2, mac->ebit,
|
|
&mac->end, &gob->end);
|
|
}
|
|
//Step 5 go trough the blocks - decode DC and TCOEF
|
|
last = 0;
|
|
for (i = 0; i < N_BLOCKS; i++) {
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "Decoding INTRADC and TCOEF, i:%d", i);
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context, 8, mac->ebit,
|
|
&mac->end, &gob->end);
|
|
|
|
if (i > 3) {
|
|
ind = mcbpc_I[mb_type_index][i - 1];
|
|
} else {
|
|
ind = cbpy_I[cbpy_type_index][i + 3];
|
|
}
|
|
|
|
if (ind == 1) {
|
|
while (last == 0) {
|
|
tcoef_type_index =
|
|
gst_rtp_h263_pay_decode_tcoef (rtph263pay, context->window);
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "TCOEF index: %d", tcoef_type_index);
|
|
if (tcoef_type_index == -1) {
|
|
GST_ERROR_OBJECT (rtph263pay,
|
|
"TCOEF index shouldn't be -1 in window: %08x", context->window);
|
|
goto beach;
|
|
}
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context,
|
|
tcoef[tcoef_type_index][2], mac->ebit, &mac->end, &gob->end);
|
|
|
|
last = tcoef[tcoef_type_index][3];
|
|
if (tcoef_type_index == 102) {
|
|
if ((context->window & 0x80000000) == 0x80000000)
|
|
last = 1;
|
|
else
|
|
last = 0;
|
|
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context, 15,
|
|
mac->ebit, &mac->end, &gob->end);
|
|
}
|
|
}
|
|
last = 0;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//We have a P frame
|
|
guint i;
|
|
guint last;
|
|
guint ind;
|
|
|
|
//Step 1 check COD
|
|
GST_TRACE_OBJECT (rtph263pay, "Checking for COD");
|
|
if ((context->window & 0x80000000) == 0x80000000) {
|
|
//The MB is not coded
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context, 1, mac->ebit,
|
|
&mac->end, &gob->end);
|
|
GST_TRACE_OBJECT (rtph263pay, "COOOOOOOOOOOD = 1");
|
|
|
|
return mac;
|
|
} else {
|
|
//The MB is coded
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context, 1, mac->ebit,
|
|
&mac->end, &gob->end);
|
|
}
|
|
|
|
//Step 2 decode MCBPC P
|
|
mb_type_index =
|
|
gst_rtp_h263_pay_decode_mcbpc_P (rtph263pay, context->window);
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "MCBPC index: %d", mb_type_index);
|
|
if (mb_type_index == -1) {
|
|
GST_ERROR_OBJECT (rtph263pay, "MB index shouldn't be -1 in window: %08x",
|
|
context->window);
|
|
goto beach;
|
|
}
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context,
|
|
mcbpc_P[mb_type_index][2], mac->ebit, &mac->end, &gob->end);
|
|
|
|
mac->mb_type = mcbpc_P[mb_type_index][5];
|
|
|
|
if (mb_type_index == 20) {
|
|
GST_TRACE_OBJECT (rtph263pay, "Stuffing skipping rest of MB header");
|
|
return mac;
|
|
}
|
|
//Step 3 decode CBPY P
|
|
cbpy_type_index =
|
|
gst_rtp_h263_pay_decode_cbpy (rtph263pay, context->window, cbpy_P);
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "CBPY index: %d", cbpy_type_index);
|
|
if (cbpy_type_index == -1) {
|
|
GST_ERROR_OBJECT (rtph263pay,
|
|
"CBPY index shouldn't be -1 in window: %08x", context->window);
|
|
goto beach;
|
|
}
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context,
|
|
cbpy_P[cbpy_type_index][2], mac->ebit, &mac->end, &gob->end);
|
|
|
|
//MB type 1 and 4 have DQUANT - we add it to MB object and jump over
|
|
if (mcbpc_P[mb_type_index][5] == 4 || mcbpc_P[mb_type_index][5] == 1) {
|
|
GST_TRACE_OBJECT (rtph263pay, "Shifting DQUANT");
|
|
|
|
mac->quant = context->window >> 30;
|
|
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context, 2, mac->ebit,
|
|
&mac->end, &gob->end);
|
|
}
|
|
//MB types < 3 have MVD1-4
|
|
if (mcbpc_P[mb_type_index][5] < 3) {
|
|
|
|
guint nmvd;
|
|
gint j;
|
|
|
|
nmvd = 2;
|
|
if (mcbpc_P[mb_type_index][5] == 2)
|
|
nmvd = 8;
|
|
|
|
for (j = 0; j < nmvd; j++) {
|
|
guint mvd_type;
|
|
|
|
mvd_type = gst_rtp_h263_pay_decode_mvd (rtph263pay, context->window);
|
|
|
|
if (mvd_type == -1) {
|
|
GST_ERROR_OBJECT (rtph263pay,
|
|
"MVD1-4 index shouldn't be -1 in window: %08x", context->window);
|
|
goto beach;
|
|
}
|
|
//set the MB mvd values
|
|
mac->mvd[j] = mvd[mvd_type][3];
|
|
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context,
|
|
mvd[mvd_type][2], mac->ebit, &mac->end, &gob->end);
|
|
}
|
|
|
|
|
|
}
|
|
//Step 5 go trough the blocks - decode DC and TCOEF
|
|
last = 0;
|
|
for (i = 0; i < N_BLOCKS; i++) {
|
|
|
|
//if MB type 3 or 4 then INTRADC coef are present in blocks
|
|
if (mcbpc_P[mb_type_index][5] > 2) {
|
|
GST_TRACE_OBJECT (rtph263pay, "INTRADC coef: %d", i);
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context, 8,
|
|
mac->ebit, &mac->end, &gob->end);
|
|
} else {
|
|
GST_TRACE_OBJECT (rtph263pay, "INTRADC coef is not present");
|
|
}
|
|
|
|
//check if the block has TCOEF
|
|
if (i > 3) {
|
|
ind = mcbpc_P[mb_type_index][i - 1];
|
|
} else {
|
|
if (mcbpc_P[mb_type_index][5] > 2) {
|
|
ind = cbpy_I[cbpy_type_index][i + 3];
|
|
} else {
|
|
ind = cbpy_P[cbpy_type_index][i + 3];
|
|
}
|
|
}
|
|
|
|
if (ind == 1) {
|
|
while (last == 0) {
|
|
tcoef_type_index =
|
|
gst_rtp_h263_pay_decode_tcoef (rtph263pay, context->window);
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "TCOEF index: %d", tcoef_type_index);
|
|
if (tcoef_type_index == -1) {
|
|
GST_ERROR_OBJECT (rtph263pay,
|
|
"TCOEF index shouldn't be -1 in window: %08x", context->window);
|
|
goto beach;
|
|
}
|
|
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context,
|
|
tcoef[tcoef_type_index][2], mac->ebit, &mac->end, &gob->end);
|
|
|
|
last = tcoef[tcoef_type_index][3];
|
|
if (tcoef_type_index == 102) {
|
|
if ((context->window & 0x80000000) == 0x80000000)
|
|
last = 1;
|
|
else
|
|
last = 0;
|
|
|
|
mac->ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context, 15,
|
|
mac->ebit, &mac->end, &gob->end);
|
|
}
|
|
}
|
|
last = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
mac->length = mac->end - mac->start + 1;
|
|
|
|
return mac;
|
|
|
|
beach:
|
|
gst_rtp_h263_pay_mb_destroy (mac);
|
|
return NULL;
|
|
}
|
|
|
|
static GstRtpH263PayMB *
|
|
gst_rtp_h263_pay_mb_new (GstRtpH263PayBoundry * boundry, guint mba)
|
|
{
|
|
GstRtpH263PayMB *mb;
|
|
gint i;
|
|
|
|
mb = (GstRtpH263PayMB *) g_malloc0 (sizeof (GstRtpH263PayMB));
|
|
|
|
mb->start = boundry->start;
|
|
mb->end = boundry->end;
|
|
mb->length = boundry->end - boundry->start + 1;
|
|
mb->sbit = boundry->sbit;
|
|
mb->ebit = boundry->ebit;
|
|
mb->mba = mba;
|
|
|
|
for (i = 0; i < 5; i++)
|
|
mb->mvd[i] = 0;
|
|
|
|
return mb;
|
|
}
|
|
|
|
static void
|
|
gst_rtp_h263_pay_mb_destroy (GstRtpH263PayMB * mb)
|
|
{
|
|
if (!mb)
|
|
return;
|
|
|
|
g_free (mb);
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_rtp_h263_pay_push (GstRtpH263Pay * rtph263pay,
|
|
GstRtpH263PayContext * context, GstRtpH263PayPackage * package)
|
|
{
|
|
|
|
/*
|
|
* Splat the payload header values
|
|
*/
|
|
guint8 *header;
|
|
GstFlowReturn ret;
|
|
GstRTPBuffer rtp = { NULL };
|
|
|
|
gst_rtp_buffer_map (package->outbuf, GST_MAP_WRITE, &rtp);
|
|
|
|
header = gst_rtp_buffer_get_payload (&rtp);
|
|
|
|
switch (package->mode) {
|
|
case GST_RTP_H263_PAYLOAD_HEADER_MODE_A:
|
|
GST_LOG_OBJECT (rtph263pay, "Pushing A packet");
|
|
gst_rtp_h263_pay_splat_header_A (header, package, context->piclayer);
|
|
break;
|
|
case GST_RTP_H263_PAYLOAD_HEADER_MODE_B:
|
|
GST_LOG_OBJECT (rtph263pay, "Pushing B packet");
|
|
gst_rtp_h263_pay_splat_header_B (header, package, context->piclayer);
|
|
break;
|
|
case GST_RTP_H263_PAYLOAD_HEADER_MODE_C:
|
|
//gst_rtp_h263_pay_splat_header_C(header, package, context->piclayer);
|
|
//break;
|
|
default:
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
/*
|
|
* timestamp the buffer
|
|
*/
|
|
GST_BUFFER_PTS (package->outbuf) = rtph263pay->first_ts;
|
|
|
|
gst_rtp_buffer_set_marker (&rtp, package->marker);
|
|
if (package->marker) {
|
|
GST_BUFFER_FLAG_SET (package->outbuf, GST_BUFFER_FLAG_MARKER);
|
|
GST_DEBUG_OBJECT (rtph263pay, "Marker set!");
|
|
}
|
|
|
|
gst_rtp_buffer_unmap (&rtp);
|
|
|
|
/*
|
|
* Copy the payload data in the buffer
|
|
*/
|
|
GST_DEBUG_OBJECT (rtph263pay, "Copying memory");
|
|
gst_buffer_copy_into (package->outbuf, rtph263pay->current_buffer,
|
|
GST_BUFFER_COPY_MEMORY, package->payload_start - rtph263pay->map.data,
|
|
package->payload_len);
|
|
gst_rtp_copy_video_meta (rtph263pay, package->outbuf,
|
|
rtph263pay->current_buffer);
|
|
|
|
ret =
|
|
gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtph263pay),
|
|
package->outbuf);
|
|
GST_DEBUG_OBJECT (rtph263pay, "Package pushed, returning");
|
|
|
|
gst_rtp_h263_pay_package_destroy (package);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_rtp_h263_pay_A_fragment_push (GstRtpH263Pay * rtph263pay,
|
|
GstRtpH263PayContext * context, guint first, guint last)
|
|
{
|
|
|
|
GstRtpH263PayPackage *pack;
|
|
|
|
pack = gst_rtp_h263_pay_package_new_empty ();
|
|
|
|
pack->payload_start = context->gobs[first]->start;
|
|
pack->sbit = context->gobs[first]->sbit;
|
|
pack->ebit = context->gobs[last]->ebit;
|
|
pack->payload_len =
|
|
(context->gobs[last]->end - context->gobs[first]->start) + 1;
|
|
pack->marker = FALSE;
|
|
|
|
if (last == context->no_gobs - 1) {
|
|
pack->marker = TRUE;
|
|
}
|
|
|
|
pack->gobn = context->gobs[first]->gobn;
|
|
pack->mode = GST_RTP_H263_PAYLOAD_HEADER_MODE_A;
|
|
pack->outbuf =
|
|
gst_rtp_base_payload_allocate_output_buffer (GST_RTP_BASE_PAYLOAD
|
|
(rtph263pay), pack->mode, 0, 0);
|
|
|
|
GST_DEBUG_OBJECT (rtph263pay, "Sending len:%d data to push function",
|
|
pack->payload_len);
|
|
|
|
return gst_rtp_h263_pay_push (rtph263pay, context, pack);
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_rtp_h263_pay_B_fragment_push (GstRtpH263Pay * rtph263pay,
|
|
GstRtpH263PayContext * context, GstRtpH263PayGob * gob, guint first,
|
|
guint last, GstRtpH263PayBoundry * boundry)
|
|
{
|
|
|
|
GstRtpH263PayPackage *pack;
|
|
guint mv;
|
|
|
|
pack = gst_rtp_h263_pay_package_new_empty ();
|
|
|
|
pack->payload_start = gob->macroblocks[first]->start;
|
|
pack->sbit = gob->macroblocks[first]->sbit;
|
|
if (first == 0) {
|
|
pack->payload_start = boundry->start;
|
|
pack->sbit = boundry->sbit;
|
|
pack->quant = gob->quant;
|
|
} else {
|
|
pack->quant = gob->macroblocks[first]->quant;
|
|
}
|
|
pack->payload_end = gob->macroblocks[last]->end;
|
|
|
|
pack->ebit = gob->macroblocks[last]->ebit;
|
|
pack->mba = gob->macroblocks[first]->mba;
|
|
pack->gobn = gob->gobn;
|
|
pack->mode = GST_RTP_H263_PAYLOAD_HEADER_MODE_B;
|
|
pack->nmvd = 0;
|
|
|
|
if (gob->macroblocks[first]->mb_type < 3) {
|
|
if (gob->macroblocks[first]->mb_type == 2)
|
|
pack->nmvd = 8;
|
|
else if (gob->macroblocks[first]->mb_type < 2)
|
|
pack->nmvd = 2;
|
|
|
|
for (mv = 0; mv < pack->nmvd; mv++)
|
|
pack->mvd[mv] = gob->macroblocks[first]->mvd[mv];
|
|
}
|
|
|
|
pack->marker = FALSE;
|
|
if (last == gob->nmacroblocs - 1) {
|
|
pack->ebit = 0;
|
|
}
|
|
|
|
if ((format_props[context->piclayer->ptype_srcformat][0] - 1 == gob->gobn)
|
|
&& (last == gob->nmacroblocs - 1)) {
|
|
pack->marker = TRUE;
|
|
}
|
|
|
|
pack->payload_len = pack->payload_end - pack->payload_start + 1;
|
|
pack->outbuf =
|
|
gst_rtp_base_payload_allocate_output_buffer (GST_RTP_BASE_PAYLOAD
|
|
(rtph263pay), pack->mode, 0, 0);
|
|
|
|
return gst_rtp_h263_pay_push (rtph263pay, context, pack);
|
|
}
|
|
|
|
|
|
static gboolean
|
|
gst_rtp_h263_pay_mode_B_fragment (GstRtpH263Pay * rtph263pay,
|
|
GstRtpH263PayContext * context, GstRtpH263PayGob * gob)
|
|
{
|
|
|
|
|
|
/*---------- MODE B MODE FRAGMENTATION ----------*/
|
|
GstRtpH263PayMB *mac, *mac0;
|
|
guint max_payload_size;
|
|
GstRtpH263PayBoundry boundry;
|
|
guint mb;
|
|
guint8 ebit;
|
|
|
|
guint first = 0;
|
|
guint payload_len;
|
|
|
|
max_payload_size =
|
|
context->mtu - GST_RTP_H263_PAYLOAD_HEADER_MODE_B - GST_RTP_HEADER_LEN;
|
|
|
|
gst_rtp_h263_pay_boundry_init (&boundry, gob->start, gob->start, gob->sbit,
|
|
0);
|
|
|
|
gob->macroblocks =
|
|
(GstRtpH263PayMB **) g_malloc0 (sizeof (GstRtpH263PayMB *) *
|
|
format_props[context->piclayer->ptype_srcformat][1]);
|
|
|
|
GST_LOG_OBJECT (rtph263pay, "GOB isn't PB frame, applying mode B");
|
|
|
|
//initializing window
|
|
context->win_end = boundry.end;
|
|
if (gst_rtp_h263_pay_move_window_right (rtph263pay, context, 32, boundry.ebit,
|
|
&boundry.end, &gob->end) != 0) {
|
|
GST_ERROR_OBJECT (rtph263pay,
|
|
"The rest of the bits should be 0, exiting, because something bad happend");
|
|
goto decode_error;
|
|
}
|
|
//The first GOB of a frame "has no" actual header - PICTURE header is his header
|
|
if (gob->gobn == 0) {
|
|
guint shift;
|
|
GST_LOG_OBJECT (rtph263pay, "Initial GOB");
|
|
shift = 43;
|
|
|
|
boundry.ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context, shift,
|
|
boundry.ebit, &boundry.end, &gob->end);
|
|
|
|
//We need PQUANT for mode B packages - so we store it
|
|
gob->quant = context->window >> 27;
|
|
|
|
//if PCM == 1, then PSBI is present - header has 51 bits
|
|
//shift for PQUANT (5) and PCM (1) = 6 bits
|
|
shift = 6;
|
|
if (context->cpm == 1)
|
|
shift += 2;
|
|
boundry.ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context, shift,
|
|
boundry.ebit, &boundry.end, &gob->end);
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "window: 0x%08x", context->window);
|
|
|
|
//Shifting the PEI and PSPARE fields
|
|
while ((context->window & 0x80000000) == 0x80000000) {
|
|
boundry.ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context, 9,
|
|
boundry.ebit, &boundry.end, &gob->end);
|
|
GST_TRACE_OBJECT (rtph263pay, "window: 0x%08x", context->window);
|
|
}
|
|
|
|
//shift the last PEI field
|
|
boundry.ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context, 1,
|
|
boundry.ebit, &boundry.end, &gob->end);
|
|
|
|
} else {
|
|
//skipping GOBs 24 header bits + 5 GQUANT
|
|
guint shift = 24;
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "INTER GOB");
|
|
|
|
//if CPM == 1, there are 2 more bits in the header - GSBI header is 31 bits long
|
|
if (context->cpm == 1)
|
|
shift += 2;
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "window: 0x%08x", context->window);
|
|
boundry.ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context, shift,
|
|
boundry.ebit, &boundry.end, &gob->end);
|
|
|
|
//We need GQUANT for mode B packages - so we store it
|
|
gob->quant = context->window >> 27;
|
|
|
|
shift = 5;
|
|
boundry.ebit =
|
|
gst_rtp_h263_pay_move_window_right (rtph263pay, context, shift,
|
|
boundry.ebit, &boundry.end, &gob->end);
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "window: 0x%08x", context->window);
|
|
}
|
|
|
|
GST_TRACE_OBJECT (rtph263pay, "GQUANT IS: %08x", gob->quant);
|
|
|
|
// We are on MB layer
|
|
|
|
mac = mac0 = gst_rtp_h263_pay_mb_new (&boundry, 0);
|
|
for (mb = 0; mb < format_props[context->piclayer->ptype_srcformat][1]; mb++) {
|
|
|
|
GST_TRACE_OBJECT (rtph263pay,
|
|
"================ START MB %d =================", mb);
|
|
|
|
//Find next macroblock boundaries
|
|
ebit = mac->ebit;
|
|
if (!(mac =
|
|
gst_rtp_h263_pay_B_mbfinder (rtph263pay, context, gob, mac, mb))) {
|
|
|
|
GST_LOG_OBJECT (rtph263pay, "Error decoding MB - sbit: %d", 8 - ebit);
|
|
GST_ERROR_OBJECT (rtph263pay, "Error decoding in GOB");
|
|
|
|
gst_rtp_h263_pay_mb_destroy (mac0);
|
|
goto decode_error;
|
|
}
|
|
|
|
/* Store macroblock for further processing and delete old MB if any */
|
|
gst_rtp_h263_pay_mb_destroy (gob->macroblocks[mb]);
|
|
gob->macroblocks[mb] = mac;
|
|
|
|
//If mb_type == stuffing, don't increment the mb address
|
|
if (mac->mb_type == 10) {
|
|
mb--;
|
|
continue;
|
|
} else {
|
|
gob->nmacroblocs++;
|
|
}
|
|
|
|
if (mac->end >= gob->end) {
|
|
GST_LOG_OBJECT (rtph263pay, "No more MBs in this GOB");
|
|
if (!mac->ebit) {
|
|
mac->end--;
|
|
}
|
|
gob->end = mac->end;
|
|
break;
|
|
}
|
|
GST_DEBUG_OBJECT (rtph263pay,
|
|
"Found MB: mba: %d start: %p end: %p len: %d sbit: %d ebit: %d",
|
|
mac->mba, mac->start, mac->end, mac->length, mac->sbit, mac->ebit);
|
|
GST_TRACE_OBJECT (rtph263pay,
|
|
"================ END MB %d =================", mb);
|
|
}
|
|
gst_rtp_h263_pay_mb_destroy (mac0);
|
|
|
|
mb = 0;
|
|
first = 0;
|
|
payload_len = boundry.end - boundry.start + 1;
|
|
GST_DEBUG_OBJECT (rtph263pay,
|
|
"------------------------- NEW PACKAGE ----------------------");
|
|
while (mb < gob->nmacroblocs) {
|
|
if (payload_len + gob->macroblocks[mb]->length < max_payload_size) {
|
|
|
|
//FIXME: payload_len is not the real length -> ignoring sbit/ebit
|
|
payload_len += gob->macroblocks[mb]->length;
|
|
mb++;
|
|
|
|
} else {
|
|
//FIXME: we should include the last few bits of the GOB in the package - do we do that now?
|
|
//GST_DEBUG_OBJECT (rtph263pay, "Pushing GOBS %d to %d because payload size is %d", first,
|
|
// first == mb - 1, payload_len);
|
|
|
|
// FIXME: segfault if mb == 0 (first MB is larger than max_payload_size)
|
|
GST_DEBUG_OBJECT (rtph263pay, "Push B mode fragment from mb %d to %d",
|
|
first, mb - 1);
|
|
if (gst_rtp_h263_pay_B_fragment_push (rtph263pay, context, gob, first,
|
|
mb - 1, &boundry)) {
|
|
GST_ERROR_OBJECT (rtph263pay, "Oooops, there was an error sending");
|
|
goto decode_error;
|
|
}
|
|
|
|
payload_len = 0;
|
|
first = mb;
|
|
GST_DEBUG_OBJECT (rtph263pay,
|
|
"------------------------- END PACKAGE ----------------------");
|
|
GST_DEBUG_OBJECT (rtph263pay,
|
|
"------------------------- NEW PACKAGE ----------------------");
|
|
}
|
|
}
|
|
|
|
/* Push rest */
|
|
GST_DEBUG_OBJECT (rtph263pay, "Remainder first: %d, MB: %d", first, mb);
|
|
if (payload_len != 0) {
|
|
GST_DEBUG_OBJECT (rtph263pay, "Push B mode fragment from mb %d to %d",
|
|
first, mb - 1);
|
|
if (gst_rtp_h263_pay_B_fragment_push (rtph263pay, context, gob, first,
|
|
mb - 1, &boundry)) {
|
|
GST_ERROR_OBJECT (rtph263pay, "Oooops, there was an error sending!");
|
|
goto decode_error;
|
|
}
|
|
}
|
|
|
|
/*---------- END OF MODE B FRAGMENTATION ----------*/
|
|
|
|
return TRUE;
|
|
|
|
decode_error:
|
|
return FALSE;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_rtp_h263_send_entire_frame (GstRtpH263Pay * rtph263pay,
|
|
GstRtpH263PayContext * context)
|
|
{
|
|
GstRtpH263PayPackage *pack;
|
|
pack =
|
|
gst_rtp_h263_pay_package_new (rtph263pay->data,
|
|
rtph263pay->data + rtph263pay->available_data,
|
|
rtph263pay->available_data, 0, 0, NULL, TRUE);
|
|
pack->mode = GST_RTP_H263_PAYLOAD_HEADER_MODE_A;
|
|
|
|
GST_DEBUG_OBJECT (rtph263pay, "Available data: %d",
|
|
rtph263pay->available_data);
|
|
|
|
pack->outbuf =
|
|
gst_rtp_base_payload_allocate_output_buffer (GST_RTP_BASE_PAYLOAD
|
|
(rtph263pay), GST_RTP_H263_PAYLOAD_HEADER_MODE_A, 0, 0);
|
|
|
|
return gst_rtp_h263_pay_push (rtph263pay, context, pack);
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_rtp_h263_pay_flush (GstRtpH263Pay * rtph263pay)
|
|
{
|
|
|
|
/*
|
|
* FIXME: GSTUF bits are ignored right now,
|
|
* - not using EBIT/SBIT payload header fields in mode A fragmentation - ffmpeg doesn't need them, but others?
|
|
*/
|
|
|
|
GstFlowReturn ret;
|
|
GstRtpH263PayContext *context;
|
|
gint i;
|
|
|
|
ret = 0;
|
|
context = (GstRtpH263PayContext *) g_malloc0 (sizeof (GstRtpH263PayContext));
|
|
context->mtu =
|
|
rtph263pay->payload.mtu - (MTU_SECURITY_OFFSET + GST_RTP_HEADER_LEN +
|
|
GST_RTP_H263_PAYLOAD_HEADER_MODE_C);
|
|
|
|
GST_DEBUG_OBJECT (rtph263pay, "MTU: %d", context->mtu);
|
|
rtph263pay->available_data = gst_buffer_get_size (rtph263pay->current_buffer);
|
|
if (rtph263pay->available_data == 0) {
|
|
ret = GST_FLOW_OK;
|
|
goto end;
|
|
}
|
|
|
|
/* Get a pointer to all the data for the frame */
|
|
gst_buffer_map (rtph263pay->current_buffer, &rtph263pay->map, GST_MAP_READ);
|
|
rtph263pay->data = (guint8 *) rtph263pay->map.data;
|
|
|
|
/* Picture header */
|
|
context->piclayer = (GstRtpH263PayPic *) rtph263pay->data;
|
|
|
|
if (context->piclayer->ptype_pictype == 0)
|
|
GST_DEBUG_OBJECT (rtph263pay, "We got an I-frame");
|
|
else
|
|
GST_DEBUG_OBJECT (rtph263pay, "We got a P-frame");
|
|
|
|
context->cpm = rtph263pay->data[6] >> 7;
|
|
|
|
GST_DEBUG_OBJECT (rtph263pay, "CPM: %d", context->cpm);
|
|
|
|
GST_DEBUG_OBJECT (rtph263pay, "Payload length is: %d",
|
|
rtph263pay->available_data);
|
|
|
|
/*
|
|
* - MODE A - If normal, I and P frames, -> mode A
|
|
* - GOB layer fragmentation
|
|
* - MODE B - If normal, I and P frames, but GOBs > mtu
|
|
* - MB layer fragmentation
|
|
* - MODE C - For P frames with PB option, but GOBs > mtu
|
|
* - MB layer fragmentation
|
|
*/
|
|
if (rtph263pay->available_data + GST_RTP_H263_PAYLOAD_HEADER_MODE_A +
|
|
GST_RTP_HEADER_LEN < context->mtu) {
|
|
ret = gst_rtp_h263_send_entire_frame (rtph263pay, context);
|
|
} else {
|
|
|
|
/*---------- FRAGMENTING THE FRAME BECAUSE TOO LARGE TO FIT IN MTU ----------*/
|
|
GstRtpH263PayBoundry bound;
|
|
gint first;
|
|
guint payload_len;
|
|
gboolean forcea = FALSE;
|
|
|
|
GST_DEBUG_OBJECT (rtph263pay, "Frame too large for MTU");
|
|
/*
|
|
* Let's go trough all the data and fragment it until end is reached
|
|
*/
|
|
|
|
gst_rtp_h263_pay_boundry_init (&bound, NULL, rtph263pay->data - 1, 0, 0);
|
|
context->gobs =
|
|
(GstRtpH263PayGob **) g_malloc0 (format_props[context->piclayer->
|
|
ptype_srcformat][0] * sizeof (GstRtpH263PayGob *));
|
|
|
|
|
|
for (i = 0; i < format_props[context->piclayer->ptype_srcformat][0]; i++) {
|
|
GST_DEBUG_OBJECT (rtph263pay, "Searching for gob %d", i);
|
|
if (!gst_rtp_h263_pay_gobfinder (rtph263pay, &bound)) {
|
|
if (i <= 1) {
|
|
GST_WARNING_OBJECT (rtph263pay,
|
|
"No GOB's were found in data stream! Please enable RTP mode in encoder. Forcing mode A for now.");
|
|
ret = gst_rtp_h263_send_entire_frame (rtph263pay, context);
|
|
goto end;
|
|
} else {
|
|
/* try to send fragments corresponding to found GOBs */
|
|
forcea = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
context->gobs[i] = gst_rtp_h263_pay_gob_new (&bound, i);
|
|
//FIXME - encoders may generate an EOS gob that has to be processed
|
|
GST_DEBUG_OBJECT (rtph263pay,
|
|
"Gob values are: gobn: %d, start: %p len: %d ebit: %d sbit: %d", i,
|
|
context->gobs[i]->start, context->gobs[i]->length,
|
|
context->gobs[i]->ebit, context->gobs[i]->sbit);
|
|
}
|
|
/* NOTE some places may still assume this to be the max possible */
|
|
context->no_gobs = i;
|
|
GST_DEBUG_OBJECT (rtph263pay, "Found %d GOBS of maximum %d",
|
|
context->no_gobs, format_props[context->piclayer->ptype_srcformat][0]);
|
|
|
|
// Make packages smaller than MTU
|
|
// A mode
|
|
// - if ( GOB > MTU) -> B mode || C mode
|
|
// Push packages
|
|
|
|
first = 0;
|
|
payload_len = 0;
|
|
i = 0;
|
|
while (i < context->no_gobs) {
|
|
|
|
if (context->gobs[i]->length >= context->mtu) {
|
|
if (payload_len == 0) {
|
|
|
|
GST_DEBUG_OBJECT (rtph263pay, "GOB len > MTU");
|
|
if (rtph263pay->prop_payload_mode || forcea) {
|
|
payload_len = context->gobs[i]->length;
|
|
goto force_a;
|
|
}
|
|
if (!context->piclayer->ptype_pbmode) {
|
|
GST_DEBUG_OBJECT (rtph263pay, "MODE B on GOB %d needed", i);
|
|
if (!gst_rtp_h263_pay_mode_B_fragment (rtph263pay, context,
|
|
context->gobs[i])) {
|
|
GST_ERROR_OBJECT (rtph263pay,
|
|
"There was an error fragmenting in mode B");
|
|
ret = GST_FLOW_ERROR;
|
|
goto end;
|
|
}
|
|
} else {
|
|
//IMPLEMENT C mode
|
|
GST_ERROR_OBJECT (rtph263pay,
|
|
"MODE C on GOB %d needed, but not supported yet", i);
|
|
/*if(!gst_rtp_h263_pay_mode_C_fragment(rtph263pay, context, context->gobs[i])) {
|
|
ret = GST_FLOW_OK;
|
|
GST_ERROR("There was an error fragmenting in mode C");
|
|
goto decode_error;
|
|
} */
|
|
goto decode_error;
|
|
}
|
|
decode_error:
|
|
i++;
|
|
first = i;
|
|
continue;
|
|
|
|
} else {
|
|
goto payload_a_push;
|
|
}
|
|
}
|
|
|
|
if (payload_len + context->gobs[i]->length < context->mtu) {
|
|
GST_DEBUG_OBJECT (rtph263pay, "GOB %d fills mtu", i);
|
|
payload_len += context->gobs[i]->length;
|
|
i++;
|
|
if (i == context->no_gobs) {
|
|
GST_DEBUG_OBJECT (rtph263pay, "LAST GOB %d", i);
|
|
goto payload_a_push;
|
|
}
|
|
|
|
} else {
|
|
payload_a_push:
|
|
GST_DEBUG_OBJECT (rtph263pay,
|
|
"Pushing GOBS %d to %d because payload size is %d", first,
|
|
first == i ? i : i - 1, payload_len);
|
|
gst_rtp_h263_pay_A_fragment_push (rtph263pay, context, first,
|
|
first == i ? i : i - 1);
|
|
payload_len = 0;
|
|
first = i;
|
|
}
|
|
continue;
|
|
|
|
force_a:
|
|
GST_DEBUG_OBJECT (rtph263pay,
|
|
"Pushing GOBS %d to %d because payload size is %d", first, i,
|
|
payload_len);
|
|
gst_rtp_h263_pay_A_fragment_push (rtph263pay, context, first, i);
|
|
payload_len = 0;
|
|
i++;
|
|
first = i;
|
|
}
|
|
|
|
|
|
}/*---------- END OF FRAGMENTATION ----------*/
|
|
|
|
/* Flush the input buffer data */
|
|
|
|
end:
|
|
gst_rtp_h263_pay_context_destroy (context,
|
|
context->piclayer->ptype_srcformat);
|
|
gst_buffer_unmap (rtph263pay->current_buffer, &rtph263pay->map);
|
|
gst_buffer_replace (&rtph263pay->current_buffer, NULL);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_rtp_h263_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer)
|
|
{
|
|
|
|
GstRtpH263Pay *rtph263pay;
|
|
GstFlowReturn ret;
|
|
|
|
rtph263pay = GST_RTP_H263_PAY (payload);
|
|
GST_DEBUG_OBJECT (rtph263pay,
|
|
"-------------------- NEW FRAME ---------------");
|
|
|
|
rtph263pay->first_ts = GST_BUFFER_PTS (buffer);
|
|
|
|
gst_buffer_replace (&rtph263pay->current_buffer, buffer);
|
|
gst_buffer_unref (buffer);
|
|
|
|
/* we always encode and flush a full picture */
|
|
ret = gst_rtp_h263_pay_flush (rtph263pay);
|
|
GST_DEBUG_OBJECT (rtph263pay,
|
|
"-------------------- END FRAME ---------------");
|
|
|
|
return ret;
|
|
}
|