[mod_av] play video without decode

This commit is contained in:
Seven Du 2020-04-03 15:23:45 +08:00 committed by Andrey Volk
parent 0db19ecdf8
commit 9d2cb8b190
7 changed files with 896 additions and 4 deletions

View File

@ -0,0 +1,3 @@
test/test_BT7.mp4
test/test_RGB.mp4
test/test_packetizer

View File

@ -13,7 +13,7 @@ endif
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
@ -22,13 +22,16 @@ mod_av_la_LIBADD = libavmod.la $(switch_builddir)/libfreeswitch.la $(AVFORMAT_
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)

View File

@ -54,6 +54,9 @@ GCC_DIAG_OFF(deprecated-declarations)
#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
@ -116,8 +119,6 @@ typedef struct record_helper_s {
uint64_t last_ts;
} record_helper_t;
/* file interface */
struct av_file_context {
@ -161,6 +162,11 @@ 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;
@ -772,6 +778,15 @@ static int flush_video_queue(switch_queue_t *q, int min)
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;
@ -1387,6 +1402,34 @@ GCC_DIAG_ON(deprecated-declarations)
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;
@ -1680,6 +1723,11 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa
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);
}
@ -2177,6 +2225,15 @@ static switch_status_t av_file_close(switch_file_handle_t *handle)
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;
@ -2249,6 +2306,7 @@ static switch_status_t av_file_read(switch_file_handle_t *handle, void *data, si
}
switch_mutex_lock(context->mutex);
while (!context->file_read_thread_started) {
switch_thread_cond_wait(context->cond, context->mutex);
}
@ -2334,6 +2392,82 @@ static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_f
}
#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;
@ -2353,6 +2487,13 @@ static switch_status_t av_file_read_video(switch_file_handle_t *handle, switch_f
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;
}

View File

@ -0,0 +1,394 @@
/*
* 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;
}

View File

@ -0,0 +1,62 @@
/*
* 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

View File

@ -162,6 +162,39 @@ FST_CORE_BEGIN("conf")
}
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;

View File

@ -0,0 +1,256 @@
/*
* 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()