Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * RTP AMR Depacketizer, RFC 3267
  3.  * Copyright (c) 2010 Martin Storsjo
  4.  *
  5.  * This file is part of FFmpeg.
  6.  *
  7.  * FFmpeg is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  *
  12.  * FFmpeg is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with FFmpeg; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20.  */
  21.  
  22. #include "libavutil/channel_layout.h"
  23. #include "avformat.h"
  24. #include "rtpdec_formats.h"
  25. #include "libavutil/avstring.h"
  26.  
  27. static const uint8_t frame_sizes_nb[16] = {
  28.     12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0
  29. };
  30. static const uint8_t frame_sizes_wb[16] = {
  31.     17, 23, 32, 36, 40, 46, 50, 58, 60, 5, 5, 0, 0, 0, 0, 0
  32. };
  33.  
  34. struct PayloadContext {
  35.     int octet_align;
  36.     int crc;
  37.     int interleaving;
  38.     int channels;
  39. };
  40.  
  41. static PayloadContext *amr_new_context(void)
  42. {
  43.     PayloadContext *data = av_mallocz(sizeof(PayloadContext));
  44.     if(!data) return data;
  45.     data->channels = 1;
  46.     return data;
  47. }
  48.  
  49. static void amr_free_context(PayloadContext *data)
  50. {
  51.     av_free(data);
  52. }
  53.  
  54. static int amr_handle_packet(AVFormatContext *ctx, PayloadContext *data,
  55.                              AVStream *st, AVPacket *pkt, uint32_t *timestamp,
  56.                              const uint8_t *buf, int len, uint16_t seq,
  57.                              int flags)
  58. {
  59.     const uint8_t *frame_sizes = NULL;
  60.     int frames;
  61.     int i;
  62.     const uint8_t *speech_data;
  63.     uint8_t *ptr;
  64.  
  65.     if (st->codec->codec_id == AV_CODEC_ID_AMR_NB) {
  66.         frame_sizes = frame_sizes_nb;
  67.     } else if (st->codec->codec_id == AV_CODEC_ID_AMR_WB) {
  68.         frame_sizes = frame_sizes_wb;
  69.     } else {
  70.         av_log(ctx, AV_LOG_ERROR, "Bad codec ID\n");
  71.         return AVERROR_INVALIDDATA;
  72.     }
  73.  
  74.     if (st->codec->channels != 1) {
  75.         av_log(ctx, AV_LOG_ERROR, "Only mono AMR is supported\n");
  76.         return AVERROR_INVALIDDATA;
  77.     }
  78.     st->codec->channel_layout = AV_CH_LAYOUT_MONO;
  79.  
  80.     /* The AMR RTP packet consists of one header byte, followed
  81.      * by one TOC byte for each AMR frame in the packet, followed
  82.      * by the speech data for all the AMR frames.
  83.      *
  84.      * The header byte contains only a codec mode request, for
  85.      * requesting what kind of AMR data the sender wants to
  86.      * receive. Not used at the moment.
  87.      */
  88.  
  89.     /* Count the number of frames in the packet. The highest bit
  90.      * is set in a TOC byte if there are more frames following.
  91.      */
  92.     for (frames = 1; frames < len && (buf[frames] & 0x80); frames++) ;
  93.  
  94.     if (1 + frames >= len) {
  95.         /* We hit the end of the packet while counting frames. */
  96.         av_log(ctx, AV_LOG_ERROR, "No speech data found\n");
  97.         return AVERROR_INVALIDDATA;
  98.     }
  99.  
  100.     speech_data = buf + 1 + frames;
  101.  
  102.     /* Everything except the codec mode request byte should be output. */
  103.     if (av_new_packet(pkt, len - 1)) {
  104.         av_log(ctx, AV_LOG_ERROR, "Out of memory\n");
  105.         return AVERROR(ENOMEM);
  106.     }
  107.     pkt->stream_index = st->index;
  108.     ptr = pkt->data;
  109.  
  110.     for (i = 0; i < frames; i++) {
  111.         uint8_t toc = buf[1 + i];
  112.         int frame_size = frame_sizes[(toc >> 3) & 0x0f];
  113.  
  114.         if (speech_data + frame_size > buf + len) {
  115.             /* Too little speech data */
  116.             av_log(ctx, AV_LOG_WARNING, "Too little speech data in the RTP packet\n");
  117.             /* Set the unwritten part of the packet to zero. */
  118.             memset(ptr, 0, pkt->data + pkt->size - ptr);
  119.             pkt->size = ptr - pkt->data;
  120.             return 0;
  121.         }
  122.  
  123.         /* Extract the AMR frame mode from the TOC byte */
  124.         *ptr++ = toc & 0x7C;
  125.  
  126.         /* Copy the speech data */
  127.         memcpy(ptr, speech_data, frame_size);
  128.         speech_data += frame_size;
  129.         ptr += frame_size;
  130.     }
  131.  
  132.     if (speech_data < buf + len) {
  133.         av_log(ctx, AV_LOG_WARNING, "Too much speech data in the RTP packet?\n");
  134.         /* Set the unwritten part of the packet to zero. */
  135.         memset(ptr, 0, pkt->data + pkt->size - ptr);
  136.         pkt->size = ptr - pkt->data;
  137.     }
  138.  
  139.     return 0;
  140. }
  141.  
  142. static int amr_parse_fmtp(AVStream *stream, PayloadContext *data,
  143.                           char *attr, char *value)
  144. {
  145.     /* Some AMR SDP configurations contain "octet-align", without
  146.      * the trailing =1. Therefore, if the value is empty,
  147.      * interpret it as "1".
  148.      */
  149.     if (!strcmp(value, "")) {
  150.         av_log(NULL, AV_LOG_WARNING, "AMR fmtp attribute %s had "
  151.                                      "nonstandard empty value\n", attr);
  152.         strcpy(value, "1");
  153.     }
  154.     if (!strcmp(attr, "octet-align"))
  155.         data->octet_align = atoi(value);
  156.     else if (!strcmp(attr, "crc"))
  157.         data->crc = atoi(value);
  158.     else if (!strcmp(attr, "interleaving"))
  159.         data->interleaving = atoi(value);
  160.     else if (!strcmp(attr, "channels"))
  161.         data->channels = atoi(value);
  162.     return 0;
  163. }
  164.  
  165. static int amr_parse_sdp_line(AVFormatContext *s, int st_index,
  166.                               PayloadContext *data, const char *line)
  167. {
  168.     const char *p;
  169.     int ret;
  170.  
  171.     if (st_index < 0)
  172.         return 0;
  173.  
  174.     /* Parse an fmtp line this one:
  175.      * a=fmtp:97 octet-align=1; interleaving=0
  176.      * That is, a normal fmtp: line followed by semicolon & space
  177.      * separated key/value pairs.
  178.      */
  179.     if (av_strstart(line, "fmtp:", &p)) {
  180.         ret = ff_parse_fmtp(s->streams[st_index], data, p, amr_parse_fmtp);
  181.         if (!data->octet_align || data->crc ||
  182.             data->interleaving || data->channels != 1) {
  183.             av_log(s, AV_LOG_ERROR, "Unsupported RTP/AMR configuration!\n");
  184.             return -1;
  185.         }
  186.         return ret;
  187.     }
  188.     return 0;
  189. }
  190.  
  191. RTPDynamicProtocolHandler ff_amr_nb_dynamic_handler = {
  192.     .enc_name         = "AMR",
  193.     .codec_type       = AVMEDIA_TYPE_AUDIO,
  194.     .codec_id         = AV_CODEC_ID_AMR_NB,
  195.     .parse_sdp_a_line = amr_parse_sdp_line,
  196.     .alloc            = amr_new_context,
  197.     .free             = amr_free_context,
  198.     .parse_packet     = amr_handle_packet,
  199. };
  200.  
  201. RTPDynamicProtocolHandler ff_amr_wb_dynamic_handler = {
  202.     .enc_name         = "AMR-WB",
  203.     .codec_type       = AVMEDIA_TYPE_AUDIO,
  204.     .codec_id         = AV_CODEC_ID_AMR_WB,
  205.     .parse_sdp_a_line = amr_parse_sdp_line,
  206.     .alloc            = amr_new_context,
  207.     .free             = amr_free_context,
  208.     .parse_packet     = amr_handle_packet,
  209. };
  210.