LCOV - code coverage report
Current view: top level - filters - dec_theora.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 103 140 73.6 %
Date: 2021-04-29 23:48:07 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2000-2021
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / XIPH Theora decoder filter
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terms of the GNU Lesser General Public License as published by
      12             :  *  the Free Software Foundation; either version 2, or (at your option)
      13             :  *  any later version.
      14             :  *
      15             :  *  GPAC is distributed in the hope that it will be useful,
      16             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  *  GNU Lesser General Public License for more details.
      19             :  *
      20             :  *  You should have received a copy of the GNU Lesser General Public
      21             :  *  License along with this library; see the file COPYING.  If not, write to
      22             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23             :  *
      24             :  */
      25             : 
      26             : 
      27             : #include <gpac/filters.h>
      28             : #include <gpac/constants.h>
      29             : #include <gpac/bitstream.h>
      30             : 
      31             : #ifdef GPAC_HAS_THEORA
      32             : 
      33             : #include <theora/theora.h>
      34             : 
      35             : #if !defined(__GNUC__)
      36             : # if defined(_WIN32_WCE) || defined (WIN32)
      37             : #  pragma comment(lib, "libtheora_static")
      38             : # endif
      39             : #endif
      40             : 
      41             : typedef struct
      42             : {
      43             :         GF_FilterPid *ipid, *opid;
      44             :         theora_info ti, the_ti;
      45             :         theora_state td;
      46             :         theora_comment tc;
      47             :         ogg_packet op;
      48             : 
      49             :         u32 cfg_crc;
      50             : 
      51             :         GF_List *src_packets;
      52             :         u64 next_cts;
      53             :         Bool has_reconfigured;
      54             : } GF_TheoraDecCtx;
      55             : 
      56           1 : static GF_Err theoradec_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
      57             : {
      58             :         const GF_PropertyValue *p;
      59             :         ogg_packet oggpacket;
      60             :         GF_BitStream *bs;
      61           1 :         GF_TheoraDecCtx *ctx = gf_filter_get_udta(filter);
      62             : 
      63             : 
      64           1 :         if (is_remove) {
      65           0 :                 if (ctx->opid) {
      66           0 :                         gf_filter_pid_remove(ctx->opid);
      67           0 :                         ctx->opid = NULL;
      68             :                 }
      69           0 :                 ctx->ipid = NULL;
      70           0 :                 return GF_OK;
      71             :         }
      72           1 :         if (! gf_filter_pid_check_caps(pid))
      73             :                 return GF_NOT_SUPPORTED;
      74             : 
      75           1 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG);
      76           1 :         if (p && p->value.data.ptr && p->value.data.size) {
      77             :                 u32 ex_crc;
      78           1 :                 if (strncmp(&p->value.data.ptr[3], "theora", 6)) return GF_NON_COMPLIANT_BITSTREAM;
      79           1 :                 ex_crc = gf_crc_32(p->value.data.ptr, p->value.data.size);
      80           1 :                 if (ctx->cfg_crc == ex_crc) return GF_OK;
      81           1 :                 ctx->cfg_crc = ex_crc;
      82             :         } else {
      83           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[Theora] Reconfiguring without DSI not yet supported\n"));
      84             :                 return GF_NOT_SUPPORTED;
      85             :         }
      86           1 :         if (!ctx->opid) {
      87           1 :                 ctx->opid = gf_filter_pid_new(filter);
      88             :         }
      89             :         //copy properties at init or reconfig
      90           1 :         gf_filter_pid_copy_properties(ctx->opid, pid);
      91           1 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_RAW) );
      92             : 
      93           1 :         if (ctx->ipid) {
      94           0 :                 theora_clear(&ctx->td);
      95           0 :                 theora_info_clear(&ctx->ti);
      96           0 :                 theora_comment_clear(&ctx->tc);
      97             :         }
      98           1 :         ctx->ipid = pid;
      99           1 :         gf_filter_pid_set_framing_mode(ctx->ipid, GF_TRUE);
     100             : 
     101           1 :         oggpacket.granulepos = -1;
     102           1 :         oggpacket.b_o_s = 1;
     103           1 :         oggpacket.e_o_s = 0;
     104           1 :         oggpacket.packetno = 0;
     105             : 
     106           1 :         theora_info_init(&ctx->ti);
     107           1 :         theora_comment_init(&ctx->tc);
     108             : 
     109           1 :         bs = gf_bs_new(p->value.data.ptr, p->value.data.size, GF_BITSTREAM_READ);
     110           1 :         while (gf_bs_available(bs)) {
     111             :                 GF_Err e = GF_OK;
     112           3 :                 oggpacket.bytes = gf_bs_read_u16(bs);
     113           3 :                 oggpacket.packet = gf_malloc(sizeof(char) * oggpacket.bytes);
     114           3 :                 gf_bs_read_data(bs, oggpacket.packet, oggpacket.bytes);
     115           3 :                 if (theora_decode_header(&ctx->ti, &ctx->tc, &oggpacket) < 0 ) {
     116             :                         e = GF_NON_COMPLIANT_BITSTREAM;
     117             :                 }
     118           3 :                 gf_free(oggpacket.packet);
     119           3 :                 if (e)  {
     120           0 :                         gf_bs_del(bs);
     121           0 :                         return e;
     122             :                 }
     123             :         }
     124           1 :         theora_decode_init(&ctx->td, &ctx->ti);
     125           1 :         gf_bs_del(bs);
     126             : 
     127           1 :         return GF_OK;
     128             : }
     129             : 
     130          13 : static GF_Err theoradec_process(GF_Filter *filter)
     131             : {
     132             :         ogg_packet op;
     133             :         yuv_buffer yuv;
     134             :         u32 i, count;
     135             :         u8 *buffer;
     136             :         u8 *pYO, *pUO, *pVO;
     137             :         u8 *pYD, *pUD, *pVD;
     138          13 :         GF_TheoraDecCtx *ctx = gf_filter_get_udta(filter);
     139             :         GF_FilterPacket *pck, *dst_pck, *src_pck, *pck_ref;
     140             :         Bool is_seek;
     141             : 
     142          13 :         pck = gf_filter_pid_get_packet(ctx->ipid);
     143          13 :         if (!pck && !gf_filter_pid_is_eos(ctx->ipid))
     144             :                 return GF_OK;
     145             : 
     146             :         memset(&yuv, 0, sizeof(yuv_buffer));
     147             :         memset(&op, 0, sizeof(ogg_packet));
     148          13 :         op.granulepos = -1;
     149             : 
     150          13 :         if (pck) {
     151          12 :                 u64 cts = gf_filter_pck_get_cts(pck);
     152             :                 u32 psize;
     153          12 :                 op.packet = (char *) gf_filter_pck_get_data(pck, &psize);
     154          12 :                 op.bytes = psize;
     155             : 
     156             : 
     157             :                 //append in cts order since we get output frames in cts order
     158          12 :                 pck_ref = pck;
     159          12 :                 gf_filter_pck_ref_props(&pck_ref);
     160          12 :                 count = gf_list_count(ctx->src_packets);
     161             :                 src_pck = NULL;
     162          12 :                 for (i=0; i<count; i++) {
     163             :                         u64 acts;
     164           0 :                         src_pck = gf_list_get(ctx->src_packets, i);
     165           0 :                         acts = gf_filter_pck_get_cts(src_pck);
     166           0 :                         if (acts==cts) {
     167           0 :                                 gf_filter_pck_unref(pck_ref);
     168           0 :                                 break;
     169             :                         }
     170           0 :                         if (acts>cts) {
     171           0 :                                 gf_list_insert(ctx->src_packets, pck_ref, i);
     172           0 :                                 break;
     173             :                         }
     174             :                         src_pck = NULL;
     175             :                 }
     176          12 :                 if (!src_pck)
     177          12 :                         gf_list_add(ctx->src_packets, pck_ref);
     178             : 
     179             :         } else {
     180             :                 //weird bug in theora dec: even though we feed empty packets, we still keep getting frames !
     181           1 :                 if (!gf_list_count(ctx->src_packets)) {
     182           1 :                         gf_filter_pid_set_eos(ctx->opid);
     183           1 :                         return GF_EOS;
     184             :                 }
     185           0 :                 op.packet = NULL;
     186           0 :                 op.bytes = 0;
     187             :         }
     188             : 
     189          12 :         if (theora_decode_packetin(&ctx->td, &op) != 0) {
     190           0 :                 src_pck = gf_list_pop_front(ctx->src_packets);
     191           0 :                 gf_filter_pck_unref(src_pck);
     192           0 :                 if (pck) {
     193           0 :                         gf_filter_pid_drop_packet(ctx->ipid);
     194           0 :                         return GF_NON_COMPLIANT_BITSTREAM;
     195             :                 }
     196           0 :                 gf_filter_pid_set_eos(ctx->opid);
     197           0 :                 return GF_EOS;
     198             :         }
     199             :         //no frame
     200          12 :         if (theora_decode_YUVout(&ctx->td, &yuv) != 0) {
     201           0 :                 if (pck) {
     202           0 :                         gf_filter_pid_drop_packet(ctx->ipid);
     203           0 :                         return GF_OK;
     204             :                 }
     205           0 :                 gf_filter_pid_set_eos(ctx->opid);
     206           0 :                 return GF_EOS;
     207             :         }
     208             : 
     209          12 :         if (memcmp(&ctx->ti, &ctx->the_ti, sizeof(theora_info))) {
     210             :                 memcpy(&ctx->the_ti, &ctx->ti, sizeof(theora_info));
     211             : 
     212           1 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_WIDTH, &PROP_UINT(ctx->ti.width));
     213           1 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_HEIGHT, &PROP_UINT(ctx->ti.height));
     214           1 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE, &PROP_UINT(ctx->ti.width));
     215           1 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FPS, &PROP_FRAC_INT(ctx->ti.fps_numerator, ctx->ti.fps_denominator) );
     216           1 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PIXFMT, &PROP_UINT(GF_PIXEL_YUV) );
     217             :         }
     218             : 
     219          12 :         dst_pck = gf_filter_pck_new_alloc(ctx->opid, ctx->ti.width*ctx->ti.height * 3 / 2, &buffer);
     220          12 :         if (!dst_pck) return GF_OUT_OF_MEM;
     221             : 
     222          12 :         pYO = yuv.y;
     223          12 :         pUO = yuv.u;
     224          12 :         pVO = yuv.v;
     225          12 :         pYD = buffer;
     226          12 :         pUD = buffer + ctx->ti.width * ctx->ti.height;
     227          12 :         pVD = buffer + 5 * ctx->ti.width * ctx->ti.height / 4;
     228             : 
     229        2892 :         for (i=0; i<(u32)yuv.y_height; i++) {
     230        2880 :                 memcpy(pYD, pYO, sizeof(char) * yuv.y_width);
     231        2880 :                 pYD += ctx->ti.width;
     232        2880 :                 pYO += yuv.y_stride;
     233        2880 :                 if (i%2) continue;
     234             : 
     235        1440 :                 memcpy(pUD, pUO, sizeof(char) * yuv.uv_width);
     236        1440 :                 memcpy(pVD, pVO, sizeof(char) * yuv.uv_width);
     237        1440 :                 pUD += ctx->ti.width/2;
     238        1440 :                 pVD += ctx->ti.width/2;
     239        1440 :                 pUO += yuv.uv_stride;
     240        1440 :                 pVO += yuv.uv_stride;
     241             :         }
     242             : 
     243          12 :         src_pck = gf_list_pop_front(ctx->src_packets);
     244          12 :         if (src_pck) {
     245          12 :                 gf_filter_pck_merge_properties(src_pck, dst_pck);
     246          12 :                 is_seek = gf_filter_pck_get_seek_flag(src_pck);
     247          12 :                 ctx->next_cts = gf_filter_pck_get_cts(src_pck);
     248          12 :                 ctx->next_cts += gf_filter_pck_get_duration(src_pck);
     249          12 :                 gf_filter_pck_unref(src_pck);
     250             :         } else {
     251             :                 is_seek = 0;
     252           0 :                 gf_filter_pck_set_cts(dst_pck, ctx->next_cts);
     253             :         }
     254             : 
     255          12 :         if (!pck || !is_seek )
     256          12 :                 gf_filter_pck_send(dst_pck);
     257             :         else
     258           0 :                 gf_filter_pck_discard(dst_pck);
     259             : 
     260          12 :         if (pck) gf_filter_pid_drop_packet(ctx->ipid);
     261             :         return GF_OK;
     262             : }
     263             : 
     264           1 : static GF_Err theoradec_initialize(GF_Filter *filter)
     265             : {
     266           1 :         GF_TheoraDecCtx *ctx = gf_filter_get_udta(filter);
     267           1 :         ctx->src_packets = gf_list_new();
     268           1 :         return GF_OK;
     269             : }
     270           1 : static void theoradec_finalize(GF_Filter *filter)
     271             : {
     272           1 :         GF_TheoraDecCtx *ctx = gf_filter_get_udta(filter);
     273             : 
     274           1 :         theora_clear(&ctx->td);
     275           1 :         theora_info_clear(&ctx->ti);
     276           1 :         theora_comment_clear(&ctx->tc);
     277             : 
     278           2 :         while (gf_list_count(ctx->src_packets)) {
     279           0 :                 GF_FilterPacket *pck = gf_list_pop_back(ctx->src_packets);
     280           0 :                 gf_filter_pck_unref(pck);
     281             :         }
     282           1 :         gf_list_del(ctx->src_packets);
     283           1 : }
     284             : 
     285             : static const GF_FilterCapability TheoraDecCaps[] =
     286             : {
     287             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
     288             :         CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
     289             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_THEORA),
     290             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
     291             : };
     292             : 
     293             : GF_FilterRegister TheoraDecRegister = {
     294             :         .name = "theoradec",
     295             :         GF_FS_SET_DESCRIPTION("Theora decoder")
     296             :         GF_FS_SET_HELP("This filter decodes Theora streams through libtheora library.")
     297             :         .private_size = sizeof(GF_TheoraDecCtx),
     298             :         .priority = 1,
     299             :         SETCAPS(TheoraDecCaps),
     300             :         .initialize = theoradec_initialize,
     301             :         .finalize = theoradec_finalize,
     302             :         .configure_pid = theoradec_configure_pid,
     303             :         .process = theoradec_process,
     304             : };
     305             : 
     306             : #endif
     307             : 
     308        2877 : const GF_FilterRegister *theoradec_register(GF_FilterSession *session)
     309             : {
     310             : #ifdef GPAC_HAS_THEORA
     311        2877 :         return &TheoraDecRegister;
     312             : #else
     313             :         return NULL;
     314             : #endif
     315             : }
     316             : 

Generated by: LCOV version 1.13