[mod_mp4, mod_mp4v, mod_mp4v2] Remove from tree

This commit is contained in:
Andrey Volk 2024-08-28 21:30:43 +03:00
parent 4f1d9cd7db
commit 4235d86aee
16 changed files with 1 additions and 1825 deletions

View File

@ -29,8 +29,6 @@ applications/mod_httapi
#applications/mod_lcr #applications/mod_lcr
#applications/mod_memcache #applications/mod_memcache
#applications/mod_mongo #applications/mod_mongo
#applications/mod_mp4
#applications/mod_mp4v2
#applications/mod_nibblebill #applications/mod_nibblebill
#applications/mod_oreka #applications/mod_oreka
#applications/mod_osp #applications/mod_osp
@ -69,7 +67,6 @@ codecs/mod_g729
codecs/mod_h26x codecs/mod_h26x
#codecs/mod_ilbc #codecs/mod_ilbc
#codecs/mod_isac #codecs/mod_isac
#codecs/mod_mp4v
codecs/mod_opus codecs/mod_opus
#codecs/mod_silk #codecs/mod_silk
#codecs/mod_siren #codecs/mod_siren

View File

@ -29,7 +29,6 @@ applications/mod_http_cache
applications/mod_lcr applications/mod_lcr
applications/mod_memcache applications/mod_memcache
applications/mod_mongo applications/mod_mongo
applications/mod_mp4
applications/mod_nibblebill applications/mod_nibblebill
applications/mod_oreka applications/mod_oreka
#applications/mod_osp #applications/mod_osp
@ -67,7 +66,6 @@ codecs/mod_g729
codecs/mod_h26x codecs/mod_h26x
codecs/mod_ilbc codecs/mod_ilbc
codecs/mod_isac codecs/mod_isac
codecs/mod_mp4v
codecs/mod_opus codecs/mod_opus
codecs/mod_silk codecs/mod_silk
codecs/mod_siren codecs/mod_siren

1
ci.sh
View File

@ -93,7 +93,6 @@ configure_freeswitch()
sed -i \ sed -i \
-e '/mod_ilbc/s/^/#/g' \ -e '/mod_ilbc/s/^/#/g' \
-e '/mod_isac/s/^/#/g' \ -e '/mod_isac/s/^/#/g' \
-e '/mod_mp4/s/^/#/g' \
-e '/mod_mongo/s/^/#/g' \ -e '/mod_mongo/s/^/#/g' \
-e '/mod_pocketsphinx/s/^/#/g' \ -e '/mod_pocketsphinx/s/^/#/g' \
-e '/mod_siren/s/^/#/g' \ -e '/mod_siren/s/^/#/g' \

View File

@ -2119,8 +2119,6 @@ AC_CONFIG_FILES([Makefile
src/mod/applications/mod_limit/Makefile src/mod/applications/mod_limit/Makefile
src/mod/applications/mod_memcache/Makefile src/mod/applications/mod_memcache/Makefile
src/mod/applications/mod_mongo/Makefile src/mod/applications/mod_mongo/Makefile
src/mod/applications/mod_mp4/Makefile
src/mod/applications/mod_mp4v2/Makefile
src/mod/applications/mod_nibblebill/Makefile src/mod/applications/mod_nibblebill/Makefile
src/mod/applications/mod_oreka/Makefile src/mod/applications/mod_oreka/Makefile
src/mod/applications/mod_osp/Makefile src/mod/applications/mod_osp/Makefile
@ -2160,7 +2158,6 @@ AC_CONFIG_FILES([Makefile
src/mod/codecs/mod_h26x/Makefile src/mod/codecs/mod_h26x/Makefile
src/mod/codecs/mod_ilbc/Makefile src/mod/codecs/mod_ilbc/Makefile
src/mod/codecs/mod_isac/Makefile src/mod/codecs/mod_isac/Makefile
src/mod/codecs/mod_mp4v/Makefile
src/mod/codecs/mod_opus/Makefile src/mod/codecs/mod_opus/Makefile
src/mod/codecs/mod_openh264/Makefile src/mod/codecs/mod_openh264/Makefile
src/mod/codecs/mod_silk/Makefile src/mod/codecs/mod_silk/Makefile

4
debian/bootstrap.sh vendored
View File

@ -35,8 +35,6 @@ supported_distros="$supported_debian_distros $supported_ubuntu_distros"
avoid_mods=( avoid_mods=(
applications/mod_limit applications/mod_limit
applications/mod_mongo applications/mod_mongo
applications/mod_mp4
applications/mod_mp4v2
applications/mod_osp applications/mod_osp
applications/mod_rad_auth applications/mod_rad_auth
applications/mod_skel applications/mod_skel
@ -712,7 +710,6 @@ Depends: \${misc:Depends}, freeswitch (= \${binary:Version}),
freeswitch-mod-g729 (= \${binary:Version}), freeswitch-mod-g729 (= \${binary:Version}),
freeswitch-mod-h26x (= \${binary:Version}), freeswitch-mod-h26x (= \${binary:Version}),
freeswitch-mod-isac (= \${binary:Version}), freeswitch-mod-isac (= \${binary:Version}),
freeswitch-mod-mp4v (= \${binary:Version}),
freeswitch-mod-opus (= \${binary:Version}), freeswitch-mod-opus (= \${binary:Version}),
freeswitch-mod-silk (= \${binary:Version}), freeswitch-mod-silk (= \${binary:Version}),
freeswitch-mod-spandsp (= \${binary:Version}), freeswitch-mod-spandsp (= \${binary:Version}),
@ -738,7 +735,6 @@ Depends: \${misc:Depends}, freeswitch (= \${binary:Version}),
freeswitch-mod-g729-dbg (= \${binary:Version}), freeswitch-mod-g729-dbg (= \${binary:Version}),
freeswitch-mod-h26x-dbg (= \${binary:Version}), freeswitch-mod-h26x-dbg (= \${binary:Version}),
freeswitch-mod-isac-dbg (= \${binary:Version}), freeswitch-mod-isac-dbg (= \${binary:Version}),
freeswitch-mod-mp4v-dbg (= \${binary:Version}),
freeswitch-mod-opus-dbg (= \${binary:Version}), freeswitch-mod-opus-dbg (= \${binary:Version}),
freeswitch-mod-silk-dbg (= \${binary:Version}), freeswitch-mod-silk-dbg (= \${binary:Version}),
freeswitch-mod-spandsp-dbg (= \${binary:Version}), freeswitch-mod-spandsp-dbg (= \${binary:Version}),

View File

@ -149,16 +149,6 @@ Description: MongoDB
This module provides an interface to MongoDB. This module provides an interface to MongoDB.
Build-Depends: libmongoc-dev Build-Depends: libmongoc-dev
Module: applications/mod_mp4
Section: contrib/comm
Description: MP4 video support
This module adds support for MP4 video playback.
Build-Depends: libmp4v2-dev
Module: applications/mod_mp4v2
Description: Adds mod_mp4v2
Adds mod_mp4v2.
Module: applications/mod_nibblebill Module: applications/mod_nibblebill
Description: Nibblebill Description: Nibblebill
This module allows for real-time accounting of a cash balance and This module allows for real-time accounting of a cash balance and
@ -333,10 +323,6 @@ Module: codecs/mod_isac
Description: mod_isac Description: mod_isac
Adds mod_isac. Adds mod_isac.
Module: codecs/mod_mp4v
Description: mod_mp4v
Adds mod_mp4v.
Module: codecs/mod_openh264 Module: codecs/mod_openh264
Description: Adds mod_openh264 Description: Adds mod_openh264
Adds mod_openh264. Adds mod_openh264.

View File

@ -722,14 +722,6 @@ Requires: %{name} = %{version}-%{release}
%description codec-vpx %description codec-vpx
iSAC Codec support for FreeSWITCH open source telephony platform iSAC Codec support for FreeSWITCH open source telephony platform
%package codec-mp4v
Summary: MP4V Video Codec support for FreeSWITCH open source telephony platform
Group: System/Libraries
Requires: %{name} = %{version}-%{release}
%description codec-mp4v
MP4V Video Codec support for FreeSWITCH open source telephony platform
%package codec-opus %package codec-opus
Summary: Opus Codec support for FreeSWITCH open source telephony platform Summary: Opus Codec support for FreeSWITCH open source telephony platform
Group: System/Libraries Group: System/Libraries
@ -1351,7 +1343,7 @@ ASR_TTS_MODULES="asr_tts/mod_flite asr_tts/mod_pocketsphinx asr_tts/mod_tts_comm
# #
###################################################################################################################### ######################################################################################################################
CODECS_MODULES="codecs/mod_amr codecs/mod_amrwb codecs/mod_bv codecs/mod_codec2 codecs/mod_g723_1 \ CODECS_MODULES="codecs/mod_amr codecs/mod_amrwb codecs/mod_bv codecs/mod_codec2 codecs/mod_g723_1 \
codecs/mod_g729 codecs/mod_h26x codecs/mod_ilbc codecs/mod_isac codecs/mod_mp4v codecs/mod_opus codecs/mod_silk \ codecs/mod_g729 codecs/mod_h26x codecs/mod_ilbc codecs/mod_isac codecs/mod_opus codecs/mod_silk \
codecs/mod_siren codecs/mod_theora" codecs/mod_siren codecs/mod_theora"
# #
@ -2075,9 +2067,6 @@ fi
%files codec-isac %files codec-isac
%{MODINSTDIR}/mod_isac.so* %{MODINSTDIR}/mod_isac.so*
%files codec-mp4v
%{MODINSTDIR}/mod_mp4v.so*
%files codec-opus %files codec-opus
%{MODINSTDIR}/mod_opus.so* %{MODINSTDIR}/mod_opus.so*
%config(noreplace) %attr(0640, freeswitch, daemon) %{sysconfdir}/autoload_configs/opus.conf.xml %config(noreplace) %attr(0640, freeswitch, daemon) %{sysconfdir}/autoload_configs/opus.conf.xml

View File

@ -1,8 +0,0 @@
include $(top_srcdir)/build/modmake.rulesam
MODNAME=mod_mp4
mod_LTLIBRARIES = mod_mp4.la
mod_mp4_la_SOURCES = mod_mp4.cpp mp4_helper.cpp
mod_mp4_la_CFLAGS = $(AM_CFLAGS)
mod_mp4_la_LIBADD = $(switch_builddir)/libfreeswitch.la
mod_mp4_la_LDFLAGS = -avoid-version -module -no-undefined -shared -lmp4v2

View File

@ -1,59 +0,0 @@
/*
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 MP4 Helper Library to Freeswitch MP4 module.
The Initial Developer of the Original Code is
Paulo Rogério Panhoto <paulo@voicetechnology.com.br>.
Portions created by the Initial Developer are Copyright (C)
the Initial Developer. All Rights Reserved.
*/
#ifndef EXCEPTION_HPP_
#define EXCEPTION_HPP_
#include <exception>
#include <string>
class Exception: public std::exception {
public:
Exception()
{
}
Exception(const std::string & message): message_(message)
{
}
Exception(const std::exception & e): message_(e.what())
{
}
Exception(const Exception & e): message_(e.message_)
{
}
virtual ~Exception() throw()
{
}
const char * what() const throw()
{
return message_.c_str();
}
private:
std::string message_;
};
#endif

View File

@ -1,550 +0,0 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2014, 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
* Paulo Rogério Panhoto <paulo@voicetechnology.com.br>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* mod_mp4 -- MP4 File Format support for video apps.
*
*/
#include <switch.h>
#include "mp4_helper.hpp"
#include "exception.hpp"
#ifndef min
#define min(x, y) ((x) < (y) ? (x) : (y))
#endif
SWITCH_MODULE_LOAD_FUNCTION(mod_mp4_load);
SWITCH_MODULE_DEFINITION(mod_mp4, mod_mp4_load, NULL, NULL);
#define VID_BIT (1 << 31)
#define VERSION 4201
#ifdef MP4_RECORD
struct file_header {
int32_t version;
char video_codec_name[32];
char video_fmtp[128];
uint32_t audio_rate;
uint32_t audio_ptime;
switch_time_t created;
};
struct record_helper {
switch_core_session_t *session;
switch_mutex_t *mutex;
int fd;
int up;
};
#endif
struct AVParams {
switch_core_session_t * session;
switch_channel_t * channel;
switch_timer_t * timer;
switch_frame_t * frame;
switch_mutex_t * mutex;
bool video;
switch_payload_t pt;
MP4::Context * vc;
bool done;
bool * quit;
};
static void *SWITCH_THREAD_FUNC record_video_thread(switch_thread_t *thread, void *obj)
{
#ifdef MP4_RECORD
record_helper *eh = reinterpret_cast<record_helper *>(obj);
switch_core_session_t *session = eh->session;
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_status_t status;
switch_frame_t *read_frame;
int bytes;
eh->up = 1;
while (switch_channel_ready(channel)) {
status = switch_core_session_read_video_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
if (!SWITCH_READ_ACCEPTABLE(status)) {
break;
}
if (switch_test_flag(read_frame, SFF_CNG)) {
continue;
}
bytes = read_frame->packetlen | VID_BIT;
switch_mutex_lock(eh->mutex);
if (write(eh->fd, &bytes, sizeof(bytes)) != (int) sizeof(bytes)) {
switch_mutex_unlock(eh->mutex);
break;
}
if (write(eh->fd, read_frame->packet, read_frame->packetlen) != (int) read_frame->packetlen) {
switch_mutex_unlock(eh->mutex);
break;
}
switch_mutex_unlock(eh->mutex);
switch_core_session_write_video_frame(session, read_frame, SWITCH_IO_FLAG_NONE, 0);
}
eh->up = 0;
#endif
return NULL;
}
SWITCH_STANDARD_APP(record_mp4_function)
{
#ifdef MP4_RECORD
switch_status_t status;
switch_frame_t *read_frame;
switch_channel_t *channel = switch_core_session_get_channel(session);
struct record_helper eh = { 0 };
switch_thread_t *thread;
switch_threadattr_t *thd_attr = NULL;
int fd;
switch_mutex_t *mutex = NULL;
switch_codec_t codec, *vid_codec;
switch_codec_implementation_t read_impl = { };
int count = 0, sanity = 30;
switch_core_session_get_read_impl(session, &read_impl);
switch_channel_answer(channel);
while (switch_channel_up(channel) && !switch_channel_test_flag(channel, CF_VIDEO)) {
switch_yield(10000);
if (count) count--;
if (count == 0) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "%s waiting for video.\n", switch_channel_get_name(channel));
count = 100;
if (!--sanity) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s timeout waiting for video.\n",
switch_channel_get_name(channel));
return;
}
}
}
if (!switch_channel_ready(channel)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "%s not ready.\n", switch_channel_get_name(channel));
return;
}
/*
if ((fd = open((char *) data, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Error opening file %s\n", (char *) data);
return;
}
*/
MP4::Context ctx(reinterpret_cast<char*>(data), true);
if (switch_core_codec_init(&codec,
"L16",
NULL,
NULL,
read_impl.samples_per_second,
read_impl.microseconds_per_packet / 1000,
1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
NULL, switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS)
{
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Audio Codec Activation Success\n");
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Audio Codec Activation Fail\n");
goto end;
}
switch_core_session_set_read_codec(session, &codec);
if (switch_channel_test_flag(channel, CF_VIDEO)) {
struct file_header h;
memset(&h, 0, sizeof(h));
vid_codec = switch_core_session_get_video_read_codec(session);
h.version = VERSION;
h.created = switch_micro_time_now();
switch_set_string(h.video_codec_name, vid_codec->implementation->iananame);
if (vid_codec->fmtp_in) {
switch_set_string(h.video_fmtp, vid_codec->fmtp_in);
}
h.audio_rate = read_impl.samples_per_second;
h.audio_ptime = read_impl.microseconds_per_packet / 1000;
if (write(fd, &h, sizeof(h)) != sizeof(h)) {
goto end;
}
switch_mutex_init(&mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
eh.mutex = mutex;
eh.fd = fd;
eh.session = session;
switch_threadattr_create(&thd_attr, switch_core_session_get_pool(session));
switch_threadattr_detach_set(thd_attr, 1);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
switch_thread_create(&thread, thd_attr, record_video_thread, &eh, switch_core_session_get_pool(session));
}
while (switch_channel_ready(channel)) {
status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
if (!SWITCH_READ_ACCEPTABLE(status)) {
break;
}
if (switch_test_flag(read_frame, SFF_CNG)) {
continue;
}
if (mutex) {
switch_mutex_lock(mutex);
}
if (write(fd, &read_frame->datalen, sizeof(read_frame->datalen)) != sizeof(read_frame->datalen)) {
if (mutex) {
switch_mutex_unlock(mutex);
}
break;
}
if (write(fd, read_frame->data, read_frame->datalen) != (int) read_frame->datalen) {
if (mutex) {
switch_mutex_unlock(mutex);
}
break;
}
if (mutex) {
switch_mutex_unlock(mutex);
}
}
end:
if (eh.up) {
while (eh.up) {
switch_cond_next();
}
}
switch_core_session_set_read_codec(session, NULL);
switch_core_codec_destroy(&codec);
#endif
}
static void *SWITCH_THREAD_FUNC play_video_function(switch_thread_t *thread, void *obj)
{
AVParams * pt = reinterpret_cast<AVParams*>(obj);
u_int next = 0, first = 0xffffffff;
uint64_t ts = 0, control = 0;
bool ok;
bool sent = true;
pt->done = false;
switch_time_t start = switch_time_now();
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pt->session), SWITCH_LOG_DEBUG, "Video thread Started\n");
while (!*pt->quit && switch_channel_ready(pt->channel)) {
if (pt->video) {
if (sent) {
switch_mutex_lock(pt->mutex);
pt->frame->packetlen = pt->frame->buflen;
ok = pt->vc->getVideoPacket(pt->frame->packet, pt->frame->packetlen, next);
switch_mutex_unlock(pt->mutex);
sent = false;
if (ok) {
switch_rtp_hdr_t *hdr = reinterpret_cast<switch_rtp_hdr_t *>(pt->frame->packet);
if(first == 0xffffffff) first = next;
next -= first;
control = next * 90000LL / pt->vc->videoTrack().track.clock;
control -= first;
hdr->ts = htonl(control);
control = control * 1000 / 90;
if (pt->pt)
hdr->pt = pt->pt;
} else break;
}
ts = switch_time_now() - start;
int64_t wait = control > ts ? (control - ts) : 0;
if (wait > 0) {
switch_cond_next();
// wait the time for the next Video frame
switch_sleep(wait);
}
if (switch_channel_test_flag(pt->channel, CF_VIDEO)) {
switch_byte_t *data = (switch_byte_t *) pt->frame->packet;
pt->frame->data = data + 12;
pt->frame->datalen = pt->frame->packetlen - 12;
switch_core_session_write_video_frame(pt->session, pt->frame, SWITCH_IO_FLAG_NONE, 0);
sent = true;
}
}
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pt->session), SWITCH_LOG_DEBUG, "Video thread ended\n");
pt->done = true;
return NULL;
}
static void *SWITCH_THREAD_FUNC play_audio_function(switch_thread_t *thread, void *obj)
{
AVParams * pt = reinterpret_cast<AVParams*>(obj);
u_int next = 0, first = 0xffffffff;
uint64_t ts = 0, control = 0;
bool ok;
bool sent = true;
switch_dtmf_t dtmf = {0};
pt->done = false;
switch_frame_t * read_frame;
while (!*pt->quit && switch_channel_ready(pt->channel)) {
// event processing.
// -- SEE switch_ivr_play_say.c:1231 && mod_dptools.c:1428 && mod_dptools.c:1919
switch_core_session_read_frame(pt->session, &read_frame, SWITCH_IO_FLAG_SINGLE_READ, 0);
if (switch_channel_test_flag(pt->channel, CF_BREAK)) {
switch_channel_clear_flag(pt->channel, CF_BREAK);
break;
}
switch_ivr_parse_all_events(pt->session);
if (switch_channel_has_dtmf(pt->channel)) {
switch_channel_dequeue_dtmf(pt->channel, &dtmf);
const char * terminators = switch_channel_get_variable(pt->channel, SWITCH_PLAYBACK_TERMINATORS_VARIABLE);
if (terminators && !strcasecmp(terminators, "none")) terminators = NULL;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pt->session), SWITCH_LOG_DEBUG, "Digit %c\n", dtmf.digit);
if (terminators && strchr(terminators, dtmf.digit)) {
std::string digit(&dtmf.digit, 0, 1);
switch_channel_set_variable(pt->channel, SWITCH_PLAYBACK_TERMINATOR_USED, digit.c_str());
break;
}
}
switch_mutex_lock(pt->mutex);
pt->frame->datalen = pt->frame->buflen;
ok = pt->vc->getAudioPacket(pt->frame->data, pt->frame->datalen, next);
switch_mutex_unlock(pt->mutex);
if (ok) {
if (pt->frame->datalen > (int) pt->frame->buflen)
pt->frame->datalen = pt->frame->buflen;
switch_core_session_write_frame(pt->session, pt->frame, SWITCH_IO_FLAG_NONE, 0);
switch_core_timer_next(pt->timer);
}
else break;
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pt->session), SWITCH_LOG_DEBUG, "Audio done\n");
*pt->quit = pt->done = true;
return NULL;
}
SWITCH_STANDARD_APP(play_mp4_function)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_frame_t write_frame = { 0 }, vid_frame = {0};
switch_codec_t codec = { 0 }, vid_codec = {0}, *read_vid_codec;
unsigned char *aud_buffer;
unsigned char *vid_buffer;
switch_timer_t timer = { 0 };
switch_codec_implementation_t read_impl = {};
bool done = false;
try {
MP4::Context vc((char *) data);
switch_payload_t pt = 0;
switch_core_session_get_read_impl(session, &read_impl);
aud_buffer = (unsigned char *) switch_core_session_alloc(session, SWITCH_RECOMMENDED_BUFFER_SIZE);
vid_buffer = (unsigned char *) switch_core_session_alloc(session, SWITCH_RECOMMENDED_BUFFER_SIZE);
/*
if (!vc.isOpen())
{
char msgbuf[1024];
sprintf(msgbuf, "PLAYBACK ERROR (%s): FILE NOT FOUND.", (char*) data);
switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, msgbuf);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Error opening file %s\n", (char *) data);
return;
}
if(!vc.isSupported())
{
char msgbuf[1024];
sprintf(msgbuf, "PLAYBACK ERROR (%s): UNSUPPORTED FORMAT OR FILE NOT HINTED.", (char*) data);
switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, msgbuf);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT,
"Error reading track info. Maybe this file is not hinted.\n");
throw 1;
}
*/
switch_channel_set_variable(channel, "rtp_force_video_fmtp", vc.videoTrack().fmtp.c_str());
switch_channel_answer(channel);
if ((read_vid_codec = switch_core_session_get_video_read_codec(session))) {
pt = read_vid_codec->agreed_pt;
}
write_frame.codec = &codec;
write_frame.data = aud_buffer;
write_frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE;
vid_frame.codec = &vid_codec;
vid_frame.packet = vid_buffer;
vid_frame.data = vid_buffer + 12;
vid_frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE - 12;
switch_set_flag((&vid_frame), SFF_RAW_RTP);
switch_set_flag((&vid_frame), SFF_PROXY_PACKET);
if (switch_core_timer_init(&timer, "soft", read_impl.microseconds_per_packet / 1000,
read_impl.samples_per_packet, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Timer Activation Fail\n");
throw 2;
}
if (switch_core_codec_init(&codec,
vc.audioTrack().codecName,
NULL,
NULL,
vc.audioTrack().clock,
vc.audioTrack().packetLength,
1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
NULL, switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Audio Codec Activation Success\n");
} else {
throw Exception("Audio Codec Activation Fail");
}
if (switch_core_codec_init(&vid_codec,
vc.videoTrack().track.codecName,
NULL,
NULL,
0,
0,
1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
NULL, switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Video Codec Activation Success\n");
} else
{
throw Exception("Video Codec Activation Fail");
}
switch_core_session_set_read_codec(session, &codec);
AVParams vpt;
vpt.session = session;
vpt.channel = channel;
vpt.frame = &vid_frame;
vpt.timer = &timer;
vpt.video = true;
vpt.pt = pt;
vpt.vc = &vc;
switch_mutex_init(&vpt.mutex, SWITCH_MUTEX_DEFAULT, switch_core_session_get_pool(session));
vpt.quit = &done;
switch_threadattr_t * thd_attr;
switch_threadattr_create(&thd_attr, switch_core_session_get_pool(session));
switch_threadattr_detach_set(thd_attr, 1);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
switch_thread_t *thread;
switch_thread_create(&thread, thd_attr, play_video_function, (void*)&vpt, switch_core_session_get_pool(session));
AVParams apt;
apt.session = session;
apt.channel = channel;
apt.frame = &write_frame;
apt.timer = &timer;
apt.video = false;
apt.vc = &vc;
apt.mutex = vpt.mutex;
apt.quit = &done;
play_audio_function(NULL, &apt);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Waiting for video thread to join.\n");
while (!vpt.done) {
switch_cond_next();
}
switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "FILE PLAYED");
} catch(const std::exception & e)
{
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "%s\n", e.what());
switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE,
(std::string("PLAYBACK_FAILED - ") + e.what()).c_str());
}catch(...)
{
switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "PLAYBACK_FAILED - See FS logs for detail.");
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Exception caught.\n");
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "All done.\n");
if (timer.interval) switch_core_timer_destroy(&timer);
switch_core_session_set_read_codec(session, NULL);
if (switch_core_codec_ready(&codec)) switch_core_codec_destroy(&codec);
if (switch_core_codec_ready(&vid_codec)) switch_core_codec_destroy(&vid_codec);
}
SWITCH_MODULE_LOAD_FUNCTION(mod_mp4_load)
{
switch_application_interface_t *app_interface;
/* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
SWITCH_ADD_APP(app_interface, "play_mp4", "play an MP4 file", "play an MP4 file", play_mp4_function, "<file>", SAF_NONE);
//SWITCH_ADD_APP(app_interface, "record_mp4", "record an MP4 file", "record an MP4 file", record_mp4_function, "<file>", SAF_NONE);
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
*/

View File

@ -1,136 +0,0 @@
/*
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 MP4 Helper Library to the Freeswitch MP4 Module.
The Initial Developer of the Original Code is
Paulo Rogério Panhoto <paulo@voicetechnology.com.br>.
Portions created by the Initial Developer are Copyright (C)
the Initial Developer. All Rights Reserved.
Contributors:
Seven Du <dujinfang@gmail.com>
*/
#include <switch.h>
#include "mp4_helper.hpp"
namespace MP4
{
Context::Context(const char * file, bool newFile)
{
if(newFile) create(file);
else open(file);
}
Context::~Context()
{
close();
}
void Context::open(const char * file)
{
fh = MP4Read(file);
if (fh == MP4_INVALID_FILE_HANDLE) throw Exception(file, "Open failed");
getTracks(file);
}
void Context::create(const char * file)
{
fh = MP4Create(file);
if (fh == MP4_INVALID_FILE_HANDLE) throw Exception(file, "Create file failed");
}
void Context::close()
{
if (!isOpen()) return;
MP4Close(fh);
}
void Context::getTracks(const char * file)
{
int i = 0;
bool audioTrack = false, videoTrack = false;
if (!isOpen()) throw Exception(file, "File is closed.");
for (;;)
{
TrackProperties track;
if((track.hint = MP4FindTrackId(fh, i++, MP4_HINT_TRACK_TYPE, 0)) == MP4_INVALID_TRACK_ID) break;
MP4GetHintTrackRtpPayload(fh, track.hint, &track.codecName, &track.payload, NULL, NULL);
track.track = MP4GetHintTrackReferenceTrackId(fh, track.hint);
if(track.track == MP4_INVALID_TRACK_ID) continue;
track.clock = MP4GetTrackTimeScale(fh, track.hint);
if (!strcmp(MP4GetTrackType(fh, track.track), MP4_AUDIO_TRACK_TYPE)) {
audioTrack = true;
if(!strncmp(track.codecName, "PCM", 3))
track.packetLength = 20;
else
track.packetLength = track.clock = 0;
audio = track;
} else if (!strcmp(MP4GetTrackType(fh, track.track), MP4_VIDEO_TRACK_TYPE)) {
videoTrack = true;
const char * sdp = MP4GetHintTrackSdp(fh, track.hint);
const char * fmtp = strstr(sdp, "fmtp");
if (fmtp) {
// finds beginning of 'fmtp' value;
for(fmtp += 5; *fmtp != ' '; ++fmtp);
++fmtp;
const char * eol = fmtp;
for(;*eol != '\r' && *eol != '\n'; ++eol);
video.fmtp = std::string(fmtp, eol);
}
video.track = track;
}
}
if (!audioTrack || !videoTrack) throw Exception(file, "Missing audio/video track.");
}
bool Context::getVideoPacket(void * buffer, u_int & size, u_int & ts)
{
return getPacket(video.track.hint, video.track.runtime, true, buffer, size, ts);
}
bool Context::getAudioPacket(void * buffer, u_int & size, u_int & ts)
{
return getPacket(audio.hint, audio.runtime, false, buffer, size, ts);
}
bool Context::getPacket(MP4TrackId hint, RuntimeProperties & rt,
bool header, void * buffer, u_int & size, u_int & ts)
{
if (rt.frame == 0 || rt.packet == rt.packetsPerFrame) {
++rt.frame;
if(!MP4ReadRtpHint(fh, hint, rt.frame, &rt.packetsPerFrame))
return false;
rt.packet = 0;
rt.last_frame = MP4GetSampleTime(fh, hint, rt.frame);
}
ts = rt.last_frame;
if (!MP4ReadRtpPacket(fh, hint, rt.packet, (uint8_t **) &buffer, &size, 0, header, true)) return false;
++rt.packet;
return true;
}
}

View File

@ -1,143 +0,0 @@
/*
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 MP4 Helper Library to Freeswitch MP4 module.
The Initial Developer of the Original Code is
Paulo Rogério Panhoto <paulo@voicetechnology.com.br>.
Portions created by the Initial Developer are Copyright (C)
the Initial Developer. All Rights Reserved.
Contributor(s):
Seven Du <dujinfang@gmail.com>
*/
#ifndef MP4_HELPER_HPP_
#define MP4_HELPER_HPP_
#include <mp4v2/mp4v2.h>
#include <string>
#include <exception>
#include <string>
typedef unsigned int u_int;
namespace MP4
{
class Exception: public std::exception {
public:
Exception(const std::string & file, const std::string & error)
: description_(file + ':' + error)
{
}
const char * what() const throw()
{
return description_.c_str();
}
~Exception() throw()
{
}
private:
std::string description_;
};
struct RuntimeProperties {
uint32_t frame; // sampleID
uint16_t packetsPerFrame;
uint16_t packet; // packetID
uint32_t last_frame; // timestamp
RuntimeProperties(): frame(0), packetsPerFrame(0), packet(0)
{
}
};
struct TrackProperties {
MP4TrackId hint;
MP4TrackId track;
char * codecName;
uint8_t payload;
uint32_t clock;
uint32_t packetLength; // packet Length in time (ms)
RuntimeProperties runtime;
TrackProperties(): hint(MP4_INVALID_TRACK_ID), track(MP4_INVALID_TRACK_ID),
codecName(NULL), payload(0), clock(0), packetLength(0)
{
}
};
typedef TrackProperties AudioProperties;
struct VideoProperties {
TrackProperties track;
std::string fmtp;
VideoProperties()
{
}
VideoProperties(const TrackProperties & rhs): track(rhs)
{
}
};
class Context {
public:
Context(const char * file, bool create = false);
~Context();
void open(const char * file);
void create(const char * file);
void close();
// returns: TRUE = has more data, FALSE = end-of-stream or failure
bool getVideoPacket(void * buffer, u_int & size, u_int & ts);
// returns: TRUE = has more data, FALSE = end-of-stream or failure
bool getAudioPacket(void * buffer, u_int & size, u_int & ts);
bool isOpen() const { return fh != MP4_INVALID_FILE_HANDLE; }
bool isSupported() const { return audio.track != MP4_INVALID_TRACK_ID && video.track.track != MP4_INVALID_TRACK_ID; }
const AudioProperties & audioTrack() const { return audio; }
const VideoProperties & videoTrack() const { return video; }
private:
MP4FileHandle fh;
AudioProperties audio;
VideoProperties video;
// Prevent copy construction.
Context(const Context &);
bool getPacket(MP4TrackId hint, RuntimeProperties & rt,
bool header, void * buffer, u_int & size, u_int & ts);
void getTracks(const char * file);
};
}
#endif

View File

@ -1,8 +0,0 @@
include $(top_srcdir)/build/modmake.rulesam
MODNAME=mod_mp4v2
mod_LTLIBRARIES = mod_mp4v2.la
mod_mp4v2_la_SOURCES = mod_mp4v2.c
mod_mp4v2_la_CFLAGS = $(AM_CFLAGS)
mod_mp4v2_la_LIBADD = $(switch_builddir)/libfreeswitch.la
mod_mp4v2_la_LDFLAGS = -avoid-version -module -no-undefined -shared -lmp4v2

View File

@ -1,772 +0,0 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2015, 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):
*
*
* mod_mp4v2 -- MP4 File Format for FreeSWITCH
*
*
* status: For write, codec is hard coded to PCMU for audio and H264 for video
* tested with lib mp4v2-2.0.0
* Read from mp4 is not supported yet.
*/
#include <switch.h>
#include <mp4v2/mp4v2.h>
#define TIMESCALE 1000
#define SampleLenFieldSize 4
#define PTIME 20
// #define HAS_SPS_PARSER
SWITCH_MODULE_LOAD_FUNCTION(mod_mp4v2_load);
SWITCH_MODULE_DEFINITION(mod_mp4v2, mod_mp4v2_load, NULL, NULL);
struct record_helper {
switch_core_session_t *session;
switch_mutex_t *mutex;
MP4FileHandle fd;
MP4TrackId video_track;
MP4TrackId audio_track;
switch_timer_t timer;
int up;
uint64_t last_pts;
};
#ifdef HAS_SPS_PARSER
#include "bs.h"
static void parse_sps_video_size(uint8_t *sps_buffer, int len, int *width, int *height)
{
sps_t sps = { 0 };
bs_t b = { 0 };
bs_init(&b, sps_buffer, len);
read_sps(&sps, &b);
*width = ((sps.pic_width_in_mbs_minus1 +1)*16) - sps.frame_crop_left_offset*2 - sps.frame_crop_right_offset*2;
*height= ((2 - sps.frame_mbs_only_flag)* (sps.pic_height_in_map_units_minus1 +1) * 16) - (sps.frame_crop_top_offset * 2) - (sps.frame_crop_bottom_offset * 2);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "H264 Profile: %d size: %dx%d\n", sps.profile_idc, *width, *height);
}
#else
// use hardcoded value
static void parse_sps_video_size(uint8_t *sps_buffer, int len, int *width, int *height)
{
*width = 1280;
*height = 720;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "We have no idea about the video size without decoding the video or actually parse the SPS, using hardcoded %dx%d\n", *width, *height);
}
#endif
static void init_video_track(MP4FileHandle mp4, MP4TrackId *video, switch_frame_t *frame)
{
int width = 0;
int height = 0;
uint8_t *sps_buffer = frame->data;
uint32_t sps_bytes = frame->datalen;
sps_buffer++;
if (frame->img) {
width = frame->img->d_w;
height = frame->img->d_h;
} else {
parse_sps_video_size(sps_buffer, sps_bytes, &width, &height);
}
MP4SetTimeScale(mp4, TIMESCALE);
*video = MP4AddH264VideoTrack(mp4, TIMESCALE, MP4_INVALID_DURATION, width, height, *(sps_buffer), *(sps_buffer+1), *(sps_buffer+2), SampleLenFieldSize - 1);
if (*video == MP4_INVALID_TRACK_ID) {
return;
}
MP4AddH264SequenceParameterSet(mp4, *video, --sps_buffer, sps_bytes);
/*
MP4SetVideoProfileLevel sets the minumum profile/level of MPEG-4 video support necessary to render the contents of the file.
ISO/IEC 14496-1:2001 MPEG-4 Systems defines the following values:
0x00 Reserved
0x01 Simple Profile @ Level 3
0x02 Simple Profile @ Level 2
0x03 Simple Profile @ Level 1
0x04 Simple Scalable Profile @ Level 2
0x05 Simple Scalable Profile @ Level 1
0x06 Core Profile @ Level 2
0x07 Core Profile @ Level 1
0x08 Main Profile @ Level 4
0x09 Main Profile @ Level 3
0x0A Main Profile @ Level 2
0x0B N-Bit Profile @ Level 2
0x0C Hybrid Profile @ Level 2
0x0D Hybrid Profile @ Level 1
0x0E Basic Animated Texture @ Level 2
0x0F Basic Animated Texture @ Level 1
0x10 Scalable Texture @ Level 3
0x11 Scalable Texture @ Level 2
0x12 Scalable Texture @ Level 1
0x13 Simple Face Animation @ Level 2
0x14 Simple Face Animation @ Level 1
0x15-0x7F Reserved
0x80-0xFD User private
0xFE No audio profile specified
0xFF No audio required
*/
MP4SetVideoProfileLevel(mp4, 0x7F);
}
static inline char *get_audio_codec_name(uint8_t audio_type)
{
switch (audio_type) {
case MP4_MP3_AUDIO_TYPE:
return "MP3";
case MP4_ULAW_AUDIO_TYPE:
return "PCMU";
case MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE:
return "L16";
case MP4_MPEG4_AUDIO_TYPE:
return "AAC";
default:
return "ERROR";
}
}
static int get_aac_sample_rate_index(unsigned int sampleRate)
{
if (92017 <= sampleRate) return 0;
if (75132 <= sampleRate) return 1;
if (55426 <= sampleRate) return 2;
if (46009 <= sampleRate) return 3;
if (37566 <= sampleRate) return 4;
if (27713 <= sampleRate) return 5;
if (23004 <= sampleRate) return 6;
if (18783 <= sampleRate) return 7;
if (13856 <= sampleRate) return 8;
if (11502 <= sampleRate) return 9;
if (9391 <= sampleRate) return 10;
return 11;
}
struct mp4_file_context {
switch_file_handle_t *handle;
switch_memory_pool_t *pool;
MP4FileHandle fd;
MP4TrackId audio;
MP4TrackId video;
uint32_t audio_frame_size;
switch_codec_t audio_codec;
switch_codec_t video_codec;
switch_mutex_t *mutex;
switch_buffer_t *buf;
uint32_t last_chunk_size;
int sps_set;
int pps_set;
switch_timer_t timer;
uint64_t last_pts;
int offset;
int audio_start;
uint8_t audio_type; // MP4 Audio Type
MP4Duration audio_duration;
switch_thread_t *video_thread;
switch_queue_t *video_queue;
};
typedef struct mp4_file_context mp4_file_context_t;
static switch_status_t do_write_video(switch_file_handle_t *handle, switch_frame_t *frame);
static void *SWITCH_THREAD_FUNC video_write_thread_run(switch_thread_t *thread, void *obj)
{
mp4_file_context_t *context = (mp4_file_context_t *)obj;
void *pop = NULL;
switch_image_t *last_img = NULL;
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_status_t encode_status = SWITCH_STATUS_SUCCESS;
uint8_t data[SWITCH_DEFAULT_VIDEO_SIZE];
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "video_write_thread start\n");
while (switch_queue_pop(context->video_queue, &pop) == SWITCH_STATUS_SUCCESS) {
switch_frame_t frame = { 0 };
if (!pop) break;
if (!last_img) { // first img
last_img = (switch_image_t *)pop;
continue;
}
frame.data = data;
frame.img = last_img;
// switch_set_flag(&frame, SFF_DYNAMIC);
do {
frame.datalen = SWITCH_DEFAULT_VIDEO_SIZE;
encode_status = switch_core_codec_encode_video(&context->video_codec, &frame);
if (encode_status == SWITCH_STATUS_SUCCESS || encode_status == SWITCH_STATUS_MORE_DATA) {
switch_assert((encode_status == SWITCH_STATUS_SUCCESS && frame.m) || !frame.m);
if (frame.datalen == 0) break;
status = do_write_video(context->handle, &frame);
}
} while(status == SWITCH_STATUS_SUCCESS && encode_status == SWITCH_STATUS_MORE_DATA);
switch_img_free(&last_img);
last_img = (switch_image_t *)pop;
}
switch_img_free(&last_img);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "video_write_thread done\n");
return NULL;
}
static void launch_video_write_thread(mp4_file_context_t *context, switch_memory_pool_t *pool)
{
//switch_thread_t *thread;
switch_threadattr_t *thd_attr = NULL;
switch_threadattr_create(&thd_attr, pool);
switch_threadattr_detach_set(thd_attr, 1);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
// switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME);
switch_thread_create(&context->video_thread, thd_attr, video_write_thread_run, context, pool);
}
static int flush_video_queue(switch_queue_t *q, int min)
{
void *pop;
if (switch_queue_size(q) > min) {
while (switch_queue_trypop(q, &pop) == SWITCH_STATUS_SUCCESS) {
switch_image_t *img = (switch_image_t *) pop;
switch_img_free(&img);
if (min && switch_queue_size(q) <= min) {
break;
}
}
}
return switch_queue_size(q);
}
static switch_status_t mp4_file_open(switch_file_handle_t *handle, const char *path)
{
mp4_file_context_t *context;
char *ext;
unsigned int flags = 0;
const char *tmp = NULL;
if ((ext = strrchr(path, '.')) == 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Format\n");
return SWITCH_STATUS_GENERR;
}
ext++;
if ((context = switch_core_alloc(handle->memory_pool, sizeof(mp4_file_context_t))) == 0) {
return SWITCH_STATUS_MEMERR;
}
memset(context, 0, sizeof(mp4_file_context_t));
context->handle = handle;
context->offset = 0;
if (handle->params && (tmp = switch_event_get_header(handle->params, "mp4v2_video_offset"))) {
context->offset = atoi(tmp);
}
context->audio_type = MP4_ULAW_AUDIO_TYPE; // default
if (handle->params && (tmp = switch_event_get_header(handle->params, "mp4v2_audio_codec"))) {
if (!strcasecmp(tmp, "PCMU")) {
context->audio_type = MP4_ULAW_AUDIO_TYPE;
} else if (!strcasecmp(tmp, "MP3")) {
context->audio_type = MP4_MP3_AUDIO_TYPE;
} else if (!strcasecmp(tmp, "AAC")) {
context->audio_type = MP4_MPEG4_AUDIO_TYPE;
} else if (!strcasecmp(tmp, "L16")) {
context->audio_type = MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE;
}
}
switch_mutex_init(&context->mutex, SWITCH_MUTEX_NESTED, handle->memory_pool);
if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
flags |= SWITCH_FOPEN_WRITE | SWITCH_FOPEN_CREATE;
if (switch_test_flag(handle, SWITCH_FILE_WRITE_APPEND) || switch_test_flag(handle, SWITCH_FILE_WRITE_OVER)) {
flags |= SWITCH_FOPEN_READ;
} else {
flags |= SWITCH_FOPEN_TRUNCATE;
}
}
if (switch_test_flag(handle, SWITCH_FILE_FLAG_READ)) {
flags |= SWITCH_FOPEN_READ;
}
if (handle->mm.samplerate) {
handle->samplerate = handle->mm.samplerate;
} else {
handle->mm.samplerate = handle->samplerate;
}
if (!handle->mm.ab) {
handle->mm.ab = 128;
}
if (!handle->mm.vb) {
handle->mm.vb = switch_calc_bitrate(handle->mm.vw, handle->mm.vh, 1, handle->mm.fps);
}
// MP4_CREATE_64BIT_DATA if file > 4G
if ((context->fd = MP4CreateEx(path, 0, 1, 1, NULL, 0, NULL, 0)) == MP4_INVALID_FILE_HANDLE) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error opening file %s\n", path);
return SWITCH_STATUS_GENERR;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "sample rate: %d, channels: %d\n", handle->samplerate, handle->channels);
if (context->audio_type == MP4_ULAW_AUDIO_TYPE) {
context->audio = MP4AddULawAudioTrack(context->fd, handle->samplerate);
MP4SetTrackIntegerProperty(context->fd, context->audio, "mdia.minf.stbl.stsd.ulaw.channels", handle->channels);
MP4SetTrackIntegerProperty(context->fd, context->audio, "mdia.minf.stbl.stsd.ulaw.sampleSize", 8);
} else if (context->audio_type == MP4_MP3_AUDIO_TYPE) {
// handle->samplerate = 44100;
context->audio = MP4AddAudioTrack(context->fd, handle->samplerate, handle->samplerate, MP4_MP3_AUDIO_TYPE);
MP4SetTrackName(context->fd, context->audio, ".mp3");
MP4SetTrackIntegerProperty(context->fd, context->audio, "mdia.minf.stbl.stsd.mp4a.channels", handle->channels);
// MP4SetTrackIntegerProperty(context->fd, context->audio, "mdia.minf.stbl.stsd..mp3.channels", handle->channels);
} else if (context->audio_type == MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE) {
context->audio = MP4AddAudioTrack(context->fd, handle->samplerate, handle->samplerate, MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE);
MP4SetTrackName(context->fd, context->audio, "lpcm");
MP4SetTrackIntegerProperty(context->fd, context->audio, "mdia.minf.stbl.stsd.mp4a.channels", handle->channels);
MP4SetTrackIntegerProperty(context->fd, context->audio, "mdia.minf.stbl.stsd.lpcm.channels", handle->channels);
MP4SetTrackIntegerProperty(context->fd, context->audio, "mdia.minf.stbl.stsd.lpcm.sampleSize", 16);
} else if (context->audio_type == MP4_MPEG4_AUDIO_TYPE) {
/* AAC object types */
#define AAC_MAIN 1
#define AAC_LOW 2
#define AAC_SSR 3
#define AAC_LTP 4
uint16_t info = 0;
info |= AAC_LOW << 11; // aacObjectType (5bit)
info |= get_aac_sample_rate_index(handle->samplerate) << 7; //(4bit)
info |= handle->channels << 3; //(4bit)
info = htons(info);
context->audio = MP4AddAudioTrack(context->fd, handle->samplerate, 1024, MP4_MPEG4_AUDIO_TYPE);
MP4SetTrackESConfiguration(context->fd, context->audio, (uint8_t *)&info, sizeof(info));
MP4SetTrackIntegerProperty(context->fd, context->audio, "mdia.minf.stbl.stsd.mp4a.channels", handle->channels);
}
handle->format = 0;
handle->sections = 0;
handle->seekable = 0;
handle->speed = 0;
handle->pos = 0;
handle->private_info = context;
context->pool = handle->memory_pool;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Opening File [%s] %dhz %s\n",
path, handle->samplerate, switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO) ? " with VIDEO" : "");
if (switch_core_codec_init(&context->audio_codec,
get_audio_codec_name(context->audio_type),
NULL,
NULL,
handle->samplerate,
PTIME,//ms
handle->channels, SWITCH_CODEC_FLAG_ENCODE,
NULL, handle->memory_pool) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Audio Codec Activation Success\n");
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Audio Codec Activation Fail\n");
goto end;
}
if (context->audio_type == MP4_MP3_AUDIO_TYPE) { // fetch frame size
uint32_t size;
uint32_t flag = 0xFFFFFFFF;
switch_core_codec_encode(&context->audio_codec, NULL, &flag, 0, 0,
(void *)&context->audio_frame_size, &size, NULL, &flag);
} else if (context->audio_type == MP4_MPEG4_AUDIO_TYPE) {
context->audio_frame_size = 1024;
}
if (switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO)) {
switch_codec_settings_t codec_settings = {{ 0 }};
codec_settings.video.bandwidth = handle->mm.vb;
codec_settings.video.fps = handle->mm.fps;
if (switch_core_codec_init(&context->video_codec,
"H264",
NULL,
NULL,
90000,
0,//ms
1, SWITCH_CODEC_FLAG_ENCODE,
&codec_settings, handle->memory_pool) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Video Codec H264 Activation Success\n");
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Video Codec H264 Activation Fail\n");
goto end;
}
switch_queue_create(&context->video_queue, 60, handle->memory_pool);
launch_video_write_thread(context, handle->memory_pool);
}
if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
MP4SetAudioProfileLevel(context->fd, 0x7F);
}
switch_buffer_create_dynamic(&context->buf, 512, 512, 1024000);
return SWITCH_STATUS_SUCCESS;
end:
if (context->fd) {
MP4Close(context->fd, 0);
context->fd = NULL;
}
return SWITCH_STATUS_FALSE;
}
static switch_status_t mp4_file_truncate(switch_file_handle_t *handle, int64_t offset)
{
mp4_file_context_t *context = handle->private_info;
switch_status_t status;
if ((status = switch_file_trunc(context->fd, offset)) == SWITCH_STATUS_SUCCESS) {
handle->pos = 0;
}
return status;
}
static switch_status_t mp4_file_close(switch_file_handle_t *handle)
{
mp4_file_context_t *context = handle->private_info;
switch_status_t status;
if (context->fd) {
MP4Close(context->fd, MP4_CLOSE_DO_NOT_COMPUTE_BITRATE);
context->fd = NULL;
}
if (switch_core_codec_ready(&context->audio_codec)) switch_core_codec_destroy(&context->audio_codec);
if (switch_core_codec_ready(&context->video_codec)) switch_core_codec_destroy(&context->video_codec);
if (context->timer.interval) {
switch_core_timer_destroy(&context->timer);
}
if (context->video_queue) {
switch_queue_term(context->video_queue);
flush_video_queue(context->video_queue, 0);
}
if (context->video_thread) {
switch_thread_join(&status, context->video_thread);
}
switch_buffer_destroy(&context->buf);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t mp4_file_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "seek not implemented\n");
return SWITCH_STATUS_FALSE;
}
static switch_status_t mp4_file_read(switch_file_handle_t *handle, void *data, size_t *len)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "read not implemented\n");
return SWITCH_STATUS_FALSE;
}
static switch_status_t mp4_file_write(switch_file_handle_t *handle, void *data, size_t *len)
{
uint32_t datalen = *len * 2 * handle->channels;
switch_status_t status = SWITCH_STATUS_SUCCESS;
uint8_t buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
uint32_t encoded_rate;
mp4_file_context_t *context = handle->private_info;
uint32_t size = 0;
uint32_t flag = 0;
if (context->audio_type == MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE) {
size = datalen;
memcpy(buf, data, datalen);
} else {
switch_core_codec_encode(&context->audio_codec, NULL,
data, datalen,
handle->samplerate,
buf, &size, &encoded_rate, &flag);
}
switch_mutex_lock(context->mutex);
if (!context->timer.interval) {
switch_core_timer_init(&context->timer, "soft", 1, 1, context->pool);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "init timer\n");
}
if (size > 0) {
MP4WriteSample(context->fd, context->audio, buf, size, context->audio_frame_size ? context->audio_frame_size : *len, 0, 1);
}
context->audio_duration += *len;
switch_mutex_unlock(context->mutex);
return status;
}
static switch_status_t mp4_file_read_video(switch_file_handle_t *handle, switch_frame_t *frame, switch_video_read_flag_t flags)
{
return SWITCH_STATUS_FALSE;
}
static switch_status_t do_write_video(switch_file_handle_t *handle, switch_frame_t *frame)
{
uint32_t datalen = frame->datalen;
switch_status_t status = SWITCH_STATUS_SUCCESS;
int is_iframe = 0;
uint32_t size;
uint8_t *hdr = NULL;
uint8_t fragment_type;
uint8_t nal_type;
uint8_t start_bit;
uint8_t end_bit;
mp4_file_context_t *context = handle->private_info;
hdr = (uint8_t *)frame->data;
fragment_type = hdr[0] & 0x1f;
nal_type = hdr[1] & 0x1f;
start_bit = hdr[1] & 0x80;
end_bit = hdr[1] & 0x40;
is_iframe = fragment_type == 5 || (fragment_type == 28 && nal_type == 5);
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%02x %02x %02x | len:%d m:%d st:%d i:%d\n", hdr[0], hdr[1], hdr[2], datalen, frame->m, start_bit, is_iframe);
if (fragment_type == 7 && !context->sps_set) { //sps
context->sps_set = 1;
init_video_track(context->fd, &context->video, frame);
if (context->video == MP4_INVALID_TRACK_ID) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error add video track!\n");
return SWITCH_STATUS_FALSE;
}
} else if (fragment_type == 8 && context->sps_set && !context->pps_set) { //pps
MP4AddH264PictureParameterSet(context->fd, context->video, hdr, datalen);
context->pps_set = 1;
}
if (fragment_type == 28) {
if (start_bit && end_bit) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "WTF?\n");
}
if (start_bit) {
nal_type |= (hdr[0] & 0x60);
size = htonl(datalen);
switch_buffer_write(context->buf, &size, 4);
switch_buffer_write(context->buf, &nal_type, 1);
switch_buffer_write(context->buf, hdr + 2, datalen - 2);
context->last_chunk_size = datalen - 1;
} else if (end_bit) {
uint32_t used;
const void *data;
uint32_t *chunk_size = NULL;
switch_buffer_write(context->buf, hdr + 2, datalen - 2);
context->last_chunk_size += datalen - 2;
used = switch_buffer_inuse(context->buf);
switch_buffer_peek_zerocopy(context->buf, &data);
chunk_size = (uint32_t *)((uint8_t *)data + used - context->last_chunk_size - 4);
*chunk_size = htonl(context->last_chunk_size);
} else {
switch_buffer_write(context->buf, hdr + 2, datalen - 2);
context->last_chunk_size += datalen - 2;
}
} else {
size = htonl(datalen);
switch_buffer_write(context->buf, &size, 4);
switch_buffer_write(context->buf, hdr, datalen);
}
if (!frame->m) {
return SWITCH_STATUS_SUCCESS;
}
switch_mutex_lock(context->mutex);
if (context->sps_set && context->pps_set) {
uint32_t used = switch_buffer_inuse(context->buf);
const void *data;
int duration = 0;
int ts = 0;
if (frame->img && frame->img->user_priv) {
ts = *(int *)frame->img->user_priv;
} else {
switch_core_timer_sync(&context->timer);
ts = context->timer.samplecount;
}
duration = ts - context->last_pts;
if (duration <= 0) duration = 1;
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "samplecount: %u, duration: %u\n", context->timer.samplecount, duration);
switch_buffer_peek_zerocopy(context->buf, &data);
context->last_pts = ts;
MP4WriteSample(context->fd, context->video, data, used, duration, 0, is_iframe);
switch_buffer_zero(context->buf);
}
switch_mutex_unlock(context->mutex);
{
int delta = context->timer.samplecount * (handle->samplerate / 1000) - context->audio_duration;
if (delta > (int)handle->samplerate) {
uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 };
size_t samples = handle->samplerate / 1000 * PTIME;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "missed audio %d samples at %d\n", delta, (int)context->audio_duration / (handle->samplerate / 1000));
while ((delta -= samples) > 0) {
mp4_file_write(handle, data, &samples);
samples = handle->samplerate / 1000 * PTIME;
}
}
}
return status;
}
static switch_status_t mp4_file_write_video(switch_file_handle_t *handle, switch_frame_t *frame)
{
mp4_file_context_t *context = handle->private_info;
if (!frame->img) {
return do_write_video(handle, frame);
} else {
switch_image_t *img = NULL;
if (!context->timer.interval) {
switch_mutex_lock(context->mutex);
switch_core_timer_init(&context->timer, "soft", 1, 1, context->pool);
switch_mutex_unlock(context->mutex);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "init timer\n");
} else {
switch_mutex_lock(context->mutex);
switch_core_timer_sync(&context->timer);
switch_mutex_unlock(context->mutex);
}
switch_img_copy(frame->img, &img);
switch_assert(img);
img->user_priv = malloc(sizeof(int));
*(int *)img->user_priv = context->timer.samplecount;
if (switch_queue_trypush(context->video_queue, img) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "video queue full, discard one frame\n");
}
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t mp4_file_set_string(switch_file_handle_t *handle, switch_audio_col_t col, const char *string)
{
return SWITCH_STATUS_FALSE;
}
static switch_status_t mp4_file_get_string(switch_file_handle_t *handle, switch_audio_col_t col, const char **string)
{
return SWITCH_STATUS_FALSE;
}
static char *supported_formats[3] = { 0 };
SWITCH_MODULE_LOAD_FUNCTION(mod_mp4v2_load)
{
switch_file_interface_t *file_interface;
supported_formats[0] = "mp4v2";
supported_formats[1] = "mp4";
/* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);
file_interface->interface_name = modname;
file_interface->extens = supported_formats;
file_interface->file_open = mp4_file_open;
file_interface->file_close = mp4_file_close;
file_interface->file_truncate = mp4_file_truncate;
file_interface->file_read = mp4_file_read;
file_interface->file_write = mp4_file_write;
file_interface->file_read_video = mp4_file_read_video;
file_interface->file_write_video = mp4_file_write_video;
file_interface->file_seek = mp4_file_seek;
file_interface->file_set_string = mp4_file_set_string;
file_interface->file_get_string = mp4_file_get_string;
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -1,8 +0,0 @@
include $(top_srcdir)/build/modmake.rulesam
MODNAME=mod_mp4v
mod_LTLIBRARIES = mod_mp4v.la
mod_mp4v_la_SOURCES = mod_mp4v.c
mod_mp4v_la_CFLAGS = $(AM_CFLAGS)
mod_mp4v_la_LIBADD = $(switch_builddir)/libfreeswitch.la
mod_mp4v_la_LDFLAGS = -avoid-version -module -no-undefined -shared

View File

@ -1,102 +0,0 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2014, 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
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Anthony Minessale II <anthm@freeswitch.org>
*
*
* mod_mp4v.c -- MP4V Video Codec
*
*/
#include <switch.h>
SWITCH_MODULE_LOAD_FUNCTION(mod_mp4v_load);
SWITCH_MODULE_DEFINITION(mod_mp4v, mod_mp4v_load, NULL, NULL);
static switch_status_t switch_mp4v_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings)
{
int encoding, decoding;
encoding = (flags & SWITCH_CODEC_FLAG_ENCODE);
decoding = (flags & SWITCH_CODEC_FLAG_DECODE);
if (!(encoding || decoding)) {
return SWITCH_STATUS_FALSE;
} else {
if (codec->fmtp_in) {
codec->fmtp_out = switch_core_strdup(codec->memory_pool, codec->fmtp_in);
}
return SWITCH_STATUS_SUCCESS;
}
}
static switch_status_t switch_mp4v_encode(switch_codec_t *codec,
switch_codec_t *other_codec,
void *decoded_data,
uint32_t decoded_data_len,
uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate,
unsigned int *flag)
{
return SWITCH_STATUS_FALSE;
}
static switch_status_t switch_mp4v_decode(switch_codec_t *codec,
switch_codec_t *other_codec,
void *encoded_data,
uint32_t encoded_data_len,
uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate,
unsigned int *flag)
{
return SWITCH_STATUS_FALSE;
}
static switch_status_t switch_mp4v_destroy(switch_codec_t *codec)
{
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_LOAD_FUNCTION(mod_mp4v_load)
{
switch_codec_interface_t *codec_interface;
/* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
SWITCH_ADD_CODEC(codec_interface, "MP4V Video (passthru)");
switch_core_codec_add_implementation(pool, codec_interface,
SWITCH_CODEC_TYPE_VIDEO, 99, "MP4V-ES", NULL, 90000, 90000, 0,
0, 0, 0, 0, 1, 1, switch_mp4v_init, switch_mp4v_encode, switch_mp4v_decode, switch_mp4v_destroy);
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
*/