--- /dev/null
+test/test_BT7.mp4
+test/test_RGB.mp4
+test/test_packetizer
noinst_LTLIBRARIES = libavmod.la
-libavmod_la_SOURCES = mod_av.c avformat.c avcodec.c
+libavmod_la_SOURCES = mod_av.c avformat.c avcodec.c switch_packetizer.c
libavmod_la_CFLAGS = $(AM_CFLAGS) $(AVFORMAT_CFLAGS) $(AVCODEC_CFLAGS) $(SWSCALE_CFLAGS) $(AVUTIL_CFLAGS) $(RESAMPLE_CFLAGS)
mod_LTLIBRARIES = mod_av.la
mod_av_la_LDFLAGS = -avoid-version -module -no-undefined -shared -lm -lz
-noinst_PROGRAMS = test/test_mod_av test/test_avformat
+noinst_PROGRAMS = test/test_mod_av test/test_avformat test/test_packetizer
test_test_mod_av_CFLAGS = $(SWITCH_AM_CFLAGS) -I../ -DSWITCH_TEST_BASE_DIR_FOR_CONF=\"${abs_builddir}/test\" -DSWITCH_TEST_BASE_DIR_OVERRIDE=\"${abs_builddir}/test\" $(AVFORMAT_CFLAGS) $(AVCODEC_CFLAGS) $(SWSCALE_CFLAGS) $(AVUTIL_CFLAGS) $(RESAMPLE_CFLAGS)
test_test_mod_av_LDFLAGS = $(AVFORMAT_LIBS) $(AVCODEC_LIBS) $(SWSCALE_LIBS) $(AVUTIL_LIBS) $(RESAMPLE_LIBS) -avoid-version -no-undefined $(SWITCH_AM_LDFLAGS)
test_test_mod_av_LDADD = libavmod.la $(switch_builddir)/libfreeswitch.la
test_test_avformat_CFLAGS = $(SWITCH_AM_CFLAGS) -I../ -DSWITCH_TEST_BASE_DIR_FOR_CONF=\"${abs_builddir}/test\" -DSWITCH_TEST_BASE_DIR_OVERRIDE=\"${abs_builddir}/test\" $(AVFORMAT_CFLAGS) $(AVCODEC_CFLAGS) $(SWSCALE_CFLAGS) $(AVUTIL_CFLAGS) $(RESAMPLE_CFLAGS)
test_test_avformat_LDFLAGS = $(AVFORMAT_LIBS) $(AVCODEC_LIBS) $(SWSCALE_LIBS) $(AVUTIL_LIBS) $(RESAMPLE_LIBS) -avoid-version -no-undefined $(SWITCH_AM_LDFLAGS)
test_test_avformat_LDADD = libavmod.la $(switch_builddir)/libfreeswitch.la
+test_test_packetizer_CFLAGS = $(SWITCH_AM_CFLAGS) -I../ -DSWITCH_TEST_BASE_DIR_FOR_CONF=\"${abs_builddir}/test\" -DSWITCH_TEST_BASE_DIR_OVERRIDE=\"${abs_builddir}/test\" $(AVFORMAT_CFLAGS) $(AVCODEC_CFLAGS) $(SWSCALE_CFLAGS) $(AVUTIL_CFLAGS) $(RESAMPLE_CFLAGS)
+test_test_packetizer_LDFLAGS = $(AVFORMAT_LIBS) $(AVCODEC_LIBS) $(SWSCALE_LIBS) $(AVUTIL_LIBS) $(RESAMPLE_LIBS) -avoid-version -no-undefined $(SWITCH_AM_LDFLAGS)
+test_test_packetizer_LDADD = libavmod.la $(switch_builddir)/libfreeswitch.la
TESTS = $(noinst_PROGRAMS)
#include <libswresample/swresample.h>
#endif
+#include <switch_packetizer.h>
+#define SLICE_SIZE (SWITCH_DEFAULT_VIDEO_SIZE + 100)
+
GCC_DIAG_ON(deprecated-declarations)
#define SCALE_FLAGS SWS_BICUBIC
#define DFT_RECORD_OFFSET 0
uint64_t last_ts;
} record_helper_t;
-
-
/* file interface */
struct av_file_context {
switch_time_t last_vid_write;
int audio_timer;
+
+ switch_bool_t no_video_decode;
+ switch_queue_t *video_pkt_queue;
+ switch_packetizer_t *packetizer;
+ AVPacket *last_read_pkt;
};
typedef struct av_file_context av_file_context_t;
return switch_queue_size(q);
}
+static void flush_video_pkt_queue(switch_queue_t *q)
+{
+ AVPacket *pkt;
+
+ while (switch_queue_trypop(q, (void **)&pkt) == SWITCH_STATUS_SUCCESS) {
+ av_packet_unref(pkt);
+ }
+}
+
static void *SWITCH_THREAD_FUNC video_thread_run(switch_thread_t *thread, void *obj)
{
av_file_context_t *context = (av_file_context_t *) obj;
if (context->has_video && pkt.stream_index == context->video_st.st->index) {
AVFrame *vframe;
switch_image_t *img;
+
+ if (context->no_video_decode) {
+ if (eof) {
+ break;
+ } else {
+ switch_status_t status;
+ AVPacket *new_pkt = malloc(sizeof(AVPacket));
+
+ if (0) { // debug
+ uint8_t *p = pkt.data;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "size = %u %x %x %x %x %x %x\n", pkt.size, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5));
+ }
+
+ av_init_packet(new_pkt);
+ av_packet_ref(new_pkt, &pkt);
+ status = switch_queue_push(context->video_pkt_queue, new_pkt);
+ // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "size = %4u flag=%x pts=%" SWITCH_INT64_T_FMT " dts=%" SWITCH_INT64_T_FMT "\n", pkt.size, pkt.flags, pkt.pts, pkt.dts);
+
+ context->vid_ready = 1;
+ if (status != SWITCH_STATUS_SUCCESS) {
+ av_packet_unref(new_pkt);
+ free(new_pkt);
+ }
+ av_packet_unref(&pkt);
+ continue;
+ }
+ }
+
if (!sync) {
switch_buffer_zero(context->audio_buffer);
sync = 1;
if (context->has_video) {
switch_queue_create(&context->eh.video_queue, context->read_fps, handle->memory_pool);
+ context->no_video_decode = handle->params && switch_true(switch_event_get_header(handle->params, "no_video_decode"));
+ if (context->no_video_decode) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Opening video in no decode mode\n");
+ switch_queue_create(&context->video_pkt_queue, 120 * 5, handle->memory_pool);
+ }
switch_mutex_init(&context->eh.mutex, SWITCH_MUTEX_NESTED, handle->memory_pool);
switch_core_timer_init(&context->video_timer, "soft", (int)(1000.0f / context->read_fps), 1, context->pool);
}
context->file_read_thread_running = 0;
}
+ if (context->video_pkt_queue) {
+ flush_video_pkt_queue(context->video_pkt_queue);
+ switch_queue_term(context->video_pkt_queue);
+ }
+
+ if (context->packetizer) {
+ switch_packetizer_close(&context->packetizer);
+ }
+
if (context->file_read_thread) {
switch_thread_join(&status, context->file_read_thread);
context->file_read_thread = NULL;
}
switch_mutex_lock(context->mutex);
+
while (!context->file_read_thread_started) {
switch_thread_cond_wait(context->cond, context->mutex);
}
}
#else
+static switch_status_t no_video_decode_packets(switch_file_handle_t *handle, switch_frame_t *frame, switch_video_read_flag_t flags)
+{
+ av_file_context_t *context = (av_file_context_t *)handle->private_info;
+ MediaStream *mst = &context->video_st;
+ AVStream *st = mst->st;
+ // AVCodecContext *ctx = st->codec;
+ // int ticks = 0;
+ // int64_t max_delta = 1 * AV_TIME_BASE; // 1 second
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ AVPacket *pkt;
+ int64_t pts;
+
+
+ if (!context->packetizer) {
+ // uint8_t *p = st->codecpar->extradata;
+ // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "size = %u %x %x %x %x %x %x\n", st->codecpar->extradata_size, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5));
+
+ context->packetizer = switch_packetizer_create(SPT_H264_SIZED_BITSTREAM, SLICE_SIZE);
+ if (!context->packetizer) return SWITCH_STATUS_FALSE;
+
+ switch_packetizer_feed_extradata(context->packetizer, st->codecpar->extradata, st->codecpar->extradata_size);
+ }
+
+ if (context->last_read_pkt) {
+ status = switch_packetizer_read(context->packetizer, frame);
+ if (status == SWITCH_STATUS_SUCCESS) {
+ av_packet_unref(context->last_read_pkt);
+ free(context->last_read_pkt);
+ context->last_read_pkt = NULL;
+ }
+ return status;
+ }
+
+ status = switch_queue_trypop(context->video_pkt_queue, (void **)&pkt);
+
+ if (status != SWITCH_STATUS_SUCCESS || !pkt) {
+ switch_cond_next();
+ return SWITCH_STATUS_BREAK;
+ }
+
+ context->last_read_pkt = pkt;
+ status = switch_packetizer_feed(context->packetizer, pkt->data, pkt->size);
+ status = switch_packetizer_read(context->packetizer, frame);
+ pts = av_rescale_q(pkt->pts, st->time_base, AV_TIME_BASE_Q);
+ frame->timestamp = pts * 9 / 100; // scale to sample 900000
+ // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "pts=%" SWITCH_INT64_T_FMT " status = %d\n", pts, status);
+
+ if (status == SWITCH_STATUS_SUCCESS) {
+ av_packet_unref(context->last_read_pkt);
+ free(context->last_read_pkt);
+ context->last_read_pkt = NULL;
+ }
+
+
+ if (status == SWITCH_STATUS_SUCCESS || status == SWITCH_STATUS_MORE_DATA) {
+ if (!context->video_start_time) {
+ context->video_start_time = switch_time_now() - pts;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "set start time: %" SWITCH_INT64_T_FMT " now: %" SWITCH_INT64_T_FMT " pts: %" SWITCH_INT64_T_FMT "\n", context->video_start_time, switch_time_now(), pts);
+ } else if (flags & SVR_BLOCK) {
+ int64_t sleep = pts - (switch_time_now() - context->video_start_time);
+ if (sleep > 0) {
+ // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "zzZ... %" SWITCH_INT64_T_FMT "\n", sleep);
+ if (sleep > 1000000) {
+ sleep = 1000000;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "but zzZ... %" SWITCH_INT64_T_FMT " at most\n", sleep);
+ }
+ switch_yield(sleep);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "video is late %" SWITCH_INT64_T_FMT "\n", sleep);
+ }
+ }
+ }
+
+ return status;
+}
+
static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_frame_t *frame, switch_video_read_flag_t flags)
{
av_file_context_t *context = (av_file_context_t *)handle->private_info;
return SWITCH_STATUS_BREAK;
}
+ if (context->no_video_decode) {
+ switch_set_flag(frame, SFF_ENCODED);
+ status = no_video_decode_packets(handle, frame, flags);
+ // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "return len=%4u nalu=%02x m=%d ts=%u\n", frame->datalen, *(uint8_t *)frame->data, frame->m, frame->timestamp);
+ return status;
+ }
+
if (handle->mm.fps > 0 && handle->mm.fps < smaller_ts) {
smaller_ts = handle->mm.fps;
}
--- /dev/null
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2020, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Seven Du <dujinfang@gmail.com>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Seven Du <dujinfang@gmail.com>
+ *
+ * switch_packetizer H264 packetizer
+ *
+ */
+
+#include <switch_packetizer.h>
+#define MAX_NALUS 256
+
+typedef struct our_h264_nalu_s {
+ const uint8_t *start;
+ const uint8_t *eat;
+ uint32_t len;
+} our_h264_nalu_t;
+
+typedef struct h264_packetizer_s {
+ switch_packetizer_bitstream_t type;
+ uint32_t slice_size;
+ int nalu_current_index;
+ our_h264_nalu_t nalus[MAX_NALUS];
+ uint8_t *extradata;
+ uint32_t extradata_size;
+ uint8_t *sps;
+ uint8_t *pps;
+ uint32_t sps_len;
+ uint32_t pps_len;
+ int sps_sent;
+ int pps_sent;
+} h264_packetizer_t;
+
+/* ff_avc_find_startcode is not exposed in the ffmpeg lib but you can use it
+ Either include the avc.h which available in the ffmpeg source, or
+ just add the declaration like we does following to avoid include that whole avc.h
+ The function is implemented in avc.h, guess we'll get rid of this later if we can directly use libx264
+
+#include <libavformat/avc.h>
+*/
+
+const uint8_t *ff_avc_find_startcode(const uint8_t *p, const uint8_t *end);
+
+static const uint8_t *fs_avc_find_startcode_internal(const uint8_t *p, const uint8_t *end)
+{
+ const uint8_t *a = p + 4 - ((intptr_t)p & 3);
+
+ for (end -= 3; p < a && p < end; p++) {
+ if (p[0] == 0 && p[1] == 0 && p[2] == 1)
+ return p;
+ }
+
+ for (end -= 3; p < end; p += 4) {
+ uint32_t x = *(const uint32_t*)p;
+ if ((x - 0x01010101) & (~x) & 0x80808080) {
+ if (p[1] == 0) {
+ if (p[0] == 0 && p[2] == 1)
+ return p;
+ if (p[2] == 0 && p[3] == 1)
+ return p+1;
+ }
+ if (p[3] == 0) {
+ if (p[2] == 0 && p[4] == 1)
+ return p+2;
+ if (p[4] == 0 && p[5] == 1)
+ return p+3;
+ }
+ }
+ }
+
+ for (end += 3; p < end; p++) {
+ if (p[0] == 0 && p[1] == 0 && p[2] == 1)
+ return p;
+ }
+
+ return end + 3;
+}
+
+static const uint8_t *fs_avc_find_startcode(const uint8_t *p, const uint8_t *end)
+{
+ const uint8_t *out= fs_avc_find_startcode_internal(p, end);
+
+ if (p < out && out < end && !out[-1]) {
+ out--;
+ }
+
+ return out;
+}
+
+SWITCH_DECLARE(switch_packetizer_t *) switch_packetizer_create(switch_packetizer_bitstream_t type, uint32_t slice_size)
+{
+ h264_packetizer_t *context = malloc(sizeof(h264_packetizer_t));
+ memset(context, 0, sizeof(h264_packetizer_t));
+ context->slice_size = slice_size;
+ context->type = type;
+ return (switch_packetizer_t *)context;
+}
+
+// for H264
+SWITCH_DECLARE(switch_status_t) switch_packetizer_feed_extradata(switch_packetizer_t *packetizer, void *data, uint32_t size)
+{
+ h264_packetizer_t *context = (h264_packetizer_t *)packetizer;
+ uint8_t *p;
+ int left = size;
+ int n_sps = 0;
+ int n_pps = 0;
+ int sps_len;
+ int pps_len;
+ int i;
+
+ if (left < 10) return SWITCH_STATUS_FALSE;
+
+ if (context->extradata) {
+ context->sps = NULL;
+ context->pps = NULL;
+ context->sps_len = 0;
+ context->pps_len = 0;
+ free(context->extradata);
+ context->extradata = NULL;
+ }
+
+ context->extradata = malloc(size);
+ if (!context->extradata) return SWITCH_STATUS_MEMERR;
+ memcpy(context->extradata, data, size);
+
+/*
+0x0000 | 01 64 00 1E FF E1 00 1F 67 64 00 1E AC C8 60 33 // E1: 1SPS 00 1F: SPS 31byte
+0x0010 | 0E F9 E6 FF C1 C6 01 C4 44 00 00 03 00 04 00 00
+0x0020 | 03 00 B8 3C 58 B6 68 01 00 05 68 E9 78 47 2C // 01: 1PPS 00 05: PPS 5byte
+*/
+
+ p = context->extradata;
+
+ if (*p != 1) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NOT supported version: %d\n", *p);
+ return SWITCH_STATUS_FALSE;
+ }
+ p += 5;
+ left -= 5;
+
+ if (left < 0) return SWITCH_STATUS_FALSE;
+
+ //sps
+ n_sps = *p & 0x1f;
+ p += 1;
+ left -= 1;
+
+ for (i = 0; i < n_sps; i++) {
+ sps_len = ntohs(*(uint16_t *)p);
+ p += sizeof(uint16_t);
+ left -= sizeof(uint16_t);
+
+ if (left < sps_len) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted data %d < %u\n", left, sps_len);
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (!context->sps) { // we only need the first one
+ context->sps = p;
+ context->sps_len = sps_len;
+ }
+
+ p += sps_len;
+ left -= sps_len;
+ }
+
+ if (left < 0) return SWITCH_STATUS_FALSE;
+
+ n_pps = *p & 0x1f;
+ p += 1;
+ left -= 1;
+
+ for (i = 0; i < n_pps; i++) {
+ pps_len = ntohs(*(uint16_t *)p);
+ p += sizeof(uint16_t);
+ left -= sizeof(uint16_t);
+
+ if (left < pps_len) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted data %d < %u\n", left, pps_len);
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (!context->pps) { // we only need the first one
+ context->pps = p;
+ context->pps_len = pps_len;
+ }
+ p += pps_len;
+ left -= pps_len;
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_DECLARE(switch_status_t) switch_packetizer_feed(switch_packetizer_t *packetizer, void *data, uint32_t size)
+{
+ h264_packetizer_t *context = (h264_packetizer_t *)packetizer;
+ const uint8_t *p = data;
+ const uint8_t *end = p + size;
+ int i = 0;
+
+ // reset everytime
+ memset(context->nalus, 0, MAX_NALUS * sizeof(our_h264_nalu_t));
+ context->nalu_current_index = 0;
+
+ // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "size = %u %x %x %x %x %x\n", size, *p, *(p+1), *(p+2), *(p+3), *(p+4));
+
+ if (context->type == SPT_H264_SIZED_BITSTREAM) {
+ int left = size;
+ uint32_t len;
+
+ while (left > 0) {
+ if (left < sizeof(uint32_t)) return SWITCH_STATUS_MORE_DATA;
+ len = htonl(*(uint32_t *)p);
+ left -= sizeof(uint32_t);
+ left -= len;
+ if (left < 0) return SWITCH_STATUS_MORE_DATA;
+ p += sizeof(uint32_t);
+
+ context->nalus[i].start = p;
+ context->nalus[i].eat = p;
+ context->nalus[i].len = len;
+
+ p += len;
+
+ // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "#%d %x len=%u\n", i, *context->nalus[i].start, context->nalus[i].len);
+ i++;
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ if (context->type == SPT_H264_SIGNALE_NALU) {
+ context->nalus[0].start = data;
+ context->nalus[0].eat = data;
+ context->nalus[0].len = size;
+
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ // SPT_H264_BITSTREAM
+ while ((p = fs_avc_find_startcode(p, end)) < end) {
+ if (!context->nalus[i].start) {
+ while (!(*p++)) ; /* eat the sync bytes, what ever 0 0 1 or 0 0 0 1 */
+ context->nalus[i].start = p;
+ context->nalus[i].eat = p;
+ } else {
+ context->nalus[i].len = p - context->nalus[i].start;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "#%d %x len=%u\n", i, *context->nalus[i].start, context->nalus[i].len);
+ while (!(*p++)) ; /* eat the sync bytes, what ever 0 0 1 or 0 0 0 1 */
+ i++;
+ context->nalus[i].start = p;
+ context->nalus[i].eat = p;
+ }
+ if (i >= MAX_NALUS - 2) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "TOO MANY SLICES!\n");
+ break;
+ }
+
+ }
+
+ context->nalus[i].len = p - context->nalus[i].start;
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_DECLARE(switch_status_t) switch_packetizer_read(switch_packetizer_t *packetizer, switch_frame_t *frame)
+{
+ h264_packetizer_t *context = (h264_packetizer_t *)packetizer;
+ uint32_t slice_size = context->slice_size;
+ our_h264_nalu_t *nalu = &context->nalus[context->nalu_current_index];
+ uint8_t nalu_hdr = 0;
+ uint8_t nalu_type = 0;
+ uint8_t nri = 0;
+ int left = nalu->len - (nalu->eat - nalu->start);
+ uint8_t *p = frame->data;
+ uint8_t start = nalu->start == nalu->eat ? 0x80 : 0;
+ int n = nalu->len / slice_size + 1;
+ int real_slice_size = nalu->len / n + 1 + 2;
+
+ if (nalu->start == NULL) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "INVALID BITSTREAM\n");
+ return SWITCH_STATUS_FALSE;
+ }
+
+ nalu_hdr = *(uint8_t *)(nalu->start);
+ nalu_type = nalu_hdr & 0x1f;
+ nri = nalu_hdr & 0x60;
+
+ if (real_slice_size > slice_size) real_slice_size = slice_size;
+ if (frame->buflen < slice_size) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "frame buffer too small %u < %u\n", frame->buflen, slice_size);
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (context->type == SPT_H264_BITSTREAM || SPT_H264_SIZED_BITSTREAM) {
+ if (nalu_type == 0x05) {
+ // insert SPS/PPS before
+ if (context->sps && !context->sps_sent) {
+ memcpy(frame->data, context->sps, context->sps_len);
+ frame->datalen = context->sps_len;
+ frame->m = 0;
+ context->sps_sent = 1;
+ return SWITCH_STATUS_MORE_DATA;
+ } else if (context->pps && !context->pps_sent) {
+ memcpy(frame->data, context->pps, context->pps_len);
+ frame->datalen = context->pps_len;
+ frame->m = 0;
+ context->pps_sent = 1;
+ return SWITCH_STATUS_MORE_DATA;
+ }
+ } else if (nalu_type == 0x07) {
+ context->sps_sent = 1;
+ } else if (nalu_type == 0x08) {
+ context->pps_sent = 1;
+ }
+ }
+
+ if (nalu->len <= slice_size) {
+ memcpy(frame->data, nalu->start, nalu->len);
+ frame->datalen = nalu->len;
+ context->nalu_current_index++;
+
+ switch_clear_flag(frame, SFF_CNG);
+
+ if (context->nalus[context->nalu_current_index].len) {
+ frame->m = 0;
+ return SWITCH_STATUS_MORE_DATA;
+ }
+
+ frame->m = 1;
+
+ if (nalu_type == 0x05) {
+ context->sps_sent = 0;
+ context->pps_sent = 0;
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ if (left <= (real_slice_size - 2)) {
+ p[0] = nri | 28; // FU-A
+ p[1] = 0x40 | nalu_type;
+ memcpy(p+2, nalu->eat, left);
+ nalu->eat += left;
+ frame->datalen = left + 2;
+ context->nalu_current_index++;
+
+ if (!context->nalus[context->nalu_current_index].len) {
+ frame->m = 1;
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ return SWITCH_STATUS_MORE_DATA;
+ }
+
+ p[0] = nri | 28; // FU-A
+ p[1] = start | nalu_type;
+ if (start) nalu->eat++;
+ memcpy(p+2, nalu->eat, real_slice_size - 2);
+ nalu->eat += (real_slice_size - 2);
+ frame->datalen = real_slice_size;
+ frame->m = 0;
+ return SWITCH_STATUS_MORE_DATA;
+}
+
+SWITCH_DECLARE(void) switch_packetizer_close(switch_packetizer_t **packetizer)
+{
+ h264_packetizer_t *context = (h264_packetizer_t *)(*packetizer);
+ if (context->extradata) free(context->extradata);
+ free(context);
+ *packetizer = NULL;
+}
--- /dev/null
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2020, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Seven Du <dujinfang@gmail.com>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Seven Du <dujinfang@gmail.com>
+ *
+ * switch_packetizer H264 packetizer
+ *
+ */
+
+#include <switch.h>
+
+#ifndef SWITCH_PACKETIZER_H
+#define SWITCH_PACKETIZER_H
+
+typedef void switch_packetizer_t;
+
+typedef enum {
+ SPT_H264_BITSTREAM, // with separator 0 0 0 1 or 0 0 1
+ SPT_H264_SIZED_BITSTREAM,
+ SPT_H264_SIGNALE_NALU,
+ SPT_VP8_BITSTREAM,
+ SPT_VP9_BITSTREAM,
+
+ // no more beyond this line
+ SPT_INVALID_STREAM
+} switch_packetizer_bitstream_t;
+
+/*
+
+ create a packetizer and feed data, to avoid data copy, data MUST be valid before the next feed, or before close.
+
+ */
+
+SWITCH_DECLARE(switch_packetizer_t *) switch_packetizer_create(switch_packetizer_bitstream_t type, uint32_t slice_size);
+SWITCH_DECLARE(switch_status_t) switch_packetizer_feed(switch_packetizer_t *packetizer, void *data, uint32_t size);
+SWITCH_DECLARE(switch_status_t) switch_packetizer_feed_extradata(switch_packetizer_t *packetizer, void *data, uint32_t size);
+SWITCH_DECLARE(switch_status_t) switch_packetizer_read(switch_packetizer_t *packetizer, switch_frame_t *frame);
+SWITCH_DECLARE(void) switch_packetizer_close(switch_packetizer_t **packetizer);
+
+#endif
}
FST_TEST_END()
+ FST_TEST_BEGIN(avformat_test_play_no_decode)
+ {
+ char path[1024];
+ switch_status_t status;
+ switch_file_handle_t fh = { 0 };
+ uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 };
+ switch_frame_t frame = { 0 };
+ switch_size_t len = SAMPLES;
+ uint32_t flags = SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT | SWITCH_FILE_FLAG_VIDEO;
+ int i = 0;
+
+ sprintf(path, "{no_video_decode=true}%s%s%s", SWITCH_GLOBAL_dirs.conf_dir, SWITCH_PATH_SEPARATOR, "../test_RGB.mp4");
+ // switch_set_string(path, "{no_video_decode=true}/usr/local/freeswitch/storage/bingbing.mp4");
+ status = switch_core_file_open(&fh, path, 1, 8000, flags, fst_pool);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+ fst_requires(switch_test_flag(&fh, SWITCH_FILE_OPEN));
+ frame.data = data;
+
+ do {
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_core_file_read(&fh, data, &len);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "status: %d len: %d\n", status, (int)len);
+ fst_check(frame.img == NULL);
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_core_file_read_video(&fh, &frame, 0);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "status: %d len: %d %02x\n", status, frame.datalen, *(uint8_t *)frame.data);
+ } while (status == SWITCH_STATUS_MORE_DATA);
+
+ switch_core_file_close(&fh);
+ }
+ FST_TEST_END()
+
+
FST_TEARDOWN_BEGIN()
{
//const char *err = NULL;
--- /dev/null
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2018, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Seven Du <seven@signalwire.com>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ *
+ * test_avformat -- avformat tests
+ *
+ */
+
+#include <test/switch_test.h>
+#include <switch_packetizer.h>
+
+#define SLICE_SIZE 4
+
+FST_CORE_BEGIN("conf")
+{
+ FST_MODULE_BEGIN(mod_av, mod_av_test)
+ {
+ FST_SETUP_BEGIN()
+ {
+ fst_requires_module("mod_av");
+ }
+ FST_SETUP_END()
+
+ FST_TEST_BEGIN(test_packetizer_bitstream)
+ {
+ switch_packetizer_t *packetizer = switch_packetizer_create(SPT_H264_BITSTREAM, SLICE_SIZE);
+ uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE];
+ switch_frame_t frame = {0};
+ switch_status_t status;
+ uint8_t h264data[] = {0, 0, 0, 1, 0x67, 1, 2, 0, 0, 0, 1, 0x68, 1, 2, 0, 0, 0, 1, 0x65, 1, 2, 3, 4, 5, 6};
+
+ frame.data = data;
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ switch_set_flag(&frame, SFF_ENCODED);
+
+ status = switch_packetizer_feed(packetizer, h264data, sizeof(h264data));
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 3);
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 3);
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 4);
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 4);
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+ fst_requires(frame.datalen == 4);
+ switch_packetizer_close(&packetizer);
+ }
+ FST_TEST_END()
+
+ FST_TEST_BEGIN(test_packetizer_sized_bitstream_has_sps_pps)
+ {
+ switch_packetizer_t *packetizer = switch_packetizer_create(SPT_H264_SIZED_BITSTREAM, SLICE_SIZE);
+ uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE];
+ switch_frame_t frame = {0};
+ switch_status_t status;
+ uint8_t h264data[] = {0, 0, 0, 3, 0x67, 1, 2, 0, 0, 0, 3, 0x68, 1, 2, 0, 0, 0, 7, 0x65, 1, 2, 3, 4, 5, 6};
+
+ frame.data = data;
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ switch_set_flag(&frame, SFF_ENCODED);
+
+ status = switch_packetizer_feed(packetizer, h264data, sizeof(h264data));
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 3);
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 3);
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 4);
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 4);
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+ fst_requires(frame.datalen == 4);
+ fst_check(frame.m == 1);
+ switch_packetizer_close(&packetizer);
+ }
+ FST_TEST_END()
+
+ FST_TEST_BEGIN(test_packetizer_sized_bitstream_no_sps_pps)
+ {
+ switch_packetizer_t *packetizer = switch_packetizer_create(SPT_H264_SIZED_BITSTREAM, SLICE_SIZE);
+ uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE];
+ switch_frame_t frame = {0};
+ switch_status_t status;
+ uint8_t h264data[] = {0, 0, 0, 3, 0x06, 1, 2, 0, 0, 0, 3, 0x09, 1, 2, 0, 0, 0, 7, 0x65, 1, 2, 3, 4, 5, 6};
+ uint8_t extradata[] = {0x01, 0x64, 0x00, 0x1e, 0xff, 0xe1, 0x00, 0x03, 0x67, 0x64, 0x00, 0xe1, 0x00, 0x03, 0x68, 0x01, 0x02};
+// 1 fps 3 bytes 1pps 3 bytes
+ frame.data = data;
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ switch_set_flag(&frame, SFF_ENCODED);
+
+ status = switch_packetizer_feed_extradata(packetizer, extradata, sizeof(extradata));
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+
+ status = switch_packetizer_feed(packetizer, h264data, sizeof(h264data));
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 3);
+ fst_check((*(uint8_t *)frame.data & 0x1f) == 0x06);
+
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u %x\n", frame.datalen, *(uint8_t *)frame.data);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 3);
+ fst_check((*(uint8_t *)frame.data & 0x1f) == 0x09);
+
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u %x\n", frame.datalen, *(uint8_t *)frame.data);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 3);
+ fst_check((*(uint8_t *)frame.data & 0x1f) == 0x07);
+
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u %x\n", frame.datalen, *(uint8_t *)frame.data);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 3);
+ fst_check((*(uint8_t *)frame.data & 0x1f) == 0x08);
+
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u %x\n", frame.datalen, *(uint8_t *)frame.data);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 4);
+ fst_check((*(uint8_t *)frame.data & 0x1f) == 0x1c);
+ fst_check(frame.m == 0);
+
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u %x\n", frame.datalen, *(uint8_t *)frame.data);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 4);
+ fst_check((*(uint8_t *)frame.data & 0x1f) == 0x1c);
+ fst_check(frame.m == 0);
+
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u %x\n", frame.datalen, *(uint8_t *)frame.data);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+ fst_requires(frame.datalen == 4);
+ fst_check((*(uint8_t *)frame.data & 0x1f) == 0x1c);
+ fst_check(frame.m == 1);
+ switch_packetizer_close(&packetizer);
+ }
+ FST_TEST_END()
+
+ FST_TEST_BEGIN(test_packetizer_invalid)
+ {
+ switch_packetizer_t *packetizer = switch_packetizer_create(SPT_H264_BITSTREAM, SLICE_SIZE);
+ uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE];
+ switch_frame_t frame = {0};
+ switch_status_t status;
+ uint8_t h264data[] = {0, 0, 2, 9, 0x67, 1, 2, 0, 0, 0, 0, 0x68, 1, 2, 0, 0, 0, 0, 0x65, 1, 2, 3, 4, 5, 6};
+
+ frame.data = data;
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ switch_set_flag(&frame, SFF_ENCODED);
+
+ status = switch_packetizer_feed(packetizer, h264data, sizeof(h264data));
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 3);
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 3);
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 4);
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_MORE_DATA);
+ fst_requires(frame.datalen == 4);
+ frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+ status = switch_packetizer_read(packetizer, &frame);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", frame.datalen);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+ fst_requires(frame.datalen == 4);
+ switch_packetizer_close(&packetizer);
+ }
+ FST_TEST_END()
+
+ FST_TEARDOWN_BEGIN()
+ {
+ //const char *err = NULL;
+ switch_sleep(1000000);
+ //fst_check(switch_loadable_module_unload_module(SWITCH_GLOBAL_dirs.mod_dir, (char *)"mod_av", SWITCH_TRUE, &err) == SWITCH_STATUS_SUCCESS);
+ }
+ FST_TEARDOWN_END()
+ }
+ FST_MODULE_END()
+}
+FST_CORE_END()