@@ -1104,6 +1104,57 @@ static int media_player_find_file_begin(struct media_player *mp) {
11041104static bool media_player_read_packet (struct media_player * mp ) {
11051105 if (!mp -> coder .fmtctx )
11061106 return true;
1107+ // Handle raw RTP file playback
1108+ if (mp -> coder .audio_raw_rtp_mode ) {
1109+ // Check if we have more data
1110+ if (mp -> coder .audio_raw_rtp_pos >= mp -> coder .audio_raw_rtp_data .len ) {
1111+ ilog (LOG_DEBUG , "End of raw RTP file reached" );
1112+ return true;
1113+ }
1114+
1115+ // Calculate bytes to read (one frame)
1116+ size_t bytes_to_read = MIN (mp -> coder .audio_raw_rtp_frame_size ,
1117+ mp -> coder .audio_raw_rtp_data .len - mp -> coder .audio_raw_rtp_pos );
1118+ // Allocate packet
1119+ mp -> coder .pkt = av_packet_alloc ();
1120+ if (!mp -> coder .pkt ) {
1121+ ilog (LOG_ERR , "Failed to allocate packet" );
1122+ return true;
1123+ }
1124+
1125+ // Fill packet with raw RTP data
1126+ if (av_new_packet (mp -> coder .pkt , bytes_to_read ) < 0 ) {
1127+ ilog (LOG_ERR , "Failed to create packet" );
1128+ av_packet_free (& mp -> coder .pkt );
1129+ return true;
1130+ }
1131+ if (bytes_to_read > 0 ) {
1132+ memcpy (mp -> coder .pkt -> data ,
1133+ mp -> coder .audio_raw_rtp_data .s + mp -> coder .audio_raw_rtp_pos ,
1134+ bytes_to_read );
1135+ }
1136+ if (bytes_to_read < mp -> coder .audio_raw_rtp_frame_size ) {
1137+ // Pad with silence if needed
1138+ memset (mp -> coder .pkt -> data + bytes_to_read ,
1139+ mp -> coder .silence_byte ,
1140+ mp -> coder .audio_raw_rtp_frame_size - bytes_to_read );
1141+ ilog (LOG_DEBUG , "Padding %zu bytes of silence" ,
1142+ mp -> coder .audio_raw_rtp_frame_size - bytes_to_read );
1143+ }
1144+ mp -> coder .audio_raw_rtp_pos += bytes_to_read ;
1145+
1146+ // Simulate packet timing (20ms per frame)
1147+ mp -> coder .pkt -> pts = mp -> last_frame_ts ;
1148+ mp -> coder .pkt -> duration = 160 ;
1149+ mp -> last_frame_ts += mp -> coder .pkt -> duration ;
1150+ // Process packet
1151+ media_player_coder_add_packet (& mp -> coder , media_player_add_packet , mp );
1152+ av_packet_free (& mp -> coder .pkt );
1153+
1154+ // Schedule next read in 20ms
1155+ mp -> next_run = rtpe_now + 20000 ;
1156+ return false;
1157+ }
11071158
11081159 int ret = av_read_frame (mp -> coder .fmtctx , mp -> coder .pkt );
11091160 if (ret < 0 ) {
@@ -1232,17 +1283,18 @@ static bool media_player_play_start(struct media_player *mp, const rtp_payload_t
12321283 return true;
12331284
12341285 mp -> next_run = rtpe_now ;
1235- // give ourselves a bit of a head start with decoding
1236- mp -> next_run -= 50000 ;
1237-
1238- // if start_pos is positive, try to seek to that position
1239- if (mp -> opts .start_pos > 0 ) {
1240- ilog (LOG_DEBUG , "Seeking to position %lli" , mp -> opts .start_pos );
1241- av_seek_frame (mp -> coder .fmtctx , 0 , mp -> opts .start_pos , 0 );
1286+ if (!mp -> coder .audio_raw_rtp_mode ) {
1287+ // give ourselves a bit of a head start with decoding
1288+ mp -> next_run -= 50000 ;
1289+
1290+ // if start_pos is positive, try to seek to that position
1291+ if (mp -> opts .start_pos > 0 ) {
1292+ ilog (LOG_DEBUG , "Seeking to position %lli" , mp -> opts .start_pos );
1293+ av_seek_frame (mp -> coder .fmtctx , 0 , mp -> opts .start_pos , 0 );
1294+ }
1295+ else // in case this is a repeated start
1296+ av_seek_frame (mp -> coder .fmtctx , 0 , 0 , 0 );
12421297 }
1243- else // in case this is a repeated start
1244- av_seek_frame (mp -> coder .fmtctx , 0 , 0 , 0 );
1245-
12461298 media_player_read_packet (mp );
12471299
12481300 return true;
@@ -1475,6 +1527,97 @@ static mp_cached_code __media_player_add_file(struct media_player *mp,
14751527 return MPC_OK ;
14761528}
14771529
1530+ static bool __media_player_open_audio_raw_rtp_file (struct media_player * mp , media_player_opts_t opts ) {
1531+ // Validate codec
1532+ if (!opts .audio_raw_rtp_codec .len ) {
1533+ ilog (LOG_ERR , "Raw RTP playback requires codec specification" );
1534+ return false;
1535+ }
1536+
1537+ // Convert file path
1538+ char file_path [PATH_MAX ];
1539+ snprintf (file_path , sizeof (file_path ), STR_FORMAT , STR_FMT (& opts .audio_raw_rtp_file ));
1540+
1541+ // Open file
1542+ FILE * f = fopen (file_path , "rb" );
1543+ if (!f ) {
1544+ ilog (LOG_ERR , "Failed to open raw RTP file: %s" , file_path );
1545+ return false;
1546+ }
1547+
1548+ // Get file size
1549+ fseek (f , 0 , SEEK_END );
1550+ long file_size = ftell (f );
1551+ fseek (f , 0 , SEEK_SET );
1552+ // Read entire file into memory
1553+ char * file_data = malloc (file_size );
1554+ if (!file_data || fread (file_data , 1 , file_size , f ) != file_size ) {
1555+ ilog (LOG_ERR , "Failed to read raw RTP file" );
1556+ fclose (f );
1557+ free (file_data );
1558+ return false;
1559+ }
1560+ fclose (f );
1561+
1562+ // Store in player context
1563+ mp -> coder .audio_raw_rtp_data .s = file_data ;
1564+ mp -> coder .audio_raw_rtp_data .len = file_size ;
1565+ mp -> coder .audio_raw_rtp_pos = 0 ;
1566+ // Set codec parameters based on input
1567+ if (opts .audio_raw_rtp_codec .len == 4 && strncasecmp (opts .audio_raw_rtp_codec .s , "PCMU" , 4 ) == 0 ) {
1568+ mp -> coder .audio_raw_rtp_codec = AV_CODEC_ID_PCM_MULAW ;
1569+ mp -> coder .audio_raw_rtp_frame_size = 160 ; // 20ms frames
1570+ mp -> coder .silence_byte = 0xFF ; // μ-law silence
1571+ mp -> coder .time_base = (AVRational ){1 , 8000 }; // Default for 8kHz audio
1572+ }
1573+ else if (opts .audio_raw_rtp_codec .len == 4 && strncasecmp (opts .audio_raw_rtp_codec .s , "PCMA" , 4 ) == 0 ) {
1574+ mp -> coder .audio_raw_rtp_codec = AV_CODEC_ID_PCM_ALAW ;
1575+ mp -> coder .audio_raw_rtp_frame_size = 160 ; // 20ms frames
1576+ mp -> coder .silence_byte = 0x55 ; // A-law silence
1577+ mp -> coder .time_base = (AVRational ){1 , 8000 }; // Default for 8kHz audio
1578+ }
1579+ else {
1580+ ilog (LOG_ERR , "Unsupported raw RTP codec: " STR_FORMAT , STR_FMT (& opts .audio_raw_rtp_codec ));
1581+ free (file_data );
1582+ return false;
1583+ }
1584+
1585+ return true;
1586+ }
1587+
1588+ static bool media_player_play_audio_raw_rtp_file (struct media_player * mp , media_player_opts_t opts ) {
1589+ const rtp_payload_type * dst_pt = media_player_play_init (mp );
1590+ if (!dst_pt )
1591+ return false;
1592+
1593+ if (!__media_player_open_audio_raw_rtp_file (mp , opts ))
1594+ return false;
1595+
1596+ // Set up fake format context
1597+ mp -> coder .fmtctx = avformat_alloc_context ();
1598+ if (!mp -> coder .fmtctx ) {
1599+ ilog (LOG_ERR , "Failed to alloc format context" );
1600+ return false;
1601+ }
1602+
1603+ // Create a dummy stream
1604+ AVStream * stream = avformat_new_stream (mp -> coder .fmtctx , NULL );
1605+ if (!stream ) {
1606+ ilog (LOG_ERR , "Failed to create stream" );
1607+ return false;
1608+ }
1609+
1610+ // Set codec parameters
1611+ stream -> time_base = mp -> coder .time_base ;
1612+ stream -> codecpar -> codec_id = mp -> coder .audio_raw_rtp_codec ;
1613+ stream -> codecpar -> codec_type = AVMEDIA_TYPE_AUDIO ;
1614+ stream -> codecpar -> channels = 1 ;
1615+ stream -> codecpar -> channel_layout = AV_CH_LAYOUT_MONO ;
1616+
1617+ mp -> coder .audio_raw_rtp_mode = true;
1618+ return media_player_play_start (mp , dst_pt , opts .codec_set );
1619+ }
1620+
14781621// call->master_lock held in W
14791622static bool media_player_play_file (struct media_player * mp , media_player_opts_t opts ) {
14801623 const rtp_payload_type * dst_pt = media_player_play_init (mp );
@@ -1681,6 +1824,10 @@ const char * call_play_media_for_ml(struct call_monologue *ml,
16811824 if (!media_player_play_db (ml -> player , opts ))
16821825 return "Failed to start media playback from database" ;
16831826 }
1827+ else if (opts .audio_raw_rtp_file .len ) {
1828+ if (!media_player_play_audio_raw_rtp_file (ml -> player , opts ))
1829+ return "Failed to start audio raw RTP file playback" ;
1830+ }
16841831 else
16851832 return "No media file specified" ;
16861833 return NULL ;
0 commit comments